کدنامهمرجع‌های مهندسی نرم‌افزار، به فارسی
گیت · فصل ۱۶Git · Chapter 16

وقتی همه‌چیز خراب شد

When everything goes wrong: Git recovery scenarios

وقتی کسی می‌گوید «همه‌چیز پرید»، بدترین واکنش این است که سریع چند دستور پاک‌کننده بزنیم. این فصل یک اتاق نجات است: اول می‌فهمیم Git هنوز چه چیزی دارد، بعد فقط همان چیزی را که لازم است حرکت می‌دهیم.

A teammate messages: “I think I lost a day of work.” Your hand reaches for reset, checkout, clean, or even recloning. Pause. First we need to learn what Git still has: perhaps the work was committed, staged, stashed, kept in another clone—or never entered Git at all.

۱۴بخشsections
۸۵دقیقهminutes
۱۸تمرین با پاسخsolved exercises
۵نمودارdiagrams

اول چه چیزی را نباید بزنیم؟What should we not run first?

قبل از هر کاری دستت را از روی reset --hard و clean -f بردار. اول بپرس کار کجا بوده: commit شده؟ ناحیه‌ آماده‌سازی شده؟ stash شده؟ فقط ردیابی‌نشده بوده؟ clone دیگری آن را دارد؟ همین جواب تعیین می‌کند Git چه شانسی برای نجات دارد.

Before trying a fix, ask: was it committed, only staged, stashed, left in the working tree or untracked, pushed, kept in another clone, or recorded as a reflog clue? Each answer leads to a different path.

Git یک دکمهٔ «برگرداندن روز گذشته» ندارد. فصل ۰۲ نشان داد commit، tree و blob چه هستند؛ فصل ۰۳ سه ناحیه را جدا کرد؛ فصل ۰۹ تفاوت reset، revert و restore را ساخت؛ و فصل ۱۰ reflog و شیءهای بی‌نام را بررسی کرد. حالا همین‌ها را در یک اتاق نجات به کار می‌گیریم، بی‌آنکه فرض کنیم هر چیزی حتماً قابل‌بازیابی است.

Git has no “restore yesterday” button. Chapter 02 established commits, trees, and blobs; Chapter 03 separated the three areas; Chapter 09 distinguished reset, revert, and restore; Chapter 10 examined reflogs and unnamed objects. Now we apply those ideas in one rescue room without assuming every bit is recoverable.

درخت تصمیم نجات Git از ثبت وضعیت شروع می‌کنیم، سپس بر اساس محل باقی‌ماندن کار به ref، object store، clone دیگر یا نسخهٔ پشتیبان بیرونی می‌رویم. مکث و ثبت وضعیتPause and record state status · HEAD · log · reflog commit / ref هست؟Commit / ref exists? inspect · make rescue ref object هنوز هست؟Does the object remain? reflog · fsck · verify clone دیگر دارد؟Another clone has it? known old object ID هرگز ذخیره نشده؟Never stored? بیرون از Git بگردLook outside Git اول بشناس؛ با ref نجات نام بده؛ دوباره بررسی کن Identify it; name a rescue ref; verify again
نمودار ۱ — از مکث و ثبت وضعیت شروع کن؛ هر شاخه یک محل احتمالی برای نسخهٔ ذخیره‌شده است، نه وعدهٔ بازیابی.Diagram 1 — Start by pausing and recording state. Each branch is a possible place to find stored work, not a recovery guarantee.
اول چیزی را پاک نکنDo not delete first

قاعدهٔ تکراری این فصل: اول چیزی را پاک نکن. اول بفهم Git الان چه چیزهایی را هنوز دارد. تا وقتی نمی‌دانی کدام اشاره‌گر یا فایل تنها نسخهٔ کارت است، reset --hard، checkout، clean، gc و prune را اجرا نکن.

The recurring rule is: Do not delete first. First find out what Git still has. Until you know which ref or file may be the only copy, do not run reset --hard, destructive checkout, clean, gc, or prune.

پروتکل نجات: یک قدم در هر نوبتRescue protocol: one change at a time

اول وضعیت را ثبت کن، نه اینکه «تمیزش» کنی. این فرمان‌ها فقط نمایی از مخزن فعلی می‌دهند؛ خروجی را در پروندهٔ حادثه نگه دار. اگر فرمانی خطا داد، همان خطا هم مدرک است.

Record the state before trying to “clean it up.” These commands inspect the current repository; keep their output with the incident notes. A failed command is evidence too.

قبل از هر تغییری / inspect first
git status --short --branch
git branch --show-current
git rev-parse --verify HEAD
git log --all --graph --decorate --oneline -30
git reflog --all --date=iso
git stash list
git remote -v
git ls-files --others --exclude-standard

در status دنبال فایل‌های آماده‌شده، تغییرهای ردیابی‌شده و ??های ردیابی‌نشده بگرد. rev-parse شناسهٔ commit فعلی را ثبت می‌کند؛ در مخزن تازه و بدون commit ممکن است شکست بخورد، و این خودش بخشی از وضعیت است. log --all تاریخچهٔ متصل به اشاره‌گرهای فعلی را می‌بیند؛ commit بی‌نام ممکن است آن‌جا نباشد. reflog فقط دفترچهٔ محلی همین clone است.

In status, look for staged paths, tracked edits, and untracked ?? entries. rev-parse records the current commit ID; it may fail in a repository with no commits, which is itself part of the state. log --all shows history reachable from current refs; an unnamed commit may be absent. A reflog belongs only to this local clone.

نمونهٔ متغیر / representative output
# نمونه است؛ branch، شناسه و فایل‌ها در مخزن تو فرق می‌کنند.
## HEAD (no branch)
 M src/app.js
?? notes.txt
7ac31f2 (HEAD) HEAD@{0}: reset: moving to HEAD~2
9bd8a61 HEAD@{1}: commit: finish parser
stash@{0}: On main: wip layout
origin  https://example.invalid/team/app.git

این نمونه می‌گوید پوشه‌ کاری هم تغییر دارد، یک فایل ردیابی‌نشده هست، و reflog از reset قبلی سرنخ داده. اما ثابت نمی‌کند کدام نسخهٔ فایل روی دیسک بهتر است یا هیچ نسخهٔ دیگری وجود ندارد. قبل از حرکت دادن اشاره‌گر، اگر HEAD commit دارد، می‌توانی فقط همان commit را با یک اشاره‌گر تازه حفظ کنی؛ تغییرهای index/پوشه‌ کاری را جدا ذخیره کن.

This sample shows working-tree changes, an untracked file, and a previous reset in the reflog. It does not prove which on-disk file is best or that no other copy exists. Before moving a ref, if HEAD names a commit, you can preserve that commit with a new ref; save index and working-tree changes separately.

حفظ شاهدهای محلی / preserve local evidence
git branch rescue/pre-recovery HEAD
git diff --binary > ../working-tree.patch
git diff --cached --binary > ../index.patch
git ls-files --others --exclude-standard

شاخهٔ rescue/pre-recovery فقط commitی را نگه می‌دارد که HEAD همین حالا به آن اشاره می‌کند؛ ناحیه‌ آماده‌سازی، تغییرهای آماده‌نشده و ردیابی‌نشده را ذخیره نمی‌کند. دو patch بالا تغییرهای ردیابی‌شده را جدا ثبت می‌کنند. فایل‌های ردیابی‌نشده را در پوشه‌ای امن کپی کن. اگر اصل مخزن مشکوک به خرابی است، اول از کل پوشهٔ مخزن یک کپی جدا برای بررسی بساز. این کپی‌ها هم فقط همان چیزی را حفظ می‌کنند که واقعاً در آن‌ها بوده؛ چیزی را که قبلاً پاک شده‌اند جادو نمی‌کنند.

The rescue/pre-recovery branch preserves only the commit HEAD currently names; it does not save the index, unstaged edits, or untracked files. The two patches capture tracked changes separately. Copy untracked files to a safe directory. If repository corruption is suspected, first make a separate filesystem copy of the repository for inspection. These copies preserve only what they actually contain; they cannot recreate something already deleted.

