bisect: پیدا کردن commit خراب
Bisect: finding the commit that introduced a bug
میدانی امروز باگ داریم و میدانی یک نسخهٔ قدیمی سالم بوده؛ اما بین این دو دهها commit هست. bisect این مسئله را از «بگرد تا پیدا کنی» به یک جستوجوی مرحلهای تبدیل میکند.
The application worked yesterday and fails today. You know a last-good revision and that the current commit is bad. There are 64 commits between them. Must you test all 64 one by one? Before answering, predict how each result might eliminate roughly half the suspects.
باگ هست؛ اما نمیدانیم کدام commit آوردشThe bug is real, but which commit introduced it?
تیم میگوید نسخهٔ قدیمی رفتار درست داشته، commit امروز خراب است و log پیام روشنی ندارد. فصل ۱۱ کمک کرد یک patch شناختهشده را انتخاب کنی؛ این بار خود commit مقصر را نمیدانیم. میتوانیم به هر نسخه برویم و همان آزمون را اجرا کنیم. سؤال این نیست که «کدام commit مشکوک به نظر میرسد؟»؛ سؤال این است که «مرز سالم و خراب دقیقاً کجاست؟»
The team says an older version behaved correctly, today's commit is broken, and the log message is unhelpful. Chapter 11 let you select a known patch; this time we do not know the culprit. We can check out versions and run the same test. The question is not “which commit looks suspicious?” but “where exactly is the good/bad boundary?”
اگر ۶۴ commit را از اول تا آخر آزمایش کنی، شاید ۶۴ بار زمان ببرد. اما وقتی نتیجهٔ یک commit میانی را بدانی، با فرض یکنواختی باگ میفهمی بازهٔ باقیمانده کدام نیمه است. این ایدهٔ جستوجوی دودویی است؛ git bisect انتخاب commitهای میانی و نگهداشتن بازه را انجام میدهد، ولی قضاوت دربارهٔ نتیجه هنوز با آزمون توست.
A linear scan may take 64 tests. But if you test a middle revision and the regression is monotonic, you know which half remains. That is binary-search intuition. git bisect selects intermediate commits and tracks the remaining range, while your test still decides good versus bad.
bisect فقط به دو نشانه و یک آزمون نیاز داردBisect needs two labels and a test
به Git دو لنگر میدهی: یک commit که مطمئنی خراب است، و یک یا چند commit که مطمئنی هنوز سالماند. سپس هر commit پیشنهادی را با همان آزمون میسنجی و نتیجه را ثبت میکنی. Git وسط بعدی را انتخاب میکند تا وقتی به اولین commit خراب در محدودهٔ قابلجستوجو برسد.
Give Git two anchors: a commit known to be bad and one or more commits known to be good. Test each suggested revision with the same check and report the result. Git chooses the next candidate until it identifies the first bad commit within the searchable range.
| برچسبLabel | معنی برای این جستوجوMeaning in this search | چه چیزی باید ثابت باشد؟What must be known? |
|---|---|---|
good | آزمون در این commit سالم است.The check passes at this commit. | همان رفتار مورد انتظار واقعاً کار میکند.The expected behavior really works. |
bad | آزمون در این commit خراب است.The check fails at this commit. | شکست همان پسرفت مورد تحقیق است.The failure is the regression being investigated. |
skip | این commit را نمیتوانم معتبر آزمایش کنم.This revision cannot be tested reliably. | ناممکنبودن آزمون را با خراب اشتباه نکن.Do not confuse untestable with bad. |
مدلِ «هرچه جلوتر برویم خرابتر میشود» مهم است. اگر باگ در یک commit میآید و commit بعدی آن را موقتاً پنهان میکند، یا چند پسرفت مستقل داری، پاسخ «اولین خراب» ممکن است به سؤال واقعیات جواب ندهد. bisect دادهٔ درست را از تست درست بهتر نمیکند.
The assumption that the bug stays present in later commits matters. If a later commit temporarily hides it, or several independent regressions exist, “first bad” may not answer your real question. Bisect cannot turn a poor test into good evidence.
یک مرز دیگر هم mergeها هستند: تاریخچه شاخهدار نیست همیشه یک خط. در حالت معمول bisect مسیر قابلدسترسی را دنبال میکند؛ --first-parent سؤال دیگری میپرسد و فقط مسیر اصلی را از mergeها میگذراند، برای پیدا کردن mergeای که پسرفت را وارد ادغام کرده. آن را بیدلیل روشن نکن؛ ممکن است دیگر دنبال commit داخل شاخهٔ ادغامشده نباشی.
Merge history adds another boundary: history is not always a single line. Normal bisect searches reachable history; --first-parent asks a different question by following only the mainline through merges, useful for finding the merge that brought a regression into integration. Do not enable it casually: you may stop searching for the original commit inside the merged branch.
آزمایش دستی: مرز خوب و خراب را پیدا کنManual experiment: find the good/bad boundary
این آزمایش یک مخزن تازه میسازد؛ فقط داخل همان پوشه اجرا کن. ۱۲ commit با پیامهای تقریباً یکسان داریم و یکی از آنها خروجی برنامه را از ۴۲ به ۴۱ عوض میکند. قبل از bisect، anchor سالم را tag میکنیم تا حدس خوبمان شناسه داشته باشد.
This experiment creates a fresh repository; run it only in that folder. It makes 12 similarly named commits, one of which changes the program's output from 42 to 41. Before bisect, tag the known-good anchor so it has a stable name.
mkdir bisect-manual cd bisect-manual git init -q git config user.name "Git Learner" git config user.email learner@example.test printf 'print(42)\n' > app.py git add app.py && git commit -m "Refresh sample" git tag known-good for n in $(seq 1 12); do if [ "$n" = 7 ]; then printf 'print(41)\n' > app.py else printf 'sample %s\n' "$n" >> notes.txt fi git add app.py notes.txt git commit -m "Refresh sample" done python3 app.py
در این نسخه، خروجی ۴۱ یعنی پسرفت دیده میشود؛ tag known-good به نسخهای با خروجی ۴۲ میرسد. اسم commitها عمداً سرنخ نیست. اگر Python 3 در محیطت نیست، قبل از شروع ابزار مناسب را آماده کن؛ نبودن runtime را بهاشتباه خراب علامت نزن.
Here, output 41 means the regression is present; known-good names a revision that prints 42. Commit messages are deliberately unhelpful. If Python 3 is unavailable, prepare the runtime before starting; do not mislabel a missing runtime as a bad revision.
git bisect start git bisect bad git bisect good known-good python3 app.py # record exactly one result for this checked-out commit: git bisect good # or: git bisect bad # repeat test + mark until Git identifies the boundary git bisect log git show --stat --oneline BISECT_OID git bisect reset
بعد از دو نشانه، Git یک commit را checkout میکند و تعداد تقریبی باقیماندهها را میگوید. خروجی برنامه را همانجا بخوان و فقط نتیجهٔ همان گزینهٔ آزمایشی را ثبت کن. وقتی تمام شد، BISECT_OID را با شناسهٔ واقعی گزارش جایگزین کن و patch commit را ببین. آخر کار reset لازم است: حالت bisect را میبندد و به شاخه/HEAD آغاز برمیگردد.
After both labels, Git checks out a candidate and reports an approximate remainder. Run the program there and mark only that candidate's result. When complete, replace BISECT_OID with the reported ID and inspect its patch. Finish with reset to end the session and return to the original branch/HEAD.
در اجرای واقعی، فرمان دستی git bisect good را وقتی میزنی که آزمون را اجرا کردهای و خروجی ۴۲ بوده؛ اگر ۴۱ بود bad. این متن دستورهای دو حالت را نشان میدهد تا جای هم عوضشان نکنی. شناسه و تعداد گامها بسته به تاریخچه فرق میکند.
In the actual run, enter git bisect good only after observing 42; enter bad after 41. The two alternatives are shown so you do not confuse them. IDs and step counts depend on history.
Bisecting: 6 revisions left to test after this (roughly 3 steps) [candidate checkout] $ python3 app.py 42 $ git bisect good Bisecting: 2 revisions left to test after this (roughly 2 steps) ... 1234abc is the first bad commit
این خروجی نمونهای است، نه نتیجهٔ ثابت همین تاریخچه: «roughly» خودش میگوید تعداد تقریبی است. مهم این است که هر بار اول گزینهٔ آزمایشی را واقعاً آزمایش کنی و بعد برچسب همان نتیجه را بزنی.
This is illustrative, not a guaranteed transcript for this history; “roughly” signals an estimate. The important order is to test the checked-out candidate first, then label that result.
اولین commit خراب را ببین؛ نتیجه را بازرسی کنInspect the reported first bad commit
پیام پایانی bisect میگوید کدام commit در محدودهٔ بررسیشده اولین بار از سالم به خراب میرسد. این نتیجه ثابت نمیکند پیام commit دربارهٔ bug حرف میزند؛ آن را از روی محتوای واقعی commit بررسی کن.
Bisect's final message identifies the first good-to-bad transition within the searched range. It does not prove the commit message mentions the bug; inspect the actual change.
git show --stat --oneline BISECT_OID git show BISECT_OID -- app.py git bisect log git bisect reset
در این آزمایش patch باید خروجی را از ۴۲ به ۴۱ عوض کرده باشد. git bisect log نشان میدهد کدام نسخهها را good/bad اعلام کردهای؛ اگر در میانهٔ کار برچسبی را اشتباه زدی، همین log مبنای بازبینی است. بعد از reset، commit مقصر را میتوانی با شاخه یا tag جداگانه نامگذاری کنی؛ session bisect اشارهگر دائمی پروژه نیست.
In this experiment, the patch should change output from 42 to 41. git bisect log records which revisions you marked good or bad; if you mislabelled one, use this log to review. After reset, preserve the culprit with a branch or tag if useful; a bisect session is not a permanent project ref.
وقتی آزمون روشن است، bisect را خودکار کنAutomate bisect when the test is unambiguous
تا اینجا خودت برنامه را اجرا و برچسب را وارد کردی. اگر آزمون کوتاه، تکرارپذیر و مستقل است، Git میتواند آن را در هر گزینهٔ آزمایشی اجرا کند. اما «خودکار» یعنی تصمیمگیری طبق کد خروجی؛ اگر قرارداد کدها را نفهمی، ممکن است همهٔ خطاهای محیط را خراب اعلام کنی.
So far you ran the program and entered each label yourself. If the check is short, repeatable, and independent, Git can run it at every candidate. But “automated” means decisions follow exit codes; misunderstand the contract and environment failures may all be marked bad.
| کد خروجی اسکریپتScript exit status | تفسیر bisect runbisect run interpretation | نمونهExample |
|---|---|---|
0 | سالم / oldgood / old | آزمون واقعاً قبول شد.The check passed. |
1–127، بهجز 125 | خراب / newbad / new | آزمون مشخصاً پسرفت را دید.The check detected the regression. |
125 | skipskip | این نسخه قابلآزمایش نیست.This revision cannot be tested. |
| هر کد دیگرOther status | توقف bisect runAbort bisect run | خطای اجرای اسکریپت یا محیط را پنهان نکن.Do not hide a script/environment error. |
در قرارداد فعلی Git، ۱۲۶ و ۱۲۷ هم داخل بازهٔ خراب قرار میگیرند: shell آنها را برای «پیدا شد ولی اجرا نشد» و «فرمان پیدا نشد» بهکار میبرد. پس اگر اسکریپت یا runtime نصب نیست، مراقب باش آن را پسرفت برنامه جا نزنی؛ با بررسی صریح وابستگیها skip کن یا اجرای bisect را متوقف کن. کدهای دیگر، از جمله خروجیهای بزرگتر از ۱۲۷، اجرای خودکار را abort میکنند.
Under Git's documented contract, 126 and 127 also fall in the bad range: shells use them for “found but not executable” and “command not found.” So if a script or runtime is missing, do not mistake that for an application regression; detect the prerequisite explicitly and skip or stop. Other statuses, including values above 127, abort the automated run.
bisect بهاندازهٔ آزمونت قابلاعتماد استBisect is only as reliable as its test
اگر تست گاهی با شبکه fail شود، زمان اجرا را بسنجد، یا خودش فایل ردیابیشده را تغییر دهد، برچسبهایی که ثبت میکنی معنای ثابتی ندارند. bisect با صداقت هرچه دادهای بازه را کم میکند؛ نمیفهمد آزمونت ناعادلانه بوده.
If a test sometimes fails because of the network, depends on timing, or modifies tracked files, your labels have no stable meaning. Bisect faithfully narrows the range based on your input; it cannot detect that your test was unfair.
قبل از شروع، آزمون را چند بار روی همان commit اجرا کن و مطمئن شو نتیجه ثابت است. آن را فقط به رفتار مورد جستوجو وصل کن، نه به هشدار unrelated یا شکست ساخت ناشی از نصبنبودن dependency. اسکریپت را بیرون مخزن نگه دار یا مطمئن شو اجرای آن ردیابیشده وضعیت را تغییر نمیدهد؛ آزمایش destructive ممنوع.
Before starting, run the check several times on the same commit and confirm stable results. Tie it only to the behavior under investigation, not unrelated warnings or a build failure caused by a missing dependency. Keep the script outside the repository or ensure it does not alter tracked state; destructive tests are out of scope.
اگر compile fail شد چون commit قدیمی با toolchain امروز سازگار نیست، این خودبهخود همان bug محصول نیست. اگر آن نسخهٔ تاریخچه را نمیتوانی معتبر بسنجی، git bisect skip یا در اسکریپت خروجی ۱۲۵ بده؛ خطای زیرساخت را به برچسب خراب تبدیل نکن.
A build failure caused by an old commit's incompatibility with today's toolchain is not automatically the product bug. If you cannot test that revision validly, use git bisect skip or return 125 from the script; do not label infrastructure failure as bad.
نسخهای که نمیشود ساخت را skip کن؛ ابهام را هم بپذیرSkip an unbuildable revision—and accept the ambiguity
یک گزینهٔ آزمایشی به dependency قدیمی نیاز دارد که دیگر در دسترس نیست. این نسخه را نمیتوانی با همان آزمون بسنجی؛ git bisect skip آن را نه خوب مینامد و نه بد. Git از اطرافش جستوجو میکند.
A candidate requires an unavailable old dependency. You cannot run the same check there; git bisect skip marks it neither good nor bad, and Git searches around it.
git status --short --branch git bisect skip git bisect log
اگر commit خراب میان چند commit skipشده باشد، Git ممکن است فقط مجموعهای از گزینهٔ آزمایشیها را گزارش کند و نتواند یکی را با قطعیت اولین خراب اعلام کند. skip را برای راحتی نزن؛ دلیلش را یادداشت کن، تعداد skipها را محدود کن، و اگر محدوده مبهم شد dependency یا محیط قدیمی را فراهم کن و آزمایش را دوباره انجام بده.
If the culprit lies among skipped commits, Git may report a set of candidates rather than one certain first bad commit. Do not skip for convenience: record why, keep skips limited, and if the result is ambiguous, restore the old dependency/environment and rerun the test.
جلسه را reset کن؛ وسطش روی شاخهٔ دیگری کار نکنReset the session; do not switch away and keep working
bisect برای آزمودن هر گزینهٔ آزمایشی، checkout را جابهجا میکند و معمولاً در حالت جداشده HEAD میمانی. پایان طبیعی آزمایش git bisect reset است؛ این فرمان session را میبندد و به شاخه/commit آغاز برمیگرداند.
Bisect checks out each candidate, usually leaving you in detached HEAD. End the experiment with git bisect reset; it closes the session and returns to the starting branch/commit.
اگر قبل از reset شاخه را دستی عوض کنی، برچسبگذاری ممکن است متوقف یا گیجکننده شود. git bisect log را برای ثبت تصمیمها بخوان؛ اگر برچسبی غلط بوده، session را با log بررسی و اصلاح کن. کار آزمایشی را در شاخهٔ bisect commit نکن مگر عمداً و با درک جداشده HEAD.
Manually switching branches before reset can interrupt or confuse the session. Read git bisect log to review decisions; if a label was wrong, use the log to correct the session. Do not commit incidental work on a bisect candidate unless you deliberately understand detached HEAD.
- پوشه کاری تمیز است و کار مهمی روی آن رها نشده.
- The worktree is clean; no valuable edits are exposed.
- یک نسخهٔ سالم و یک نسخهٔ خراب را با همان آزمون واقعاً تأیید کردهای.
- You verified one good and one bad revision with the same test.
- میدانی در پایان reset کنی و چه چیزی را گزارش بدهی.
- You know to reset at the end and what evidence to report.
وقتی نتیجه عجیب است، اول فرضها را بررسی کنWhen the result looks strange, check your assumptions
| نشانهSymptom | احتمالPossibility | بررسی بعدیNext check |
|---|---|---|
| نسخهٔ بد بهعنوان سالم پیدا شدKnown-bad revision marked good | anchor یا آزمون اشتباهWrong anchor or test | تست را دوباره روی دو انتها اجرا کن؛ اگر لازم است session را reset و از نو بساز.Rerun the check at both endpoints; reset and restart if needed. |
| هر بار نتیجه عوض میشودResults vary between runs | تست flaky، زمانمحور یا وابسته به شبکهFlaky, timing-sensitive, or network-dependent test | پیش از ادامه پایدارش کن؛ نتیجهٔ بیثبات را برچسب نزن.Stabilise it before continuing; do not label an unstable result. |
| bisect روی commit نامربوط توقف کردBisect stops at an unrelated revision | نسخه build/test نمیشود یا dependency تغییر کردهRevision cannot build/test or dependencies changed | اگر واقعاً ناممکن است skip کن؛ اگر اسکریپت خراب است abort را بررسی کن.Skip only if genuinely untestable; investigate aborts as script errors. |
| همهٔ commitها خراب شدندEvery commit is bad | سالم anchor معتبر نیست یا آزمون bug را جدا نمیکند.Good anchor is invalid or the test does not isolate the bug. | commit سالم قدیمیتر و آزمون دقیقتر پیدا کن.Find an earlier known-good commit and a sharper test. |
اگر bisect نتیجهٔ عجیب داد، اول خود آزمون و دو مرز سالم/خراب را زیر سؤال ببر. این ابزار فقط بر اساس جوابهایی که تو به آن میدهی دامنه را نصف میکند؛ اگر آزمون ناپایدار یا مرز اولیه اشتباه باشد، سرعت بیشتر فقط تو را سریعتر به جواب غلط میرساند.
Before repairing anything, inspect git bisect log and both endpoints. Change one assumption—anchor or test—not both at once. That lets you see which correction changed the result.
پروژهٔ کوچک: پسرفت را پیدا کنMini-project: find the regression
از ۴۰ commit به یک تغییر قابلاثبات
From 40 commits to one provable change
در این پروژه یک پسرفت بین دهها commit پنهان شده. اول چند مرحله را دستی میروی تا منطق جستوجو را ببینی؛ بعد همان آزمون را خودکار میکنی. در پایان فقط شناسهٔ «اولین commit خراب» کافی نیست؛ باید بتوانی توضیح بدهی چرا آزمون تو این مرز را معتبر میداند.
Create a repository with 40 commits and one mid-history regression. Messages must not reveal the culprit. First bisect manually for a few steps; then reset and solve the same incident with a test script.
ساخت تاریخچه
Build the history
از پوشهٔ تازه اجرا کن؛ Python 3 و Bash لازم است. test اسکریپت را در پوشهٔ والد نگه میداریم تا checkoutهای Git آن را تغییر ندهند.
Run from a fresh folder; Python 3 and Bash are required. Keep the test script in the parent directory so Git checkouts cannot alter it.
mkdir bisect-capstone && cd bisect-capstone git init -q git config user.name "Git Learner" git config user.email learner@example.test printf 'print(42)\n' > app.py git add app.py && git commit -m "Refresh sample" git tag known-good for n in $(seq 1 40); do if [ "$n" = 23 ]; then printf 'print(41)\n' > app.py else printf 'sample %s\n' "$n" >> notes.txt fi git add app.py notes.txt git commit -m "Refresh sample" done python3 app.py
باگ در commit شمارهٔ ۲۳ از حلقه وارد شده، اما پیامها عمداً یکساناند. اول git bisect start، current را خراب و known-good را سالم بزن؛ چند بار برنامه را روی گزینهٔ آزمایشیها اجرا و برچسب درست بده. تعداد گام را با ۵ یا ۶ تضمین نکن: Git آن را با شکل گراف و بازه حساب میکند.
The bug enters at loop commit 23, but messages are deliberately identical. Start bisect, mark current bad and known-good good; manually run the program on several candidates and label them. Do not demand exactly five or six steps; Git's choices depend on the graph and range.
راه خودکار را با آزمون مستقل بساز
Automate it with an external test
cat > ../test-bisect.sh <<'EOF' #!/bin/sh command -v python3 >/dev/null 2>&1 || exit 125 actual=$(python3 app.py 2>/dev/null) || exit 1 [ "$actual" = "42" ] EOF chmod +x ../test-bisect.sh git bisect reset git bisect start git bisect bad HEAD git bisect good known-good git bisect run ../test-bisect.sh git bisect log git show --stat --oneline BISECT_OID git show BISECT_OID -- app.py git bisect reset
این اسکریپت برای پسرفت خروجی غیر از ۴۲ را خراب میکند؛ اگر خود برنامه crash کند هم خراب است، چون اجرای برنامه بخشی از رفتاری است که داریم میسنجیم. اما اگر Python نصب نیست، ۱۲۵ میدهد تا گزینهٔ آزمایشی skip شود. اگر skip نزدیک مرز زیاد شد، پاسخ میتواند مبهم باشد؛ محیط را درست کن و دوباره آزمایش بگیر. بعد از یافتن commit، patch باید ثابت کند خروجی را عوض کرده است.
The script marks output other than 42 as bad; a crashing application is also bad because execution is part of the behavior under test. But if Python is missing, it returns 125 so that candidate is skipped. Too many skips near the boundary may make the result ambiguous; repair the environment and rerun. The final patch must prove it changed the output.
اگر اسکریپت پیدا نشد یا ۱۲۷ دیدیIf the script is missing or you see 127
مطمئن شو از داخل پوشهٔ bisect-capstone فرمان میزنی و فایل ../test-bisect.sh executable است. ۱۲۷ یعنی فرمان پیدا نشد و طبق قرارداد خراب شمرده میشود؛ در این تمرین نباید آن را با پسرفت اشتباه بگیری.
Make sure you run from bisect-capstone and that ../test-bisect.sh is executable. Status 127 means a command was not found and is treated as bad by the contract; do not confuse that setup error with the regression.
- سالم و خراب endpoint چه بودند و چرا به آنها اعتماد داری؟
- Which revisions were the good and bad endpoints, and why are they trustworthy?
- آزمون دقیقاً چه رفتار قابلمشاهدهای را سنجید؟
- What observable behavior did the test check?
- bisect کدام commit را معرفی کرد و کدام خط patch آن را ثابت میکند؟
- Which commit did bisect identify, and which patch line proves it?
- آیا نسخهٔ تاریخچه skip شد یا نتیجه محدودیتی داشت؟ بعد از reset روی کدام شاخهای؟
- Were any revisions skipped or was the result limited? Which branch are you on after reset?
تمرینها: از پیشبینی تا پروندهٔ واقعیExercises: from prediction to real incidents
اینجا هدف حفظ دستور bisect نیست. بعضی تمرینها ازت میخواهند تعداد تقریبی آزمونها را پیشبینی کنی، بعضی کیفیت آزمون را بسنجی و بعضی تشخیص بدهی چه وقت skip باعث میشود جواب نهایی مبهم بماند.
Whenever a result needs interpretation, state what you know and what remains unproven. Bisect builds its answer from your labels.
۶۴ commit میان آخرین نسخهٔ سالم و نسخهٔ خراب داری. آیا bisect تضمین میکند دقیقاً ۶ تست لازم است؟
There are 64 commits between the last known-good and the broken revision. Does bisect guarantee exactly six tests?
معیار: شهود نصفکردن را از تضمین تعداد گام جدا کن.
Success: distinguish the halving intuition from a guaranteed step count.
راهنمایی / Hint
گراف همیشه یک زنجیرهٔ بینقص نیست.
History is not always a perfect line.
توضیح / Explanation
نه. در زنجیرهٔ ساده انتظار چند گام لگاریتمی داریم، اما mergeها، شکل DAG، چند سالم anchor و skipها مسیر و تعداد را عوض میکنند. Git خودش هم تعداد را «تقریبی» میگوید.
No. A simple line suggests logarithmic tests, but merges, DAG shape, multiple good anchors, and skips alter the path. Git itself reports only an estimate.
نسخهٔ امروز bug دارد و tag v1.8.0 سالم است. اولین فرمانهای session چیست؟
Today's revision has the bug and tag v1.8.0 is good. What are the opening session commands?
معیار: هر دو انتهای جستوجو با برچسب درست معرفی شوند.
Success: label both search endpoints correctly.
راهنمایی / Hint
وضعیت کاری را هم پیش از شروع ببین.
Check the worktree before starting too.
قدم امن / Safe start
اول git status --short --branch و اجرای آزمون روی هر دو انتها. سپس git bisect start، git bisect bad HEAD و git bisect good v1.8.0. برچسب سالم را از نام نسخه حدس نزن؛ آزمون باید سلامت را تأیید کند.
Check status and run the test at both endpoints. Then start bisect, mark HEAD bad and the tag good. Do not trust the version name alone; the test must confirm it is good.
گزینهٔ آزمایشی خروجی مورد انتظار میدهد. چه برچسبی میزنی و این برچسب دربارهٔ commit بعدی چه میگوید؟
A candidate produces the expected output. What label do you enter, and what does it tell bisect about later commits?
معیار: معنای سالم را به این آزمون وصل کن.
Success: tie good to this specific test.
راهنمایی / Hint
پیشفرض، حضور پایدار پسرفت در تاریخچه است.
The assumption is that the regression persists in later history.
پاسخ / Answer
git bisect good. میگویی همین commit این آزمون را میگذراند؛ بنابراین در فرض ساده، transition خراب باید بعدتر باشد. اگر باگ بعداً پنهان شده باشد، فرض یکنواختی میشکند.
Run git bisect good. You are saying this commit passes this test, so under the simple model the transition is later. If a later commit hides the bug, that monotonicity assumption fails.
بعد از اتمام جستوجو هنوز در جداشده HEAD هستی. چطور session را میبندی و مطمئن میشوی برگشتهای؟
After the search, you are still in detached HEAD. How do you close the session and verify where you returned?
معیار: هم reset و هم بررسی شاخه را بگویی.
Success: name both reset and branch verification.
راهنمایی / Hint
bisect خودش را پاکسازی میکند.
Bisect has a cleanup command.
پاسخ / Answer
git bisect reset، بعد git status --short --branch. reset به موقعیت اولیه برمیگردد؛ وضعیت ثابت میکند روی کدام شاخه هستی و آیا تغییر محلی داری.
Run git bisect reset, then git status --short --branch. Reset returns to the starting position; status proves the branch and whether local edits remain.
گزینهٔ آزمایشی را ساخت کردی و ساخت سبز شد، اما رفتار مورد بررسی را اجرا نکردی. آیا سالم است؟
The candidate builds, but you did not exercise the behavior under investigation. Is it good?
معیار: ساخت را با اثبات رفتار یکی نگیری.
Success: do not confuse a successful build with proof of behavior.
راهنمایی / Hint
پرسش bisect از ابتدا چه بود؟
What question was the bisect session meant to answer?
مدرک ناکافی / Insufficient evidence
نه. ساخت ثابت میکند کد compile میشود، نه اینکه bug بازتولید میشود. آزمونی بساز که همان رفتار گزارششده را اجرا کند؛ سپس گزینهٔ آزمایشی را سالم یا خراب علامت بزن.
No. A build proves compilation, not whether the reported bug reproduces. Create a check that exercises the exact behavior, then label the candidate.
متن خطا در گزینهٔ آزمایشی با متن ticket فرق دارد، ولی آزمون هم fail میشود. آیا خراب است؟
The candidate's error differs from the ticket wording, but the test still fails. Is it bad?
معیار: بفهمی آیا همان شرط را میسنجی.
Success: determine whether the same condition is being tested.
راهنمایی / Hint
متن شکست کافی نیست؛ رفتار هدف را مقایسه کن.
The error text alone is insufficient; compare target behavior.
آزمون را دقیق کن / Sharpen the test
تا وقتی ثابت نکردهای شکست همان پسرفت است، خراب نزن. خروجی مورد انتظار را با ticket و نسخهٔ سالم مقایسه کن؛ اگر علت نامرتبط مثل نبود dependency است، skip یا اصلاح محیط لازم است.
Do not mark bad until you prove the failure is the target regression. Compare expected behavior with the ticket and known-good version; if it is an unrelated missing dependency, skip or repair the environment.
تا اینجا خود bisect را دیدهای. از اینجا کیفیت جواب به کیفیت آزمون بستگی دارد. قبل از علامتزدن سالم یا خراب، مطمئن شو آزمون واقعاً همان باگی را میسنجد که دنبال اولین ورودش هستی.
You have now seen bisect itself. From here, the answer is only as good as the test. Before marking good or bad, make sure the test truly measures the bug whose introduction you are locating.
تست گاهی pass و گاهی fail میشود، بدون تغییر commit. چرا اجرای bisect run را عقب میاندازی؟
The test sometimes passes and sometimes fails on the same commit. Why delay bisect run?
معیار: خطر برچسب ناپایدار را شرح بده.
Success: explain unstable labels.
راهنمایی / Hint
هر وضعیت یک رأی برای گزینهٔ آزمایشی است.
Each status is a vote about a candidate.
تکرارپذیری لازم است / Make it repeatable
یک بار تصادفی pass/fail شدن، دادهٔ غلط به الگوریتم میدهد و مرز را جابهجا میکند. وابستگی شبکه/زمان را حذف یا اولیه ثابت بگذار؛ روی یک commit چند بار اجرا کن و فقط بعد از نتیجهٔ پایدار شروع کن.
A random pass/fail feeds wrong labels and can move the boundary. Remove network/timing dependencies or fix the seed; repeat on one commit and start only when results are stable.
روی یک گزینهٔ آزمایشی dependency نصب نیست. میدانی بدون آن ساخت نمیشود؛ اما نمیدانی رفتار برنامه خراب است یا نه. چه میکنی؟
A candidate lacks a dependency, so you know it cannot build, but not whether the application behavior is broken. What do you do?
معیار: خراب و untestable را جدا نگه دار.
Success: distinguish bad from untestable.
راهنمایی / Hint
این گزینهٔ آزمایشی هیچکدام از دو انتها نیست.
This candidate is neither known endpoint.
skip با دلیل / Skip with a reason
اگر نمیتوانی رفتار را معتبر آزمایش کنی، git bisect skip بزن و دلیل و commit را یادداشت کن. اگر dependency قابلبازیابی است، بهتر است محیط همان نسخه را بسازی تا skipهای مبهم کمتر شوند.
If you cannot test the behavior reliably, use git bisect skip and record the revision and reason. If the dependency can be restored, recreating the historical environment is better and reduces ambiguity.
اسکریپت برای «مهمترین خروجی برابر ۴۲ است» چه وضعیتهایی باید برگرداند؟
Which statuses should a script return for “the key output equals 42”?
معیار: سه حالت pass، پسرفت و untestable را جدا کن.
Success: separate pass, regression, and untestable states.
راهنمایی / Hint
کد ۱۲۵ معنای ویژه دارد.
Status 125 has a special meaning.
قرارداد خروجی / Exit contract
۰ برای خروجی ۴۲؛ یک کد ۱ تا ۱۲۷ جز ۱۲۵ برای پسرفت؛ ۱۲۵ فقط وقتی گزینهٔ آزمایشی واقعاً آزمایشناپذیر است. خطای تست را خودکار ۱۲۵ نکن اگر crash خود برنامه بخشی از bug است.
Return 0 for 42; a status from 1–127 except 125 for the regression; 125 only when the revision truly cannot be tested. Do not map an application crash to 125 if crashing is the bug.
اسکریپت بهدلیل تایپ اشتباه فرمان ۱۲۷ میدهد. bisect run آن را چه میفهمد؟
A typo makes the script return 127. How does bisect run interpret it?
معیار: معنای shell را از معنای bisect جدا کن.
Success: distinguish shell meaning from bisect's mapping.
راهنمایی / Hint
مستند رسمی یک استثنا دارد.
The manual defines one exception.
خروجی خطرناک / A dangerous status
۱۲۷ یعنی shell فرمان را پیدا نکرده، اما برای bisect run در بازهٔ خراب قرار میگیرد. این میتواند نتیجهٔ غلط بسازد؛ اسکریپت را تعمیر کن و session را با endpointهای درست از نو اجرا کن.
127 means the shell could not find a command, but bisect run treats it as bad. This can produce a false result; repair the script and rerun with correct endpoints.
اسکریپت خروجی ۱۳۰ میدهد. این کد در قرارداد bisect run چه میکند؟
The script exits 130. What does bisect run do under its status contract?
معیار: خروجی بالاتر از بازهٔ خراب را تشخیص بده.
Success: identify a status outside the bad range.
راهنمایی / Hint
خراب فقط تا ۱۲۷ است.
The bad range stops at 127.
اجرای خودکار متوقف میشود / The run aborts
کدهای خارج از ۰، بازهٔ ۱–۱۲۷ بهجز ۱۲۵، و ۱۲۵ باعث abort میشوند. علت ۱۳۰ را در script/log پیدا کن؛ آن را سالم یا خراب تلقی نکن.
Statuses outside 0, 1–127 except 125, and 125 abort the run. Diagnose why 130 occurred; it is neither a good nor bad label.
سه commit پشت سر هم را skip کردهای و Git چند گزینهٔ آزمایشی را ممکن میداند. آیا میتوانی یکی را «اولین خراب» قطعی اعلام کنی؟
You skipped three consecutive commits and Git reports several possible candidates. Can you declare one as the definite first bad?
معیار: مرز دانستهها را در گزارش حفظ کن.
Success: preserve uncertainty in the report.
راهنمایی / Hint
چه کسی نتیجهٔ commitهای skipشده را میداند؟
Who knows the results of skipped commits?
مجموعهٔ مظنونها / Candidate set
نه. گذار ممکن است داخل همان بازه باشد؛ گزارش باید گزینهٔ آزمایشی set را بگوید، نه یک culprit قطعی. dependency/محیط را برگردان، روی آن commitها تست کن و جستوجو را از نو یا با log اصلاحشده ادامه بده.
No. The transition may lie within that interval; report the candidate set, not a definite culprit. Restore the dependency/environment, test those revisions, and restart or correct the search log.
تمرینهای آخر حالتهای واقعیتری مثل skip و آزمون ناپایدار دارند. اگر Git نتواند یک commit را آزمایش کند، ممکن است به جای یک جواب قطعی فقط چند گزینهٔ ممکن باقی بماند؛ این ابهام را پنهان نکن.
The final exercises add real-world complications such as skip and flaky tests. If Git cannot test some commits, the result may be a set of possible culprits instead of one definitive commit; do not hide that ambiguity.
bug فقط روی شاخهای رخ میدهد که merge شده؛ خروجی bisect در تاریخچهٔ mergeدار گیجکننده است. چه پرسش دیگری میتوانی با --first-parent بپرسی؟
The bug appears on an integrated branch, and merge-heavy history makes the result confusing. What different question can --first-parent answer?
معیار: محدودیت گزینه را هم بگو.
Success: state the option's limitation too.
راهنمایی / Hint
این گزینه commitهای داخلی شاخهٔ ادغامشده را دنبال نمیکند.
It does not follow commits inside merged side branches.
از integration بپرس / Ask about integration
با first-parent میپرسی کدام نقطه در مسیر اصلی، معمولاً کدام merge، پسرفت را وارد کرد. این میتواند merge culprit را پیدا کند، اما دیگر جستوجوی commit اصلی داخل شاخهٔ قابلیت نیست.
First-parent asks which point on the mainline—often which merge—introduced the regression. It can identify the integration commit, but it no longer searches for the original commit inside the feature branch.
خروجی manual bisect یک commit با پیام «Refresh sample» معرفی میکند. چه مدرکی لازم است قبل از اعلام علت؟
Manual bisect reports a commit named “Refresh sample.” What evidence is needed before declaring the cause?
معیار: خروجی bisect را با خود تغییر تطبیق بده.
Success: connect the bisect result to the actual patch.
راهنمایی / Hint
git show را روی شناسهٔ گزارششده اجرا کن.
Run git show on the reported ID.
اولین مدرک، آخرین مدرک نیست / First clue, not final proof
با git show --stat فایلهای تغییرکرده و با git show OID -- app.py خط دقیق را بخوان؛ ببین آیا واقعاً خروجی ۴۲ را ۴۱ کرده. همچنین صحت endpointها و آزمون را گزارش کن؛ bisect فقط مرز را بر اساس برچسبها پیدا کرده.
Use git show --stat for paths and git show OID -- app.py for the exact line; confirm it changed output 42 to 41. Also report endpoint and test validity: bisect found a boundary based on your labels.
تست اسکریپت یک فایل ردیابیشده را برای ذخیرهٔ cache تغییر میدهد. چرا باید قبل از اجرای خودکار بازطراحیاش کنی؟
The test script modifies a tracked cache file. Why redesign it before automated bisect?
معیار: اثر جانبی روی گزینهٔ آزمایشی بعدی را تشخیص بده.
Success: identify side effects on later candidates.
راهنمایی / Hint
هر بار Git tree را عوض میکند، اسکریپت هم اجرا میشود.
The script runs after each checkout.
آزمون باید فقط مشاهده کند / Keep the test observational
تغییر فایل میتواند checkout بعدی را آلوده یا متوقف کند و نتیجه را وابسته به ترتیب سازد. cache را بیرون مخزن یا در temp پوشه بگذار، اسکریپت را idempotent کن و قبل از شروع وضعیت تمیز را ثابت کن.
The mutation can pollute or block later checkouts and make results order-dependent. Put cache outside the repository or in a temp directory, make the test idempotent, and verify a clean state before starting.
تگ سالمی که انتخاب کردی خودش آزمون را fail میکند. کدام دو فرض اصلی را دوباره بررسی میکنی؟
Your chosen good tag fails the test. Which two core assumptions do you recheck?
معیار: anchor و معنای test را جدا کن.
Success: separate anchor validity from test meaning.
راهنمایی / Hint
آیا tag سالم است؟ آیا آزمون رفتار درست را میسنجد؟
Is the tag actually good? Does the test measure the right behavior?
از نو anchor بساز / Re-establish an anchor
همان آزمون را روی tag و نسخهٔ جاری دستی اجرا کن. شاید tag اشتباه/قدیمی است، یا test expectation غلط یا محیط متفاوت دارد. تا یک سالم واقعی نیافتی bisect معنی ندارد؛ session را reset کن و با anchorهای معتبر شروع کن.
Run the same check manually on the tag and current revision. The tag may be wrong/old, or the expectation/environment may differ. Without a genuinely good anchor, bisect is meaningless; reset and restart with valid endpoints.
روی گزینهٔ آزمایشی دستور git bisect good را زدی، بعد فهمیدی تست را اصلاً اجرا نکردهای. برای اصلاح چه میکنی؟
You marked a candidate good, then realised you never ran the test. How do you correct it?
معیار: log و بازپخش را بهعنوان ابزار ممیزی بشناس.
Success: use log/replay as an audit mechanism.
راهنمایی / Hint
تصمیمهای ثبتشده قابلدیدناند.
Recorded decisions can be inspected.
خطا را پنهان نکن / Correct the recorded decision
با git bisect log فرمان اشتباه را پیدا کن. راه امن، ذخیره و ویرایش log برای حذف برچسب غلط، سپس git bisect reset و git bisect replay FILE طبق مستند است؛ یا session را reset و از endpointهای درست دوباره آغاز کن. بعد گزینهٔ آزمایشی را واقعاً تست کن.
Find the mistaken label with git bisect log. A documented repair is to save/edit the log, remove the bad entry, reset, and replay it; alternatively reset and restart from valid endpoints. Then actually test the candidate.
bisect یک commit را معرفی کرده، ولی دو بار از چهار بار همان تست روی آن pass شده. مدیر میخواهد همان را culprit اعلام کند. گزارش محتاطانهات چیست؟
Bisect reports a commit, but the test passes twice out of four runs on it. A manager wants to declare it the culprit. What is your careful report?
معیار: قطعیت الگوریتم را از قطعیت داده جدا کن.
Success: distinguish algorithmic output from data reliability.
راهنمایی / Hint
برچسب ناپایدار، مرز قابلاعتماد نمیسازد.
Unstable labels cannot establish a reliable boundary.
نتیجه فعلاً قطعی نیست / The result is not yet conclusive
گزارش کن که Git با برچسبهای واردشده این commit را مرز معرفی کرده، اما آزمون flaky است و شواهد برای اعلام علت کافی نیست. منبع نوسان را حذف، آزمون را ثابت و همان بازه را دوباره bisect کن؛ خروجی الگوریتم جای کیفیت داده را نمیگیرد.
Report that Git found this boundary from the labels supplied, but the test is flaky and evidence is insufficient to assign cause. Stabilise the check and rerun the range; algorithm output cannot compensate for poor data.
از commit مقصر به سیاست کار تیمیFrom the culprit commit to team workflow
تا اینجا دنبال یک commit مقصر بودیم. از فصل بعد زاویه عوض میشود: چطور تیم طوری شاخه بسازد و زود ادغام کند که فاصلهها و درگیریهای بزرگ کمتر شوند؟
Bisect helped locate a change in history. But if regressions are usually found late, the problem may not be one commit: branches may live too long or integration may happen too rarely. Chapter 13 moves from one repository to team policy: how long should branches live, and how often should integration happen?
مرور کوتاهQuick reference
- قبل از شروع، endpoint سالم و خراب را با همان آزمون ثابت کن.
- Verify both endpoints with the same stable test before starting.
git bisect start، سپس خراب و سالم؛ گزینهٔ آزمایشی را آزمایش و درست برچسب بزن.- Start bisect, mark bad and good, then test and label each candidate correctly.
git bisect skipفقط وقتی این commit واقعاً قابلآزمایش نیست؛ skipها ممکن است پاسخ را مبهم کنند.- Skip only genuinely untestable commits; skips can make the result ambiguous.
bisect run: صفر سالم، ۱ تا ۱۲۷ بهجز ۱۲۵ خراب، ۱۲۵ skip؛ کدهای دیگر abort.bisect run: zero is good, 1–127 except 125 is bad, 125 skips; other statuses abort.- commit معرفیشده را با
showو patch واقعی بررسی کن. - Inspect the reported commit and actual patch with
show. - پایان:
git bisect resetو تأیید شاخه باstatus --short --branch. - Finish with
git bisect resetand verify your branch with status.
برای قرارداد دقیق نسخهٔ نصبشده، راهنمای رسمی bisect را ببین. نوع گراف، mergeها، anchorها و commitهای skipشده روی مسیر جستوجو اثر میگذارند؛ تعداد مراحل را وعده نده و آزمون را بخشی از نتیجه بدان.
For the exact contract in your installed version, consult the official git bisect manual. Graph shape, merges, anchors, and skipped commits affect the search; do not promise a fixed number of steps, and treat the test as part of the result.