דילוג לתוכן
Git וניהול גרסאות

git stash ו־cherry-pick: לשמור שינויים בצד ולהעביר commit בודד בלי לבנות הכול מחדש

git stash הוא אחד הכלים הכי שימושיים ב־Git כשאתם באמצע שינוי, משהו דחוף נכנס, ואתם לא רוצים לעשות commit חצי־אפוי רק כדי “לנקות שולחן”. יחד עם git cherry-pick, אפשר לשמור עבודה זמנית בצד, להעביר commit בודד מענף אחר, ואז לחזור בדיוק לנקודה שבה הייתם.

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

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

מתי משתמשים ב־git stash ומתי ב־cherry-pick?

שתי הפקודות פותרות בעיות שונות, אבל הן נפגשות הרבה ביום־יום:

  • git stash שומר שינויים מקומיים זמניים בצד, בלי ליצור commit רגיל.
  • git cherry-pick לוקח commit קיים מענף אחר ומיישם אותו על הענף הנוכחי.

דוגמה נפוצה: אתם עובדים על פיצ’ר בענף feature-report, אבל פתאום צריך להעביר תיקון קטן שכבר קיים בענף אחר אל main. הבעיה: יש לכם קבצים פתוחים ושינויים לא גמורים. במקום לעשות commit מבולגן או להעתיק קוד ידנית, שומרים את העבודה עם git stash, עוברים לענף המתאים, עושים cherry-pick, ואז מחזירים את העבודה מה־stash.

מה להכין מראש לפני שמריצים פקודות

לפני עבודה עם stash ו־cherry-pick, כדאי לוודא שלושה דברים:

  1. אתם יודעים באיזה ענף אתם נמצאים: git branch --show-current.
  2. אתם רואים את מצב הקבצים: git status --short.
  3. אתם יודעים איזה commit רוצים להעביר: git log --oneline בענף שבו הוא נמצא.

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

git stash בפועל: לשמור שינויים בצד ולחזור לעבודה נקייה

הדוגמה הבאה מיועדת ל־Linux/macOS עם bash. היא יוצרת תיקיית Git זמנית, מבצעת commit ראשון, משנה קובץ, שומרת את השינוי ב־stash, ומציגה את הרשימה.

bash
#!/usr/bin/env bash
set -e

repo=$(mktemp -d)
cd "$repo"

git init -q
git config user.name "Demo User"
git config user.email "[email protected]"
git checkout -q -b main

printf "line one\n" > notes.txt
git add notes.txt
git commit -q -m "Add notes"

printf "work in progress\n" >> notes.txt

echo "Before stash:"
git status --short

git stash push -m "wip: notes change"

echo "After stash:"
git status --short

echo "Stashes:"
git stash list

מה קורה כאן? לפני ה־stash תראו שורה כמו M notes.txt, כלומר הקובץ השתנה. אחרי git stash push, פלט git status --short יהיה ריק, כי סביבת העבודה חזרה למצב נקי. בסוף תראו רשומה דומה ל־stash@{0}: On main: wip: notes change.

הפקודה git stash push -m "..." עדיפה על git stash קצר, כי ההודעה עוזרת להבין אחר כך מה שמרתם. אחרי שבוע, stash@{3} בלי תיאור לא אומר הרבה.

להחזיר stash: apply מול pop

יש שתי דרכים נפוצות להחזיר שינויים מה־stash:

פקודה מה היא עושה מתי להשתמש
git stash apply מחזירה את השינויים אבל משאירה את ה־stash ברשימה כשאתם רוצים ניסיון בטוח יותר
git stash pop מחזירה את השינויים ומנסה למחוק את ה־stash מהרשימה כשאתם בטוחים שזה ה־stash הנכון

במקרה של קונפליקט, git stash pop בדרך כלל לא ימחק את ה־stash, כדי שלא תאבדו את השינוי. עדיין, למתחילים עדיף להשתמש קודם ב־apply, לבדוק שהכול בסדר, ורק אחר כך למחוק ידנית עם git stash drop.

שמירת קבצים חדשים עם git stash

ברירת המחדל של git stash שומרת שינויים בקבצים ש־Git כבר מכיר. אם יצרתם קובץ חדש שלא נוסף עם git add, הוא לא תמיד ייכנס ל־stash. לשם כך משתמשים ב־-u, קיצור של untracked.

גם הדוגמה הזו מיועדת ל־Linux/macOS עם bash:

bash
#!/usr/bin/env bash
set -e

repo=$(mktemp -d)
cd "$repo"

git init -q
git config user.name "Demo User"
git config user.email "[email protected]"
git checkout -q -b main

printf "app=demo\n" > settings.txt
git add settings.txt
git commit -q -m "Add settings"

printf "mode=local\n" >> settings.txt
printf "temporary note\n" > scratch.txt

echo "Before stash:"
git status --short

git stash push -u -m "wip: settings and scratch file"

echo "After stash:"
git status --short

git stash apply

echo "After apply:"
git status --short

echo "scratch.txt content:"
cat scratch.txt

