Pull Requests, Drafts, Linked Issues, Change Sets, and Collaboration Patterns: Diagnostics, Failure Modes, Security, and Performance
Diagnose pull requests from preserved base/head/ref evidence when the wrong branch, unrelated commits, force-pushes, closing keywords, checks, or deployment assumptions make the hosted review object misleading or unsafe.
Learning objectives
- Apply a repeatable diagnostic sequence to wrong base/head, unrelated-commit, stale-review, and policy/check failures.
- Engineer an intentionally misleading pull request whose topic branch contains an unrelated base commit and repair it without hiding the cause.
- Distinguish changing PR metadata/base from rewriting Git history and know when each correction is appropriate.
- Explain why force-pushing during active review is a communication and evidence hazard even when technically permitted.
- Prevent accidental Issue closure caused by closing keywords and distinguish approval/check success from deployment success.
- Keep destructive/security-sensitive GitHub and Git operations isolated to disposable resources with explicit recovery context.
\, command substitution such as $(...), and
printf are labeled Git Bash/Bash/zsh. In PowerShell, use
the same Git/gh arguments on one line or use the backtick
for line continuation; PowerShell-native alternatives are shown for
the key file-writing steps. The GitHub resource semantics are
identical.
1. Diagnostic sequence: preserve → scope → inspect → correct → verify
- Preserve evidence: PR URL/number, title/body, base/head names and OIDs, commit list, diff summary, check rollup, review state, and linked Issues.
- Identify scope: which repository owns base/head, which ref moved, which Issue/check/workflow/deployment is being discussed.
-
Inspect: Git graph and merge base,
gh pr view/diff, REST response, rules/checks, and workflow logs when present. - Choose least destructive correction: metadata edit or base change before history rewrite; new commit before force push when possible.
- Verify independently: repeat both local Git and hosted PR inspection; never trust only the surface you just changed.
2. Failure matrix: symptom is not cause
| Symptom | Likely layers to inspect | Do not jump to |
|---|---|---|
| PR shows unrelated commits | branch ancestry, merge base, selected base branch | force-push immediately |
| PR targets wrong branch | PR base metadata, release policy | recreate PR automatically |
| “No permission” updating PR | head ownership, repository role, fork policy, token | repository Admin |
| Checks green but release failed | check SHA/context, merge SHA, deployment system | rerun random checks |
| Issue closed after merge | PR body/development link + default branch + auto-close setting | reopen without fixing lifecycle policy |
| Review comments look outdated | base/head changed, force push/rebase, file lines moved | delete review history |
3. Intentionally broken example: the branch was based on the wrong line
This example creates a base branch
experiment/base containing an unrelated commit, then
creates topic branch feature/actual-change from it. The
author intends to target main but forgets that the
topic inherited the experiment commit. The PR is valid GitHub
state—but its change set is misleading.
gh auth status --active --hostname github.com
OWNER=$(gh api user --jq .login)
REPO="c07-pr-diagnostics-lab"
gh repo create "$OWNER/$REPO" --public --add-readme --clone
cd "$REPO"
DEFAULT_BRANCH=$(git branch --show-current)
# Wrong foundation: add an unrelated experiment commit.
git switch -c experiment/base
printf '
Unrelated experiment marker.
' >> README.md
git add README.md
git commit -m "experiment: unrelated base marker"
git push -u origin experiment/base
# Actual topic inherits that unrelated commit.
git switch -c feature/actual-change
printf '
Actual Chapter 07 diagnostic change.
' >> README.md
git add README.md
git commit -m "feat: actual diagnostic change"
git push -u origin feature/actual-change
PR_URL=$(gh pr create --base "$DEFAULT_BRANCH" --head feature/actual-change --title "feat: actual diagnostic change" --body "Intentionally broken Chapter 07 example: inspect before repair.")
PR_NUMBER=${PR_URL##*/}
4. Preserve the original cause before repair
gh pr view "$PR_NUMBER" --json number,baseRefName,baseRefOid,headRefName,headRefOid,commits,files,mergeable,url
gh pr diff "$PR_NUMBER" --name-only
git fetch origin "$DEFAULT_BRANCH" experiment/base feature/actual-change
git log --graph --oneline --decorate --all --max-count=12
printf 'merge_base='; git merge-base "origin/$DEFAULT_BRANCH" origin/feature/actual-change
git log --oneline "origin/$DEFAULT_BRANCH..origin/feature/actual-change"
Expected diagnosis: the range to main contains
two commits—the unrelated experiment commit and the
intended feature commit. GitHub is not “adding random commits”; the
head branch ancestry actually contains them.
5. Two valid repairs depend on intended integration target
| Intent | Least destructive repair | Why |
|---|---|---|
The feature truly depends on
experiment/base and should land there first
|
Change the PR base to experiment/base |
No history rewrite; PR now reviews only the incremental feature commit |
The feature should land directly on main and
must not contain experiment history
|
Recreate/rebase/cherry-pick a clean branch from
main
|
Git ancestry must change; metadata alone cannot erase an inherited commit |
For this lab, choose the first interpretation so we can repair without rewriting history:
gh pr edit "$PR_NUMBER" --base experiment/base
gh pr view "$PR_NUMBER" --json baseRefName,baseRefOid,headRefName,headRefOid,commits,files
git log --oneline "origin/experiment/base..origin/feature/actual-change"
git diff --stat "origin/experiment/base...origin/feature/actual-change"
Now the PR should contain one incremental commit. The original Git graph did not change; only the PR base coordinate changed.
6. Wrong head repository or branch: inspect ownership before pushing
A common fork failure is assuming origin means
upstream. Chapter 04 established that remote names are conventions.
Before pushing a review fix, inspect the PR’s
headRepository, headRepositoryOwner, and
headRefName, then compare with
git remote -v. If you push the same branch name to a
different repository, the PR does not magically follow that other
ref.
gh pr view PR_NUMBER -R OWNER/REPO --json headRepository,headRepositoryOwner,headRefName,headRefOid,baseRefName
git remote -v
git branch -vv
7. Force-pushing during active review: technically possible, operationally expensive
A rebase or history edit changes commit OIDs. Force-pushing the
rewritten branch can invalidate reviewers’ mental anchors, make
comments outdated, change check SHAs, and complicate
audit/reproduction. The master contract therefore does not teach
plain git push --force as a routine fix.
--force-with-lease over plain force, and verify that
the remote still points where you expect. This chapter does not need
a force push.
8. Accidental Issue closure: diagnose the automation input
If an Issue closes after a PR merges, inspect the PR body, commit
messages, manually linked development relationship, repository
auto-close setting, and target/default branch. Reopening the Issue
restores lifecycle state but does not remove the cause. If the
relation should be informational only, replace
Closes #N with a plain reference such as
Related to #N.
gh pr view PR_NUMBER -R OWNER/REPO --json body,baseRefName,closingIssuesReferences
gh issue view ISSUE_NUMBER -R OWNER/REPO --json number,state,stateReason,url
9. Approval, checks, merge, and deployment are four different states
An approving review answers a human governance question. A green check answers a configured automated-validation question for a particular revision. Merge state answers whether GitHub integrated the PR. Deployment evidence answers whether an environment changed. Incident diagnosis fails when teams compress those into one “green” status.
| Claim | Authoritative evidence |
|---|---|
| Reviewer approved | PR reviews / review decision |
| Required tests passed | specific check runs and tested SHA |
| PR merged | PR merged state + merge commit / base history |
| Production updated | deployment/environment/release system evidence |
10. Example policy rejection: good code can still be non-mergeable
A repository can require reviews or status checks through branch protection/rulesets. A PR may have a clean Git merge and still be blocked by policy. Conversely, a PR can have approval but be unmergeable because of a Git conflict. Diagnose the class of blocker before changing code.
gh pr view PR_NUMBER -R OWNER/REPO --json mergeable,mergeStateStatus,reviewDecision,statusCheckRollup,baseRefName,headRefOid
gh pr checks PR_NUMBER -R OWNER/REPO --required
If your free disposable repository has no required checks/protection, treat this as a read-only model/fixture; do not enable paid or organization controls merely to manufacture a failure.
11. Security-sensitive and destructive operations in PR incidents
- Force update a branch/tag: rewrites shared ref history and can invalidate review/check evidence.
- Delete a head branch: can disrupt active review or automation; inspect PR dependencies first.
- Transfer/delete repository: inventory PRs, forks, Actions, Pages, packages, webhooks, and external integrations first.
- Change workflow permissions/secrets: affects code-execution trust; never “fix CI” by exposing secrets to fork code.
- Bypass policy: converts a failed control into an unreviewed governance exception; record who/why/evidence when legitimate.
12. Cleanup the intentionally broken lab
gh pr close "$PR_NUMBER" --comment "Diagnostic cause preserved and repair verified."
cd ..
printf 'archive-check=%s/%s
' "$OWNER" "$REPO"
gh repo archive "$OWNER/$REPO"
rm -rf "$REPO"
13. Lesson summary
A misleading PR is usually explainable from base/head coordinates and ancestry. Preserve that evidence before mutation. Change metadata when the target is wrong; change Git history only when the branch ancestry is wrong. Force-pushes, closing keywords, checks, approvals, and deployment status all belong to distinct operational layers.
Knowledge check
A PR contains an inherited experiment commit. Can changing the title remove it?
No. The commit is in the head branch ancestry; title/body metadata cannot change Git history.
When is changing the PR base the least destructive repair?
When the head branch really is intended to be reviewed relative to a different existing base branch.
Why preserve old head/base OIDs before a history rewrite?
They provide evidence and a recovery/reference point if reviews/checks become invalid or the rewrite goes wrong.
Checks passed and the PR is approved, but production is broken. What evidence plane is missing?
Deployment/environment/release evidence. PR validation and human approval do not prove deployment success.
An Issue was auto-closed unintentionally. Is reopening it a complete fix?
No. Reopen if needed, then remove/correct the closing relationship or policy that caused the lifecycle mutation.
Authoritative references
Changing a pull request base branch
Branches and pull-request comparisons
Linking a pull request to an issue
Managing protected branches
Approving workflow runs from forks
gh pr edit
gh pr checks
REST API endpoints for pull requests
REST API versions
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.