پروتکل در یک نگاهThe protocol at a glance
قدمStepکارActionچیزی که هنوز ثابت نشدهWhat remains unproven
۱دستورهای مخرب را متوقف کنStop destructive commandsهنوز نمی‌دانیم کار کجا ماندهWe do not yet know where the work remains
۲–۴وضعیت، شاخه/HEAD، گراف و reflog را ثبت کنRecord status, branch/HEAD, graph, and refloglog عادی، شیء بی‌نام را الزاماً نشان نمی‌دهدOrdinary log may omit unnamed objects
۵اشاره‌گر، patch و کپی فایل‌های ردیابی‌نشده را نگه دارPreserve refs, patches, and untracked-file copiesاشاره‌گر نجات همهٔ فایل‌های روی دیسک نیستA rescue ref is not a copy of every disk file
۶–۸یک چیز را تعمیر و همان شاهد را دوباره بررسی کنRepair one thing and repeat the same checkموفقیت یک فرمان به‌تنهایی صحت همه‌چیز را ثابت نمی‌کندOne successful command does not prove everything is correct

نام گم شد یا خودِ شیء؟ این دو یکی نیستندWas the name lost—or the object?

فصل ۰۵ گفت شاخه یک اشاره‌گر سبک است؛ فصل ۰۲ گفت commit به tree و blob می‌رسد. حذف شاخه می‌تواند نام را بردارد، در حالی که شیءهای commit هنوز مدتی در مخزن مانده باشند. از طرف دیگر اگر شیء خراب یا واقعاً حذف شده باشد، ساختن شاخه تازه چیزی برای پرکردن ندارد.

Chapter 05 showed that a branch is a lightweight ref; Chapter 02 showed how a commit reaches trees and blobs. Deleting a branch can remove the name while commit objects remain in the repository for a time. But if an object is corrupt or actually gone, creating another branch gives it nothing to point to.

حذف ref در برابر حذف object حذف نام branch ممکن است commit و فایل‌هایش را در object database باقی بگذارد؛ اگر خود object حذف یا خراب شده باشد، ref به‌تنهایی آن را بازسازی نمی‌کند. نام شاخهbranch ref refs/heads/feature حذف می‌شودcan be deleted commit objectcommit object tree → blobs ممکن است باقی بماندmay remain بررسیinspect reflog · fsck · cat-file تا وقتی پیدا شودif still present نام بهname points to OID را بشناسverify OID رسیدن به GC یا خرابی object می‌تواند این زنجیره را قطع کند؛ ref تازه دادهٔ غایب را نمی‌سازد. Garbage collection or object corruption can break the chain; a new ref cannot recreate missing data.
نمودار ۲ — اشاره‌گر نام و مسیر دسترسی است؛ شیء محتوای ذخیره‌شده. یافتن اشاره‌گر قدیمی با اثبات سلامت شیء فرق دارد.Diagram 2 — A ref is a name and access path; an object is stored content. Finding an old ref is not the same as proving object integrity.

git reflog --all را برای حرکت نام‌ها بخوان؛ اگر شناسه‌ای پیدا شد، قبل از تغییر، خود commit را با git show --stat --oneline OID بشناس و با git cat-file -e 'OID^{commit}' وجود commit را بررسی کن. اگر قرار است نگهش داری، یک rescue شاخه بساز و با git show-ref بررسی کن که نام تازه واقعاً به همان شناسه اشاره می‌کند.

Read ref movements with git reflog --all. If you find an ID, inspect the commit with git show --stat --oneline OID before changing anything, and test its existence with git cat-file -e 'OID^{commit}'. If you need to keep it, create a rescue branch and confirm with git show-ref that the new name points to that ID.

اگر اشاره‌گر پیدا نشد، git log --all کافی نیست؛ این دستور همهٔ شیءهای بی‌نام را نمی‌گردد. روی مخزن سالم یا کپیِ جداشده می‌توانی git fsck --full --no-reflogs --unreachable را بررسی کنی. خروجی ممکن است commitهای unreachable بدهد؛ هر کدام را با git show بخوان. fsck ابزار جست‌وجو/بررسی است، نه تعمیرکار جادویی.

If no ref appears, git log --all is not enough; it does not enumerate every unnamed object. On a healthy repository or a separate copy, inspect git fsck --full --no-reflogs --unreachable. It may list unreachable commits; examine candidates with git show. Fsck is an inspection tool, not a magic repair service.

نمونهٔ خروجی متغیر / representative output
unreachable commit 71ab23c...
unreachable tree 22cd901...
dangling blob 4fe1a02...

این خروجی می‌گوید چند شیء در مخزن هستند ولی از ریشه‌هایی که این اجرا بررسی کرده reachable نیستند. شناسهٔ commit را می‌توانی وارسی و در صورت نیاز اشاره‌گر کنی؛ blob به‌تنهایی ممکن است اسم فایل یا زمان ویرایش را نداشته باشد. حتی یک شیء سالم هم ثابت نمی‌کند تمام تغییرهای روز گمشده ذخیره شده‌اند.

This output says some objects exist but are not reachable from the roots checked by this run. You can inspect a commit ID and, if useful, create a ref for it. A blob alone may have no filename or edit timestamp. Even a healthy object does not prove that every change from the lost day was stored.

دفترچهٔ reflog محلی است و ردهایش نگهداری ابدی ندارند؛ زمان نگهداری می‌تواند با تنظیم Git فرق کند. وقتی سرنخ معتبر پیدا شد، با ساختن اشاره‌گر آن را زود از حالت بی‌نام بیرون بیاور. در دوران نجات git gc، git prune یا expire کردن reflog را اجرا نکن. مرجع‌ها: reflog و fsck.

Reflogs are local, and their entries are not retained forever; the period can vary with Git configuration. Once you have a credible clue, promptly protect it with a ref. During recovery, do not run git gc, git prune, or reflog expiration. See the official git-reflog and git-fsck manuals.

rebase بد: اگر هنوز وسطش هستی، زور نزنA bad rebase: if it is still in progress, do not force through

rebase وسط تعارض متوقف شده و حالا نمی‌دانی کدام فایل باید بماند. هنوز operation فعال است؟ اول git status را بخوان و تغییرهای قبل از rebase را تا جایی که می‌توانی جدا نگه دار. اگر تصمیم گرفتی همین تلاش را کنار بگذاری، git rebase --abort مسیر استاندارد پایان‌دادن به rebase نیمه‌کاره است؛ بعد دوباره همان وضعیت و گراف را بررسی کن.

A rebase stopped at a conflict, and you no longer know which version should remain. Is the operation still active? Read git status first and preserve pre-rebase changes separately where possible. If you decide to abandon this attempt, git rebase --abort is the standard way to end an in-progress rebase; then inspect the same status and graph again.

نمونهٔ وضعیت / representative state
interactive rebase in progress; onto 42bc113
Last command done (1 command done):
   pick 8af0312 add parser
No commands remaining.
You are currently editing a commit while rebasing branch 'feature' on 'main'.

پیام بالا می‌گوید Git هنوز در وضعیت rebase است؛ هنوز ثابت نمی‌کند کار شروع‌شده قبل از rebase یا ناحیه‌ آماده‌سازی بی‌خطر است. اگر rebase قبلاً کامل شده و نتیجه بد است، --abort دیگر کار نمی‌کند. reflog و پیام‌های rebase را پیدا کن، شناسه‌ شیء قبلی را با show بررسی کن و قبل از حرکت دادن شاخه، آن را با اشاره‌گر نجات حفظ کن. ORIG_HEAD ممکن است سرنخ باشد، اما بعد از عملیات دیگر هم می‌تواند عوض شده باشد؛ بدون بررسی کورکورانه reset نکن.

The output says Git is still in a rebase; it does not prove that pre-rebase work or the index is safe. If the rebase already completed, --abort no longer applies. Find the reflog entries and rebase messages, inspect the old OID with show, and preserve it with a rescue ref before moving the branch. ORIG_HEAD may help, but later operations can change it; never reset to it blindly.

تاریخچهٔ پیش و پس از rebase و سرنخ reflog rebase commitهای تازه‌ای می‌سازد و branch را به آن‌ها می‌برد؛ reflog محلی ممکن است OID قدیمی branch را نشان دهد تا پیش از انتخاب دوباره بررسی و نام‌گذاری شود. پیش از rebasebefore rebase A — B — Cfeature → C (old OID) پس از rebaseafter rebase A — M — B' — C'feature → C' (new OID) reflog محلیlocal reflogmay record old → new OID قدیمی را بررسی کن؛ ref نجات بساز؛ بعد تصمیم بگیر Inspect old OID; create rescue ref; then decide
نمودار ۳ — rebase تاریخچهٔ بازنویسی‌شده می‌سازد؛ reflog ممکن است جابه‌جایی محلی شاخه را ثبت کند، نه اینکه clone دیگری را برگرداند.Diagram 3 — Rebase creates rewritten history; the local reflog may record the branch move, but it does not restore another clone.