לפני ה־stash תראו גם שינוי בקובץ קיים וגם קובץ חדש: בערך M settings.txt ו־?? scratch.txt. אחרי ה־stash סביבת העבודה נקייה. אחרי git stash apply, שני השינויים חוזרים, כולל הקובץ החדש. הפלט של cat scratch.txt יהיה temporary note.

cherry-pick: להעביר commit בודד מענף לענף

git cherry-pick לא “מעביר” commit במובן של גזירה והדבקה. הוא לוקח את השינוי שבוצע ב־commit מסוים ומייצר commit חדש בענף הנוכחי. בדרך כלל ההודעה נשמרת, אבל מזהה ה־commit יהיה שונה, כי ההורה שלו שונה.

הדוגמה הבאה יוצרת ענף feature, מבצעת בו שני commits, ואז מעבירה רק את הראשון אל main.

bash
#!/usr/bin/env bash
set -e

repo=$(mktemp -d)
cd "$repo"

git init -q
git config user.name "Demo User"
git config user.email "[email protected]"
git checkout -q -b main

printf "invoice\n" > app.txt
git add app.txt
git commit -q -m "Add base app file"

git checkout -q -b feature
printf "tax label\n" >> app.txt
git add app.txt
git commit -q -m "Add tax label"
pick_hash=$(git rev-parse HEAD)

printf "debug note\n" >> app.txt
git add app.txt
git commit -q -m "Add debug note"

git checkout -q main
git cherry-pick "$pick_hash"

echo "Current file:"
cat app.txt

echo "Recent commits:"
git log --oneline -3

בסיום, הקובץ app.txt בענף main יכיל את invoice ואת tax label, אבל לא את debug note. זה בדיוק היתרון של cherry-pick: אפשר לבחור commit יחיד בלי למזג את כל הענף.

התרחיש הקלאסי: stash ואז cherry-pick

נניח שאתם על main עם שינוי מקומי, אבל צריכים להביא commit מענף hotfix. אם תנסו לעבור ענף או לבצע cherry-pick בזמן שיש שינויים מתנגשים, Git עשוי לעצור. הסדר הבטוח הוא:

  1. בודקים מצב: git status --short.
  2. שומרים עבודה זמנית: git stash push -u -m "wip before cherry-pick".
  3. מבצעים את ה־cherry-pick: git cherry-pick COMMIT_HASH.
  4. מריצים בדיקות או לפחות בודקים קבצים רלוונטיים.
  5. מחזירים עבודה: git stash apply.
  6. אם הכול תקין, מוחקים את ה־stash: git stash drop.

הדוגמה הבאה מדגימה את כל הרצף בסביבת בדיקה. היא מיועדת ל־Linux/macOS עם bash:

bash
#!/usr/bin/env bash
set -e

repo=$(mktemp -d)
cd "$repo"

git init -q
git config user.name "Demo User"
git config user.email "[email protected]"
git checkout -q -b main

printf "version=1\n" > config.txt
git add config.txt
git commit -q -m "Add config"

git checkout -q -b hotfix
printf "safe=true\n" >> config.txt
git add config.txt
git commit -q -m "Add safe flag"
hotfix_hash=$(git rev-parse HEAD)

git checkout -q main
printf "local_note=unfinished\n" > local.txt

git stash push -u -m "wip: local note before hotfix"
git cherry-pick "$hotfix_hash"
git stash apply

echo "config.txt:"
cat config.txt

echo "local.txt:"
cat local.txt

הפלט הצפוי יראה ש־config.txt כולל גם version=1 וגם safe=true, ובמקביל local.txt חזר עם local_note=unfinished. כלומר, ה־hotfix נכנס לענף, והעבודה המקומית לא נעלמה.

איך למצוא את ה־commit הנכון ל־cherry-pick

הדרך הפשוטה היא לעבור לענף שבו נמצא השינוי ולהציג היסטוריה קצרה:

bash
git log --oneline --decorate -10

חפשו את הודעת ה־commit המתאימה, העתיקו את המזהה הקצר שמופיע בתחילת השורה, ואז חזרו לענף היעד. אם אתם לא בטוחים מה יש בתוך commit מסוים, בדקו אותו לפני הפעולה:

bash
git show --stat COMMIT_HASH

כדי לראות את התוכן המלא של השינוי:

bash
git show COMMIT_HASH

אל תעשו cherry-pick לפי תחושת בטן. אם ההודעה היא fix stuff, בדקו את הקבצים. הרבה טעויות קורות כי commit אחד כולל גם תיקון חשוב וגם שינוי ניסיוני לא קשור.

קונפליקטים: מה עושים כש־Git עוצר באמצע

קונפליקט ב־stash או ב־cherry-pick אינו אסון. הוא אומר ש־Git לא יודע לחבר לבד שתי גרסאות של אותו אזור בקובץ. התהליך דומה:

  1. מריצים git status ורואים אילו קבצים בקונפליקט.
  2. פותחים את הקבצים ומחפשים סימונים כמו <<<<<<<, =======, >>>>>>>.
  3. משאירים את התוכן הנכון ומוחקים את סימוני הקונפליקט.
  4. מריצים git add FILE_NAME.
  5. אם זה cherry-pick: git cherry-pick --continue.

