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

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.

۱۳بخشsections
۸۰دقیقهminزمان مطالعهmin
۱۸تمرین با پاسخsolved exercises
۴نمودارdiagrams

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.

bash · create isolated bisect-manual
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.

manual bisect · test each checked-out candidate
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.

representative output · IDs and counts vary
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.

Each good or bad result narrows the remaining candidate interval one test result labels one candidate known goodanchor candidatetest + mark known badanchor good → discard later side · bad → discard earlier side
نمودار ۲ — هر برچسب، بخشی از بازه را حذف می‌کند: سالم سمت بعد از گزینهٔ آزمایشی را کنار می‌گذارد و خراب سمت قبل از آن را. این نتیجه فقط با anchor و آزمون درست معنا دارد.
Diagram 2 — Each label removes part of the interval: good discards the later side, bad the earlier side. The result is meaningful only with valid anchors and a correct test.

اولین 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.

inspect evidence before closing the session
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.
125skipskipاین نسخه قابل‌آزمایش نیست.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 run checks out a candidate, runs a script, maps its status, then chooses the next candidate checkoutcandidate revision run test scriptsame check each time map exit status0 / bad / 125 nextor abort repeat until the boundary is identified
نمودار ۳ — در هر دور، Git گزینهٔ آزمایشی را checkout می‌کند، اسکریپت اجرا می‌شود و کد خروجی به good/bad/skip یا abort تبدیل می‌شود. حلقه فقط وقتی معتبر است که اسکریپت همان پرسش را بی‌طرفانه جواب دهد.
Diagram 3 — Each round checks out a candidate, runs the script, and maps its status to good, bad, skip, or abort. The loop is valid only if the script answers the same question reliably.

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.

خراب یعنی آزمون شکست خورد، نه اینکه «هر چیزی» خراب بودbad means this test failed, not that “anything” broke

اگر 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.

skip a candidate that cannot be tested
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.

Skipped revisions create an ambiguous interval around the good-bad boundarya skipped test leaves more than one possible boundary goodknown skipuntestable skipuntestable badknown culprit may be in either skipped revision
نمودار ۴ — سالم و خراب حد را می‌سازند، اما دو skip میانشان اجازه نمی‌دهند بگوییم گذار دقیقاً در کدام نقطه رخ داده. پاسخ محتاطانه، مجموعهٔ مظنون‌هاست.
Diagram 4 — Good and bad anchor the range, but two skips prevent an exact transition point. The careful answer is a candidate set.

جلسه را 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.

سه چیز پیش از شروعThree checks before starting
  • پوشه‌ کاری تمیز است و کار مهمی روی آن رها نشده.
  • 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 goodanchor یا آزمون اشتباه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

آزمایش دورریختنی · حدود ۳۵ دقیقهDisposable experiment · about 35 minutes

از ۴۰ 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.

bash · generate 40 deliberately bland commits
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

parent directory · test-bisect.sh
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.

گزارش کوتاه رخدادShort incident report
  • سالم و خراب 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.

۱پایه / Beginner

۶۴ 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.

۲پایه / Beginner

نسخهٔ امروز 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.

۳پایه / Beginner

گزینهٔ آزمایشی خروجی مورد انتظار می‌دهد. چه برچسبی می‌زنی و این برچسب دربارهٔ 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.

۴پایه / Beginner

بعد از اتمام جست‌وجو هنوز در جداشده 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.

۵پایه / Beginner

گزینهٔ آزمایشی را ساخت کردی و ساخت سبز شد، اما رفتار مورد بررسی را اجرا نکردی. آیا سالم است؟

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.

۶میانی / Intermediate

متن خطا در گزینهٔ آزمایشی با متن 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.

۷میانی / Intermediate

تست گاهی 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.

۸میانی / Intermediate

روی یک گزینهٔ آزمایشی 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.

۹میانی / Intermediate

اسکریپت برای «مهم‌ترین خروجی برابر ۴۲ است» چه وضعیتهایی باید برگرداند؟

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.

۱۰میانی / Intermediate

اسکریپت به‌دلیل تایپ اشتباه فرمان ۱۲۷ می‌دهد. 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.

۱۱میانی / Intermediate

اسکریپت خروجی ۱۳۰ می‌دهد. این کد در قرارداد 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.

۱۲پیشرفته / Advanced

سه 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.

۱۳پیشرفته / Advanced

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.

۱۴پرونده / Incident

خروجی 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.

۱۵پرونده / Incident

تست اسکریپت یک فایل ردیابی‌شده را برای ذخیرهٔ 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.

۱۶پرونده / Incident

تگ سالمی که انتخاب کردی خودش آزمون را 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.

۱۷پرونده / Incident

روی گزینهٔ آزمایشی دستور 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.

۱۸پروندهٔ تیمی / Team incident

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

پیداکردن مرز سالم و خرابFind the good/bad boundary
  • قبل از شروع، 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 reset and 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.