اگر تعارض فقط از merge می‌آید، git merge --abort را بعد از ثبت وضعیت و حفظ تغییرهای مهم به کار ببر. اگر توالی cherry-pick هنوز ادامه دارد، git cherry-pick --abort را انتخاب کن. abort برای operation درحال‌اجراست؛ بعد از commit نهایی باید با توجه به خصوصی یا مشترک بودن نتیجه، ادامه بدهی. مستند pull نیز پایان‌دادن به merge/rebase نیمه‌کاره را شرح می‌دهد.

If the conflict is from a merge, use git merge --abort only after recording state and preserving important changes. If a cherry-pick sequence is still active, choose git cherry-pick --abort. Abort applies to an operation in progress; after a final commit, the next step depends on whether the result is private or shared. The git-pull documentation also describes ending an in-progress merge or rebase.

commit روی شاخهٔ اشتباه: commit را پیدا کن، بعد جابه‌جایش کنCommitted on the wrong branch: locate the commit before moving it

یک commit درست ساخته‌ای، اما روی main بوده نه روی feature. اول با git status --short --branch و git log --all --graph --decorate --oneline مطمئن شو کدام اشاره‌گر جلو رفته و آیا commit از قبل در مقصد هست. یک نام نجات برای شناسه‌ شیء بساز، بعد روی شاخهٔ مقصد برو و فقط وقتی patch را لازم داری cherry-pick کن.

You made a valid commit, but on main instead of feature. First use git status --short --branch and git log --all --graph --decorate --oneline to see which ref moved and whether the target already contains the commit. Name the OID with a rescue ref, switch to the intended branch, and cherry-pick only if that patch is needed there.

نمونهٔ گراف / representative graph
* 94ac210 (HEAD -> main) add export option
* 122bc40 (feature/export) start exporter

قبل از جابه‌جایی، commit را با git show --stat 94ac210 بررسی کن. اگر commit خصوصی و فقط روی شاخهٔ غلط است، نگه‌داشتن یک کپی روی شاخهٔ مقصد معمولاً امن‌تر از پاک‌کردن عجولانهٔ شاخهٔ اول است. اگر قبلاً push یا با دیگران share شده، تاریخچهٔ آن‌ها را با reset بازنویسی نکن؛ راه جبران را با تیم انتخاب کن، معمولاً با commit تازه یا revert.

Inspect the commit with git show --stat 94ac210 before moving anything. If it is private and only on the wrong branch, preserving a copy on the intended branch is usually safer than hurriedly deleting the first branch. If it was pushed or shared, do not rewrite teammates’ history with reset; agree on a forward correction, often a new commit or revert.

push اجباری اشتباه: سراغ clone دوم برو، نه reflog سرورAccidental force-push: inspect a second clone, not a server reflog

مخزن راه دور حالا روی commit قدیمی‌تر ایستاده و commit همکار از شاخه ناپدید شده. فرض نکن می‌توانی به دفترچهٔ داخلی GitHub یا GitLab دسترسی بگیری؛ کاربر عادی معمولاً reflog سمت سرور را نمی‌بیند. اول از همکار بخواه هیچ پاک‌سازی یا fetch/force تازه‌ای نکند و در clone خودش git status و git log --all --graph را نگه دارد. یک clone دوم یا شناسه‌ شیء شناخته‌شده می‌تواند مدرک واقعی باشد.

The remote now points to an older commit, and a teammate’s commit vanished from the branch. Do not assume you can access GitHub or GitLab’s internal server reflog; ordinary users generally cannot. Ask the teammate not to clean or force-push again, and preserve git status and git log --all --graph from their clone. A second clone or known OID is actual evidence.

نجات تاریخچهٔ مشترک با clone دوم و شناسهٔ قدیمی force push شاخهٔ remote را به tip قدیمی برده است؛ clone دوم هنوز OID قبلی را نگه می‌دارد و می‌تواند آن را با rescue ref و push عادی در شاخه‌ای تازه منتشر کند. remote / mainremote / main now → old tip commit همکار غایبteammate commit missing clone دومsecond clone known old OID still present object را بررسی کنinspect the object rescue ref و شاخهٔ تازهrescue ref and new branch push rescue/old-tip بدون overwrite دوبارهdo not overwrite again پیش از بازگرداندن main، OID و همکارها را هماهنگ کن Before restoring main, coordinate the OID and collaborators
نمودار ۴ — clone دوم شناسه‌ شیء قدیمی را نگه داشته؛ اول آن را بررسی و روی اشاره‌گر نجات نام‌گذاری می‌کند. این مسیر فرض نمی‌کند کاربر به reflog سرور دسترسی دارد.Diagram 4 — A second clone retains the old OID, inspects it, and names it with a rescue ref. The flow does not assume user access to a server reflog.

اگر شناسه‌ شیء را شناختی، اول git show --stat OID و git cat-file -e 'OID^{commit}' را در clone دوم اجرا کن. بعد git branch rescue/old-tip OID و git push origin rescue/old-tip را بزن تا یک اشاره‌گر تازهٔ معمولی روی مخزن راه دور ساخته شود. این کار main را برنمی‌گرداند؛ فقط commit را از خطر بی‌نام‌ماندن دور می‌کند. بازگرداندن main به شناسه‌ شیء درست، تصمیم هماهنگی تیم است؛ شناسهٔ قدیمی را از گفت‌وگوی همکار یا ثبت ساخت تأیید کن، نه از حدس.

Once identified, run git show --stat OID and git cat-file -e 'OID^{commit}' in the second clone. Then create git branch rescue/old-tip OID and git push origin rescue/old-tip to publish an ordinary new ref. This does not restore main; it protects the commit from remaining unnamed. Restoring main to the right OID is a team coordination decision. Confirm the old ID from a teammate or build record, not a guess.

secret در commit: اول اعتبارنامه را باطل کنA secret in a commit: revoke the credential first

یک token واقعی وارد commit شده و شاید push هم شده. اولین کار پاک‌کردن commit نیست؛ اعتبارنامه را فوراً revoke یا rotate کن. تا وقتی secret معتبر است، حتی مخفی‌کردن شاخه هم کسی را که قبلاً آن را دیده متوقف نمی‌کند.

A real token entered a commit and may have been pushed. The first action is not deleting the commit; revoke or rotate the credential immediately. While it remains valid, hiding a branch cannot stop someone who already saw it.

از secret کپی تازه نسازDo not create another copy of the secret

مقدار را در خروجی تمرین، issue، chat، screenshot یا همین گزارش رخداد نچسبان. دسترسی و زمان push را مشخص کن، مسئول سامانه را با مسیر امن خبر کن، کلید جایگزین بساز و مصرف کلید قدیمی را بررسی کن.

Do not paste the value into exercise output, an issue, chat, screenshot, or the incident report. Identify its access and push time, contact the system owner securely, issue a replacement, and review use of the old key.

بعد از باطل‌کردن، تیم می‌تواند با هماهنگی تاریخچه را بازنویسی و نسخهٔ میزبان را اصلاح کند؛ اما push اجباری رمز را از cloneها، forkها، cacheها یا فایل‌های دانلودشده پس نمی‌گیرد. برای حذف کامل‌تر ممکن است پشتیبانی میزبان هم لازم باشد. راهنمای GitHub صریحاً چرخاندن اعتبارنامه را قدم اول می‌داند: Removing sensitive data from a مخزن. برای GitLab یا میزبان دیگر، فرایند و پشتیبانی همان سرویس را بررسی کن.

After revocation, the team may coordinate a history rewrite and hosted-repository cleanup, but force-pushing cannot recall the secret from clones, forks, caches, or downloaded files. The host may need to assist with complete removal. GitHub explicitly makes credential rotation the first step in its sensitive-data removal guide. For GitLab or another host, check that service’s process and support.

شیء خراب شده یا فایل اصلاً ذخیره نشده؟Is an object corrupt—or was the file never stored?

دو جمله ممکن است هر دو با «فایلم نیست» شروع شوند، ولی پاسخشان یکی نیست. در حالت اول Git شیء را قبلاً داشته و حالا خطای اتصال، شیء گمشده یا hash mismatch می‌دهد. در حالت دوم فایل ردیابی‌نشده بوده، هرگز ناحیه‌ آماده‌سازی نشده و کسی پاکش کرده. git fsck --full برای بررسی صحت و اتصال شیءهاست؛ خودش شیء خراب را تعمیر یا نسخهٔ سالم را از هیچ‌جا تولید نمی‌کند.

