מפתח
ברוכים הבאים!
קורס מקיף ללימוד Git מאפס עד רמה מתקדמת. הקורס מכסה את כל הנושאים החשובים: מיסודות, דרך branches ומיזוג, ועד workflows מקצועיים, rebase ו-pull requests.
למה Git?
- 📚 מערכת בקרת הגרסאות הפופולרית בעולם
- 🌳 עבודה במקביל על פיצ'רים בעזרת branches
- 🔄 שיתוף קוד בקלות עם GitHub, GitLab ו-Bitbucket
- ⏪ שחזור מלא של היסטוריית הפרויקט בכל רגע נתון
📚 מבנה הקורס
חלק א' - יסודות
| מספר | נושא | קובץ |
|---|---|---|
| 1 | רקע והיסטוריה | 01_background.md |
| 2 | התקנה והגדרות | 02_installation.md |
| 3 | מושגי יסוד | 03_basic_concepts.md |
חלק ב' - מחזור החיים הבסיסי
| מספר | נושא | קובץ |
|---|---|---|
| 4 | יצירת מאגר ראשון | 04_first_repo.md |
| 5 | status, add, commit | 05_status_add_commit.md |
| 6 | היסטוריה ו-log | 06_history_log.md |
| 7 | ביטול שינויים | 07_undo_changes.md |
חלק ג' - Branches ו-Merge
| מספר | נושא | קובץ |
|---|---|---|
| 8 | Branches | 08_branches.md |
| 9 | Merge | 09_merge.md |
| 10 | פתרון קונפליקטים | 10_conflicts.md |
חלק ד' - עבודה מרחוק
| מספר | נושא | קובץ |
|---|---|---|
| 11 | Remote Repositories | 11_remote.md |
| 12 | push, pull, fetch | 12_push_pull_fetch.md |
| 13 | Clone | 13_clone.md |
| 14 | gitignore | 14_gitignore.md |
חלק ה' - כלים מתקדמים
| מספר | נושא | קובץ |
|---|---|---|
| 15 | Stash | 15_stash.md |
| 16 | Tags | 16_tags.md |
| 17 | Rebase | 17_rebase.md |
| 18 | Cherry-pick | 18_cherry_pick.md |
| 19 | reset ו-revert | 19_reset_revert.md |
חלק ו' - Workflow ועבודת צוות
| מספר | נושא | קובץ |
|---|---|---|
| 20 | Workflows נפוצים | 20_workflow.md |
| 21 | Pull Requests | 21_pull_requests.md |
| 22 | שיטות עבודה מומלצות | 22_best_practices.md |
📁 תיקיית דוגמאות
| מספר | נושא | קובץ |
|---|---|---|
| 1 | יצירת מאגר ראשון | 01_first_repo.sh |
| 2 | status, add, commit | 02_status_add_commit.sh |
| 3 | היסטוריה ו-log | 03_log_history.sh |
| 4 | ביטול שינויים | 04_undo_changes.sh |
| 5 | Branches | 05_branches.sh |
| 6 | Merge | 06_merge.sh |
| 7 | פתרון קונפליקט | 07_conflict_resolution.sh |
| 8 | Remote, push, pull | 08_remote_push_pull.sh |
| 9 | gitignore | 09_gitignore_example.sh |
| 10 | Stash workflow | 10_stash_workflow.sh |
| 11 | Tags | 11_tags.sh |
| 12 | Rebase | 12_rebase.sh |
| 13 | Cherry-pick | 13_cherry_pick.sh |
| 14 | reset ו-revert | 14_reset_revert.sh |
| 15 | Workflow מלא | 15_full_workflow.sh |
🚀 איך להתחיל?
צעד 1: בדיקת התקנה
git --version
אם לא מותקן, יש להוריד מ-git-scm.com.
צעד 2: הגדרת זהות (פעם אחת בלבד)
git config --global user.name "השם שלך"
git config --global user.email "[email protected]"
git config --global init.defaultBranch main
git config --global core.editor "code --wait"
צעד 3: יצירת מאגר ראשון
mkdir my-project
cd my-project
git init
echo "# My Project" > index.md
git add index.md
git commit -m "Initial commit"
📖 סדר לימוד מומלץ
- שבוע 1: יסודות + מחזור חיים בסיסי (פרקים 1-7)
- שבוע 2: Branches, Merge וקונפליקטים (פרקים 8-10)
- שבוע 3: עבודה מול remote ו-gitignore (פרקים 11-14)
- שבוע 4: כלים מתקדמים (פרקים 15-19)
- שבוע 5: Workflows, Pull Requests ו-Best Practices (פרקים 20-22)
📋 נושאים שנכללים
- ✅ מושגי יסוד: Repository, Working Directory, Staging Area, HEAD
- ✅ מחזור החיים: status, add, commit, log
- ✅ ביטול שינויים: restore, reset, revert, checkout
- ✅ ניהול branches: branch, switch, checkout
- ✅ Merge ו-Fast-forward
- ✅ פתרון קונפליקטים ידני
- ✅ עבודה עם remotes (origin, upstream)
- ✅ push, pull, fetch ו-tracking branches
- ✅ Clone ופרוטוקולים (HTTPS / SSH)
- ✅ קובץ .gitignore ותחביר
- ✅ Stash לשמירה זמנית
- ✅ Tags (lightweight ו-annotated)
- ✅ Rebase ו-interactive rebase
- ✅ Cherry-pick
- ✅ ההבדל בין reset ל-revert
- ✅ Workflows: Git Flow, GitHub Flow, Trunk-based
- ✅ Pull Requests וקוד-רוויו
- ✅ הודעות commit נכונות וקונבנציות שמות branches
🛠️ כלים מומלצים
Git ב-CLI
- Git Bash (Windows) - מובנה בהתקנה הרשמית
- Terminal / iTerm2 (macOS)
- GNOME Terminal / Konsole (Linux)
עורכי קוד עם אינטגרציה
- Visual Studio Code - אינטגרציה מובנית מצוינת
- GitLens - תוסף VS Code חזק במיוחד
ממשקים גרפיים
- GitHub Desktop - פשוט ומותאם ל-GitHub
- Sourcetree - חינמי מ-Atlassian
- GitKraken - ממשק מתקדם
בהצלחה בלימוד Git! 🎉
רקע והיסטוריה
מהו Git?
Git היא מערכת בקרת גרסאות (Version Control System - VCS) מבוזרת ופתוחת קוד. היא מאפשרת לעקוב אחרי שינויים בקבצי קוד לאורך זמן, לחזור לגרסאות קודמות, לעבוד במקביל עם מפתחים אחרים ולמזג שינויים בצורה מסודרת.
היסטוריה קצרה
- 2005 - Linus Torvalds, יוצר ליבת Linux, פיתח את Git לאחר שמערכת בקרת הגרסאות הקודמת (BitKeeper) הפסיקה להיות חינמית לפיתוח Linux
- המטרות שהוגדרו: מהירות, פשטות בעיצוב, תמיכה חזקה בעבודה לא לינארית (אלפי branches במקביל), פיזור מלא ויכולת להתמודד עם פרויקטים גדולים
- 2008 - GitHub עלתה לאוויר והפכה את Git לסטנדרט בעולם הקוד הפתוח
למה צריך מערכת בקרת גרסאות?
- היסטוריה מלאה - אפשר לראות מי שינה כל שורה ומתי
- שחזור - חזרה למצב יציב במקרה של תקלה
- שיתוף פעולה - מספר מפתחים יכולים לעבוד על אותו פרויקט בלי לדרוס אחד את השני
- ניסיונות בטוחים - אפשר ליצור branch לניסוי פיצ'ר חדש בלי לפגוע ב-main
- גיבוי - הקוד נשמר גם אצל המפתחים וגם בשרת מרוחק
Git לעומת מערכות אחרות
| תכונה | Git | SVN (Subversion) |
|---|---|---|
| ארכיטקטורה | מבוזר (כל מפתח מחזיק עותק מלא) | מרכזי (שרת אחד) |
| מהירות | מהיר מאוד (פעולות מקומיות) | איטי יותר (תלוי ברשת) |
| Branches | זול ופשוט - אלפי branches זה נורמלי | יקר ומורכב |
| עבודה offline | אפשרית באופן מלא | מוגבלת מאוד |
| Snapshot vs Diff | שומר snapshots של הפרויקט | שומר diffs בלבד |
מושג ה-DVCS (Distributed VCS)
ב-Git, כל מפתח מחזיק עותק מלא של ההיסטוריה. אין שרת "אמת" - יש שרת מרכזי (כמו GitHub) רק בהסכמה. המשמעות:
- אפשר לעבוד גם בלי חיבור לאינטרנט
- אם השרת המרכזי נופל - לכל מפתח יש עותק מלא
- כל פעולה מקומית (commit, branch, log) היא מהירה
התקנה והגדרות
התקנה לפי מערכת הפעלה
Windows
- הורדה מהאתר הרשמי: git-scm.com
- הרצת ה-installer (להשאיר את ההגדרות בברירת המחדל לרוב המקרים)
- ההתקנה כוללת גם את Git Bash - מעטפת bash מלאה ל-Windows
macOS
# דרך Homebrew (מומלץ)
brew install git
# או דרך Xcode Command Line Tools
xcode-select --install
Linux (Debian / Ubuntu)
sudo apt update
sudo apt install git
Linux (Fedora / RHEL)
sudo dnf install git
בדיקת ההתקנה
git --version
הפלט אמור להיות משהו כמו git version 2.43.0.
הגדרה ראשונית (פעם אחת בחיים)
לפני שמתחילים להשתמש ב-Git, חובה להגדיר זהות. הזהות הזו תופיע בכל commit שתבצע:
git config --global user.name "Nafi Shvinger"
git config --global user.email "[email protected]"
הגדרות מומלצות נוספות
# הגדרת VS Code כעורך ברירת המחדל
git config --global core.editor "code --wait"
# שם ה-branch הראשי החדש יהיה main במקום master
git config --global init.defaultBranch main
# טיפול נכון ב-line endings (חשוב ב-Windows)
git config --global core.autocrlf true # Windows
git config --global core.autocrlf input # macOS / Linux
# צבעים בפלט של git
git config --global color.ui auto
בדיקת הגדרות
# הצגת כל ההגדרות
git config --list
# הצגת הגדרה ספציפית
git config user.name
git config user.email
# מציאת מיקום קובץ ההגדרות
git config --list --show-origin
רמות הגדרה
ל-Git שלוש רמות של הגדרות, מהכללית לפרטנית:
| רמה | דגל | מיקום הקובץ | תחולה |
|---|---|---|---|
| System | --system |
/etc/gitconfig |
כל המשתמשים במחשב |
| Global | --global |
~/.gitconfig |
משתמש מסוים |
| Local | --local |
.git/config |
מאגר ספציפי |
הרמה הפרטנית יותר מנצחת. למשל, אפשר להגדיר אימייל אחר רק עבור פרויקט אחד:
cd my-work-project
git config --local user.email "[email protected]"
מושגי יסוד
לפני שמתחילים לעבוד עם פקודות, חשוב להבין את המושגים המרכזיים. כל פקודת Git מבוססת עליהם.
Repository (מאגר)
מאגר Git הוא תיקייה שמכילה את הפרויקט יחד עם תיקיית .git/ נסתרת. תיקיית .git/ היא ה"מוח" של המאגר - היא מכילה את כל ההיסטוריה, ההגדרות, ה-branches וה-commits.
my-project/
├── .git/ ← תיקיית הניהול של git
├── src/
├── index.md
└── package.json
אם תמחק את .git/ - איבדת את כל ההיסטוריה והפרויקט חוזר להיות תיקייה רגילה.
Working Directory (תיקיית העבודה)
הקבצים שאתה רואה ועורך בעורך הקוד. זה המצב "החי" של הפרויקט.
Staging Area (Index)
אזור ביניים בין תיקיית העבודה לבין ההיסטוריה. כאן אתה "מסמן" אילו שינויים יכנסו ל-commit הבא. השלב הזה מאפשר לבצע commits מדויקים שמכילים רק את מה שרלוונטי.
Commit
צילום מצב (snapshot) של הפרויקט בנקודת זמן מסוימת. כל commit כולל:
- כל השינויים מהפעם הקודמת
- מחבר ותאריך
- הודעה (message)
- מזהה SHA-1 ייחודי (לדוגמה:
a3f5c2d) - הצבעה ל-commit ההורה
Branch
מצביע ל-commit מסוים. כשמוסיפים commit חדש, ה-branch מתקדם איתו. branches מאפשרים פיתוח מקביל - כל פיצ'ר ב-branch משלו, בלי להפריע ל-main.
HEAD
מצביע מיוחד שמראה איפה אתה נמצא כרגע - בדרך כלל ה-branch הפעיל וה-commit האחרון בו.
תרשים זרימה: ארבעת המצבים של קובץ
[Working Directory] → git add → [Staging Area] → git commit → [Repository]
↓
git push
↓
[Remote Repository]
Tracked vs Untracked
| מצב | תיאור |
|---|---|
| Untracked | קובץ חדש ש-git עוד לא יודע על קיומו |
| Tracked - Unmodified | קובץ ש-git מכיר ולא השתנה מאז ה-commit האחרון |
| Tracked - Modified | קובץ ש-git מכיר והשתנה אבל עוד לא ב-staging |
| Staged | קובץ ש-git מכיר, השתנה והוסף ל-staging |
דוגמה זרימה מלאה
- יצרת קובץ חדש
app.js- הוא Untracked - ביצעת
git add app.js- הוא עבר ל-Staged - ביצעת
git commit -m "..."- הוא עכשיו Tracked - Unmodified - ערכת אותו - הוא Tracked - Modified
git add app.js- שוב Stagedgit commit- וחוזר חלילה
יצירת מאגר ראשון
ישנן שתי דרכים להתחיל לעבוד עם git: ליצור מאגר חדש מאפס, או לשכפל מאגר קיים מ-GitHub.
יצירת מאגר חדש - git init
# יצירת תיקייה חדשה והפיכתה למאגר
mkdir my-project
cd my-project
git init
הפלט יהיה משהו כמו:
Initialized empty Git repository in /path/to/my-project/.git/
מאותו רגע, תיקיית my-project היא מאגר git.
יצירה ישירה עם שם
git init my-project
cd my-project
הפקודה תיצור גם את התיקייה וגם תאתחל אותה.
שכפול מאגר קיים - git clone
לוקח מאגר קיים (לרוב מ-GitHub) ויוצר עותק מקומי שלו:
# שכפול עם HTTPS
git clone https://github.com/user/repo.git
# שכפול לתיקייה בעלת שם מותאם
git clone https://github.com/user/repo.git my-folder
# שכפול עם SSH (דורש מפתח SSH)
git clone [email protected]:user/repo.git
git clone עושה כמה דברים בו-זמנית:
1. יוצר תיקייה מקומית
2. מעתיק את כל ההיסטוריה
3. יוצר remote בשם origin שמצביע למקור
4. עושה checkout ל-branch הראשי
מבנה תיקיית .git/
לאחר git init או git clone, תיווצר תיקייה נסתרת בשם .git/:
.git/
├── HEAD ← מצביע ל-branch הנוכחי
├── config ← הגדרות מקומיות של המאגר
├── description ← תיאור (משמש בעיקר ל-GitWeb)
├── hooks/ ← scripts אוטומטיים
├── info/ ← מידע נוסף (כמו exclude)
├── objects/ ← כל ה-commits, trees ו-blobs
├── refs/ ← המצביעים של ה-branches וה-tags
└── logs/ ← היסטוריית שינויים של refs
⚠️ אזהרה: אסור לערוך ידנית את הקבצים בתיקיית
.git/. אם משהו ישתבש - עלולים לאבד את כל ההיסטוריה.
בדיקה שהמאגר אותחל
# הצגת ה-branch הנוכחי וסטטוס הקבצים
git status
# במאגר ריק תקבל:
# On branch main
# No commits yet
# nothing to commit
ה-commit הראשון
# יצירת קובץ
echo "# My Project" > index.md
# הוספה ל-staging
git add index.md
# commit ראשון
git commit -m "Initial commit"
מזל טוב - יש לך מאגר עם commit ראשון.
status, add, commit
שלוש הפקודות המרכזיות במחזור החיים היומיומי של עבודה עם git.
git status
הפקודה הראשונה שתריץ אחרי כל פעולה. היא מראה לך את המצב הנוכחי של המאגר:
git status # תצוגה מפורטת
git status -s # תצוגה קצרה (short)
git status -b # כולל מידע על ה-branch
מה מציג git status?
- ה-branch הנוכחי
- קבצים שהשתנו אבל לא ב-staging (Modified)
- קבצים ב-staging מוכנים ל-commit (Staged)
- קבצים חדשים שלא מנוטרים (Untracked)
תצוגה קצרה - git status -s
M src/app.js ← קובץ שונה אבל לא ב-staging
M src/utils.js ← קובץ ב-staging
A src/new.js ← קובץ חדש שהוסף ל-staging
?? src/temp.js ← קובץ חדש שלא מנוטר
git add
מוסיף קבצים ל-staging area - מסמן אותם להיכלל ב-commit הבא.
git add file.js # קובץ ספציפי
git add file1.js file2.js # מספר קבצים
git add . # כל הקבצים בתיקייה הנוכחית ומתחתיה
git add -A # כל השינויים בכל המאגר
git add *.js # לפי תבנית (pattern)
git add src/ # תיקייה שלמה
git add -p # אינטראקטיבי - בוחר חתיכות מקובץ
-p (patch mode) - הוספה אינטראקטיבית
הדגל -p מאפשר להוסיף רק חלקים מקובץ. שימושי במיוחד כשעבדת על שני שינויים בקובץ אחד ורוצה להפריד אותם ל-2 commits נפרדים.
git commit
יוצר commit חדש מתוך הקבצים שנמצאים ב-staging.
git commit -m "Add login form" # commit עם הודעה
git commit # פותח עורך לכתיבת הודעה ארוכה
git commit -am "Fix typo in header" # add + commit לכל הקבצים tracked
git commit --amend # עריכת ה-commit האחרון
git commit --amend --no-edit # הוספה ל-commit האחרון בלי לשנות הודעה
הודעות commit טובות
- כותבים בזמן ציווי (Imperative):
Add login form, לאAdded login form - כותרת קצרה (עד 50 תווים), אם צריך - שורה ריקה ואז תיאור מפורט
- מתארים למה השינוי, לא רק מה השתנה
- אנגלית (קונבנציה רווחת בעולם הקוד הפתוח)
דוגמאות טובות:
- Fix EditAbout save flow: validate before API calls
- Extract useIsMentor hook and replace duplicated logic
- Stabilize DojoHomeScreen layout by memoizing photos
דוגמאות רעות:
- update
- fixed bug
- changes
הסרת קבצים
git rm file.js # מחיקה מהדיסק + staging
git rm --cached file.js # הסרה מ-git, השארה בדיסק
git rm -r folder/ # מחיקת תיקייה רקורסיבית
--cached שימושי כשהוספת בטעות קובץ שאמור להיות ב-.gitignore (כמו .env).
שינוי שם / העברת קבצים
git mv old-name.js new-name.js
git mv src/file.js lib/file.js
זה שווה ערך ל:
mv old-name.js new-name.js
git add new-name.js
git rm old-name.js
דוגמת זרימה מלאה
# 1. בודקים מה השתנה
git status
# 2. רואים שיש 3 קבצים שהשתנו
# 3. מוסיפים רק 2 מהם ל-staging
git add src/login.js src/styles.css
# 4. בודקים שוב
git status
# 5. עושים commit
git commit -m "Add login form with basic styling"
# 6. הקובץ השלישי עוד מחכה - ממשיכים לעבוד עליו
היסטוריה ו-log
אחרי שמתחילים לעבוד עם git, ההיסטוריה הולכת ומתעבה. הפקודות בקובץ הזה עוזרות לחקור את ההיסטוריה הזו: מה השתנה, מתי, על ידי מי ולמה.
git log - צפייה בהיסטוריה
git log # היסטוריה מלאה
git log --oneline # שורה אחת לכל commit
git log --graph # ייצוג גרפי של branches
git log --all # מכל ה-branches
git log -n 5 # 5 commits אחרונים
git log -p # עם ה-diff המלא של כל commit
git log --stat # סיכום שינויים (כמה שורות בכל קובץ)
הצירוף הפופולרי
git log --all --decorate --oneline --graph
נותן תצוגה יפה של כל ה-branches יחד. אפשר לקצר ל-git log --adog.
סינון לפי קריטריונים
git log --since="2 weeks" # לפי תאריך
git log --until="2025-01-01"
git log --author="Nafi" # לפי מחבר
git log --grep="login" # חיפוש בהודעות commit
git log file.js # היסטוריה של קובץ ספציפי
git log -p file.js # עם diff מלא לקובץ
git log -S "functionName" # חיפוש מתי שורה הוספה / נמחקה
git show - מידע על commit ספציפי
git show # ה-commit האחרון (HEAD)
git show HEAD # אותו דבר
git show HEAD~1 # commit אחד אחורה
git show HEAD~3 # 3 commits אחורה
git show a3f5c2d # לפי SHA חלקי
git show main # ה-commit שאליו main מצביע
הפלט מציג: - מי המחבר ומתי - הודעת ה-commit - ה-diff המלא של מה שהשתנה
git diff - השוואות
git diff # working directory מול staging
git diff --staged # staging מול ה-commit האחרון
git diff --cached # זהה ל---staged
git diff HEAD # working directory מול ה-commit האחרון
git diff main feature/login # השוואה בין branches
git diff a3f5c2d b8e7f1a # השוואה בין שני commits
git diff HEAD~3 HEAD # השוואה בין נקודות בהיסטוריה
git diff main -- file.js # השוואה של קובץ ספציפי בלבד
git blame - מי שינה כל שורה
git blame file.js
git blame -L 10,20 file.js # רק שורות 10-20
הפלט מציג ליד כל שורה את ה-SHA, המחבר והתאריך של ה-commit שהוסיף את השורה. שימושי כשרוצים להבין למה קוד מסוים נכתב.
git grep - חיפוש בקבצי המאגר
git grep "TODO" # חיפוש בכל הקבצים tracked
git grep -n "TODO" # עם מספרי שורה
git grep "useState" -- "*.jsx" # רק בקבצי jsx
git grep "useState" HEAD~3 # חיפוש בנקודה היסטורית
reflog - ההצלה האחרונה
git reflog
reflog מציג את כל הפעולות שעשית ב-HEAD - גם פעולות שהיו "destructive" (כמו git reset --hard). אם בטעות מחקת commits, אתה כמעט תמיד יכול לשחזר אותם דרך reflog:
git reflog
# ...
# a3f5c2d HEAD@{2}: commit: Add important feature
# ...
git reset --hard a3f5c2d
דוגמאות מעשיות
"מי הוסיף את השורה הזו ולמה?"
git blame -L 50,55 src/auth.js
# מוצאים SHA, ואז:
git show a3f5c2d
"איפה ההודעה 'fix login' הופיעה?"
git log --grep="fix login" --oneline
"מה השתנה השבוע?"
git log --since="1 week ago" --oneline --author="Nafi"
ביטול שינויים
אחת היכולות החשובות ביותר ב-git: לחזור אחורה. הקובץ הזה מכסה איך לבטל שינויים בכל אחת מהרמות - working directory, staging area ו-commits.
ביטול שינויים ב-Working Directory
מצב: ערכת קובץ אבל לא הוספת אותו ל-staging, ואתה רוצה להחזיר אותו למצב המקורי.
git restore file.js # ביטול שינויים בקובץ ספציפי
git restore . # ביטול כל השינויים בתיקייה הנוכחית
git checkout -- file.js # תחביר ישן (עדיין עובד)
⚠️ אזהרה: הפעולה הזו מוחקת את השינויים שלא נשמרו - בלי אפשרות שחזור.
הסרה מ-Staging Area
מצב: עשית git add בטעות לקובץ שלא רצית לכלול ב-commit הבא.
git restore --staged file.js # מסיר מ-staging, השינויים נשארים בקובץ
git reset HEAD file.js # תחביר ישן
הקובץ חוזר למצב Modified - השינויים בו נשמרים, אבל הוא לא ב-staging.
ביטול commits
יש שתי גישות עיקריות לביטול commits: reset ו-revert. ההבדל ביניהן קריטי.
git reset - מחיקת commits מההיסטוריה
reset משנה היסטוריה - "מחזיר את הזמן אחורה". בא בשלוש רמות:
git reset --soft HEAD~1 # מבטל commit, השינויים נשארים ב-staging
git reset --mixed HEAD~1 # מבטל commit + staging (ברירת מחדל)
git reset --hard HEAD~1 # מבטל commit + staging + working directory
| דגל | commit | staging | working dir |
|---|---|---|---|
--soft |
מתבטל | נשאר | נשאר |
--mixed |
מתבטל | מתבטל | נשאר |
--hard |
מתבטל | מתבטל | מתבטל |
⚠️
--hardהוא הרסני: הוא מוחק את כל השינויים שלא נשמרו ב-commits. השתמש רק כשאתה בטוח לחלוטין.
דוגמאות שימוש
# "ביטלתי את ה-commit האחרון אבל אני רוצה לערוך אותו"
git reset --soft HEAD~1
# עכשיו השינויים ב-staging - ערוך וקצור commit חדש
# "סיימתי עם ה-commit הזה, אני רוצה למחוק אותו לגמרי"
git reset --hard HEAD~1
git revert - יצירת commit הפוך
revert לא משנה היסטוריה - הוא יוצר commit חדש שמבטל commit קיים. זו הדרך הבטוחה לבטל commit שכבר נשלח לשרת המרוחק (כי הוא לא דורס היסטוריה).
git revert a3f5c2d # יוצר commit חדש שמבטל את a3f5c2d
git revert HEAD # מבטל את ה-commit האחרון
git revert HEAD~1 # מבטל את ה-commit הקודם (לא האחרון)
reset לעומת revert - מתי להשתמש במה?
| מצב | להשתמש ב |
|---|---|
| ה-commit מקומי, לא נשלח לשרת | reset |
| ה-commit כבר נשלח לשרת המרוחק | revert |
| רוצים למחוק את ההיסטוריה | reset --hard |
| רוצים לבטל את האפקט אבל לשמור היסטוריה | revert |
🚫 כלל ברזל: אם ה-commit כבר נשלח לשרת ומישהו אחר משתמש ב-branch הזה - לעולם אל תעשה
reset. השתמש ב-revert.
שחזור קובץ מ-commit ישן
git checkout HEAD -- file.js # שחזור קובץ למצבו ב-HEAD
git checkout HEAD~3 -- file.js # שחזור קובץ למצבו לפני 3 commits
git restore --source=HEAD~3 file.js # תחביר חדש
שחזור commit "אבוד" עם reflog
הזכרנו ב-06_history_log.md שיש את git reflog שמציג את כל הפעולות שעשית. אם בטעות עשית reset --hard ואיבדת commits:
git reflog
# מחפשים את ה-SHA של ה-commit האבוד
git reset --hard a3f5c2d
זו הדרך לחזור גם מטעויות שנראות בלתי הפיכות.
תיקון ה-commit האחרון
מצב: עשית commit ושכחת להוסיף קובץ, או טעית בהודעה.
# הוספת קבצים שנשכחו ל-commit האחרון
git add forgotten-file.js
git commit --amend --no-edit
# שינוי הודעת ה-commit האחרון
git commit --amend -m "Better message"
⚠️
--amendמשנה את ה-SHA של ה-commit. אם כבר עשית push - תצטרךforce pushשזה מסוכן ל-branches משותפים.
Branches
מהו branch?
Branch הוא מצביע ל-commit מסוים. כשמוסיפים commit חדש - ה-branch מתקדם איתו אוטומטית. branches הם הלב של git: הם מאפשרים לך לעבוד על מספר פיצ'רים במקביל בלי שאחד יפריע לשני.
ה-branch הראשי
כשיוצרים מאגר חדש, נוצר אוטומטית branch ראשי. בעבר נקרא master, היום נקרא main. זהו ה-branch שמייצג את הקוד היציב.
git config --global init.defaultBranch main
פקודות בסיסיות
צפייה ב-branches
git branch # רשימת branches מקומיים
git branch -a # כל ה-branches כולל remote
git branch -r # רק remote branches
git branch -v # עם ה-commit האחרון של כל branch
ה-branch הנוכחי מסומן בכוכבית *.
יצירת branch
git branch feature/login # יצירה בלי מעבר
git branch feature/login HEAD~3 # יצירה מ-commit ספציפי
מעבר ל-branch
git checkout feature/login # תחביר ישן
git switch feature/login # תחביר חדש (מומלץ)
יצירה + מעבר בו-זמנית
git checkout -b feature/login # תחביר ישן
git switch -c feature/login # תחביר חדש
מחיקה
git branch -d feature/login # מחיקה בטוחה (רק אם הקוד מוזג)
git branch -D feature/login # מחיקה כפויה
שינוי שם
git branch -m old-name new-name # branch אחר
git branch -m new-name # ה-branch הנוכחי
checkout מול switch
git switch נוסף ב-Git 2.23 (2019) כדי להפריד בין שני שימושים שונים של checkout:
| פקודה | תפקיד |
|---|---|
git switch |
מעבר בין branches |
git restore |
שחזור קבצים |
git checkout |
עושה את שניהם (תחביר ישן, עדיין נתמך) |
דוגמת זרימה - פיצ'ר חדש
# 1. עוברים ל-main ומסנכרנים
git switch main
git pull
# 2. יוצרים branch חדש לפיצ'ר
git switch -c feature/user-profile
# 3. עובדים ועושים commits
# ...עריכות...
git add .
git commit -m "Add user profile component"
# ...עוד עריכות...
git add .
git commit -m "Add avatar upload to user profile"
# 4. דוחפים לשרת
git push -u origin feature/user-profile
# 5. אחרי שה-PR התקבל ומוזג, מנקים
git switch main
git pull
git branch -d feature/user-profile
מודל מנטלי
חשבו על branches ככבישים מקבילים. ה-main הוא הכביש הראשי. כשמתחילים לעבוד על פיצ'ר חדש, יוצרים יציאה הצידה - branch חדש - שמתחיל מאותה נקודה. עובדים עליו, מוסיפים commits, ובסוף "חוזרים" ל-main דרך merge.
main: A───B───C─────────────M
\ /
feature: D───E───F
ב-A, B, C ו-M ה-main התקדם. ה-feature התחיל מ-C (יציאה), הוסיף D, E ו-F, ובסוף התמזג ב-M.
מינוח קונבנציונלי
feature/login- פיצ'ר חדשbugfix/header-alignment- תיקון באגhotfix/critical-security-issue- תיקון דחוף ב-productionrelease/v2.0- הכנה לשחרור גרסהexperiment/new-architecture- ניסוי
סיכום
- branches זולים ב-git - אל תהססו ליצור אותם
- branch אחד = פיצ'ר אחד = PR אחד
- מוחקים branches שהושלמו - השאירו את הרשימה נקייה
Merge
Merge הוא הפעולה שמשלבת שינויים מ-branch אחד אל branch אחר. זוהי הדרך העיקרית להחזיר עבודה מ-feature branch אל main.
מבנה הפקודה
git switch main # קודם עוברים ל-branch היעד
git merge feature/login # ואז מבקשים למזג לתוכו את feature/login
חשוב לזכור: git merge X ממזג את X לתוך ה-branch הנוכחי, לא הפוך.
שני סוגי merge
1. Fast-forward merge
קורה כשה-branch היעד לא קיבל commits חדשים מאז שה-feature התפצל ממנו. במקרה הזה, git פשוט "מזיז" את המצביע של main קדימה - בלי commit חדש של merge.
לפני:
main: A───B───C
\
feature: D───E
אחרי merge:
main: A───B───C───D───E
(feature)
2. Three-way merge
קורה כשגם ה-feature וגם ה-main קיבלו commits חדשים מאז הפיצול. git יוצר commit מיוחד שנקרא "merge commit" שיש לו שני הורים.
לפני:
main: A───B───C───F───G
\
feature: D───E
אחרי merge:
main: A───B───C───F───G───M
\ /
feature: D───E───
M הוא ה-merge commit. יש לו שני הורים: G (מ-main) ו-E (מ-feature).
דגלים שימושיים
git merge feature/login # merge רגיל (fast-forward אם אפשר)
git merge --no-ff feature/login # יוצר תמיד merge commit, גם ב-fast-forward
git merge --ff-only feature/login # רק fast-forward, אחרת fail
git merge --squash feature/login # מאחד את כל ה-commits ל-commit אחד
git merge --abort # ביטול merge בעיצומו (בעת קונפליקט)
--no-ff
הדגל --no-ff יוצר תמיד merge commit, גם כשאפשר היה לעשות fast-forward. היתרון: ההיסטוריה משמרת את הקבוצה של ה-commits שהיו ב-feature. ארגונים רבים מעדיפים את הסגנון הזה כי הוא מתעד שהיה כאן פיצ'ר נפרד.
--squash
מאחד את כל ה-commits של ה-feature ל-commit אחד. שימושי כשעבדת על feature עם 20 commits מבולגנים ואתה רוצה שב-main זה ייראה כ-commit נקי אחד.
git switch main
git merge --squash feature/messy-feature
git commit -m "Add login form"
דוגמת זרימה מלאה
# 1. סיימת את הפיצ'ר ב-feature/login
git switch feature/login
git status # נקי?
# 2. מסנכרנים את main לפני המיזוג
git switch main
git pull
# 3. ממזגים את feature/login לתוך main
git merge feature/login
# 4. דוחפים לשרת
git push
# 5. מוחקים את ה-feature branch
git branch -d feature/login
git push origin --delete feature/login
מה קורה כשיש קונפליקט?
אם git לא מצליח למזג אוטומטית - יש קונפליקט. הקובץ הבא (10_conflicts.md) מסביר איך להתמודד עם זה.
תקציר: עורכים את הקבצים, מסירים את הסימונים <<<<<<<, עושים git add ו-git commit. אם רוצים לבטל את כל ה-merge: git merge --abort.
merge מול rebase
יש דרך נוספת לשלב שינויים: rebase. ההבדל המרכזי:
- merge - שומר את כל ההיסטוריה כפי שהיתה, יוצר merge commit
- rebase - "משכתב" את ההיסטוריה כך שתהיה לינארית, בלי merge commits
נדבר על rebase בקובץ נפרד (17_rebase.md).
פתרון קונפליקטים
קונפליקט (conflict) קורה כאשר git לא מצליח למזג אוטומטית בין שני branches - בדרך כלל כי שניהם שינו את אותן שורות באותו קובץ. כל מי שעובד עם git ייתקל בזה - וזה לא מפחיד אחרי שמבינים את התהליך.
מתי קורה קונפליקט?
- בעת
git merge- שני branches שינו את אותו קוד - בעת
git rebase- דומה, על בסיס שונה - בעת
git pull- ה-main המקומי שלך וה-main של השרת התקדמו אחד מהשני - בעת
git cherry-pick- ה-commit שאתה מעתיק נוגע באותו קוד
איך נראה קובץ עם קונפליקט?
git מסמן את הקונפליקט בתוך הקובץ עם סימנים מיוחדים:
function greet(name) {
<<<<<<< HEAD
return `Hello, ${name}!`;
=======
return `Hi there, ${name}!`;
>>>>>>> feature/friendly-greeting
}
הסימנים אומרים:
- <<<<<<< HEAD - מתחיל הצד שלך (ה-branch הנוכחי, בדרך כלל main)
- ======= - מפריד בין שתי הגרסאות
- >>>>>>> feature/friendly-greeting - מסיים את הצד הנכנס (ה-branch השני)
תהליך פתרון - שלב אחר שלב
1. זיהוי הקבצים בקונפליקט
git status
תראה משהו כמו:
both modified: src/greet.js
both modified: src/utils.js
2. עריכת הקבצים
פותחים כל קובץ בעורך. שלוש אפשרויות:
א. לבחור גרסה אחת
function greet(name) {
return `Hi there, ${name}!`;
}
ב. לשלב את שתיהן
function greet(name) {
return `Hi ${name}, hello!`;
}
ג. לכתוב משהו חדש לגמרי שמתחשב בשני הצדדים
חשוב: למחוק לחלוטין את הסימנים <<<<<<<, =======, >>>>>>>.
3. סימון כפתור
git add src/greet.js
git add src/utils.js
(או git add . אם פתרת את כולם)
4. סיום ה-merge
git commit
git יפתח עורך עם הודעה אוטומטית. אפשר להשאיר את ההודעה הזו ולשמור.
5. בדיקה שהכל תקין
git status
git log --oneline -5
דוגמה מלאה
# התחלנו merge
git merge feature/new-design
# קיבלנו:
# Auto-merging src/Header.jsx
# CONFLICT (content): Merge conflict in src/Header.jsx
# Automatic merge failed; fix conflicts and then commit the result.
# בודקים מה במצב קונפליקט
git status
# פותחים את src/Header.jsx, מתקנים, שומרים
# (עורכים ידנית)
# מסמנים כפתור
git add src/Header.jsx
# ממשיכים את ה-merge
git commit
ביטול merge בעיצומו
אם החלטת שאתה לא רוצה להמשיך עם ה-merge:
git merge --abort
הפקודה תחזיר את הכל למצב שלפני שהתחלת את ה-merge.
עבור rebase:
git rebase --abort
כלים שמקלים על פתרון קונפליקטים
VS Code
VS Code מציג את הקונפליקטים בצורה ויזואלית עם כפתורים: "Accept Current Change", "Accept Incoming Change", "Accept Both Changes", "Compare Changes". שימוש מאוד פשוט.
Mergetool
git mergetool
פותח את ה-mergetool שהגדרת (vimdiff, meld, kdiff3 וכו').
טיפים למניעת קונפליקטים
- סנכרון תכוף:
git pullאוgit fetch && git mergeכל יום - branches קצרים: ככל שה-feature חי יותר זמן, יותר קונפליקטים יצטברו
- תיאום עם הצוות: אם יודעים שמישהו עובד על אותו קובץ - מדברים
- commits אטומיים: שינויים קטנים וממוקדים קל יותר למזג
מה לא לעשות בקונפליקט
- ❌ אל תמחק את כל הקובץ ותכתוב מחדש - אתה עלול לאבד שינויים חשובים
- ❌ אל תשאיר את הסימנים
<<<<<<<בקוד - הקוד לא יקומפל - ❌ אל תעשה
git checkout --על קובץ שיש בו קונפליקט - תאבד עבודה - ❌ אל תפניק - קונפליקטים זה חלק רגיל מהעבודה
Remote Repositories
Remote הוא מאגר git שנמצא בשרת מרוחק (לרוב GitHub, GitLab או Bitbucket). הוא מאפשר לכל חברי הצוות לעבוד על אותו פרויקט ולשתף את השינויים ביניהם.
מהו remote?
באופן טכני, remote הוא רק קיצור (alias) לכתובת URL של מאגר אחר. כשמשכפלים מאגר עם git clone, נוצר אוטומטית remote בשם origin שמצביע על הכתובת ממנה שכפלת.
פקודות בסיסיות
צפייה ב-remotes
git remote # רשימת השמות
git remote -v # רשימה עם הכתובות (verbose)
git remote show origin # מידע מפורט על remote ספציפי
הפלט של -v:
origin https://github.com/user/repo.git (fetch)
origin https://github.com/user/repo.git (push)
הוספת remote
git remote add origin https://github.com/user/repo.git
git remote add upstream https://github.com/original-owner/repo.git
הקוד הראשון מוסיף remote בשם origin. הקוד השני מוסיף remote נוסף בשם upstream - שימושי כשעובדים עם fork.
שינוי כתובת של remote קיים
git remote set-url origin https://github.com/user/new-repo.git
הסרה ושינוי שם
git remote remove origin
git remote rename origin upstream
שמות מקובלים
| שם | למה משמש |
|---|---|
origin |
המאגר המרכזי שלך - בדרך כלל מ-GitHub |
upstream |
המאגר המקורי, כשעובדים מ-fork |
המוסכמה: כשאתה מבצע fork ב-GitHub, אתה משכפל את ה-fork שלך - אז origin הוא ה-fork. אתה מוסיף ידנית upstream שמצביע על המאגר המקורי כדי לסנכרן ממנו עדכונים.
remote branches
remote branches הם branches שקיימים בשרת המרוחק. ב-git המקומי שלך הם מופיעים בשם <remote>/<branch>:
git branch -r # רק remote branches
# origin/main
# origin/develop
# origin/feature/login
git branch -a # מקומיים + remote
חשוב: remote branches הם read-only ב-מקומי שלך. אתה לא יכול לעבוד ישירות עליהם - אתה צריך ליצור branch מקומי שעוקב אחרי remote branch.
tracking branches
Branch מקומי יכול "לעקוב" אחרי remote branch - זה אומר ש-git pull ו-git push ידעו לאן לדחוף ומאיפה למשוך אוטומטית, בלי שתצטרך לציין בכל פעם.
# יצירת branch מקומי שעוקב אחרי remote
git checkout -b feature/login origin/feature/login
# או בתחביר חדש
git switch -c feature/login --track origin/feature/login
# קביעת tracking ל-branch קיים
git branch -u origin/feature/login
# ביטול tracking
git branch --unset-upstream
דוגמה: עבודה עם fork
# 1. עשית fork ב-GitHub והקלונת אותו
git clone https://github.com/your-username/repo.git
cd repo
# 2. הוספת את המאגר המקורי כ-upstream
git remote add upstream https://github.com/original-owner/repo.git
# 3. בודקים את הרשימה
git remote -v
# origin https://github.com/your-username/repo.git (fetch)
# origin https://github.com/your-username/repo.git (push)
# upstream https://github.com/original-owner/repo.git (fetch)
# upstream https://github.com/original-owner/repo.git (push)
# 4. סנכרון מ-upstream
git fetch upstream
git checkout main
git merge upstream/main
git push origin main
push, pull, fetch
שלוש הפקודות שמסנכרנות בין המאגר המקומי שלך לבין המאגר המרוחק.
git fetch - הורדה ללא מיזוג
fetch מוריד שינויים מהשרת אבל לא ממזג אותם לקוד שלך. הוא מעדכן רק את ה-remote branches המקומיים (origin/main למשל), בלי לגעת ב-branches המקומיים שלך.
git fetch # מ-origin (ברירת מחדל)
git fetch origin # אותו דבר במפורש
git fetch upstream # מ-remote ספציפי
git fetch --all # מכל ה-remotes
git fetch --prune # מנקה גם branches שנמחקו בשרת
מתי להשתמש?
- כשרוצים לבדוק אם יש עדכונים בלי לסכן את העבודה הנוכחית
- לפני
git mergeאוgit rebaseידני
git pull - fetch + merge
pull הוא בעצם fetch + merge בפעולה אחת. הוא מוריד שינויים מהשרת וממזג אותם מיד לתוך ה-branch המקומי שלך.
git pull # מה-tracking branch
git pull origin main # מ-branch ספציפי
git pull --rebase # fetch + rebase במקום merge
git pull --ff-only # רק אם אפשר fast-forward
pull עם rebase
git pull --rebase
הופך את ההיסטוריה לנקייה יותר. במקום ליצור merge commit, הוא לוקח את ה-commits המקומיים שלך ומיישר אותם מעל ה-commits החדשים מהשרת.
אפשר להפוך את זה להגדרה דיפולטיבית:
git config --global pull.rebase true
git push - שליחה לשרת
push שולח את ה-commits המקומיים שלך לשרת המרוחק.
git push # ל-tracking branch
git push origin main # מפורש: לאן ולאיזה branch
git push -u origin feature/login # קביעת tracking + push (פעם ראשונה)
git push --all # כל ה-branches המקומיים
git push --tags # כל ה-tags
git push origin --delete feature/login # מחיקת branch מרוחק
-u (set-upstream)
בפעם הראשונה שאתה דוחף branch חדש לשרת:
git push -u origin feature/login
הדגל -u (קיצור של --set-upstream) קובע שמעכשיו ה-branch המקומי feature/login עוקב אחרי origin/feature/login. בפעמים הבאות מספיק git push בלי פרמטרים.
push מסוכן: --force
git push --force # מסוכן! דורס היסטוריה בשרת
git push -f # אותו דבר, קצר יותר
git push --force-with-lease # בטוח יותר
--force דורס את ההיסטוריה בשרת בלי בדיקות. אם מישהו אחר דחף בינתיים - העבודה שלו תאבד.
--force-with-lease בודק קודם שמה שיש בשרת זה מה שאתה מצפה. אם מישהו אחר דחף - הפקודה נכשלת ולא עושה נזק.
🚫 כלל ברזל: לעולם אל תעשה
--forceל-mainאו ל-branches משותפים.
ההבדל בין fetch ל-pull
| fetch | pull | |
|---|---|---|
| מוריד שינויים מהשרת | ✅ | ✅ |
| משנה remote branches מקומיים | ✅ | ✅ |
| משנה את ה-branch המקומי שלך | ❌ | ✅ |
| בטוח לחלוטין | ✅ | יכול לגרום לקונפליקטים |
דוגמת זרימה יומית
# בוקר - מסנכרנים את main
git switch main
git pull
# יוצרים branch לפיצ'ר
git switch -c feature/dashboard
# עובדים, עושים commits
git add .
git commit -m "Add dashboard skeleton"
git add .
git commit -m "Wire dashboard data fetching"
# דוחפים לראשונה
git push -u origin feature/dashboard
# ממשיכים לעבוד...
git add .
git commit -m "Add dashboard charts"
# דחיפה רגילה
git push
# בסוף היום - בודקים אם יש שינויים ב-main
git fetch
git log main..origin/main # מה חדש ב-main?
טעויות נפוצות
"fatal: The current branch has no upstream"
קרה בעת git push בלי -u ב-branch חדש. הפתרון:
git push -u origin <branch-name>
"Updates were rejected because the remote contains work that you do not have"
מישהו אחר דחף בינתיים. הפתרון:
git pull --rebase
git push
Clone
git clone היא הדרך לקבל עותק מלא של מאגר git קיים אל המחשב המקומי. זו אחת מהפקודות הראשונות שכל מפתח מריץ.
הפקודה הבסיסית
git clone https://github.com/user/repo.git
הפקודה תיצור תיקייה בשם repo, תעתיק לתוכה את כל הקבצים, וכן את כל ההיסטוריה (תיקיית .git/).
שכפול לשם תיקייה אחר
git clone https://github.com/user/repo.git my-folder
הקוד יעתיק את המאגר לתיקייה my-folder במקום לתיקייה בעלת שם המאגר.
מה קורה ב-clone?
git clone מבצע שורה של פעולות:
1. יוצר תיקייה חדשה
2. מעתיק את כל ה-objects מהמאגר המרוחק
3. יוצר אוטומטית remote בשם origin שמצביע על הכתובת
4. עושה checkout ל-branch ה-default של השרת (בדרך כלל main)
5. מגדיר tracking בין ה-branch המקומי ל-origin/<branch>
פרוטוקולים
יש שלושה פרוטוקולים נפוצים לשכפול:
HTTPS
git clone https://github.com/user/repo.git
- יתרונות: עובד תמיד, אין צורך בהגדרות מקדימות
- חסרונות: דורש סיסמה / token לכל push
SSH
git clone [email protected]:user/repo.git
- יתרונות: אין סיסמה בכל פעם, מאובטח מאוד
- חסרונות: דורש הגדרת SSH key חד פעמית
git protocol
git clone git://github.com/user/repo.git
לרוב לא נמצא בשימוש - אין הצפנה ואין אימות.
איזה פרוטוקול לבחור?
לעבודה יומיומית, SSH מומלץ. ההגדרה החד פעמית שווה את החיסכון של אי הזנת סיסמה כל פעם.
הגדרת SSH ל-GitHub:
1. יצירת מפתח: ssh-keygen -t ed25519 -C "[email protected]"
2. העתקת המפתח הציבורי: cat ~/.ssh/id_ed25519.pub
3. הוספה ב-GitHub: Settings → SSH Keys → New SSH Key
4. בדיקה: ssh -T [email protected]
דגלים שימושיים
Shallow clone (חיסכון בזמן ובשטח דיסק)
git clone --depth=1 https://github.com/user/repo.git
מוריד רק את ה-commit האחרון, בלי כל ההיסטוריה. שימושי כשמדובר במאגר ענק (כמו ליבת לינוקס) ואתה רק רוצה להריץ את הקוד.
שכפול branch ספציפי
git clone --branch develop https://github.com/user/repo.git
git clone -b feature/login https://github.com/user/repo.git
מתחיל ישר ב-branch מסוים במקום ב-default.
שכפול עם submodules
git clone --recurse-submodules https://github.com/user/repo.git
אם למאגר יש submodules, הדגל הזה מוריד גם אותם.
שכפול ללא checkout
git clone --no-checkout https://github.com/user/repo.git
מוריד את ההיסטוריה אבל לא יוצר את הקבצים בתיקייה. שימושי לאוטומציה.
דוגמאות מעשיות
שכפול פרויקט לעבודה
git clone [email protected]:DojoAppOrg/dojoapp.git
cd dojoapp
npm install
שכפול פרויקט לבדיקה (shallow)
git clone --depth=1 https://github.com/facebook/react.git
cd react
# חוקרים את הקוד...
שכפול fork
# שכפול ה-fork שלך
git clone [email protected]:nafi052/repo.git
cd repo
# הוספת המאגר המקורי כ-upstream
git remote add upstream https://github.com/original/repo.git
gitignore
קובץ .gitignore מציין ל-git אילו קבצים ותיקיות לא צריך לעקוב אחריהם. זה אחד הקבצים החשובים ביותר במאגר.
למה צריך .gitignore?
- קבצים שנוצרים אוטומטית:
node_modules/,dist/,build/ - קבצים מקומיים:
.vscode/,.idea/,.DS_Store - קבצי סודות:
.env,credentials.json - קבצי לוג:
*.log,npm-debug.log - קבצי הפצה:
*.exe,*.apk
ללא .gitignore, היית מוסיף בטעות כל מיני קבצים זמניים, מנפח את המאגר ובמקרים מסוכנים - מסכן סודות.
איך משתמשים?
יוצרים קובץ בשם .gitignore בשורש המאגר (אפשר גם בתיקיות פנימיות). git יקרא אותו אוטומטית.
touch .gitignore
git add .gitignore
git commit -m "Add gitignore"
תחביר
דוגמאות בסיסיות
# הערות מתחילות עם #
# קבצים בודדים
secret.txt
.env
# כל הקבצים בעלי סיומת מסוימת
*.log
*.tmp
*.bak
# תיקיות (השם + /)
node_modules/
dist/
build/
# קבצים בתיקייה ספציפית
src/temp/
# רקורסיבי - בכל מקום במאגר
**/logs/
**/*.cache
שלילה - להחריג מתוך החרגה
*.log # מתעלם מכל קבצי log
!important.log # חוץ מ-important.log
תבניות מתקדמות
# כל קבצי .js בתוך build, אבל לא בכל מקום
build/**/*.js
# קבצים שמתחילים בנקודה
.env*
# שלילה ספציפית
!.env.example
דוגמה לפרויקט Node.js / React
# Dependencies
node_modules/
.pnp
.pnp.js
# Production build
dist/
build/
# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor / OS
.vscode/
.idea/
.DS_Store
Thumbs.db
# Testing
coverage/
# Misc
*.tgz
.cache/
דוגמה לפרויקט React Native / Expo
# Expo
.expo/
dist/
web-build/
# Native
ios/Pods/
android/.gradle/
android/app/build/
*.apk
*.aab
# Dependencies
node_modules/
# Env
.env
google-services.json
GoogleService-Info.plist
איך להתחיל .gitignore לפרויקט חדש?
האתר gitignore.io מייצר עבורך .gitignore לפי הטכנולוגיות שאתה משתמש בהן. למשל: Node, VisualStudioCode, macOS.
GitHub גם מציעה תבניות רשמיות לכל שפה.
בעיה נפוצה: קובץ שכבר tracked
אם הוספת קובץ ל-.gitignore אבל הוא כבר נמצא בהיסטוריה, git ימשיך לעקוב אחריו. הסיבה: .gitignore משפיע רק על קבצים untracked.
הפתרון: להסיר את הקובץ מ-staging (לא מהדיסק):
git rm --cached .env
git commit -m "Remove .env from tracking"
עכשיו git יתעלם מ-.env בעתיד, אבל הקובץ עדיין על הדיסק שלך.
.gitkeep - שמירת תיקייה ריקה
git לא עוקב אחרי תיקיות ריקות. אם אתה רוצה לשמור תיקייה ריקה במאגר, נהוג ליצור בה קובץ ריק בשם .gitkeep:
mkdir uploads
touch uploads/.gitkeep
git add uploads/.gitkeep
זו לא פקודת git רשמית - פשוט מוסכמה.
בדיקה: למה git מתעלם מקובץ?
git check-ignore -v file.log
הפלט יראה לך איזה כלל ב-.gitignore גורם לזה.
Stash - שמירה זמנית
git stash הוא כלי לשמירה זמנית של שינויים שעוד לא מוכנים ל-commit. הוא מאפשר לך "להזיז הצידה" את העבודה הנוכחית, לעבור ל-branch אחר או לעשות משהו דחוף, ולחזור אחר כך לשינויים.
מתי להשתמש?
- אתה באמצע פיצ'ר ופתאום צריך לתקן באג דחוף ב-main
- אתה רוצה לעשות
git pullאבל יש לך שינויים לא commited - ניסית משהו ואתה רוצה לשמור לזמן קצר בלי ליצור commit
פקודות בסיסיות
git stash # שמירת השינויים הנוכחיים
git stash save "WIP login" # עם הודעה (תחביר ישן)
git stash push -m "WIP login" # עם הודעה (תחביר חדש)
git stash -u # כולל untracked files
git stash -a # גם untracked וגם ignored
הפקודה git stash שומרת:
- שינויים בקבצים tracked
- שינויים ב-staging area
ולא שומרת (כברירת מחדל): - קבצים untracked - קבצים ignored
צפייה ב-stashes
git stash list # רשימת כל ה-stashes
# stash@{0}: WIP on main: a3f5c2d Add login form
# stash@{1}: On feature/x: WIP login
git stash show # סיכום של ה-stash האחרון
git stash show -p # diff מלא של ה-stash האחרון
git stash show stash@{1} # stash ספציפי
שחזור (apply ו-pop)
יש שתי דרכים להחזיר stash:
git stash pop # שחזור + מחיקה מהרשימה
git stash apply # שחזור בלי מחיקה
git stash apply stash@{1} # שחזור stash ספציפי
| pop | apply | |
|---|---|---|
| משחזר את השינויים | ✅ | ✅ |
| מוחק את ה-stash מהרשימה | ✅ | ❌ |
| מסוכן (אובדן עבודה אם יש קונפליקט) | יותר | פחות |
טיפ: אם אתה לא בטוח, השתמש ב-apply קודם. אם הכל עבד טוב, מחק ידנית עם git stash drop.
מחיקה
git stash drop # מחיקת ה-stash האחרון
git stash drop stash@{1} # מחיקת stash ספציפי
git stash clear # מחיקת כל ה-stashes
⚠️ אחרי
clearאין דרך פשוטה לשחזר. רק דרךgit fsckובמאמץ.
דוגמת זרימה: באג דחוף באמצע פיצ'ר
# אתה באמצע עבודה על feature/dashboard
git status
# יש שינויים לא commited
# מגיע באג דחוף ב-main
git stash push -m "WIP dashboard"
# עוברים ל-main
git switch main
git pull
# יוצרים hotfix
git switch -c hotfix/critical-bug
# ...מתקנים...
git add .
git commit -m "Fix critical login bug"
git push -u origin hotfix/critical-bug
# חוזרים לפיצ'ר
git switch feature/dashboard
git stash pop
# ממשיכים לעבוד מאיפה שהפסקת
טעויות נפוצות
"לא רואה שינויים אחרי stash"
git stash list
ודא שה-stash נשמר. אם רואה אותו ברשימה - הכל בסדר, רק תעשה git stash pop.
"stash pop גרם לקונפליקט"
זה קורה כשעבר זמן מאז ה-stash וה-branch השתנה. הפתרון:
1. פותרים את הקונפליקט כרגיל (כמו ב-merge)
2. מסמנים git add על הקבצים המתוקנים
3. אם השתמשת ב-pop והקונפליקט נפתר, ה-stash נשאר ברשימה - מחק ידנית עם git stash drop
"שכחתי על stash מלפני חודש"
git stash list
תמיד תבדוק לפני שאתה מנקה. ההיסטוריה של stashes לא מוצגת ב-git log הרגיל.
אלטרנטיבה: WIP commit
יש מי שמעדיפים להימנע מ-stash ופשוט ליצור commit זמני בשם "WIP" (Work In Progress):
git add .
git commit -m "WIP"
# ...עושים מה שצריך...
git reset --soft HEAD~1 # מבטלים את ה-WIP commit
לכל שיטה יש יתרונות. stash שומר את הרשימה נקייה, WIP commit פחות מסוכן לאיבוד.
Tags
Tag הוא תווית לcommit מסוים. בניגוד ל-branches שמתקדמים עם כל commit חדש, tag הוא קבוע - הוא תמיד מצביע על אותו commit. השימוש העיקרי: סימון גרסאות שחרור (releases) כמו v1.0.0.
שני סוגי tags
Lightweight tag
תווית פשוטה - בעצם רק שם שמצביע ל-commit, בלי מטא-דאטה נוספת.
git tag v1.0.0
git tag v1.0.0 a3f5c2d # על commit ספציפי
Annotated tag
תווית מלאה עם מטא-דאטה: שם המתייג, אימייל, תאריך, הודעה. זה הסטנדרט המקצועי.
git tag -a v1.0.0 -m "Release version 1.0.0"
git tag -a v1.0.0 a3f5c2d -m "Release version 1.0.0"
| Lightweight | Annotated | |
|---|---|---|
| מטא-דאטה (מחבר, תאריך, הודעה) | ❌ | ✅ |
| מתאים ל-releases | ❌ | ✅ |
| משמש לסימון פנימי | ✅ | ✅ |
מומלץ: השתמש תמיד ב-annotated tags ל-releases.
פקודות בסיסיות
צפייה ב-tags
git tag # רשימת כל ה-tags
git tag -l "v1.*" # סינון לפי תבנית
git show v1.0.0 # פרטי tag
יצירה
git tag v1.0.0 # lightweight
git tag -a v1.0.0 -m "Release 1.0.0" # annotated
git tag -a v1.0.0 a3f5c2d -m "..." # על commit ספציפי
מחיקה
git tag -d v1.0.0 # מחיקה מקומית
git push origin :refs/tags/v1.0.0 # מחיקה מהשרת
git push origin --delete v1.0.0 # תחביר חדש
דחיפת tags לשרת
חשוב: git push רגיל לא דוחף tags. צריך לציין במפורש.
git push origin v1.0.0 # tag ספציפי
git push origin --tags # כל ה-tags
git push --follow-tags # רק annotated tags שקשורים ל-commits שנדחפים
שמות tags - Semantic Versioning
הקונבנציה המקובלת בעולם הקוד הפתוח:
v<MAJOR>.<MINOR>.<PATCH>
- MAJOR - שינוי שבור תאימות אחורה
- MINOR - פיצ'ר חדש שלא שובר
- PATCH - תיקון באג
דוגמאות:
- v1.0.0 - גרסה ראשונה
- v1.0.1 - תיקון באג
- v1.1.0 - פיצ'ר חדש
- v2.0.0 - שינוי שובר תאימות
- v1.0.0-beta.1 - גרסת בטא
- v1.0.0-rc.1 - release candidate
דוגמת שחרור גרסה
# 1. ודא שאתה ב-main ומסונכרן
git switch main
git pull
# 2. יצירת tag annotated
git tag -a v1.2.0 -m "Release 1.2.0
- Add user profile page
- Fix login race condition
- Improve dashboard performance"
# 3. דחיפה לשרת
git push origin v1.2.0
# 4. ב-GitHub, יצירת Release מהTag (אופציונלי)
checkout ל-tag
אפשר "לבקר" ב-commit של tag:
git checkout v1.0.0
זה מעביר אותך למצב "detached HEAD" - אתה לא ב-branch כלשהו. לעבודה רגילה, צור branch חדש מ-tag:
git checkout -b hotfix/v1.0.1 v1.0.0
חיפוש tag רלוונטי
git describe # ה-tag הקרוב ביותר ל-HEAD
git describe --tags # כולל lightweight
git describe HEAD # אותו דבר במפורש
הפלט יראה משהו כמו v1.2.0-3-ga3f5c2d - אומר שאתה 3 commits אחרי tag v1.2.0.
Rebase
git rebase הוא דרך אלטרנטיבית לשלב שינויים בין branches. בניגוד ל-merge שיוצר merge commit, rebase "משכתב" את ההיסטוריה כך שתהיה לינארית ונקייה.
מה rebase עושה?
באופן עקרוני, rebase אומר: "קח את ה-commits של ה-branch הזה, ושים אותם מעל branch אחר".
לפני rebase:
main: A───B───C───F───G
\
feature: D───E
אחרי git rebase main (כשאתה ב-feature):
main: A───B───C───F───G
\
feature: D'───E'
ה-commits D ו-E קיבלו hash חדש (D' ו-E') כי ההורה שלהם השתנה. ההיסטוריה עכשיו לינארית.
מתי להשתמש?
✅ rebase מתאים כש:
- ה-branch הוא שלך בלבד ולא נדחף לשרת או לא משותף
- אתה רוצה היסטוריה לינארית נקייה לפני merge ל-main
- אתה רוצה לאחד / לערוך commits לפני PR
❌ rebase לא מתאים כש:
- ה-branch כבר משותף עם מפתחים אחרים
- ה-commits כבר נדחפו ל-shared branch
- אתה לא בטוח מה אתה עושה
🚫 כלל הזהב של rebase: לעולם אל תעשה rebase על branches משותפים שכבר נדחפו לשרת.
פקודות בסיסיות
git rebase main # rebase על main
git rebase --continue # המשך אחרי פתרון קונפליקט
git rebase --abort # ביטול ה-rebase
git rebase --skip # דילוג על commit בעייתי
דוגמת זרימה: rebase פשוט
# אתה ב-feature/login שמבוסס על main מלפני שבוע
git switch feature/login
git fetch origin
# rebase על המצב העדכני של main
git rebase origin/main
# אם יש קונפליקטים - פותרים אותם, מסמנים ב-add
git add resolved-file.js
git rebase --continue
# בסוף - דחיפה (force-with-lease, כי שינית היסטוריה)
git push --force-with-lease
Interactive rebase
הכלי החזק ביותר ב-git לעריכת היסטוריה. מאפשר לך לערוך, לאחד, למחוק או לסדר מחדש commits.
git rebase -i HEAD~5 # 5 commits אחרונים
git rebase -i main # כל ה-commits מאז שהתפצלת מ-main
git יפתח עורך עם רשימת ה-commits:
pick a3f5c2d Add login form
pick b8e7f1a Add validation
pick c9d2e3b Fix typo
pick d1f4a5b Add logout
pick e2g5b6c Fix typo again
לכל commit אפשר להחליף את pick באחת מהפעולות:
| פעולה | משמעות |
|---|---|
pick (p) |
להשתמש ב-commit כמו שהוא |
reword (r) |
לערוך את ההודעה |
edit (e) |
לעצור ולערוך את הקוד |
squash (s) |
לאחד עם ה-commit הקודם |
fixup (f) |
כמו squash, אבל בלי לערוך הודעה |
drop (d) |
למחוק את ה-commit |
reorder |
מספיק להזיז שורה |
דוגמה: איחוד שני commits ל-1
pick a3f5c2d Add login form
fixup b8e7f1a Add validation ← מאוחד עם הקודם
fixup c9d2e3b Fix typo ← מאוחד עם הקודם
pick d1f4a5b Add logout
fixup e2g5b6c Fix typo again ← מאוחד עם הקודם
אחרי שמירה - יישארו רק 2 commits נקיים.
rebase מול merge
| merge | rebase | |
|---|---|---|
| יוצר merge commit | ✅ | ❌ |
| משנה היסטוריה | ❌ | ✅ |
| היסטוריה לינארית | ❌ | ✅ |
| בטוח לbranches משותפים | ✅ | ❌ |
| משמר את "מתי באמת קרה כל commit" | ✅ | ❌ |
הבעיה של force push אחרי rebase
אחרי rebase, ה-commits המקומיים שלך לא מסתנכרנים עם השרת (כי הם קיבלו hash חדש). אתה צריך force push:
git push --force-with-lease
--force-with-lease בטוח יותר מ---force כי הוא בודק שאף אחד אחר לא דחף בינתיים.
דוגמה: rebase לפני PR
# 1. סיימת לעבוד על feature
git switch feature/dashboard
git status
# 2. רואים את ה-commits ב-branch
git log --oneline main..HEAD
# a3f5c2d Add dashboard
# b8e7f1a fix
# c9d2e3b WIP
# d1f4a5b WIP
# e2g5b6c Add dashboard charts
# 3. interactive rebase לאיחוד
git rebase -i main
# בעורך: מאחדים את ה-WIP commits ל-commit אחד נקי
# 4. דחיפה
git push --force-with-lease
Cherry-pick
git cherry-pick מעתיק commit ספציפי מ-branch אחד אל ה-branch הנוכחי. השם בא מהדימוי של "לקטוף דובדבן" - לבחור בדיוק את מה שאתה רוצה.
מתי להשתמש?
- תיקון באג ב-feature branch שצריך גם ב-main דחוף
- העברת commit אחד מ-branch ניסוי ל-branch אמיתי
- "צ'יפ" של commit מ-PR שעוד לא התקבל
- Hotfix שצריך גם ב-release branch ישן
פקודה בסיסית
git cherry-pick a3f5c2d
git ייצור commit חדש ב-branch הנוכחי שלך עם אותם השינויים של a3f5c2d. ה-SHA של ה-commit החדש יהיה שונה.
מספר commits
# commit אחד
git cherry-pick a3f5c2d
# מספר commits
git cherry-pick a3f5c2d b8e7f1a c9d2e3b
# טווח (לא כולל את הראשון)
git cherry-pick a3f5c2d..c9d2e3b
# טווח (כולל את הראשון)
git cherry-pick a3f5c2d^..c9d2e3b
דגלים שימושיים
git cherry-pick -x <commit> # מוסיף "(cherry picked from commit ...)" להודעה
git cherry-pick -e <commit> # פותח עורך לעריכת ההודעה
git cherry-pick -n <commit> # רק staging, בלי commit אוטומטי
git cherry-pick --signoff <commit> # מוסיף Signed-off-by
כשיש קונפליקט
cherry-pick יכול להיכשל אם ה-commit נוגע בקוד שכבר השתנה ב-branch הנוכחי. הזרימה זהה ל-merge:
git cherry-pick a3f5c2d
# CONFLICT (content): Merge conflict in src/login.js
# פותרים את הקונפליקט ידנית, מסמנים
git add src/login.js
# ממשיכים
git cherry-pick --continue
# או מבטלים
git cherry-pick --abort
דוגמת זרימה: hotfix ל-release ישן
# מצב: גילית באג ב-v1.0 שתיקנת ב-main
# צריך להחזיר את התיקון גם ל-release/v1.0
# 1. מוצאים את ה-commit של התיקון ב-main
git log --oneline | grep "fix login"
# a3f5c2d Fix login race condition
# 2. עוברים ל-release branch
git switch release/v1.0
# 3. cherry-pick של התיקון
git cherry-pick a3f5c2d
# 4. אם הקוד שונה - פותרים קונפליקט, ממשיכים
git add .
git cherry-pick --continue
# 5. דוחפים
git push origin release/v1.0
הבעיה עם cherry-pick
cherry-pick יוצר שני commits עם אותו תוכן אבל hash שונה. זה יכול לסבך merges עתידיים:
main: A───B───C───D───E
\
release/v1.0: F───G───D' (D' = cherry-pick של D)
אם בעתיד תנסה למזג release/v1.0 ל-main, git ינסה להוסיף את D' ל-main - שכבר יש בו את D. בדרך כלל git חכם מספיק לזהות את זה, אבל לפעמים נוצרים קונפליקטים מיותרים.
מסקנה: cherry-pick הוא כלי שימושי, אבל לא ה-default. אם אפשר לעשות merge - עדיף.
דוגמה: cherry-pick של מספר commits עוקבים
# רוצים את 3 ה-commits האחרונים מ-feature/auth
git switch main
git cherry-pick feature/auth~2..feature/auth
# זה מעתיק 3 commits: feature/auth~2, feature/auth~1, feature/auth
דוגמה: cherry-pick רק עם staging
# רוצים לקחת commit אבל לערוך אותו לפני ה-commit החדש
git cherry-pick -n a3f5c2d
# עכשיו השינויים ב-staging אבל אין commit
git status
# עורכים, מוסיפים שינויים, מבצעים commit
git commit -m "Adapted login fix from main"
reset ו-revert
שתי הפקודות הללו "מבטלות" שינויים, אבל בדרכים שונות מאוד. הבחנה בין השתיים היא קריטית.
ההבדל המרכזי
| reset | revert | |
|---|---|---|
| משנה היסטוריה | ✅ | ❌ |
| יוצר commit חדש | ❌ | ✅ |
| בטוח ל-branches משותפים | ❌ | ✅ |
| ניתן לשחזור | קשה | קל |
git reset
reset "מחזיר את הזמן אחורה" - הוא מזיז את ה-branch למצביע על commit ישן יותר. ה-commits שהיו אחריו "נעלמים" (פיזית הם עדיין במאגר, אבל לא מקושרים).
שלוש רמות
git reset --soft HEAD~1 # רק זז אחורה, השינויים נשארים ב-staging
git reset --mixed HEAD~1 # זז אחורה + מנקה staging (ברירת מחדל)
git reset --hard HEAD~1 # זז אחורה + מנקה staging + מנקה working dir
--soft
לפני:
working dir: כל השינויים
staging: כל השינויים
HEAD: commit C (אחרון)
git reset --soft HEAD~1
אחרי:
working dir: כל השינויים
staging: כל השינויים
HEAD: commit B
שימוש: ביטלת commit, השינויים מוכנים לקצור ל-commit חדש.
--mixed (ברירת מחדל)
אחרי:
working dir: כל השינויים
staging: ריק
HEAD: commit B
שימוש: ביטלת commit, השינויים בקבצים אבל צריך לבחור מחדש מה ל-stage.
--hard
אחרי:
working dir: ריק (חזר למצב של commit B)
staging: ריק
HEAD: commit B
שימוש: למחוק לחלוטין את העבודה האחרונה.
🚫
--hardהוא הרסני! השתמש רק כשאתה בטוח. אין דרך פשוטה לחזור.
דוגמאות
# ביטול 3 commits אחרונים, השארת השינויים בקבצים
git reset --mixed HEAD~3
# חזרה לגרסה ישנה של main, מוחק הכל שאחרי
git reset --hard a3f5c2d
# הסרת קובץ מ-staging (השינויים נשארים בקובץ)
git reset HEAD file.js
git revert
revert יוצר commit חדש שמבטל את האפקט של commit ישן. ההיסטוריה לא משתנה - ה-commit הישן עדיין שם, ויש אחריו commit חדש שהוא ההפך שלו.
git revert a3f5c2d # יוצר commit חדש שמבטל את a3f5c2d
git revert HEAD # מבטל את ה-commit האחרון
git revert HEAD~3..HEAD # מבטל 3 commits אחרונים
git revert -n a3f5c2d # רק staging, בלי commit אוטומטי
דוגמה
לפני revert:
main: A───B───C───D───E
git revert C
אחרי:
main: A───B───C───D───E───C' ← C' מבטל את C
C' הוא commit חדש שהשינויים שלו הם בדיוק ההפך של C.
מתי להשתמש במה?
השתמש ב-reset כש:
- ה-commit עדיין מקומי, לא נדחף לשרת
- אתה רוצה למחוק עבודה שלא רוצים לראות בהיסטוריה
- אתה צריך לחזור אחורה כדי להתחיל מחדש
השתמש ב-revert כש:
- ה-commit כבר נדחף לשרת
- ה-branch משותף עם מפתחים אחרים
- אתה רוצה לתעד שהיה כאן באג ותיקנו אותו
הזרימה המעשית
# מצב: עשית commit עם באג ועדיין לא דחפת
git reset --soft HEAD~1
# מתקנים, עושים commit חדש
# מצב: עשית commit עם באג וכבר דחפת
git revert HEAD
git push
reset לקובץ ספציפי
git reset HEAD file.js # מסיר מ-staging
git restore --staged file.js # תחביר חדש - אותו דבר
דוגמה מלאה: ביטול pull לא רצוי
# עשית git pull וקיבלת שינויים שלא רצית
git reflog
# a3f5c2d HEAD@{0}: pull: Fast-forward
# b8e7f1a HEAD@{1}: commit: My last commit before pull
# ...
# חוזרים למצב לפני ה-pull
git reset --hard b8e7f1a
אזהרה גדולה לסיום
🚫 לעולם אל תעשה
git reset --hardעל branch משותף שכבר נדחף לשרת.אם אתה צריך לבטל commits ב-branch שכבר נדחף - השתמש ב-
revert. תמיד.
Workflows נפוצים
Workflow הוא קונבנציה מוסכמת על איך הצוות עובד עם git. אין "workflow נכון אחד" - יש כמה גישות פופולריות, כל אחת מתאימה לסוג אחר של פרויקט. הקובץ הזה מציג את השלוש הנפוצות.
1. Feature Branch Workflow
הפשוט והנפוץ ביותר. מתאים לרוב הצוותים הקטנים והבינוניים.
עקרונות
mainתמיד יציב וזמין לפריסה- כל פיצ'ר מקבל branch משלו
- merge ל-main מתבצע דרך Pull Request
זרימה
# 1. סנכרון main
git switch main
git pull
# 2. branch חדש לפיצ'ר
git switch -c feature/user-profile
# 3. עבודה וcommits
git add .
git commit -m "Add user profile component"
# 4. דחיפה
git push -u origin feature/user-profile
# 5. יצירת Pull Request ב-GitHub
# 6. אחרי merge - ניקוי
git switch main
git pull
git branch -d feature/user-profile
יתרונות
- פשוט להבנה
- מתאים לכל גודל צוות
- ה-main נשאר נקי ויציב
2. Git Flow
זרימה מורכבת יותר עם branches קבועים מרובים. מתאימה לפרויקטים גדולים עם releases מתוכננים.
Branches קבועים
| Branch | תפקיד |
|---|---|
main |
קוד שנמצא ב-production |
develop |
אינטגרציה - הקוד הבא לפריסה |
Branches זמניים
| תבנית | תפקיד |
|---|---|
feature/* |
פיצ'ר חדש - מ-develop ל-develop |
release/* |
הכנה לרילוז - מ-develop ל-main + develop |
hotfix/* |
תיקון דחוף - מ-main ל-main + develop |
זרימה
# פיצ'ר חדש
git switch develop
git switch -c feature/login
# ...עבודה...
git switch develop
git merge feature/login
# הכנת release
git switch -c release/v1.2.0 develop
# ...בדיקות, תיקוני באגים קטנים...
git switch main
git merge release/v1.2.0
git tag v1.2.0
git switch develop
git merge release/v1.2.0
# Hotfix
git switch -c hotfix/critical-bug main
# ...תיקון...
git switch main
git merge hotfix/critical-bug
git tag v1.2.1
git switch develop
git merge hotfix/critical-bug
יתרונות
- מתאים ל-releases מסודרים
- הפרדה ברורה בין production ל-development
חסרונות
- מורכב
- לא מתאים לפרויקטים עם CI/CD רציף
3. GitHub Flow
הפשוט מבין השלושה. כל פיצ'ר ישר ל-main, בלי develop באמצע.
עקרונות
- רק branch אחד קבוע:
main - כל פיצ'ר ב-feature branch
- merge ל-main = פריסה ל-production מיד
זרימה
git switch main
git pull
git switch -c feature/login
# ...עבודה...
git push -u origin feature/login
# יצירת PR
# מיזוג + פריסה אוטומטית
מתי מתאים?
- פרויקטי SaaS עם CI/CD חזק
- צוותים שעושים deploy מספר פעמים ביום
- מוצרי web
4. Trunk-Based Development
קיצון של GitHub Flow - branches קצרים מאוד (שעות-יום), לפעמים בכלל ישירות ל-main מאחורי feature flags.
עקרונות
- אין branches ארוכי-חיים
- שינויים קטנים, תכופים
- שימוש ב-feature flags לכיבוי פיצ'רים שלא מוכנים
- CI חזק שמוודא ש-main תמיד יציב
מתי מתאים?
- צוותים עם תרבות CI/CD בוגרת
- צוותים גדולים שצריכים להימנע מקונפליקטים גדולים
איך לבחור?
| צוות | המלצה |
|---|---|
| 1-3 מפתחים | Feature Branch או GitHub Flow |
| 4-15 מפתחים, web product | GitHub Flow |
| צוות עם releases מסודרים | Git Flow |
| צוות גדול עם CI/CD חזק | Trunk-Based |
קונבנציות שמות branches
לא משנה איזה workflow בחרת - השמות צריכים להיות מובנים:
feature/user-profile
feature/checkout-flow
bugfix/header-alignment
bugfix/login-error-message
hotfix/security-vulnerability
release/v2.0
experiment/new-architecture
chore/update-deps
הקידומת מסבירה מיד מהו ה-branch. השם אחריה ברור ותמציתי.
Pull Requests
Pull Request (PR) - או Merge Request ב-GitLab - הוא הדרך הרשמית להצעה למזג שינויים מ-branch אחד לאחר. זה מנגנון מרכזי של עבודה צוותית: מאפשר code review, דיון, בדיקות אוטומטיות והתאמות לפני שהשינוי נכנס ל-main.
מהו PR בעצם?
טכנית, PR הוא "אני מבקש לקחת את ה-commits מ-branch X ולמזג אותם ל-branch Y". מסביב לבקשה הזו, GitHub מציע:
- צפייה ב-diff
- תגובות על שורות קוד ספציפיות
- אישורים (approvals)
- בדיקות אוטומטיות (CI)
- היסטוריה של דיונים
איך יוצרים PR?
דרך CLI עם gh
gh pr create --title "Add user profile" --body "Description here"
דרך GitHub UI
- דוחפים את ה-branch לשרת:
git push -u origin feature/user-profile - נכנסים ל-GitHub - יוצא באנר "Compare & pull request"
- ממלאים title ו-description
- בוחרים reviewers
- לוחצים "Create pull request"
כתיבת תיאור PR טוב
תיאור טוב חוסך זמן ל-reviewers. הוא עונה על השאלות: - מה השתנה? - למה? - איך לבדוק?
פורמט מומלץ
## Summary
- Add user profile component with avatar and bio
- Wire profile data fetching from /api/users/me
- Add edit button that opens modal
## Changed files
| File | Change |
|------|--------|
| `src/screens/UserProfile.jsx` | New component |
| `src/api/users.js` | Add fetchCurrentUser |
| `src/components/EditModal.jsx` | New shared modal |
## Related PR
- DojoAppOrg/dojoadmin#42 (admin side)
## Test plan
- [ ] Open profile screen, verify avatar loads
- [ ] Edit bio, save, verify it updates
- [ ] Test on iOS + Android
- [ ] Verify error state when API fails
כללים לכתיבת PR
✅ עשה
- כותרת קצרה ותמציתית (פחות מ-70 תווים)
- תיאור מסביר את הלמה
- קישור ל-issue או PR קשורים
- checklist לבדיקות
- screenshots / GIFs לשינויי UI
❌ אל תעשה
- אל תפתח PR ענק עם 50 קבצים שונים - חתוך לפרקים
- אל תכתוב "fixes stuff" - תהיה ספציפי
- אל תמזג בלי אישור (אלא אם אתה לבד)
- אל תמזג עם CI נכשל
גודל PR
PR טוב הוא קטן. ככלל אצבע: - פחות מ-400 שורות שינוי - פחות מ-10 קבצים - מטרה אחת ברורה
PR ענק קשה לעשות לו review כראוי, ולכן יקבל "LGTM" שטחי או יישכח לימים.
Code Review
כשאתה reviewer
- קרא את התיאור קודם
- קרא את ה-diff מתחילה לסוף
- שאל שאלות אם משהו לא ברור
- הצע alternative אם יש לך רעיון, אבל תן למחבר להחליט
- אישור (approve) רק אחרי שאתה מבין את כל השינוי
כשאתה מחבר
- ענה על כל תגובה (גם ב-thumbs up)
- אל תיקח ביקורת אישית
- עדכן את הקוד או הסבר למה לא
- מסמן comments כ-Resolved רק אחרי תיקון
תגובה על השינויים
GitHub מאפשר 3 סוגי תגובות:
| תגובה | מתי |
|---|---|
| Comment | הערות, שאלות, הצעות |
| Approve | הקוד מוכן למיזוג |
| Request changes | יש בעיה - חובה לתקן לפני merge |
merge strategies ב-GitHub
GitHub מציע 3 דרכים למזג PR:
| אופציה | מה קורה | מתי |
|---|---|---|
| Create a merge commit | יוצר merge commit (--no-ff) | רוצים לראות שהיה PR |
| Squash and merge | מאחד את כל ה-commits לאחד | רוצים היסטוריה נקייה |
| Rebase and merge | rebase את ה-commits על main | רוצים היסטוריה לינארית בלי merge commits |
הבחירה תלויה בקונבנציה של הצוות. הרבה ארגונים מעדיפים Squash and merge לפיצ'רים, כי זה משאיר את main עם commit אחד ברור לכל פיצ'ר.
דוגמת זרימה מלאה
# 1. עבדת על feature/login, סיימת
git add .
git commit -m "Add login form with validation"
git push -u origin feature/login
# 2. יוצרים PR
gh pr create --title "Add login form" --body "$(cat <<'EOF'
## Summary
- Add login form with email + password
- Wire to /api/auth/login
- Show error message on failure
## Test plan
- [ ] Login with valid credentials
- [ ] Login with wrong password (shows error)
- [ ] Loading state during request
EOF
)"
# 3. ה-CI רץ, reviewer מסתכל, מבקש שינוי
# 4. עובדים על השינוי
git add .
git commit -m "Address PR feedback: add password visibility toggle"
git push
# 5. ה-reviewer מאשר
# 6. squash and merge
# 7. מנקים
git switch main
git pull
git branch -d feature/login
שיטות עבודה מומלצות
אוסף עקרונות שאספתי לאורך השנים. ככל שתעבוד עם git יותר, תאמץ את העקרונות האלה אוטומטית.
הודעות commit
הכלל החשוב ביותר: למה, לא מה
הקוד מראה מה השתנה. ההודעה צריכה להסביר למה.
❌ רע: Update LoginForm.jsx
✅ טוב: Fix login race condition when user double-clicks submit
❌ רע: Add new file
✅ טוב: Extract useIsMentor hook to remove duplication across screens
פורמט
- כותרת: עד 50 תווים
- שורה ריקה
- תיאור (אופציונלי): פסקאות ארוכות יותר עם פרטים
Stabilize DojoHomeScreen layout
Memoize photos array to prevent unnecessary re-renders.
Add early return when state is null to avoid flashing.
Resolves flicker reported by QA on slow networks.
זמן ציווי (Imperative)
כותבים כאילו אתה נותן הוראה לקוד:
✅ Add validation to login form
❌ Added validation to login form
❌ Adding validation to login form
פעלים נפוצים
- Add - הוספת פיצ'ר חדש
- Fix - תיקון באג
- Update - שיפור פיצ'ר קיים
- Refactor - ארגון מחדש בלי שינוי התנהגות
- Remove - מחיקה
- Extract - הוצאת קוד לפונקציה / hook
- Wire - חיבור בין רכיבים
- Stabilize - תיקון יציבות
ללא קונבנציות לא נחוצות
לא חייבים feat:, fix:, chore: (Conventional Commits) אלא אם הצוות שלך משתמש בזה. גם לא צריכים אימוג'ים.
Commits אטומיים
כל commit צריך לעשות דבר אחד. אם אתה משתמש ב"and" בהודעה - זה סימן לחלק את ה-commit:
❌ רע: Add login form and fix dashboard styling
✅ טוב: שני commits נפרדים
יתרונות של commits אטומיים: - קל לעשות revert ל-commit ספציפי - היסטוריה ברורה - code review קל יותר - bisect (חיפוש באג בינארי) עובד טוב
Branches
שמות תיאוריים
feature/user-profile-page
bugfix/login-error-message
hotfix/security-patch-cve-2024
chore/upgrade-react-19
branches קצרי-חיים
ככל שה-branch חי יותר זמן - יותר קונפליקטים יצטברו. עדיף branches קטנים שמתמזגים מהר. אם פיצ'ר גדול - מחלקים אותו לכמה PRs.
מחיקה אחרי merge
אל תשאיר branches שכבר מוזגו. תנקה אחרי עצמך:
git branch -d feature/done
git push origin --delete feature/done
סנכרון תכוף
פעם ביום לפחות
git switch main
git pull
git switch your-feature
git rebase main # או git merge main
ככה אתה לא מגיע למצב של 50 קונפליקטים ביום שאתה מנסה למזג ל-main.
.gitignore נקי
כל פרויקט חייב .gitignore הולם לפני ה-commit הראשון. אחרת אתה מוסיף בטעות node_modules, .env ועוד.
ראה 14_gitignore.md לדוגמאות.
אבטחה
לעולם לא לdcommit סודות
- API keys
- סיסמאות
- private keys
- access tokens
- קבצי
.env
אם בטעות הוספת סוד וכבר עשית push:
1. שנה את הסוד מיד (זה הצעד הקריטי)
2. השתמש ב-BFG Repo-Cleaner או git filter-repo כדי להסיר מההיסטוריה
3. force push (אחרי תיאום עם הצוות)
בדיקה לפני git add .
git status # לפני
git diff # לפני
git add . # רק אחרי שראית שהכל ok
פעולות הרסניות - בזהירות
אסור על branches משותפים
git push --forceל-maingit rebaseשל commits שכבר נדחפוgit reset --hardל-commits שכבר נדחפו
השתמש בחלופות בטוחות
git push --force-with-leaseבמקום--forcegit revertבמקוםgit reset --hardmergeבמקוםrebaseל-branches משותפים
Pull Requests קטנים
PR שמכיל 1000 שורות שינוי לא יקבל review טוב. עדיף 5 PRs של 200 שורות כל אחד.
חוקי הפלדה
- לעולם אל תעשה force push ל-main
- לעולם אל תcommit סודות
- לפני push - תמיד
git statusו-git diff --staged - לפני pull - תמיד
git statusשלא מאבד עבודה - בכל ספק -
git stashושאל מישהו
כלי עזר מומלצים
Aliases חוסכי זמן
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.unstage 'reset HEAD --'
git config --global alias.last 'log -1 HEAD'
git config --global alias.visual 'log --graph --oneline --all'
git config --global alias.adog 'log --all --decorate --oneline --graph'
Hooks
ב-.git/hooks/ אפשר להגדיר scripts שרצים אוטומטית. לדוגמה, pre-commit שמריץ linter לפני כל commit.
Husky (ל-Node.js)
ספרייה שמקלה על ניהול hooks ושיתופם בין מפתחים.
סיכום: הרגלים יומיומיים
- 🌅 בוקר:
git pullבמצב נקי - 💻 לפני עבודה: branch חדש מ-main מעודכן
- ✏️ commits אטומיים, הודעות ברורות
- 🔄 לפני סיום יום:
git push - 🧹 אחרי merge: ניקוי branches
- 🚫 בכל ספק:
git status, אל תעשה כלום הרסני