אם הסתבכתם במהלך cherry-pick ורוצים לחזור למצב שלפני הפעולה, השתמשו ב־git cherry-pick --abort. זה לא מוחק commits קיימים; זה מבטל את פעולת ה־cherry-pick שלא הושלמה.

טעויות נפוצות והודעות שגיאה אמיתיות

error: Your local changes to the following files would be overwritten by checkout

ההודעה המלאה תיראה בערך כך:

output
error: Your local changes to the following files would be overwritten by checkout:
    app.txt
Please commit your changes or stash them before you switch branches.
Aborting

המשמעות: יש שינוי מקומי שייפגע אם Git יעבור ענף. הפתרון: אם זה שינוי זמני, הריצו git stash push -u -m "wip before switching branches". אם זה שינוי מוכן, בצעו commit מסודר. אל תשתמשו ב־git checkout -f בלי להבין שהוא עלול למחוק שינויים מקומיים.

No local changes to save

זו לא בדיוק שגיאה, אלא הודעה מ־Git:

text
No local changes to save

היא מופיעה כשמריצים git stash ואין ל־Git מה לשמור. בדקו עם git status --short. אם יש קבצים חדשים שמופיעים כ־??, השתמשו ב־git stash push -u. אם גם שם אין כלום, כנראה שסביבת העבודה באמת נקייה.

fatal: bad revision

בעת cherry-pick ייתכן שתראו:

output
fatal: bad revision 'abc123'

המשמעות: Git לא מזהה את מזהה ה־commit שסיפקתם. אולי העתקתם תו חסר, אולי אתם בריפוזיטורי הלא נכון, ואולי הענף שמכיל את ה־commit לא קיים אצלכם מקומית. בדקו עם git log --oneline --all --decorate. אם אתם עובדים מול remote, ודאו שהמידע המקומי מעודכן לפני שאתם מחפשים את ה־commit.

error: could not apply

ב־cherry-pick עם קונפליקט, תראו הודעה בסגנון:

output
error: could not apply 1a2b3c4... Add tax label
hint: After resolving the conflicts, mark them with
hint: "git add/rm <pathspec>", then run
hint: "git cherry-pick --continue".

הפתרון הוא לא להריץ שוב cherry-pick. קודם פותרים את הקונפליקט בקבצים, מריצים git add, ואז git cherry-pick --continue. אם החלטתם שהפעולה לא נכונה, הריצו git cherry-pick --abort.

הבדלים קטנים שעושים סדר גדול

  • stash apply לא מוחק את ה־stash — טוב לניסיון זהיר.
  • stash pop כן מנסה למחוק — נוח, אבל פחות זהיר.
  • cherry-pick יוצר commit חדש — אל תצפו לאותו hash.
  • לא חייבים לשמור הכול ב־stash אחד — אפשר לתת הודעות ברורות ולנהל כמה stashes.
  • קונפליקט אינו כישלון — זו נקודת החלטה ידנית.

שאלות שקוראים באמת שואלים

האם stash נשמר גם אחרי סגירת הטרמינל?

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

אפשר לעשות cherry-pick לכמה commits?

כן, אבל בזהירות. אפשר להריץ git cherry-pick HASH1 HASH2, או טווח commits, אבל במדריך הזה התמקדנו ב־commit בודד כי זה התרחיש הבטוח והנפוץ ביותר. כשמעבירים כמה commits, חשוב להבין את הסדר והתלויות ביניהם.

מה עדיף: merge או cherry-pick?

אם אתם רוצים להביא ענף שלם, לרוב merge מתאים יותר. אם אתם צריכים שינוי נקודתי מענף אחר, cherry-pick מתאים. שימוש מוגזם ב־cherry-pick עלול ליצור היסטוריה מבלבלת, במיוחד אם אותם שינויים מגיעים אחר כך גם דרך merge.

האם stash מתאים לקוד חשוב?

רק לזמן קצר. אם הקוד חשוב, עדיף commit בענף עבודה. stash מצוין להפסקה רגעית, אבל הוא פחות גלוי, פחות מתועד, וקל לשכוח מה יש בו. אפשר לראות תוכן של stash עם git stash show -p stash@{0}.

סיכום מעשי

git stash ו־git cherry-pick הם שילוב חזק כשצריך לעבוד נקי בלי להפוך שינוי קטן לפרויקט גדול. שומרים עבודה זמנית בצד, מביאים commit מדויק, בודקים שהכול תקין, ואז מחזירים את העבודה המקומית.

הכלל החשוב הוא לא למהר: לפני כל פעולה בדקו git status, תנו הודעות ברורות ל־stash, ודאו שאתם בוחרים את ה־commit הנכון. עם כמה הרגלים פשוטים, הפקודות האלה מפסיקות להיות “פקודות חירום” והופכות לכלי עבודה יומיומי, יציב וברור.