Two reports can both begin with “my file is gone” but need different answers. In the first, Git once had an object and now reports a connectivity error, a missing object, or a hash mismatch. In the second, an untracked file was never staged and somebody deleted it. git fsck --full checks object validity and connectivity; it does not repair a corrupt object or manufacture a healthy copy.

بررسی، نه تعمیر / inspect, do not repair
git fsck --full
# نمونه‌های ممکن، بسته به مخزن:
missing blob 4fe1a02...
error: hash mismatch 7bd83d...

این نمونه فقط نشان می‌دهد شیء مورد انتظار در مخزن فعلی مشکل دارد. عملیات housekeeping را متوقف کن، از مخزن کپی بگیر، شناسهٔ شیء را ثبت کن و یک clone یا نسخه‌ پشتیبان سالم را بیاب. اگر آن شیء در نسخهٔ سالم موجود و قابل‌دسترسی باشد، می‌شود از آن منبع دوباره fetch/نسخه‌برداری کرد؛ بدون آن، fsck چیزی برای جایگزینی ندارد. به سراغ gc، prune یا fsck --lost-found روی تنها نسخهٔ اصلی نرو.

This sample only says an expected object has a problem in the current repository. Stop housekeeping, copy the repository, record the object ID, and locate a healthy clone or backup. If that object exists in an accessible healthy copy, it may be fetched or copied from there; without such a source, fsck has nothing to replace it with. Avoid gc, prune, or fsck --lost-found on the only original copy.

اگر فایلی ردیابی‌نشده و هیچ‌وقت ناحیه‌ آماده‌سازی نشده بود، Git هیچ blobی از آن نگرفته است. بررسی status یا fsck نمی‌تواند محتوایی را که هرگز وارد پایگاه اشیا نشده برگرداند. سطل زباله، محلی تاریخچه ویرایشگر، عکس فوری سیستم، نسخه‌ پشتیبان یا نسخهٔ همکار را جدا بررسی کن؛ اگر هیچ‌کدام نیست، صادقانه بگو بازیابی از Git ممکن نیست. همین مرز، فرق «اشاره‌گر گم‌شده» با «داده‌ای که هرگز ذخیره نشد» است.

If a file was untracked and never staged, Git never received a blob for it. Neither status nor fsck can restore content that never entered the object database. Check the recycle bin, editor local history, system snapshots, backups, or a teammate’s copy; if none exists, say honestly that Git cannot recover it. That is the boundary between a lost ref and data that was never stored.

مرز میان دادهٔ ذخیره‌شده و دادهٔ هرگز ذخیره‌نشده commit، object بی‌نام یا clone دیگر ممکن است مدرک محتوای ذخیره‌شده باشند؛ فایل untracked که هرگز stage نشده از هیچ‌کدام قابل بازیابی نیست و باید نسخهٔ بیرونی داشت. Git قبلاً محتوا را ذخیره کردهGit stored content before commit / ref unreachable object سرنخ را پیدا کن؛ object و محتوا را بررسی کنFind a clue; inspect object and content Git هیچ‌وقت محتوا را نگرفتهGit never received the content فایل untrackeduntracked file فقط نسخهٔ بیرونی: backup، editor، همکارOnly an external copy: backup, editor, teammate reflog و fsck می‌توانند سرنخ بدهند، نه ضمانت؛ دادهٔ هرگز ذخیره‌نشده در Git نیست. Reflog and fsck offer clues, not guarantees; never-stored data is not in Git.
نمودار ۵ — چپ، شیء ذخیره‌شده‌ای است که شاید سرنخی داشته باشد؛ راست، محتوایی است که Git اصلاً دریافت نکرده و منبع نجاتش باید بیرون Git باشد.Diagram 5 — On the left, a stored object may leave a clue. On the right, Git never received the content, so recovery must come from outside Git.

git clean ابزار نجات نیستgit clean is not a rescue command

این فرمان ردیابی‌نشدهها را حذف می‌کند؛ همان جایی که ممکن است تنها کپی فایل هنوز آن‌جا باشد. اگر فقط می‌خواهی ببینی چه چیزهایی هدف حذف‌اند، از حالت پیش‌نمایش استفاده کن:

This command removes untracked files—the very place where the only remaining copy might live. If you only want to see what would be targeted, use its preview mode:

فقط پیش‌نمایش / dry run only
git clean -nd

نمونهٔ خروجی می‌تواند مسیرهای ردیابی‌نشده را فهرست کند؛ -n یعنی حذف واقعی انجام نمی‌شود و -d پوشه‌ها را هم در پیش‌نمایش می‌آورد. حتی با خروجی ظاهراً بی‌خطر، فرمان واقعی را در اتاق نجات اجرا نمی‌کنیم. -x دامنه را به فایل‌های ignored هم گسترش می‌دهد. این فرمان برای پاک‌سازی آگاهانه است، نه پیدا کردن فایل گمشده. مستند رسمی clean تفاوت preview و حذف را توضیح می‌دهد.

Representative output may list untracked paths; -n means no deletion happens, and -d includes directories in the preview. Even if the list looks harmless, we do not run the real deletion command in a rescue room. -x expands the target to ignored files too. This is for deliberate cleanup, not finding a missing file. See the official git-clean manual.

preview مجوز اجرای حذف نیستA preview is not permission to delete

اگر مسیر ناشناخته است، از آن بیرون کپی بگیر یا مالکیتش را روشن کن. فهرست git clean -n فقط می‌گوید Git چه چیزی را حذف می‌کند؛ نمی‌گوید آن فایل برای تو بی‌ارزش است.

If a path is unfamiliar, copy it elsewhere or identify its owner. git clean -n only says what Git would remove; it does not say the file is disposable.

مینی‌پروژه: اتاق نجاتMini-project: the rescue room

آزمایش دورریختنی · حدود ۴۵ دقیقهDisposable incident lab · about 45 minutes

چهار نشانه، دو clone، بدون پاک‌سازی قهرمانانه

Four symptoms, two clones, no heroic cleanup

این پروژه مثل یک اتاق نجات است: چند مشکل هم‌زمان جلویت می‌گذاریم، ولی هیچ دستور مرحله‌به‌مرحله‌ای در شروع نمی‌دهیم. اول باید وضعیت را ثبت کنی، بعد بفهمی کدام داده هنوز در Git یا clone دیگر وجود دارد و فقط بعد از آن دست به تعمیر بزنی.

A team reports that feature history vanished, an important commit is missing from log, a teammate’s branch moved backward after a force-push, and a local note disappeared. Your job is not to find the fastest command; show what evidence locates stored data, what you preserved safely, and where Git has no answer.

مرز ایمنی

Safety boundary

فقط زیر پوشه‌ای تازه به نام rescue-room کار کن. دستورات ساخت رخداد عمداً شاخه را حذف، reset یا push اجباری می‌کنند؛ آن‌ها را هرگز در مخزن کاری اجرا نکن. همهٔ تمرین روی دو clone دورریختنی و یک bare مخزن راه دور محلی است. هیچ‌جا gc یا prune اجرا نکن.

Work only beneath a newly created rescue-room directory. The incident setup deliberately deletes a branch, resets, and force-pushes; never run it in a working repository. Everything happens in two disposable clones and a local bare remote. Do not run gc or prune.

مخزن حادثه را بساز

Create the incident repositories

فقط در پوشهٔ تازه / fresh disposable directory only
mkdir rescue-room
cd rescue-room
git init --bare --initial-branch=main remote.git
git clone remote.git writer
git -C writer config user.name "Rescue Learner"
git -C writer config user.email "rescue@example.test"
printf 'base\n' > writer/app.txt
git -C writer add app.txt
git -C writer commit -m "base"
git -C writer push -u origin main
git clone remote.git witness
git -C witness config user.name "Witness"
git -C witness config user.email "witness@example.test"

حالا رخدادهای محلی را دقیقاً با فرمان‌های زیر در writer بساز. همهٔ reset --hardها فقط روی clone دورریختنی‌اند. در پایان شناسه‌ شیءها را یادداشت کن؛ قرار نیست جواب نجات در صفحه داده شود.

Create the local incidents in writer with the commands below. Every reset --hard is confined to the disposable clone. Record the OIDs; the rescue answer is intentionally not supplied.

فقط در writer دورریختنی / disposable writer only
git -C writer switch -c lost-feature
printf 'feature one\n' > writer/feature.txt
git -C writer add feature.txt
git -C writer commit -m "feature one"
printf 'feature two\n' >> writer/feature.txt
git -C writer commit -am "feature two"
git -C writer switch main
git -C writer branch -D lost-feature

