DevOps
Part 6 of 6 · GitBranching, PR Hygiene, Bisect, Worktrees & Hooks
Day-to-day collaboration is branching policy, pull-request hygiene, bisect, worktrees, and hooks. Trunk-based development keeps main releasable with short branches. Bisect is a binary search over commits. Worktrees are extra checkouts on one object database.
- 1Gist
- 2Maps
- 3Q&A
- 4Sandbox
Voice readout needs Web Speech Synthesis in this browser.
Question ladder
L1
How do you ship an unfinished feature on trunk-based main?
Answer
Hide it behind a feature flag or a dark launch so main stays releasable.
L2
Why keep commits buildable if the forge will squash?
Answer
You still rebase and bisect locally before the squash. Some teams rebase-merge and never squash.
L3
What does bisect do with merge commits?
Answer
It still checks out trees and runs your test. First-parent walks are a way to stay on the mainline when that is the policy.
L4
How is a worktree different from a submodule?
Answer
A worktree is another checkout of the same repository. A submodule is a nested, different repository.
L5
Can a client hook be the security boundary?
Answer
No. --no-verify skips it. Protected branches and CI are the enforcement.
L6
How do sparse-checkout and a partial clone differ?
Answer
Sparse-checkout limits the working-tree paths. A partial clone limits which objects were fetched. The build still needs a correct dependency graph.
L7
A pull request is too large. What do you do?
Answer
Split by concern, or stack pull requests. Do not mix a drive-by refactor with the feature.
Failure modes
A long feature branch with no flag
main cannot integrate it, and the branch cannot stay releasable. Trunk-based development fails closed.
Bisect on a history that does not build
The first red commit is a broken intermediate, not the regression.
Stash as a multi-day shelf
Stashes collide and get dropped. A private commit or a worktree keeps the work named.
Trusting pre-commit as the only gate
Anyone can skip a client hook. The server and CI must repeat the rule.
Bisect of a flaky test
Good and bad answers lie. Quarantine the flake before you trust the search.
Misconceptions
GitFlow is required once you have releases.
A tag on main, or a short release branch, is enough for many teams. Long-lived develop branches add merge delay.
Squash means intermediate commits can be garbage.
You still live on those commits until the squash. Broken intermediates waste the rebase and any local bisect.
A second clone is the same as a worktree.
A worktree shares the object database. A second clone fetches and stores objects again.
Interviewer traps
Describing pipeline stages when asked how you keep main releasable.
Answer with short branches, flags, and bisectable history. Point required checks at the CI/CD cluster without redesigning it.
Using blame to find when a behavior broke across many files.
blame names the last edit of a line. bisect names the first bad commit of a test.
Design scenario
Same prompt for every reader.
Requirements
Short-lived branches. Linear main by squash or rebase. Bisect the regression. Do not stash the bugfix over the bisect checkout.
Traffic / scale
Several pull requests a day. Feature branches live less than two days.
Latency
Bisect should be a log-time test run, not a manual reading of the log.
Consistency
Each landed commit or squash on main builds, so the first bad commit is meaningful.
Availability
The bugfix proceeds in a second worktree. The bisect checkout stays put.
Failure assumptions
- The test used for bisect is flaky.
- A local pre-commit hook is skipped with --no-verify.
Constraints
- main stays releasable.
- Do not mix the bugfix into the bisect working tree.
Prompt
Twelve engineers, strong CI, and a releasable main. One regression appeared since tag v2.3.0. A second bugfix must proceed while that bisect is in progress.
API
Which commands start bisect, add a worktree, and tidy the private commits before review?
Data
What must be true of each commit on main for the bisect result to be fair?
Architecture
Where do client hooks stop, and where do protected branches and CI take over?
main must stay releasable while a feature is unfinished
Prefer
Short branch, flag, squash or rebase onto main
The branch lives for hours or a couple of days. Incomplete behavior stays dark. The landed commit builds.
- Integration happens while the change is small.
- Bisect on main has a fair good and bad.
- A second checkout is a worktree, not a stash pile.
Alternative
A long-lived develop branch and a kitchen-sink pull request
Ceremony feels safe. The merge delay and the mixed diff are where regressions hide.
- Hotfixes need two landings.
- Bisect stops on commits that never built.
- Review cannot tell the feature from the refactor.
From a short branch to a bisect that means something
Hygiene is what makes the later binary search fair.
- 1
Branch from fresh main
fetch, then switch -c from origin/main. Rebase onto it while the branch is private. - 2
One concern
Tidy with interactive rebase before review. Read the range log. Lease-push. - 3
Land a buildable result
Squash or rebase per policy. main stays releasable, with a flag if the feature is dark. - 4
Bisect the regression
Mark bad and good, run the test, reset when you have the first bad commit.
Overview
This page is the habit layer on top of the object model and the rewrite rules. Interviewers ask how your team actually branches, how a pull request stays reviewable, and how you find the commit that broke production without reading the whole log.
Required status checks are a pipeline concern. The place those checks are designed is CI/CD Pipelines. Here, the only claim is that a protected branch can require them. This page does not re-teach stages, artifacts, or signing.
Trunk-based versus a light GitFlow
| Dimension | Trunk-based | GitFlow lite |
|---|---|---|
| Primary line | main always releasable | develop plus main or a release branch |
| Feature lifetime | Hours to a few days | Often longer |
| Release | A tag on main, or a short release branch | release/*, then merge |
| Hotfix | Branch from main, merge back fast | hotfix/* into main and develop |
| Best when | CI is strong and flags exist | Versioned releases and a slower QA window |
| Failure if wrong | Long features with no flags break trunk | Ceremony slows a small team |
Flow
- 1
1. main stays releasable
- next2. Short feature branch
- 2
2. Short feature branch
- next3. Squash or rebase merge
- 3
3. Squash or rebase merge
- next4. main is releasable
- 4
4. main is releasable
- next5. Hotfix from main
- 5
5. Hotfix from main
- next6. Hotfix merges back
- 6
6. Hotfix merges back
Lesson map
Branching, PR Hygiene, Bisect, Worktrees & Hooks
Day-to-day collaboration is branching policy, pull-request hygiene, bisect, worktrees, and hooks. Trunk-based development keeps main releasable with short branches. Bisect is a binary search over commits. Worktrees are extra checkouts on one object database.
Architecture. Architecture
Select a node to see why it exists, or an edge to see the protocol, direction, effect, and consequence.
Mermaid export
flowchart TB eng["Engineer"] git["Git bisect"] test["Test script"] eng -->|1. bad is HEAD,| git git -->|2. Checkout a| eng eng -->|3. Run the test| test test -->|4. Mark good or| git git -->|5. Repeat until| eng
git fetch origin
git switch -c feat/rate-limit origin/mainCommit, rebase onto origin/main while the branch is yours, open the pull request, squash-merge or rebase-merge, and delete the branch.
Pull-request hygiene
| Practice | Why it beats a sloppy history | Failure if you skip it |
|---|---|---|
| Atomic commits with real messages | Review and bisect | A wall of WIP hides the bug |
| Buildable intermediate commits | Bisect can land on green or red honestly | A false blame on a commit that never built |
| One concern per pull request | Faster review | A kitchen-sink change |
| Rebase or squash before merge | Quiet main | Noise merges |
| Ticket or pull-request link in the squash message | You can find the review later | An orphan squash |
| No secrets and no generated noise | The diff is the change | A leaked credential |
git fetch origin
git rebase -i origin/main
git log --oneline origin/main..HEAD
git push --force-with-leaseIf you squash on the forge, local buildable commits still help the rebase you do before that squash. Some teams never squash and rebase-merge instead. Either policy wants a story you can read with log.
A pull request that is too large should be split by concern or stacked. Do not mix a drive-by refactor with the feature.
Bisect
git bisect start
git bisect bad
git bisect good v2.3.0
./scripts/test.sh && git bisect good || git bisect bad
git bisect resetAutomated:
git bisect start HEAD v2.3.0
git bisect run ./scripts/test.sh
git bisect resetSequence
- 1
Engineer → Git bisect
1. bad is HEAD, good is the tag
- 2
Git bisect → Engineer
2. Checkout a midpoint
- 3
Engineer → Test script
3. Run the test
- 4
Test script → Git bisect
4. Mark good or bad
- 5
Git bisect → Engineer
5. Repeat until the first bad
Bisect is O(log N). Scrolling the log is linear. The search assumes the test is deterministic and the commits you land on can build. That is the hygiene rule coming back. A flaky test will mark good and bad at random. Quarantine it first. Do not teach flake isolation here.
Merge commits are valid trees. Bisect checks them out like any other commit. A first-parent walk is how some teams stay on the mainline. Know that the command is still judging trees, not the prettiness of the graph.
git blame answers who last touched a line. It does not answer when a behavior broke across several files. That is bisect.
Worktrees
| Approach | Parallel branch work | Beats | Failure |
|---|---|---|---|
| A second worktree | git worktree add ../fix-login fix/login | Stash thrash and a full second clone | Forgetting worktree remove |
| A full second clone | Strong isolation | Disk and a divergent fetch config | Two object databases to update |
| Stash and switch | A few seconds of WIP | Easy to start | Stash conflicts and lost shelves |
git fetch origin
git worktree add -b fix/login ../repo-login origin/main
cd ../repo-login
cd ../repo
git worktree remove ../repo-loginThe worktree shares the object database, so it is cheaper than a second clone. It is not a submodule. A submodule is a different repository nested in the tree.
Do not stash several days of work. Commit on a private branch, or add a worktree. A stash is for a brief interruption.
Hooks, signing, and sparse-checkout
| Hook | Typical use | What actually enforces it |
|---|---|---|
pre-commit | Lint, format, secret scan | CI required checks |
commit-msg | Conventional commits | A bot or a server hook |
pre-push | Block a mistaken force | Protected branches |
Client hooks are a convenience. --no-verify skips them. Treat that flag as an escape hatch, not a habit. The source of truth is CI and the server.
git commit -S
git log --show-signatureSigned commits help provenance. Protected branches can require them. Rewriting a signed commit requires a new signature. That constraint lives on the surgery page. Here, the point is that the branch can demand the signature.
Sparse-checkout, for a monorepo:
git sparse-checkout init --cone
git sparse-checkout set services/api libs/commonSparse-checkout limits which paths appear in the working tree. A partial clone limits which objects were downloaded. They are related and they are not the same. The build system still needs the real dependency graph. Sparse-checkout does not invent a smaller build.
Debugging toolkit
| Tool | Best for | Not for |
|---|---|---|
| bisect | A regression with a known good and a known bad | A flaky test you have not quarantined |
| blame | Who last touched a line | A behavior that moved across many files |
| worktree | Two branches at once | Replacing a CI matrix |
| stash | Seconds of WIP | Days of work. Commit instead |
How many steps
Press Run. Snippets must be self-contained — no network, files, or native modules.
Press Run. Snippets must be self-contained — no network, files, or native modules.
Interview Q&A
How do you do trunk-based development with an unfinished feature?
Answer
Hide it behind a feature flag or a dark launch. main stays releasable. The branch does not live for weeks.
Why keep commits buildable if you squash at the end?
Answer
You still rebase and sometimes bisect those commits before the squash. Teams that rebase-merge never get a squash to hide a broken intermediate.
Does bisect work with merge commits?
Answer
Yes. It checks out the tree and runs your test. Some teams restrict the walk to first-parent so they stay on the mainline. The result is still about trees, not about the graph style.
Worktree versus submodule?
Answer
A worktree is a second checkout of this repository, sharing objects. A submodule is a pointer to a different repository.
Can hooks be enforced?
Answer
Local hooks are optional and can be skipped with --no-verify. Server hooks, protected branches, and CI are what you can require.
Sparse-checkout versus monorepo tooling?
Answer
Sparse-checkout shrinks the working tree. It does not fix the build graph. You still need the tooling that knows which packages depend on which.
The pull request is too large. What do you do?
Answer
Split it by concern, or stack smaller pull requests. Keep refactors out of the feature change so review and bisect both stay honest.
A hotfix lands during a release freeze. Then what?
Answer
Branch from the release or from main according to the model you actually use. Cherry-pick across the gap. Run CI on the release branch. Do not rewrite main to make the freeze look tidy.
Pitfalls
- A trunk-based rule with feature branches that live for a month and no flags.
bisect runon a test that fails for reasons other than the regression.worktree addand neverworktree remove, untilgit worktree listis a graveyard.- Signing commits locally and never requiring signatures on the protected branch, or the reverse story told as if they were the same control.
- Answering a bisect question with a pipeline diagram. Say the test command. Send the pipeline design to the CI/CD page.
Name a known-good tag and a bad HEAD. Say the two marks, what the test script must return, and why you would add a worktree before fixing the bug you just found.