reflog: تور نجات
Reflog: Git's local safety journal
گاهی commit هنوز در پایگاهداده هست، اما دیگر هیچ شاخه معمولی به آن اشاره نمیکند. reflog دفترچهٔ محلی حرکت اشارهگرهاست و اغلب همان ردّی را نگه میدارد که برای نجات لازم داری.
You moved two commits out of the ordinary log path with reset --hard. Is Git broken—or did you only move the branch name? Before running another command, ask: “Where is the record of that ref's earlier position?”
دو commit از دید log ناپدید شدندTwo commits vanished from log
دیروز سه commit روی main داشتی. امروز با عجله زدی git reset --hard HEAD~2. حالا git log --oneline فقط commit پایه را نشان میدهد. همکارت میگوید: «تاریخچه پاک شد.» آیا واقعاً commitها حذف شدهاند، یا فقط نامی که به آنها میرسید جابهجا شده؟
Yesterday, main had three commits. In a hurry today, you ran git reset --hard HEAD~2. Now git log --oneline shows only the base commit. A teammate says, “The history was deleted.” Were the commits actually erased, or did only the name that reached them move?
فصل ۰۹ یاد داد reset شاخه را حرکت میدهد و --hard فایلهای ردیابیشده را هم با مقصد جور میکند. اما شیءهای commit ممکن است هنوز در مخزن باشند. حالا باید سرنخ درست را بخوانیم: git reflog.
Chapter 09 showed that reset moves a branch and --hard also makes tracked files match the target. The commit objects may still be in the repository. Now we need the right clue: git reflog.
main را از C به A برد. reflog محلی حرکت اشارهگر را ثبت میکند، نه نسخهای جدا از همهٔ فایلها.main from C to A. The local reflog records the ref movement, not a separate copy of every file.reflog دفترچهٔ حرکت نامهاستA reflog is a journal of ref movements
Git یک شاخه را بهصورت نامی میبیند که به commit اشاره میکند. هر بار آن اشارهگر در مخزن محلی حرکت کند، reflog معمولاً ردّ حرکت قبلی و تازه را ثبت میکند. HEAD هم reflog خودش را دارد؛ برای همین رفتوآمد میان شاخهها و commitهای جداشده اغلب سرنخ میگذارد.
Git treats a branch as a name pointing to a commit. When that pointer moves in a local repository, a reflog usually records the old and new positions. HEAD has its own reflog too, which is why branch switches and detached-HEAD commits often leave clues.
| چیزی که میبینیWhat you inspect | چه چیزی میگوید؟What it tells you | چه چیزی را نمیگوید؟What it does not tell you |
|---|---|---|
git log | commitهای قابلدسترسی از اشارهگرهای انتخابشدهCommits reachable from the selected refs | همهٔ حرکتهای قبلی اشارهگرEvery earlier ref movement |
git reflog | رویدادهای ثبتشده برای HEAD در همین مخزنRecorded HEAD events in this repository | تاریخچهٔ مشترک یا هر بایت ویرایشShared history or every byte of an edit |
git fsck | شیءهایی که از ریشههای بررسیشده قابلدسترسی نیستندObjects unreachable from the roots being checked | اینکه شیء یتیم متعلق به کدام فایل/نسخه بودهWhich file or version an orphan belonged to |
HEAD@{0} یعنی آخرین جایگاه ثبتشدهٔ HEAD؛ HEAD@{1} جایگاه قبلی در دفترچه. این عدد، شمارهٔ والد commit نیست. برای حرکت در زنجیرهٔ والدها از HEAD~1 استفاده میکنی. این دو نحو شبیه هماند ولی یک سؤال را جواب نمیدهند.
HEAD@{0} means the latest recorded position of HEAD; HEAD@{1} is the previous reflog position. That number is not a commit-parent number. Use HEAD~1 to walk to a parent. The syntax looks similar, but the questions differ.
HEAD@{1} میتواند C باشد؛ اما HEAD~1 والد commit فعلی را میپرسد.HEAD@{1} may be C, while HEAD~1 asks for the current commit's parent.آزمایش امن: commitها را به مسیر برگردانSafe experiment: bring commits back into reach
این فرمان مخرب را فقط در مخزن تمرینی تازه اجرا کن. سه commit میسازیم، عمداً main را دو قدم عقب میبریم و بعد با خواندن reflog یک شاخه نجات میسازیم. قبل از شروع، پوشهٔ جداگانه انتخاب کن.
Run this destructive command only in a fresh practice repository. We will make three commits, deliberately move main back two steps, then use the reflog to create a rescue branch. Start in a separate disposable folder.
mkdir reflog-lab cd reflog-lab git init -b main git config user.name "Reflog Learner" git config user.email reflog@example.test printf 'A\n' > notes.txt git add notes.txt git commit -m "A: baseline" printf 'B\n' >> notes.txt git commit -am "B: add second line" printf 'C\n' >> notes.txt git commit -am "C: add third line" git log --oneline --decorate -3
خروجی log را نگه دار؛ شناسهٔ کامل commit C را هم با git rev-parse HEAD ثبت کن. حالا پیشبینی کن بعد از reset چه چیزی در log میماند و فایل روی دیسک چه محتوایی خواهد داشت.
Keep the log output and record C's full ID with git rev-parse HEAD. Predict what will remain in log and what the disk file will contain after the reset.
git reset --hard HEAD~2 git log --oneline --decorate git status --short cat notes.txt git reflog
a1a1a1a HEAD@{0}: reset: moving to HEAD~2
c3c3c3c HEAD@{1}: commit: C: add third line
b2b2b2b HEAD@{2}: commit: B: add second line
a1a1a1a HEAD@{3}: commit (initial): A: baselineحالا باید main روی A باشد و فایل هم فقط خط A را داشته باشد؛ این را reset واقعاً انجام داده. در reflog دنبال رویداد reset: moving to HEAD~2 بگرد. ثبت قبلی معمولاً C را نشان میدهد. به شمارهٔ ثابت تکیه نکن؛ پیام، زمان، شناسه و نتیجهٔ git show را با هم بسنج.
Now main should point to A and the file should contain only line A; reset really did that. In the reflog, find reset: moving to HEAD~2. The preceding entry commonly identifies C. Do not trust a memorised index: compare the message, time, ID, and git show.
git show --stat --oneline <C_ID_FROM_REFLOG> git branch rescue/after-reset <C_ID_FROM_REFLOG> git show-ref --verify refs/heads/rescue/after-reset git log --oneline --decorate --all
شاخهٔ نجات را با شناسهای بساز که واقعاً بررسی کردهای. آخرین فرمان باید دوباره B و C را در گراف نشان دهد، اما main هنوز روی A است. حالا commitها از اشارهگر قابلدسترسیاند و میتوانی با آرامش تصمیم بگیری ادامهٔ کار چه باشد؛ هنوز فایلهای C را روی main نیاوردهای.
Create the rescue branch from the ID you actually inspected. The final command should show B and C in the graph again while main stays at A. The commits are now reachable through a ref, so you can decide what to do next; you have not yet brought C's files onto main.
شمارهٔ دفترچه را با شمارهٔ والد اشتباه نگیرDo not confuse journal positions with parent numbers
عبارتهای زمانی را میتوان مستقیم به دستور بدهی. برای نمونه git show 'HEAD@{1}' شیئی را نشان میدهد که HEAD در رویداد قبلی به آن اشاره میکرد؛ git show HEAD~1 والد commit فعلی را میخواند. برای تاریخ هم HEAD@{yesterday} ممکن است مفید باشد، اما نتیجه به ثبتهای موجود و قالب زمان وابسته است.
You can pass time-based selectors directly to commands. For example, git show 'HEAD@{1}' displays the object HEAD pointed to at the previous event; git show HEAD~1 reads the current commit's parent. A date selector such as HEAD@{yesterday} can help too, but depends on available entries and date parsing.
| عبارتExpression | پرسشی که جواب میدهدQuestion it answers | تفسیر نکن بهعنوانDo not read it as |
|---|---|---|
HEAD@{0} | آخرین جایگاه ثبتشدهٔ HEAD کدام است؟What is HEAD's latest recorded position? | والد صفرمA “zeroth parent” |
HEAD@{1} | در رویداد قبلی HEAD کجا بود؟Where was HEAD at the previous event? | commit والد فعلیThe current commit's parent |
HEAD~1 | والد اول commit فعلی چیست؟What is the first parent of the current commit? | رویداد قبلی دفترچهThe previous journal event |
main@{yesterday} | اشارهگر محلی main در زمان تقریبی دیروز کجا بود؟Where was local main around yesterday? | تضمین نگهداری نامحدودA guarantee of indefinite retention |
اگر selector داخل PowerShell یا shell دیگری بد تفسیر شد، آن را داخل نقلقول بگذار؛ شکل نقلقول بسته به shell فرق دارد. قبل از ساختن rescue، شناسه را با git show --no-patch --format=fuller بررسی کن. reflogها میتوانند با هر فرمان بعدی شمارهگذاری تازهای پیدا کنند.
If a selector is misread by PowerShell or another shell, quote it; quoting syntax varies by shell. Before creating a rescue ref, inspect the ID with git show --no-patch --format=fuller. Later commands can change reflog numbering.
اول اشارهگر نجات بساز، بعد دربارهٔ ادغام تصمیم بگیرCreate a rescue ref first; decide how to integrate later
اگر commit موردنظر را پیدا کردی، آن را فقط در ذهن نگه ندار. یک نام تازه به آن وصل کن تا دیگر صرفاً به ثبت موقت reflog وابسته نباشد. اول شناسه را ببین، بعد شاخه بساز، و در پایان مقصد شاخه را ثابت کن.
Once you find the commit, do not leave it only in your notes. Attach a new name so it is no longer dependent on a temporary reflog entry. Inspect the ID, create a branch, then verify its target.
git show --no-patch --format=fuller <OID> git branch rescue/found-commit <OID> git show-ref --verify refs/heads/rescue/found-commit git log --oneline --decorate --all -5
این فرمانها هیچ چیزی را از شاخهٔ فعلی پاک نمیکنند. اما «نجات» با «بازگرداندن فایلها به main» یکی نیست؛ کار بعدی ممکن است مقایسه، cherry-pick یا ادغام باشد. تا وقتی هنوز مطمئن نیستی، اشارهگر نجات را نگه دار و مقصد را دستکاری نکن.
These commands do not remove anything from the current branch. But “rescue” is not the same as “put the files back on main”; the next step might be comparison, cherry-pick, or a merge. If uncertain, keep the rescue ref and leave the destination untouched.
شاخه پاک شده؛ اول دفترچهٔ HEAD را بخوانA branch was deleted; start with HEAD's journal
حذف شاخه معمولاً نامی را که به commit اشاره میکرد برمیدارد. reflog همان شاخه ممکن است همراهش پاک شود؛ پس فرض نکن git reflog show feature هنوز کار میکند. اگر قبل از حذف از شاخه بیرون آمدهای، reflog مربوط به HEAD میتواند رویداد آخرِ checkout و commitهای ساختهشده را نشان بدهد.
Deleting a branch removes the name that pointed to its commit. The branch's own reflog may be removed with it, so do not assume git reflog show feature still works. If you switched away before deleting it, HEAD's reflog can show the checkout and earlier commits.
git reflog --date=local git reflog show --all --date=local git show --stat --oneline <candidate-OID> git branch rescue/deleted-feature <candidate-OID> git show-ref --verify refs/heads/rescue/deleted-feature
اگر شاخه را در clone دیگری حذف کردهاند، reflog clone تو آن حرکت را ندارد؛ هر دفترچه محلی است. اگر هیچ رد مناسبی پیدا نشد، سراغ بخش شیءهای یتیم و git fsck برو. خروجی را با git show بررسی کن؛ صرف دیدن یک SHA دلیل نمیشود commit موردنظرت باشد.
If the branch was deleted in another clone, your reflog does not contain that event; each journal is local. If no useful entry appears, move to unreachable objects and git fsck. Inspect candidates with git show; seeing a SHA alone does not prove it is the commit you need.
rebase محلی نتیجهٔ بد داد؟ نوک شاخه قبلی را پیدا کنA local rebase went wrong? Find the previous tip
فصل ۰۷ نشان داد rebase commitهای تازه میسازد و شاخه را به آنها میرساند. اگر نتیجه خراب شد، git reflog show topic را بخوان و رویدادهای rebase را پیدا کن. ممکن است قبل و بعد از بازپخش چند ثبت ببینی؛ شماره را از حفظ انتخاب نکن.
Chapter 07 showed that rebase creates replacement commits and moves the branch to them. If the result is wrong, inspect git reflog show topic and locate the rebase events. You may see several entries from before and after replay; do not choose an index from memory.
git reflog show topic --date=local git show --stat --oneline <candidate-OID> git log --oneline --graph --decorate --all --boundary -12 git branch rescue/topic-before-rebase <candidate-OID> git show-ref --verify refs/heads/rescue/topic-before-rebase
شاخهٔ rescue فقط یک مقصد امن برای بررسی میسازد. حالا میتوانی تغییرهای دو طرف را مقایسه کنی و با دانستههای فصل ۰۷ تصمیم بگیری. اگر کار را روی مخزن راه دور push کردهای یا همکاران بر پایهاش commit ساختهاند، توقف کن و با تیم هماهنگ شو؛ بازیابی محلی مجوز push اجباری نیست.
The rescue branch creates a safe point for inspection. Now compare both lines and choose a next step using Chapter 07's model. If the work was pushed or teammates built on it, stop and coordinate; local recovery is not permission to force-push.
commit در جداشده HEAD گم بهنظر میرسدA detached-HEAD commit appears lost
در فصل ۰۵ دیدی HEAD گاهی مستقیم به commit اشاره میکند، نه به نام branch. اگر آنجا commit بسازی و بعد به main برگردی، main آن commit را دنبال نمیکند. اما رویداد commit معمولاً در reflog مربوط به HEAD سرنخ میگذارد.
Chapter 05 showed that HEAD can point directly to a commit instead of naming a branch. A commit made there is not automatically followed by main when you switch back. But the commit event usually leaves a clue in HEAD's reflog.
git reflog --date=local git show --stat --oneline <detached-commit-OID> git branch rescue/detached-work <detached-commit-OID> git log --oneline --decorate --all -8
بعد از ساخت شاخه، commit دیگر بینام نیست. قبل از آنکه بگویی «فایلها را برگرداندم»، با git show محتوای عکس فوری و با log والدش را بررسی کن. reflog کمک میکند نامزد را پیدا کنی؛ تصمیم اینکه این کار باید وارد کدام شاخه شود هنوز با توست.
After creating the branch, the commit is no longer unnamed. Before saying “the files are back,” inspect the snapshot with git show and its ancestry with log. The reflog helps locate a candidate; choosing which branch should receive it is still your decision.
محتوای ناحیه آمادهسازیشده ممکن است بماند، حتی وقتی reflog کمکی نمیکندStaged content may remain even when the reflog cannot help
فصل ۰۱ ثابت کرد git add محتوا را در پایگاه اشیا مینویسد. اگر بعداً آن محتوا از ناحیه آمادهسازی هم کنار برود، ممکن است blob بینام و بیاشارهگر شود. reflog فقط حرکت اشارهگرها را میبیند، نه فهرست blobهای ساختهشده؛ برای جستوجوی شیء یتیم ابزار دیگری لازم است.
Chapter 01 demonstrated that git add writes content into the object database. If that content later leaves the index too, its blob may become unnamed and unreferenced. The reflog records ref movement, not a catalog of created blobs; a different tool is needed to search for unreachable objects.
بیا در همان reflog-lab یک فایل تازه بساز، نسخهٔ اول را ناحیه آمادهسازی کن و بعد محتوای دیگری را دوباره ناحیه آمادهسازی کن. فایل روی دیسک و ناحیه آمادهسازی حالا نسخهٔ دوم را دارند؛ نسخهٔ اول commit یا اشارهگر ندارد، اما ممکن است blob آن هنوز در پایگاه اشیا باشد:
In the disposable reflog-lab, create a new file, stage version one, then stage different content. The disk and index now hold version two; version one has no commit or ref, but its blob may still be in the object database:
printf 'draft version one\n' > scratch.txt git add scratch.txt printf 'draft version two\n' > scratch.txt git add scratch.txt git fsck --no-reflogs --unreachable
unreachable blob 8c9b2f1…
فقط گزینهٔ آزمایشیای را باز کن که نوعش blob است و محتوایش با نسخهٔ گمشده جور درمیآید. شناسهها و شیءهای دیگر فرق میکنند. این راه ثابت نمیکند نام فایل یا نسخهٔ قبلیِ درست را یافتهای؛ آن را با یادداشتها، diffها یا کپی دیگری تطبیق بده.
Inspect only a candidate whose type is blob and whose content matches the missing version. IDs and other objects vary. This does not prove you found the right filename or version; compare against notes, diffs, or another copy.
git fsck --no-reflogs --unreachable git cat-file -t <candidate-OID> git cat-file -p <candidate-OID>
این بررسی ممکن است commit، tree یا blobهای زیادی را نام ببرد؛ شناسهٔ هرکدام را اول با -t طبقهبندی کن. اگر blob متن است، cat-file -p محتوا را چاپ میکند، اما لزوماً نام فایل، مسیر یا رابطهاش با نسخهٔ قبلی را نمیداند. پیش از استخراج، خروجی را به فایل تازه و جدا هدایت کن؛ روی فایل فعلی ننویس.
This inspection may list many commits, trees, or blobs; classify each candidate with -t first. For a text blob, cat-file -p prints the content, but may not know its filename, path, or relationship to an earlier version. Before extracting anything, write it to a new separate file; do not overwrite current work.
stash هم اشارهگر محلی و reflog خودش را دارد و ممکن است با git reflog show refs/stash بررسی شود؛ جزئیات ساخت و اعمال stash را فصل ۱۱ یاد میگیریم. هیچکدام از این راهها تضمین ابدی نیستند: آنچه Git هرگز به شیء تبدیل نکرده، از reflog یا fsck ظاهر نمیشود.
A stash also has a local ref and reflog that you can inspect with git reflog show refs/stash; Chapter 11 will teach creating and applying stashes. None of these paths is an eternal guarantee: content Git never turned into an object will not appear in a reflog or fsck.
ORIG_HEAD سرنخ کمکی است، نه دفترچهٔ کاملORIG_HEAD is a clue, not a complete journal
برخی فرمانهای بزرگ مثل reset، rebase یا merge ممکن است قبل از حرکت، جای قبلی HEAD را در ORIG_HEAD بگذارند. اما این نام همیشه وجود ندارد و عملیات بعدی میتواند آن را عوض کند. اگر هست، فقط بخوانش و با reflog و گراف تطبیق بده:
Some large operations such as reset, rebase, or merge may save the earlier HEAD in ORIG_HEAD. But it is not always present, and a later operation may replace it. If it exists, inspect it and compare it with the reflog and graph:
git show --no-patch --format=fuller ORIG_HEAD git rev-parse --verify ORIG_HEAD git reflog --date=local
اگر rev-parse خطا داد، این اشارهگر حاضر نیست؛ چیزی را خراب نکردهای. اگر شناسه چاپ شد، هنوز باید commit و تغییرهایش را با git show بشناسی. آن را کورکورانه به reset --hard نده؛ ممکن است بهروزرسانی تازهتری را کنار بزند.
If rev-parse fails, the ref is absent; you have not broken anything. If it prints an ID, still identify the commit and its changes with git show. Do not feed it blindly to reset --hard; that could discard newer work.
این دفترچه دائمی نیستThe journal is not permanent
reflog محلی است و زمان نگهداریاش تنظیمپذیر. در تنظیم پیشفرض نسخههای رایج Git، ردهای قابلدسترسی معمولاً تا ۹۰ روز و ردهای unreachable معمولاً تا ۳۰ روز نگه داشته میشوند؛ تنظیمات مخزن/کاربر و نسخه میتوانند فرق کنند. این مهلت وعدهٔ بازیابی نیست و شیءها هم ممکن است با پاکسازی از بین بروند.
A reflog is local and its retention is configurable. In common Git defaults, reachable entries are generally kept for 90 days and unreachable entries for 30 days; repository/user configuration and versions can differ. These windows are not a recovery promise, and cleanup can eventually remove objects too.
| چیزی که منقضی میشودWhat expires | رفتار پیشفرض مستندشدهDocumented default | نتیجهٔ عملیPractical consequence |
|---|---|---|
| reflog entry قابلدسترسی | معمولاً ۹۰ روزUsually 90 days | قدیمیبودن را با ماندگاری اشتباه نگیرDo not assume old means retained |
| entry مربوط به object unreachable | معمولاً ۳۰ روزUsually 30 days | همان روز rescue اشارهگر بسازCreate a rescue ref promptly |
| object یتیم | ممکن است بعداً پاک شود؛ بازه به تنظیم prune و نگهداری وابسته استMay later be pruned; timing depends on prune and maintenance settings | در زمان نجات سراغ gc/prune نروAvoid gc/prune during recovery |
برای خود شیءهای unreachable هم یک ساعت قطعی وجود ندارد. در مستندات فعلی، git gc معمولاً prune شیءهای جدا را با مهلت پیشفرض حدود دو هفته انجام میدهد؛ تنظیم gc.pruneExpire، cruft packها و اینکه هنوز اشارهگر یا reflogی شیء را نگه داشته باشد، نتیجه را عوض میکنند. این عدد هم مهلت تضمینی نیست.
Unreachable objects have no guaranteed countdown either. Current documentation says git gc normally prunes loose objects with a default grace period of about two weeks; gc.pruneExpire, cruft packs, and whether any ref or reflog still retains an object all affect the result. This is not a guaranteed recovery window.
در حادثه، کار درست «تمیزکاری» نیست. git gc، git prune، git reflog expire یا فرمانهای دارای --prune=now را اجرا نکن. اول شناسه را پیدا و با اشارهگر نجات حفظ کن؛ بعد که وضعیت روشن شد دربارهٔ نگهداری مخزن تصمیم بگیر.
During an incident, cleanup is not the next move. Do not run git gc, git prune, git reflog expire, or commands using --prune=now. First identify and preserve the object with a rescue ref; decide on repository maintenance only after the situation is clear.
اگر سرنخ درست را پیدا نکردیWhen the expected clue is missing
شمارهٔ رد اشتباه بود
The reflog index was wrong
اگر سرنخی که انتظار داری در reflog نیست، قبل از نتیجهگیری بپرس این اتفاق روی همین clone افتاده یا نه، کدام اشارهگر حرکت کرده و آیا محتوا اصلاً زمانی در Git ذخیره شده بود. reflog قوی است، اما دفتر ثبت همهٔ اتفاقهای دنیا نیست.
Indices shift after new events. Read the reflog again, identify the entry by message and time, inspect its explicit OID with show, then create a branch.
دنبال رخداد clone دیگر میگردی
You are looking for another clone's event
reflogها بین مخزنهای راه دور push نمیشوند. در همان cloneای بررسی کن که فرمان در آن اجرا شده؛ اگر وجود ندارد، از همکار بخواه clone محلی خودش را بررسی کند.
Reflogs are not pushed to remotes. Inspect the clone where the command ran; if you do not have it, ask the teammate to inspect their local clone.
reflog چیزی نشان نمیدهد
The reflog shows no candidate
ممکن است log منقضی/حذف شده باشد یا حرکت در clone دیگری رخ داده باشد. git fsck --no-reflogs --unreachable را فقط برای شیءهای ذخیرهشده بررسی کن؛ خروجی را نوعبندی کن و برای blob بینام، مسیر اصلی را قطعی فرض نکن.
The log may have expired/been removed, or the event happened in another clone. Use git fsck --no-reflogs --unreachable to inspect stored objects only; classify results and do not assume an unnamed blob's original path.
میخواهی مخزن را زود پاکسازی کنی
You want to clean up the repository first
فعلاً نه. پاکسازی میتواند فرصت را کم کند. هر برنامهٔ نگهداری را متوقف نکن مگر دلیل عملیاتی مشخصی داری؛ اما فرمان prune/gc دستی نزن و از مخزن نسخهٔ جدا بگیر، سپس بازیابی را آنجا بررسی کن.
Not yet. Cleanup can reduce your options. Do not interfere with maintenance without an operational reason, but avoid manual prune/gc; make a separate repository copy and investigate recovery there.
تمرینها: سرنخ را پیدا کن، نه شمارهٔ حفظی راExercises: find evidence, not a memorised index
شمارههای HEAD@{n} را حفظ نکن. در هر مخزن و هر زمان فرق میکنند. متن رخداد، زمان و شناسه را بخوان و وقتی فکر کردی commit درست را پیدا کردهای، اول با show نگاهش کن؛ بعد یک شاخهٔ نجات بساز.
Each exercise is a small incident. Before opening the solution, say which ref or object you are tracing and what remains unproven.
بعد از reset، git log فقط A را نشان میدهد. کدام فرمان را اول میزنی و چه چیزی دنبالش میگردی؟
After reset, git log shows only A. What do you run first, and what are you looking for?
راهنماHint
commitها ممکن است هنوز شیء باشند؛ دنبال حرکت اشارهگر بگرد.
The commits may still be objects; inspect ref movements.
پاسخ و دلیلReasoning
git reflog را بزن و رد مربوط به reset و شناسه شیء قبلی را پیدا کن. سپس git show OID. خالیبودن log عادی فقط میگوید از اشارهگرهای فعلی به آن commit نمیرسی.
Run git reflog, find the reset entry and prior OID, then inspect it with git show OID. An ordinary log only says current refs do not reach that commit.
در reflog نوشته HEAD@{1}. آیا این یعنی والد commit فعلی؟
The reflog shows HEAD@{1}. Does that mean the current commit's parent?
راهنماHint
یکی زمان را میشمارد، یکی پیوند commit را.
One counts events; the other follows commit links.
بیا بازش کنیمTwo different selectors
نه. HEAD@{1} جایگاه قبلی HEAD در reflog است؛ HEAD~1 والد اول commit فعلی است. بعد از checkout، reset یا commit ممکن است پاسخشان کاملاً متفاوت شود.
No. HEAD@{1} is HEAD's previous reflog position; HEAD~1 is the current commit's first parent. Checkout, reset, or commit events can make them differ.
شناسه شیء مشکوک پیدا شده. یک فرمان برای دیدن پیام و عکس فوری و یک فرمان برای ثابتکردن rescue شاخه بنویس.
You found a suspicious OID. Name a command to inspect its message/snapshot and one to prove a rescue branch exists.
راهنماHint
اول show، بعد شاخه و show-ref.
Inspect first, then name and verify.
راهحل و توضیحSafe sequence
git show --stat --oneline OID را بخوان؛ اگر هدف درست بود، git branch rescue/candidate OID و git show-ref --verify refs/heads/rescue/candidate. ساخت شاخه بدون بررسی میتواند فقط اشتباه را نامگذاری کند.
Inspect with git show --stat --oneline OID; if it is the target, create git branch rescue/candidate OID and verify with show-ref. Naming before inspection can preserve the wrong candidate.
همکارت شاخه را در لپتاپ خودش حذف کرده. چرا reflog لپتاپ تو احتمالاً آن رویداد را ندارد؟
A teammate deleted a branch on their laptop. Why will your reflog probably not contain that event?
راهنماHint
کدام بخش Git به مخزن راه دور push میشود؟
Which part of Git is pushed to the remote?
چرا این جواب درست استLocal scope
reflog محلی است و با push/fetch به clone دیگر منتقل نمیشود. از همکارت بخواه reflog خودش را بخواند یا OID/نسخهٔ پشتیبان را بدهد؛ رهگیریِ مخزن راه دور شاخهها دفترچهٔ رویدادهای clone او نیستند.
A reflog is local and is not transferred by push/fetch. Ask the teammate to inspect theirs or provide the OID/backup; remote-tracking branches are not their clone's event journal.
میخواهی HEAD@{yesterday} را در PowerShell اجرا کنی، اما shell بخشی از عبارت را تفسیر میکند. چه کار میکنی؟
You want to use HEAD@{yesterday} in PowerShell, but the shell parses part of it. What do you do?
راهنماHint
shell و Git دو لایهٔ تفسیر دارند.
The shell and Git parse separate layers.
بررسی جوابQuote the selector
عبارت را طبق قواعد همان shell نقلقول کن، مثلاً git show 'HEAD@{yesterday}' در shellهای POSIX؛ در PowerShell از نقلقولی استفاده کن که آن shell میپذیرد. اگر کار نکرد، شکل دستور shell را بررسی کن، نه اینکه معنای Git را حدس بزنی.
Quote it according to that shell, for example git show 'HEAD@{yesterday}' in POSIX shells; use quoting accepted by PowerShell there. If it fails, check shell syntax rather than guessing Git semantics.
reflog نشان میدهد شاخه از C به A reset شده. تیم گفته فایل C لازم است اما هنوز باید معلوم شود کدام تغییر را نگه داریم. قدم بعد چیست؟
The reflog shows a branch reset from C to A. The team needs C's files but has not decided which changes to keep. What next?
راهنماHint
اول نتیجه را حفظ کن، بعد مقایسه.
Preserve the candidate before comparing.
پاسخ پیشنهادیRescue without premature integration
شناسه شیء C را با show تأیید و rescue/C بساز. بعد diff/log دو اشارهگر را بررسی و با تیم دربارهٔ cherry-pick/merge تصمیم بگیر. داشتن rescue اشارهگر به معنی اینکه همهٔ C باید روی main بیاید نیست.
Verify C with show and create rescue/C. Compare both refs, then decide with the team whether to cherry-pick or merge. A rescue ref does not mean all of C belongs on main.
تا اینجا چند چیز گمشده را از روی دفترچهٔ حرکت اشارهگرها پیدا کردی. از اینجا تمرینها عمداً سرنخهای شبیهبههم میدهند؛ شمارهٔ HEAD@{n} را حدس نزن، متن رخداد و شناسه را بخوان.
So far you have recovered lost-looking work from the journal of ref movements. From here the clues look intentionally similar; do not guess HEAD@{n}. Read the event text and object ID.
شاخه حذف شده و git reflog show feature خطا میدهد. کدام دفترچه را بعد بررسی میکنی؟
A branch was deleted and git reflog show feature errors. Which journal do you inspect next?
راهنماHint
شاخه reflog ممکن است حذف شده باشد؛ checkout از کجا ثبت میشود؟
The branch log may be gone; where is checkout recorded?
پاسخ و دلیلStart with HEAD
git reflog یا git reflog --all را بخوان؛ بهویژه checkout از قابلیت و commitهای قبل از آن. اگر این clone آن حرکت را نداشته، clone همکار را بررسی کن. حذف اشارهگر شاخه ممکن است reflog خودش را هم برداشته باشد.
Inspect git reflog or git reflog --all, especially the checkout from feature and its preceding commits. If this clone lacks the event, inspect the teammate's clone. Deleting a branch may remove its own reflog.
میخواهی بدانی HEAD در ساعت مشخصی کجا بود. آیا HEAD~3 همین را میگوید؟
You want to know where HEAD was at a particular time. Does HEAD~3 answer that?
راهنماHint
یکی زمان، دیگری نسل commit.
One is time; the other is ancestry depth.
بیا بازش کنیمUse a time selector
نه؛ HEAD~3 سه والد اول در گراف را دنبال میکند. برای جایگاه زمانی از selectorهای reflog مثل HEAD@{yesterday} استفاده کن و شناسه شیء را با ردهای اطراف تأیید کن.
No; HEAD~3 follows three first-parent links. Use a reflog selector such as HEAD@{yesterday} for a time-based position and verify it against neighboring entries.
یک رد را خواندی و rescue شاخه ساختی. چه دو بررسیای ثابت میکند شاخه به commit مدنظر رسیده؟
You read an entry and created a rescue branch. Which two checks prove it points to the intended commit?
راهنماHint
یکی اشارهگر را میخواند، دیگری تاریخچه را.
One checks the ref; the other checks history.
راهحل و توضیحTwo kinds of evidence
git show-ref --verify refs/heads/rescue/name وجود و شناسه شیء شاخه را نشان میدهد؛ git log --oneline --decorate rescue/name جای آن را در رابطهٔ والدها میگذارد. برای محتوای واقعی، خود commit را با git show بخوان.
show-ref verifies the branch and its OID; log places it in ancestry. Use git show to inspect the actual commit content.
بعد از rebase، سه شناسه شیء نامزد داری. چرا انتخاب HEAD@{1} بدون بررسی ایدهٔ بدی است؟
After a rebase, you have three candidate OIDs. Why is choosing HEAD@{1} without inspection a bad idea?
راهنماHint
rebase میتواند چند event ثبت کند.
A rebase can record multiple events.
چرا این جواب درست استInterpret the event
نزدیکترین رد شاید commit توقف، checkout یا بازپخش میانی باشد نه نوک شاخه قبل از rebase. پیامها، زمان، شناسه شیءها و گراف را کنار هم بگذار؛ گزینهٔ آزمایشی را با show بررسی و بعد rescue کن.
The nearest entry may be a stopped commit, checkout, or intermediate replay—not the pre-rebase tip. Compare messages, times, IDs, and graph; inspect the candidate, then rescue it.
یک blob از fsck پیدا شده. آیا با این کار نام فایل و مسیرش هم معلوم است؟
You found a blob through fsck. Does that reveal its filename and path?
راهنماHint
نام مسیر در blob ذخیره نمیشود.
A blob does not store its pathname.
بررسی جوابContent is not path identity
خیر. blob بایتهای محتوا را نگه میدارد؛ نام و مسیر در treeها هستند. با cat-file -p محتوا را ببین، اما بدون tree یا شواهد دیگر مسیر اصلی ممکن است نامعلوم بماند.
No. A blob stores content bytes; names and paths live in trees. Inspect with cat-file, but without a tree or other evidence the original path may remain unknown.
ورودی ORIG_HEAD پیدا شد و به commit معقولی میرسد. آیا حالا reset --hard ORIG_HEAD امن است؟
ORIG_HEAD exists and points to a plausible commit. Is reset --hard ORIG_HEAD now safe?
راهنماHint
پوشهٔ کاری و هدف فرمان را هم بررسی کن.
Inspect the working tree and the command's effect too.
پاسخ پیشنهادیA clue is not permission
نه خودبهخود. ORIG_HEAD ممکن است از عملیات دیگری مانده باشد. اول آن commit را با show بشناس، کار محلی را جدا حفظ کن و اشارهگر نجات بساز. reset hard میتواند ویرایشهای ردیابیشدهٔ فعلی را کنار بگذارد.
Not automatically. ORIG_HEAD may come from another operation. Identify it, preserve local work separately, and create a rescue ref first. Hard reset can discard current tracked edits.
تمرینهای پایانی مرز مهم فصل را میسنجند: reflog فقط دربارهٔ چیزی کمک میکند که Git زمانی شناخته باشد. اگر محتوا هیچوقت stage، commit یا stash نشده، باید صادقانه این محدودیت را بگویی.
The final exercises test the chapter’s most important boundary: reflog can only help with data Git once knew about. If content was never staged, committed, or stashed, say that limitation honestly.
دو هفته از reset گذشته و شاخه نجات نساختهای. آیا ۳۰ روز را میتوانی تضمین فرض کنی؟
Two weeks have passed since reset and you made no rescue branch. Can you treat 30 days as a guarantee?
راهنماHint
پیشفرض با قول برابر نیست.
A default is not a promise.
پاسخ و دلیلInspect now
نه. زمانهای پیشفرض قابل تنظیماند و نگهداری ممکن است متفاوت بوده باشد. فوری reflog و fsck را بررسی کن؛ اگر شیء درست را پیدا کردی، شاخه بساز و تأییدش کن. زمان را برای تعلل مجوز ندان.
No. Defaults are configurable and maintenance may differ. Inspect reflog and fsck now; if you find the right object, create and verify a branch. Do not use the window as permission to wait.
همکارت میخواهد برای «خلوتکردن مخزن» همین الان git gc --prune=now بزند، در حالی که داری commit گمشده را نجات میدهی. چه میگویی؟
A teammate wants to run git gc --prune=now to “tidy the repository” while you are rescuing a lost commit. What do you say?
راهنماHint
شیء unreachable ممکن است هدف prune باشد.
An unreachable object may be exactly what pruning targets.
بیا بازش کنیمPreserve first, maintain later
فعلاً اجرا نکند. اول شناسه شیء را پیدا، بررسی و به rescue اشارهگر وصل کن؛ حتیالمقدور نسخهٔ جدا از مخزن بگیر. prune-now میتواند شیءهای unreachable را حذف کند و همزمانی نوشتن هم خطر دارد.
Ask them to wait. Find and inspect the OID, attach a rescue ref, and preferably make a separate repository copy. Prune-now can remove unreachable objects, and concurrent writes add risk.
بعد از حذف شاخه، reflog خودش نیست؛ در HEAD یک checkout از آن شاخه میبینی. چه چیزی را هنوز باید ثابت کنی؟
After branch deletion, its own reflog is gone; HEAD shows a checkout from that branch. What must you still prove?
راهنماHint
checkout بهتنهایی commit موردنظر را مشخص نمیکند.
A checkout alone does not identify the target commit.
راهحل و توضیحID and content
شناسه شیء رویداد را با show بررسی کن: پیام، والدها و فایلها باید با قابلیت موردنظر بخوانند. سپس شاخه نجات بساز و اشارهگر را verify کن. ممکن است روی شاخه چند commit ساخته شده باشد و checkout فقط نوک شاخه آن لحظه را برگرداند.
Inspect the event's OID with show: message, parents, and files should match the intended feature. Then create and verify the rescue branch. Several commits may have existed; the checkout records only the tip at that moment.
بعد از rebase، یکی از commitهای قدیمی را پیدا کردی. آیا این ثابت میکند کل شاخهٔ قبل از rebase را کامل داری؟
After rebase, you found one old commit. Does that prove you have the entire pre-rebase branch?
راهنماHint
یک commit ممکن است فقط بخشی از زنجیره باشد.
One commit may be only part of the series.
چرا این جواب درست استTrace parents and the tip
نه. والد chain و commit نوک شاخه موردنظر را با git log OID دنبال کن و با گراف قبل/بعد تطبیق بده. rescue را به نوک شاخه درست وصل کن، نه به اولین commitی که آشنا به نظر میرسد.
No. Trace its parent chain and intended tip with git log OID, then compare the graph before and after. Point rescue at the correct tip, not the first familiar-looking commit.
فایل تازهای را در ویرایشگر نوشتی، هرگز ذخیره یا ناحیه آمادهسازی نکردی، سپس ویرایشگر بسته شد. آیا reflog یا fsck راه قطعی دارد؟
You typed a new file in an editor, never saved or staged it, then the editor closed. Is reflog or fsck a guaranteed route?
راهنماHint
Git باید قبلاً بایتها را دریافت کرده باشد.
Git must have received the bytes first.
بررسی جوابRecovery boundary
نه. Git نه اشارهگری برای آن و نه شیءی از محتوایش دارد. نسخههای ویرایشگر، autosave، سیستم فایل یا نسخه پشتیبان را جدا بررسی کن؛ تضمین نده اگر هیچکدام چیزی ثبت نکردهاند.
No. Git has neither a ref movement nor an object containing that text. Check editor recovery, autosave, filesystem, or backups separately; do not promise recovery if none recorded it.
reflog خالی است، fsck چند commit و blob نامرتبط داده و کسی gc هم اجرا کرده. سه قدم محتاطانهٔ بعدی چیست؟
The reflog is empty, fsck lists unrelated commits and blobs, and someone has run gc. What are three careful next steps?
راهنماHint
پاکسازی را متوقف کن، نسخهها را جدا کن، شواهد را دستهبندی کن.
Stop cleanup, isolate copies, and classify evidence.
پاسخ پیشنهادیDo not guess at the incident
۱) gc/prune بیشتر نزن و از مخزن فعلی نسخهٔ جدا بگیر. ۲) cloneهای دیگر و نسخه پشتیبانها را بررسی کن؛ اشارهگرها و reflogهای هرکدام محلیاند. ۳) روی کپی، شیءها را با نوع، پیام، والد و محتوا طبقهبندی کن و فقط گزینهٔ آزمایشی تأییدشده را rescue کن. ممکن است محتوای واقعاً ذخیرهنشده یا pruneشده قابلبازیابی نباشد.
1) Stop gc/prune and copy the repository. 2) Check other clones and backups; refs and reflogs are local. 3) On the copy, classify objects by type, message, parents, and content; rescue only a verified candidate. Never-stored or already-pruned content may be unrecoverable.
پروژهٔ کوچک: میز نجات GitMini-project: the Git rescue desk
در این پروژه چند چیز عمداً «گم» شدهاند، اما عجله نمیکنیم چیزی را سر جایش هل بدهیم. اول فقط ردها را پیدا میکنی و برای هر مورد یک نام نجات میسازی. وقتی مطمئن شدی چیزی قابل دسترسی است، تازه دربارهٔ ادغام یا جابهجایی شاخه تصمیم میگیری.
Three realistic incidents arrive at your desk: a deleted branch, a mistaken reset, and a commit made in detached HEAD. Reproduce all three in a disposable repository and write a short report for each: symptom, ref/object found, rescue decision, and final proof.
برای شروع هر پرونده یک مخزن دورریختنی در پوشهٔ جدا بساز تا رویدادهای پروندهٔ قبلی شمارهٔ سرنخهای بعدی را جابهجا نکند. فرمانهای حذف و reset فقط در همین پوشههای آزمایشی اجرا شوند.
Give each incident its own disposable repository in a separate folder so earlier events do not shift later clues. Run deletion and reset commands only in these practice folders.
- پروندهٔ شاخهٔ حذفشدهDeleted branch
شاخه بساز، دو commit مرتبط روی آن ثبت کن، از آن بیرون برو و شاخه را حذف کن. با reflog محلی شناسه شیء نوک شاخه را پیدا کن، با show محتوایش را ثابت کن، rescue شاخه بساز و گراف نهایی را ثبت کن.
Create a branch, make two related commits, switch away, and delete it. Find the tip OID in the local reflog, verify it with show, create a rescue branch, and record the final graph.
- پروندهٔ resetMistaken reset
سه commit روی main بساز و به عمد دو commit آخر را با hard reset از مسیر عادی خارج کن. reflog را بخوان، گزینهٔ آزمایشیها را مقایسه کن، rescue شاخه بساز و ثابت کن main هنوز همانجاست.
Make three commits on main and deliberately move it back two commits with hard reset. Inspect the reflog, compare candidates, create a rescue branch, and prove main has not moved again.
- پروندهٔ جداشده HEADDetached-HEAD commit
به commit مشخص checkout کن، commit تازه بساز و به main برگرد. از reflog HEAD شناسه را پیدا کن، محتوا و والد را بررسی و شاخه نجات بساز.
Check out a specific commit, create a new commit, and return to main. Find its ID in HEAD's reflog, inspect content and parent, and create a rescue branch.
راهاندازی دورریختنی هر پروندهDisposable setup for each case
هر قطعه را در پوشهٔ جداگانه اجرا کن؛ نام پوشهها را عوض نکن و فرمان حذف/reset را بیرون از آنها نزن. این کدها فقط خرابی را میسازند؛ مسیر نجات را خودت از روی reflog پیدا کن.
Run each block in its own folder. Keep the folders separate and do not run deletion/reset commands elsewhere. These commands create the incident; use the reflog yourself to find the recovery path.
mkdir rescue-case-branch cd rescue-case-branch git init -b main git config user.name "Rescue Learner" git config user.email rescue@example.test printf 'base\n' > notes.txt git add notes.txt git commit -m "baseline" git switch -c topic printf 'topic one\n' >> notes.txt git commit -am "topic: first change" printf 'topic two\n' >> notes.txt git commit -am "topic: second change" git switch main git branch -D topic
mkdir ../rescue-case-reset cd ../rescue-case-reset git init -b main git config user.name "Rescue Learner" git config user.email rescue@example.test printf 'A\n' > notes.txt git add notes.txt git commit -m "A: baseline" printf 'B\n' >> notes.txt git commit -am "B: second commit" printf 'C\n' >> notes.txt git commit -am "C: third commit" git reset --hard HEAD~2
mkdir ../rescue-case-detached cd ../rescue-case-detached git init -b main git config user.name "Rescue Learner" git config user.email rescue@example.test printf 'base\n' > notes.txt git add notes.txt git commit -m "baseline" git switch --detach HEAD printf 'detached work\n' >> notes.txt git commit -am "commit made while detached" git switch main
- خروجی reflog را با پیام رویداد، زمان و شناسه شیء نگه دار.
- Save the reflog entry with its message, time, and OID.
- commit نجاتیافته را با show و والدهایش بشناس.
- Identify the rescued commit with show and inspect its parents.
- با show-ref و
log --graph --allثابت کن rescue نام دارد و شاخه اصلی ناخواسته حرکت نکرده. - Use show-ref and
log --graph --allto prove the rescue is named and the original branch did not move unexpectedly.
اگر در یکی از پروندهها سرنخ پیدا نشدIf one case has no visible clue
اول مطمئن شو در clone درست هستی و زمان ردها را بر اساس شمارهٔ فعلی دوباره بخوان. بعد، روی نسخهٔ جدا از مخزن، شیءهای unreachable را با fsck بررسی کن. هیچ پاکسازیای را برای «آسانترشدن» انجام نده؛ اگر Git محتوا را ذخیره نکرده یا شیء حذف شده باشد، گزارش باید همین محدودیت را صادقانه بگوید.
First confirm you are in the right clone and reread entries rather than trusting old indices. Then inspect unreachable objects on a separate repository copy. Do not clean anything to “make it easier”; if Git never stored the content or the object was removed, report that limit honestly.
همهچیز را به آخرین وضعیتِ ذخیرهشده برنمیگردانیمNot every interruption needs a rescue
reflog برای وقتی عالی است که نامها جابهجا شدهاند، اما هر وقفهای بحران نیست. گاهی فقط میخواهی کار نیمهتمام را چند دقیقه کنار بگذاری، یک تغییر مشخص را به خط دیگری ببری یا یک نسخه را اسمگذاری کنی؛ فصل بعد همین سه نیاز روزمره را کنار هم میگذارد.
You now know that a reflog records ref movements in this clone and how a rescue branch preserves a discovered commit beyond journal expiry. But sometimes nothing is wrong—you simply need to pause. Chapter 11 introduces three small tools for interruption and selective movement: stash, cherry-pick, and tag.
مرور کوتاهQuick reference
git reflogبرای رویدادهای HEAD وgit reflog show REFبرای دفترچهٔ یک اشارهگر محلی است.git reflogshows HEAD events;git reflog show REFinspects a local ref's journal.HEAD@{n}جایگاه زمانی reflog است؛HEAD~nاز والدهای commit میگذرد.HEAD@{n}is a reflog position;HEAD~nwalks commit parents.- شناسه شیء را با
showبررسی کن؛ اول rescue اشارهگر بساز و آن را با show-ref ثابت کن. - Inspect the OID with show; create a rescue ref first and verify it with show-ref.
- reflog به clone دیگر منتقل نمیشود؛ هر clone دفترچهٔ خودش را دارد.
- A reflog is not transferred to another clone; each clone has its own journal.
- برای شیء بیاشارهگر، fsck شاید سرنخ بدهد؛ نام مسیر blob تضمین نیست.
- For unreferenced objects, fsck may help; a blob's original path is not guaranteed.
- کار هرگز ذخیرهنشده در Git را نمیتوان با reflog یا fsck تضمینی بازگرداند.
- Reflog and fsck cannot guarantee recovery of work Git never stored.
- هنگام نجات، gc/prune/expire را اجرا نکن؛ بعد از یافتن، اشارهگر نجات را زود بساز.
- During recovery, avoid gc/prune/expire; create a rescue ref promptly after finding the object.
اگر چیزی گم به نظر رسید، اول فرق «نام دیگر به آن نمیرسد» با «Git هرگز آن را ذخیره نکرده» را روشن کن. تمام قدرت reflog از همین تفاوت میآید.
Work Git stored often leaves a recovery clue for some time; work Git never stored may never come back from Git. Find the clue, identify the commit, create a rescue ref, then decide what to do.
برای جزئیات جاری، راهنمای رسمی reflog، branch، نسخهٔ تاریخچهها و selectorهای زمانی، fsck و gc را ببین. بازههای نگهداری تنظیمپذیرند؛ git config --show-origin --get-regexp 'gc\.(reflogExpire|reflogExpireUnreachable|pruneExpire)' کمک میکند تنظیم مؤثر را پیدا کنی.
For current details, see the official manuals for git reflog, git branch, revision and date selectors, git fsck, and git gc. Retention is configurable; git config --show-origin --get-regexp 'gc\.(reflogExpire|reflogExpireUnreachable|pruneExpire)' helps locate effective settings.