git -C writer switch -c reset-case
printf 'reset one\n' > writer/reset.txt
git -C writer add reset.txt
git -C writer commit -m "reset checkpoint one"
printf 'reset two\n' >> writer/reset.txt
git -C writer commit -am "reset checkpoint two"
git -C writer rev-parse HEAD
git -C writer reset --hard HEAD~2

git -C writer switch --detach main
printf 'detached work\n' > writer/detached.txt
git -C writer add detached.txt
git -C writer commit -m "detached work"
git -C writer rev-parse HEAD
git -C writer switch main

برای رخداد مشترک یک commit تازه روی main بساز و push کن؛ clone دوم آن را fetch کند. سپس فقط writer را به عقب ببر و با --force-with-lease push کن. شاهد را fetch دوباره نکن تا اشاره‌گر قدیمی‌اش بماند.

Create and push one new commit on main; fetch it in the second clone. Then move only writer backward and push with --force-with-lease. Do not fetch again in witness, so its older remote-tracking ref remains available.

incident مشترک، فقط clone دورریختنی / shared incident in disposable clones only
git -C writer switch main
printf 'shared commit\n' >> writer/app.txt
git -C writer commit -am "shared work"
git -C writer push
git -C witness fetch origin
git -C writer reset --hard HEAD~1
git -C writer push --force-with-lease origin main

برای پروندهٔ ردیابی‌نشده، فایل را داخل writer بساز تا وضعیت آن را با ?? نشان دهد؛ بعد فقط همان فایل آزمایش را حذف کن. Git Bash فرض شده است.

For the untracked-file ticket, create the file inside writer so status shows ??, then delete only that named lab file. These shell examples assume Git Bash.

فقط فایل آزمایش / exact disposable file only
printf 'never staged or committed\n' > writer/never-tracked.txt
git -C writer status --short
rm writer/never-tracked.txt

پرونده‌هایی که باید حل کنی

Incident tickets to solve

  1. شاخهٔ حذف‌شدهDeleted branch

    نام lost-feature دیگر وجود ندارد. دو commit موردنیاز را با مدرک پیدا کن، قبل از حرکت شاخه آن‌ها را بشناس و به نامی پایدار وصل کن.

    The name lost-feature no longer exists. Locate its two commits with evidence, inspect them, and give them stable names before moving any branch.

  2. reset سختHard reset

    در reset-case دو commit در log عادی دیده نمی‌شوند. شناسه‌ها را پیدا کن، اشاره‌گر نجات بساز و ثابت کن پوشه‌ کاری اکنون و commit بازیابی‌شده را با هم اشتباه نگرفته‌ای.

    Two commits on reset-case no longer appear in ordinary log. Find their IDs, create rescue refs, and prove you have not confused the current working tree with the recovered commits.

  3. commit در جداشده HEADCommit made in detached HEAD

    یک commit ساخته شده، اما نام شاخه‌ای جلو نرفته. شناسه‌ شیء را مشخص کن و یک اشاره‌گر بساز تا از حالت بی‌نام بیرون بیاید.

    A commit exists, but no branch name advanced. Identify its OID and create a ref so it is no longer unnamed.

  4. push اجباری اشتباهAccidental force-push

    در witness شناسه‌ شیء قدیمی را پیدا و بررسی کن؛ یک شاخهٔ rescue با push معمولی منتشر کن. تا وقتی تیم تأیید نکرده، main را دوباره force نکن.

    In witness, locate and inspect the old OID, then publish a rescue branch with a normal push. Do not force-push main again until the team agrees.

  5. فایل ردیابی‌نشده حذف‌شدهDeleted untracked file

    در writer فایل یادداشت را بساز تا وضعیت آن را ?? نشان دهد؛ آن را نه ناحیه‌ آماده‌سازی و نه commit کن، سپس فقط همان فایل تمرینی را حذف کن. گزارش بده Git چه چیزی برای بازیابی دارد و بیرون Git کجا باید دنبال نسخه بگردی.

    Create a note inside writer so status shows ??; never stage or commit it, then delete only that lab file. Report what Git has to recover and which external locations you would check.

معیار تحویل

Acceptance criteria

  • برای هر رخداد: نشانه، خروجی/شناسهٔ مدرک، تشخیص، تعمیر یا محدودیت بازیابی، و یک بررسی نهایی ثبت شده باشد.
  • For each incident, record the symptom, evidence/ID, diagnosis, repair or recovery limit, and one final verification.
  • هیچ شناسه‌ شیء نجات‌یافته‌ای بدون بررسی با show به شاخه اصلی وصل نشده باشد.
  • No recovered OID is attached to a primary branch without being inspected with show.
  • نسخهٔ commit گمشده در clone دوم با یک اشاره‌گر تازه قابل مشاهده باشد؛ reflog سرور را مفروض نگرفته باشی.
  • The missing commit is visible under a new ref from the second clone; no server reflog was assumed.
  • برای فایل ردیابی‌نشده فقط نسخهٔ بیرونی را بررسی کرده باشی و قول بازیابی Git نداده باشی.
  • For the untracked file, you checked external copies and made no Git-recovery promise.
  • git status --short --branch و git log --all --graph --decorate --oneline برای وضعیت پایانی نگه داشته شده باشد.
  • Keep final git status --short --branch and git log --all --graph --decorate --oneline output.
اگر یک commit در خروجی اول پیدا نشدIf a commit is missing from the first view

تفاوت سه پرسش را نگه دار: branch/ref هنوز هست؟ reflog محلی حرکتی ثبت کرده؟ شیء در مخزن مانده؟ اگر جواب همه منفی است، clone دوم و نسخه‌های بیرونی را بررسی کن؛ و اگر باز هم مدرکی نداری، نتیجه را به‌عنوان «نامعلوم/غیرقابل‌تضمین» گزارش کن.

Keep three questions separate: does a branch/ref remain, did a local reflog record movement, and does the object remain in the repository? If all are negative, inspect a second clone and external copies; if evidence is still absent, report the result as unknown/not guaranteed.

۱۸ پروندهٔ رخداد18 incident tickets

هر پرونده را با دستور شروع نکن؛ با مشاهده شروع کن. گاهی حرفه‌ای‌ترین جواب این است که «فعلاً هیچ دستور مخربی نزن» و گاهی هم باید صریح بگویی «Git این فایل را هرگز ذخیره نکرده و بازیابی‌اش را تضمین نمی‌کند».

Start every answer with evidence. If the right answer is “do nothing destructive yet” or “Git cannot guarantee recovery,” say so and name the next check.

معیار موفقیت هر پرونده: وضعیت را از روی شاهد درست تشخیص بده، اقدام امن بعدی یا مرز بازیابی را روشن بگو و ادعایی فراتر از مدرک نکن.

Success criterion for every ticket: diagnose from the right evidence, state the safe next action or recovery limit, and claim no more than the evidence supports.

پرونده ۱Ticket 1تشخیصTriage

همکارت می‌گوید روز کاری‌اش گم شده. هنوز چیزی اجرا نکرده. در یک دقیقهٔ اول چه چیزهایی ثبت می‌کنی و کدام فرمان‌ها را فعلاً ممنوع می‌کنی؟

A teammate says a day of work is gone and has run nothing yet. What do you record in the first minute, and which commands do you temporarily rule out?

راهنماHint

پوشهٔ کاری، ناحیه‌ آماده‌سازی، شاخه/HEAD، stash، clone دیگر را جدا بپرس.

Ask separately about working tree, index, branch/HEAD, stash, and another clone.

پاسخ و دلیلReasoning

وضعیت، شاخه/HEAD، گراف و reflog را ثبت می‌کنم؛ مسیرهای ردیابی‌نشده و stash را هم می‌شمارم و می‌پرسم چه چیزی push شده یا در clone دیگر هست. تا آن‌جا reset --hard، clean، gc و prune ممنوع‌اند. این فهرست محل‌های احتمالی را باز می‌کند، ولی هنوز وجود نسخه را ثابت نمی‌کند.

Record status, branch/HEAD, graph, and reflog; enumerate untracked paths and stashes, and ask what was pushed or exists in another clone. Until then, rule out reset --hard, clean, gc, and prune. This list opens possible locations but does not prove a copy exists.

پرونده ۲Ticket 2خواندن وضعیتRead status

خروجی ## HEAD (no branch)، M src/app.js و ?? notes.txt است. کدام دو نسخهٔ فایل در Git معلوم‌اند و کدام مسیر نیازمند بررسی بیرونی است؟

Output shows ## HEAD (no branch), M src/app.js, and ?? notes.txt. Which file versions are represented in Git, and which path needs an external check?

راهنماHint

M برای فایل ردیابی‌شده است؛ ?? هنوز وارد ناحیه‌ آماده‌سازی نشده.

M marks a tracked file; ?? has not entered the index.

بیا بازش کنیمInterpretation

src/app.js نسخهٔ commit/index و نسخهٔ پوشه‌ کاری دارد؛ ابتدا diff و آماده‌شده diff را جدا ببین. notes.txt در این لحظه ردیابی‌نشده است، پس اگر پاک شده و هرگز ناحیه‌ آماده‌سازی نشده، Git محتوا ندارد. ممکن است نسخهٔ بیرونی یا stash/worktree دیگری وجود داشته باشد؛ وضعیت به‌تنهایی تاریخچهٔ کامل فایل را نمی‌گوید.

src/app.js has commit/index and working-tree versions; inspect unstaged and staged diffs separately. notes.txt is currently untracked, so if deleted and never staged, Git has no content for it. An external copy or another stash/worktree may exist; status alone does not reveal the file’s whole history.

پرونده ۳Ticket 3شاخهٔ اشتباهWrong branch

commit add export option روی main است، ولی باید در قابلیت باشد. همکار می‌گوید «همین الان main را reset کن». پاسخ امن‌تر و شرطش چیست؟

Commit add export option landed on main but belongs on a feature branch. A teammate says, “Reset main now.” What is safer, and under what condition?

راهنماHint

ابتدا ببین commit push یا share شده یا نه و مقصد آن را ندارد.

First check whether it was pushed/shared and whether the target already contains it.

راه‌حل و توضیحDecision

شناسه‌ شیء را با show وارسی و اشاره‌گر نجات بساز. اگر commit فقط محلی و خصوصی است، می‌توانی با هماهنگی مقصد شاخه آن را cherry-pick کنی؛ اما اگر مشترک شده، reset شاخهٔ مشترک را جابه‌جا می‌کند. از commit تازه یا revert طبق وضعیت استفاده کن، نه پاک‌کردن کور.

Inspect the OID with show and create a rescue ref. If the commit is private and local, it can be cherry-picked to the target; if shared, resetting moves a shared branch. Prefer a new commit or revert according to state over blind deletion.

پرونده ۴Ticket 4شاخه حذف‌شدهDeleted branch

git branch -D feature/report اجرا شد. آیا حالا حتماً commitها gone هستند؟ کدام دو سرچشمه را جدا می‌گردی؟

git branch -D feature/report was run. Are the commits necessarily gone? Which two sources do you inspect separately?

راهنماHint

نام شاخه و شیءهای زیرش را یکی نگیر.

Do not confuse the branch name with the objects it pointed to.

چرا این جواب درست استAnswer

خیر. reflogهای در دسترس را برای شناسه‌ شیء بگرد؛ حذف شاخه ممکن است reflog همان شاخه را هم برداشته باشد، پس HEAD reflog و git fsck --full --no-reflogs --unreachable را نیز در مخزن مناسب بررسی کن. هر commit نامزد را با show بشناس و قبل از پاک‌سازی اشاره‌گر بساز. وجود نسخه تضمین نیست.

No. Search available reflogs for an OID; deleting a branch may also remove its branch reflog, so inspect HEAD’s reflog and git fsck --full --no-reflogs --unreachable in the appropriate repository. Identify candidates with show and create refs before cleanup. A surviving copy is not guaranteed.

پرونده ۵Ticket 5reset سختHard reset

بعد از reset --hard HEAD~3 سه commit از log رفته‌اند. HEAD@{1} را دیده‌ای، اما کافی است همان را فوراً reset کنی؟

After reset --hard HEAD~3, three commits vanished from log. You see HEAD@{1}; is it safe to reset to it immediately?

راهنماHint

شمارهٔ reflog با هر حرکت عوض می‌شود؛ اول شناسه‌ شیء و محتوا را بازرسی کن.

Reflog indices move as actions occur; inspect the OID and content first.

بررسی جوابReasoning

نه. reflog را تازه بخوان، شناسه‌ شیء دقیق را بردار، با show --stat و والدهایش تأیید کن، بعد شاخه نجات بساز. این کار commitهای ذخیره‌شده را نگه می‌دارد؛ تغییرهای آماده‌نشده که hard reset قبلاً overwrite کرده تضمیناً داخل reflog نیستند.

No. Reread the current reflog, copy the exact OID, inspect it and its parents with show, then create a rescue branch. This preserves stored commits; unstaged edits overwritten by hard reset are not guaranteed to be in a reflog.

پرونده ۶Ticket 6rebaseRebase

rebase هنوز تعارض دارد. همکار پیشنهاد می‌دهد دستی فایل‌ها را پاک و از نو بسازی. گزینهٔ کم‌ریسک چیست و قبل از آن چه می‌کنی؟

A rebase is still conflicted. A teammate suggests deleting files and rebuilding them manually. What is the lower-risk option, and what do you do first?

راهنماHint

در این مورد operation هنوز فعال است.

Here the operation is still in progress.

پاسخ پیشنهادیAnswer

اول وضعیت، ناحیه‌ آماده‌سازی و تغییرهای محلی را ثبت/حفظ کن. اگر تغییرهای مهمی نیست و می‌خواهی rebase را کنار بگذاری، git rebase --abort را اجرا کن، بعد همان وضعیت و گراف را دوباره مقایسه کن. اگر operation تمام شده، abort دیگر راه‌حل نیست؛ شناسه‌ شیء قبلی را از reflog پیدا و قبل از حرکت اشاره‌گر نام‌گذاری کن.

Record status, index, and local edits first. If there is no valuable unpreserved work and you want to abandon the rebase, run git rebase --abort, then compare the same status and graph again. If it already completed, abort no longer applies; locate the old OID in reflog and name it before moving refs.

از اینجا پرونده‌ها سخت‌تر می‌شوند، چون چند سرنخ هم‌زمان وجود دارد. قبل از حرکت، یک جدول کوچک برای خودت بساز: «چه چیزی گم به نظر می‌رسد؟ آخرین بار کجا ذخیره شده؟ چه نام یا clone دیگری ممکن است هنوز به آن برسد؟»

From here the incidents get harder because several clues exist at once. Before moving anything, make a small table: “what looks lost, where was it last stored, and which name or clone may still reach it?”

پرونده ۷Ticket 7جداشده HEADDetached HEAD

در جداشده HEAD یک commit ساخته‌ای و حالا هنوز همان‌جا هستی. ساده‌ترین قدم پیشگیرانه چیست؟ اگر قبلاً شاخه عوض کرده‌ای، چه مدرکی می‌خواهی؟

You made a commit in detached HEAD and are still there. What is the simplest preventive step? If you already switched branches, what evidence do you need?

راهنماHint

اگر HEAD هنوز روی همان commit است، نیازی به جست‌وجوی کور در شیءها نیست.

If HEAD still points at the commit, there is no need for a blind object hunt.

پاسخ و دلیلLow-risk path

در همان لحظه git switch -c rescue/detached-work اجرا کن تا commit نام بگیرد. اگر قبلاً جابه‌جا شدی، reflog/known شناسه‌ شیء را بخوان، commit را با show بررسی کن و سپس شاخه بساز. اگر شناسه‌ شیء پیدا نشد یا شیء نگه‌داری نشده، بازیابی را تضمین نکن.

Run git switch -c rescue/detached-work immediately to name the commit. If you already moved away, inspect the reflog or known OID, verify it with show, then create a branch. If no OID is found or the object was not retained, do not guarantee recovery.

پرونده ۸Ticket 8cherry-pick نیمه‌کارهCherry-pick in progress

یک cherry-pick تعارض خورده و تغییرهای قبل از آن هم مهم‌اند. آیا --abort را بی‌ثبت وضعیت می‌زنی؟

A cherry-pick conflicts, and pre-existing changes matter too. Would you run --abort without recording state?

راهنماHint

abort operation را لغو می‌کند، اما یادداشت وضعیت قبل، امکان مقایسه می‌دهد.

Abort cancels the operation; a before-state record lets you compare what happened.

بیا بازش کنیمAnswer

نه. وضعیت، diff و ناحیه‌ آماده‌سازی را نگه دار و اگر ممکن است patch/کپی امن بساز. بعد اگر قصد لغو داری git cherry-pick --abort بزن و دوباره همان‌ها را بخوان. abort را با recovery دلخواه یکی ندان؛ نتیجه را با شاهد قبل مقایسه کن.

No. Save status, diff, and index evidence and make a safe patch/copy if possible. If you choose to cancel, run git cherry-pick --abort and inspect them again. Do not equate abort with arbitrary recovery; compare against the recorded before-state.

پرونده ۹Ticket 9merge اشتباهWrong merge

merge commit اشتباه به شاخهٔ مشترک push شده. reset --hard HEAD^ یا revert -m 1؟ چه چیزی انتخاب را تعیین می‌کند؟

A wrong merge commit was pushed to a shared branch. reset --hard HEAD^ or revert -m 1? What determines the choice?

راهنماHint

آیا تاریخچه را دیگران گرفته‌اند؟ اگر merge revert می‌شود، کدام والد خط اصلی بود؟

Have others consumed the history? If reverting a merge, which parent was the mainline?

راه‌حل و توضیحReasoning

روی شاخه مشترک معمولاً commit تازه‌ای با revert می‌سازیم تا اشاره‌گر مشترک را عقب نکشیم. برای merge باید والد اصلی را با گراف/show مشخص کرد؛ -m 1 را حفظ نکن مگر والد اول واقعاً خط مقصد باشد. اگر merge هنوز commit نشده، ابتدا حفظ وضعیت و سپس merge --abort را بسنج.

On a shared branch, usually add a revert commit rather than moving the shared pointer backward. For a merge, identify the mainline parent from graph/show; do not use -m 1 unless the first parent is actually the target line. If it is uncommitted, preserve state and consider merge --abort.

پرونده ۱۰Ticket 10clone دومSecond clone

همکار می‌گوید «مخزن راه دور reflog را ببین» تا شاخه حذف‌شده را پیدا کند. کاربر عادی به کدام شواهد واقعی‌تر تکیه می‌کند؟

A teammate says, “Check the remote reflog” to find a deleted branch. Which evidence is more realistic for an ordinary user?

راهنماHint

چه کسی محلی reflog دارد؟ clone دوم چه چیزی از قبل fetch کرده؟

Who owns local reflogs? What did a second clone fetch earlier?

چرا این جواب درست استAnswer

دنبال clone همکار، اشاره‌گر رهگیریِ مخزن راه دور قدیمی، شناسه‌ شیء ثبت‌شده در log/build یا branch/tag نجات بگرد. reflog در clone خود هر نفر محلی است و معمولاً کاربر به دفترچهٔ سرور دسترسی ندارد. هر شناسه باید قبل از push دوباره با show بررسی شود.

Look for a teammate’s clone, an old remote-tracking ref, an OID recorded by logs/builds, or an existing rescue branch/tag. Reflogs are local to each clone, and ordinary users generally cannot inspect the server’s journal. Verify every ID with show before publishing it.

پرونده ۱۱Ticket 11push اجباریForce-push

بعد از push اجباری، شاخه مخزن راه دور به commit قدیمی برگشته ولی clone دوم شناسه‌ شیء تازه را دارد. اولین push تو باید به main force شود؟

After a force-push, the remote branch moved back but a second clone has the newer OID. Should your first push force main forward?

راهنماHint

اول commit را نام‌گذاری و از حذف دوباره محافظت کن.

Name and protect the commit before changing a shared branch again.

بررسی جوابAnswer

نه. شناسه‌ شیء را در clone دوم بازرسی کن، یک شاخه rescue بساز و با push معمولی همان شاخهٔ جدید را منتشر کن. سپس با صاحب شاخه، CI و مصرف‌کننده‌ها هماهنگ کن که آیا main باید جابه‌جا شود. push نجات یک اشاره‌گر جدا می‌سازد؛ به‌تنهایی همهٔ cloneها را اصلاح نمی‌کند.

No. Inspect the OID in the second clone, create a rescue branch, and publish that new branch with a normal push. Then coordinate with branch owners, CI, and consumers about whether main should move. The rescue push creates a separate ref; it does not repair every clone.

پرونده ۱۲Ticket 12secretSecret

یک API key روی شاخه خصوصی commit و push شده، اما PR هنوز باز است. آیا اول commit را از شاخه پاک می‌کنی؟

An API key was committed and pushed to a private branch, but the PR is still open. Do you remove the commit from the branch first?

راهنماHint

خصوصی‌بودن شاخه اعتبار اعتبارنامه را تضمین نمی‌کند.

A private branch does not guarantee credential safety.

پاسخ پیشنهادیCorrect order

اول دسترسی را revoke/rotate کن و مسئول سرویس را امن مطلع کن. secret را دوباره در گزارش یا issue کپی نکن. بعد از ارزیابی دامنه و هماهنگی cloneها، تاریخچه را پاک‌سازی و از میزبان دربارهٔ cacheها بپرس. پاک‌کردن شاخه رمز قبلاً افشاشده را زنده نمی‌کند.

First revoke/rotate access and securely notify the service owner. Do not repeat the secret in a report or issue. Then assess exposure, coordinate clones, rewrite history if appropriate, and ask the host about caches. Deleting the branch does not revive an exposed credential.

تمرین‌های آخر عمداً یک مورد غیرقابل‌نجات هم دارند. مهارت حرفه‌ای فقط برگرداندن داده نیست؛ تشخیص جایی است که Git هیچ مدرکی ندارد و نباید وعدهٔ بی‌پشتوانه بدهی.

The final exercises deliberately include an unrecoverable Git case. Professional recovery is not only about bringing data back; it is also about recognizing when Git has no evidence and refusing to make an unsupported promise.

پرونده ۱۳Ticket 13corruptionCorruption

git fsck --full گزارش missing blob داده. آیا تکرار fsck با flagهای بیشتر blob را بازسازی می‌کند؟

git fsck --full reports a missing blob. Will rerunning it with more flags reconstruct the blob?

راهنماHint

ابزار صحت‌سنجی با منبع نسخه‌ پشتیبان یکی نیست.

A verifier is not a backup source.

پاسخ و دلیلAnswer

نه. کپی مخزن را حفظ کن، شناسه‌ شیء را ثبت کن و clone/backup سالمی پیدا کن که همان blob را داشته باشد. fsck می‌گوید چه رابطه یا شیءی ناقص است؛ اگر هیچ منبع سالمی وجود ندارد، نمی‌تواند محتوای مفقود را بسازد.

No. Preserve a repository copy, record the OID, and find a healthy clone/backup containing that blob. Fsck identifies a missing object or link; without a healthy source, it cannot create the missing content.

پرونده ۱۴Ticket 14ردیابی‌نشدهUntracked

فایل ideas.md در وضعیت به شکل ?? بود، پاک شده و هیچ‌کس ناحیه‌ آماده‌سازی نکرده است. چه چیزی را می‌توانی با اطمینان بگویی؟

ideas.md showed as ??, was deleted, and nobody staged it. What can you state confidently?

راهنماHint

Git فقط شیءی می‌تواند پیدا کند که زمانی گرفته باشد.

Git can only search for an object it once received.

بیا بازش کنیمRecovery boundary

در این فرض Git blobی از فایل نساخته، پس reflog/fsck از خود Git محتوایی ندارد که برگرداند. نسخه‌ پشتیبان، سطل زباله، ویرایشگر تاریخچه، عکس فوری یا clone انسانی را بررسی کن. نمی‌توانی بگویی هیچ نسخه‌ای در دنیا نیست؛ فقط Git نسخه‌ای ذخیره نکرده است.

Under these assumptions Git never made a blob, so reflog/fsck has no Git-stored content to restore. Check backups, recycle bin, editor history, snapshots, or a teammate’s clone. You cannot claim no copy exists anywhere—only that Git did not store one.

پرونده ۱۵Ticket 15بدون تغییرClean

مشاور می‌گوید برای دیدن فایل‌های زائد، git clean -fd اجرا کن و بعد از روی Trash برگردان. قدم امن جایگزین چیست؟

A consultant says to run git clean -fd to see junk files and restore them from Trash later. What is the safe alternative?

راهنماHint

پیش‌نمایش هم فرمان جدا دارد.

There is a separate preview mode.

راه‌حل و توضیحAnswer

هیچ‌وقت برای «دیدن» فرمان حذف واقعی را اجرا نمی‌کنم. در پوشهٔ درست git clean -nd فقط فهرست احتمالی را نشان می‌دهد؛ مسیرها را بررسی و هر مورد مهم را بیرون مخزن کپی می‌کنم. Trash راه برگشت تضمینی نیست.

Never run the real deletion command merely to preview. In the correct repository, git clean -nd lists candidates only; inspect paths and copy anything valuable outside the repository. A trash bin is not a guaranteed undo.

پرونده ۱۶Ticket 16push شده؟Was it pushed?

commit روی شاخه فعلی نیست، اما git log --all هم آن را نشان نمی‌دهد. آیا می‌توان نتیجه گرفت شیء پاک شده؟

The commit is not on the current branch, and git log --all does not show it. Can you conclude its object was deleted?

راهنماHint

--all همهٔ اشاره‌گرهاست، نه همهٔ شیءهای مخزن.

--all means all refs, not all repository objects.

چرا این جواب درست استAnswer

نه. log تاریخچهٔ قابل‌دسترسی از اشاره‌گرها را نمایش می‌دهد. reflog و در کپی مناسب fsck را بررسی کن؛ clone دیگر و شناسه‌ شیء شناخته‌شده را هم بپرس. اگر پیدا نشد فقط می‌گویی در سرچشمه‌های فعلی با شواهد فعلی پیدا نشد، نه اینکه حتماً پاک شده یا حتماً هست.

No. Log displays history reachable from refs. Inspect reflogs and, on an appropriate copy, fsck; also ask about another clone and known OIDs. If absent, report only that current sources did not find it—not that it is certainly gone or present.

پرونده ۱۷Ticket 17انتخاب روشChoose a method

یک commit بد به main مشترک رسیده و همه آن را fetch کرده‌اند. owner می‌خواهد «log را تمیز کند». بین reset و revert چه مدرکی مهم است؟

A bad commit reached shared main and everyone fetched it. The owner wants to “clean up the log.” What evidence matters when choosing reset or revert?

راهنماHint

تاریخچهٔ عمومی را چه cloneهایی مصرف کرده‌اند و هدف اصلاح چیست؟

Which clones consumed the shared history, and what is the correction meant to achieve?

بررسی جوابDecision and cost

چون commit را همه گرفته‌اند، reset تاریخچهٔ مشترک را بازنویسی و مصرف‌کننده‌ها را مجبور به هماهنگی می‌کند. revert معمولاً commit تازه‌ای می‌سازد که اثر قبلی را معکوس می‌کند و اصل را در تاریخچه نگه می‌دارد. اگر secret بوده، اول rotate؛ انتخاب فنی آن را پس نمی‌گیرد.

Since everyone consumed the commit, reset rewrites shared history and forces consumers to coordinate. Revert usually adds a new commit that reverses the effect while preserving the original in history. If it contained a secret, rotate it first; neither choice recalls the credential.

پرونده ۱۸Ticket 18گزارش حادثهIncident report

دو ساعت گذشته؛ reflog محلی رد نشان نمی‌دهد، fsck هم شناسه‌ شیء نامزد نداده و clone همکار در دسترس نیست. مدیر می‌خواهد قول بدهی تا ظهر همه‌چیز برمی‌گردد. چه می‌گویی و چه گزینه‌ای هنوز باقی است؟

Two hours have passed; the local reflog has no entry, fsck found no candidate OID, and the teammate’s clone is unavailable. A manager wants you to promise everything will be back by noon. What do you say, and what options remain?

راهنماHint

نبودن شاهد در چند جا هنوز برابر اثبات حذف ابدی نیست؛ اما مجوز قول هم نیست.

Missing evidence in several places does not prove permanent deletion—but it does not justify a promise.

پاسخ پیشنهادیHonest report

می‌گویم در مخزن فعلی ref/object قابل‌تأیید پیدا نشده و بازیابی Git فعلاً اثبات‌پذیر نیست؛ زمان تحویل را تضمین نمی‌کنم. از teammate نسخه‌ پشتیبان، ویرایشگر محلی تاریخچه، system عکس فوری، لپ‌تاپ دیگر و فایل تولیدیهای ساخت را با رضایت و دسترسی امن می‌خواهیم. تا آن زمان اصل داده را دست‌نخورده نگه می‌داریم و جست‌وجو را ثبت می‌کنیم.

I would report that no verifiable ref/object was found in the current repository and Git recovery is not yet demonstrated; I would not promise a delivery time. Ask about teammate backups, editor local history, system snapshots, another laptop, and build artifacts through authorized access. Preserve the original data and document the search.

یادآوری پیش از خروج از اتاق نجاتBefore leaving the rescue room

برای هر commitی که پیدا کردی: شناسه‌ شیء را با show بشناس، اشاره‌گر نجات بساز، با show-ref آن را تأیید کن و بعد برای تغییر بعدی تصمیم بگیر. این چهار کار را با «همه‌چیز درست شد» یکی نگیر؛ فایل‌های پوشه‌ کاری، pushها و cloneهای دیگر جداگانه تأیید می‌خواهند.

For every commit you find: inspect the OID with show, create a rescue ref, verify it with show-ref, then decide the next change. Do not confuse these steps with “everything is fixed”; working-tree files, pushes, and other clones need separate verification.

از این‌جا به بعد فرمان تازه‌ای پشتش پنهان نیستFrom here, there is no new command to hide behind

اینجا آموزش ابزار تازه تمام می‌شود. سه پروژهٔ پایانی دیگر نردهٔ آموزشی ندارند: اول باید تاریخچه را خودت طراحی کنی، بعد با یک تیم واقعی‌تر جلو بروی و در آخر یک مخزن خراب را با شاهد و بدون حدس نجات بدهی.

Chapter 16 ends the teaching arc for new tools, not the practice of Git. The final projects ask you to design history, collaborate with a team, and recover when something slips using the model built since Chapter 01. A good question does not always begin with “Which command should I run?” Sometimes it starts with “What does Git still have right now?”

گزارش نجات منMy rescue report

  • نشانه و زمان وقوع را ثبت کردم.
  • I recorded the symptom and when it happened.
  • وضعیت، اشاره‌گرها، شناسه‌ شیءها و cloneهای بررسی‌شده را مشخص کردم.
  • I listed status, refs, OIDs, and clones examined.
  • ref/patch/copyهای حفاظتی و محدودیت هرکدام را می‌دانم.
  • I know which refs/patches/copies protect what—and their limits.
  • پس از تعمیر، همان بررسی اولیه را تکرار کردم.
  • After repair, I repeated the original check.
  • برای چیزی که Git ذخیره نکرده، قول بازیابی نداده‌ام.
  • I made no recovery promise for content Git never stored.

نقشهٔ سریع اتاق نجاتRescue-room quick reference

نشانهSymptomاولین شاهدFirst evidenceمرزLimit
commit از log گم استCommit missing from logreflog --all؛ بعد fsck در مخزن مناسبممکن است شیء منقضی یا حذف شده باشدThe object may have expired or been removed
شاخه اشتباه حرکت کردهWrong branch movedstatus، graph، showاگر مشترک است reset را خودسرانه تحمیل نکنDo not unilaterally reset a shared branch
push اجباری کار همکار را پنهان کردهForce-push hid a teammate’s workclone دوم یا شناسه‌ شیء قدیمیSecond clone or known old OIDreflog سرور را برای کاربر مفروض نگیرDo not assume user access to server reflog
secret وارد commit شدهSecret entered a commitاعتبار و زمان انتشار اعتبارنامهCredential validity and exposure timeاول revoke/rotate؛ rewrite افشا را پس نمی‌گیردRevoke/rotate first; rewrite cannot recall exposure
ردیابی‌نشده حذف شدهUntracked file deletedنسخه‌ پشتیبان، ویرایشگر، clone یا عکس فوریBackup, editor, clone, or snapshotاگر Git هرگز ذخیره نکرده، Git چیزی برای برگرداندن نداردIf Git never stored it, Git has nothing to restore
شیء ناقص/خرابMissing/corrupt objectgit fsck --full و clone سالمfsck تشخیص می‌دهد؛ منبع سالم لازم استFsck diagnoses; a healthy source is required

قاعدهٔ آخر دوره همان قاعدهٔ اول اتاق نجات است: قبل از پاک‌کردن چیزی، بفهم Git هنوز چه چیزی دارد. بعد یک تغییر کوچک بده و همان چیزی را که اول اندازه گرفتی دوباره بررسی کن.

The final rule is the one we began with: do not delete first; find out what Git still has. Then make one change, verify it, and report honestly.

اگر خواستی جزئیات فرمان را دنبال کنیFor the exact command behavior

برای گزینه‌ها و رفتار نسخهٔ نصب‌شده، راهنماهای رسمی reflog، fsck، reset، clean و push را ببین. دستور نجات را در مخزن دورریختنی تمرین کن؛ نام نسخهٔ تاریخچه، تنظیمات و مدت نگهداری reflog می‌تواند فرق کند.

For options and behavior in your installed Git version, consult the official manuals for reflog, fsck, reset, clean, and push. Practice recovery commands in a disposable repository; revision names, configuration, and reflog retention can differ.