Chapter 07Lesson 04~190 minutes

Merge Requests, Drafts, Review Threads, Suggestions, and Change Collaboration: Diagnostics, Failure Modes, Security, and Performance

Diagnose wrong targets, misleading diffs, prematurely resolved discussions, fork-pipeline trust mistakes, stale evidence, and incorrect issue-closing assumptions without hiding the original failure.

DiagnosticsWrong targetStale threadsDiff limitsFork securityRecovery

Learning objectives

  • Diagnose an MR that targets the wrong branch without merging, force-pushing, or recreating work unnecessarily.
  • Prove why a resolved thread can still represent an unfixed code problem.
  • Inspect generated/collapsed/too-large diff areas instead of treating the web summary as complete evidence.
  • Identify the exact trust escalation when fork code is executed with parent-project CI resources.
  • Diagnose linked-issue closing assumptions using target/default-branch and merge-state evidence.
Availability baseline (verified 2026-08-21). Core merge requests, draft/ready state, reviewers, comments/review threads, suggestions, basic approvals, branch/fork workflows, merge-request pipelines, the Merge Requests/Discussions REST APIs, and the glab mr command family are documented for Free/Premium/Ultimate on GitLab.com, Self-Managed, and Dedicated. On GitLab Free, eligible users can approve an MR, but approvals are optional and do not enforce a merge gate; required approval rules are Premium/Ultimate. Draft MRs cannot merge until marked ready, but by default they run the same MR pipelines as ready MRs. Current fork-pipeline behavior and protected-resource rules are version-sensitive, so production policy must be checked against the deployed GitLab version.

1. The MR diagnostic sequence

When an MR looks wrong, do not begin by toggling settings until the symptom disappears. Preserve the state that explains the failure:

  1. Preserve evidence: MR IID, source/target branches, source SHA, target SHA, diff refs, pipeline ID/source, discussion state, and relevant UI/error text.
  2. Identify scope: source project, target project, branch/ref, MR, pipeline/job/runner, variable/resource boundary, and linked issue.
  3. Inspect policy: role, protected branch, merge checks, tier-dependent approval rules, CI source, and default-branch semantics.
  4. Choose least destructive correction: retarget, push a normal corrective commit, reopen a thread, rerun only after trust review, or update an issue link.
  5. Verify causality: prove the intended state changed and unrelated refs/resources did not.

2. Broken example: the MR targets the wrong branch

Create a harmless branch release/ch07-old in a disposable project, then intentionally open the training MR against it instead of main. The code itself can be correct while the delivery intent is wrong.

# Evidence first; do not merge.
glab api "projects/$PROJECT_ID/merge_requests/$MR_IID"   --jq '{iid,state,draft,source_branch,target_branch,sha,diff_refs,detailed_merge_status}'   | tee ch07-diagnostics/wrong-target.json

# Compare remote branch identities.
git fetch origin --prune
git show --no-patch --oneline "origin/main"
git show --no-patch --oneline "origin/release/ch07-old"

# Least-destructive correction if main is truly intended.
glab mr update "$MR_IID" -R "$REPO" --target-branch main --yes

glab api "projects/$PROJECT_ID/merge_requests/$MR_IID"   --jq '{iid,source_branch,target_branch,sha,diff_refs,detailed_merge_status}'   | tee ch07-diagnostics/retargeted.json

Retargeting changes the comparison base and can materially change the diff, conflicts, CI behavior, approvals, and issue-closing intent. Therefore “target now says main” is not enough—review the recalculated diff and checks again.

3. Failure mode: resolved thread, unchanged code

A reviewer flags an unsafe line. Someone selects Resolve thread without pushing a fix. GitLab correctly records the thread as resolved; the application cannot infer whether the human concern was substantively addressed.

HEAD_BEFORE="$(glab api "projects/$PROJECT_ID/merge_requests/$MR_IID" --jq '.sha')"

glab api "projects/$PROJECT_ID/merge_requests/$MR_IID/discussions" --paginate   --jq '.[] | {id,notes:[.notes[] | {resolvable,resolved,body}]}'   | tee ch07-diagnostics/discussions.json

HEAD_AFTER="$(glab api "projects/$PROJECT_ID/merge_requests/$MR_IID" --jq '.sha')"
printf 'before=%s
after=%s
' "$HEAD_BEFORE" "$HEAD_AFTER"

If the thread changed to resolved while the relevant source SHA/diff did not change, preserve that contradiction. Reopen the thread or add a new review note, then require a corrective commit. Do not rewrite history just to make the review timeline look cleaner.

4. Failure mode: web diff does not show everything at first glance

GitLab collapses generated files and can collapse or suppress very large diffs for performance. The UI can therefore say that some changes are not shown. A reviewer who scans only expanded files has incomplete evidence.

# Local Git is an independent view of the source/target comparison.
git fetch origin --prune
git diff --stat "origin/$TARGET_BRANCH...origin/$SOURCE_BRANCH"
git diff --name-status "origin/$TARGET_BRANCH...origin/$SOURCE_BRANCH"

# Inspect a specific file locally if GitLab collapsed it.
git diff "origin/$TARGET_BRANCH...origin/$SOURCE_BRANCH" -- path/to/generated-or-large-file

# glab also provides the MR diff.
glab mr diff "$MR_IID" -R "$REPO" > ch07-diagnostics/mr-full-review.txt

Do not assume that local Git and the MR UI are comparing identical endpoints until you verify source/target SHAs. The correct response to hidden output is better inspection, not blindly increasing instance diff limits or ignoring the file.

5. Failure mode: “run in parent” turns untrusted code into privileged execution

An external fork MR fails because it cannot reach a parent-project secret. A maintainer clicks Run pipeline in the parent project without reviewing the fork’s CI changes. The resulting pipeline can use the fork branch’s CI configuration while running with parent-project CI settings/resources and the triggering member’s permissions.

The first fix is not “make the secret visible.” Preserve the MR diff, especially .gitlab-ci.yml and included CI configuration, then decide whether parent-context execution is justified. If not, keep validation in the fork or design a safe, secretless validation path.

Never demonstrate this failure with a real credential. Use a fixture such as PARENT_SECRET=<redacted-not-created> and reason about reachability. If a real secret is exposed, revoke/rotate it before editing history or logs.

6. Failure mode: “Closes #12” does not always mean the issue will close now

GitLab’s issue-closing semantics are tied to the default branch and project configuration. A closing pattern in an MR that is merely open, closed without merge, or integrated only into a non-default branch does not justify declaring the issue delivered.

# Preserve the actual target and MR final state.
glab api "projects/$PROJECT_ID/merge_requests/$MR_IID"   --jq '{iid,state,target_branch,merge_commit_sha,merged_at}'

glab issue view "$ISSUE_IID" -R "$REPO"

glab repo view -R "$REPO" --output json --jq '{default_branch}'

If the MR merged into a release branch rather than the default branch, follow the actual release/integration flow. Do not manually close the issue merely to make the board look complete unless your team intentionally changes the work-item state and records why.

7. Failure mode: treating one mergeability field as final truth

The Merge Requests API documents detailed_merge_status as the richer status field. Some state is calculated asynchronously. A transient checking result is not a failure and a stale has_conflicts observation may need a refreshed request after mergeability computation.

for n in 1 2 3; do
  glab api "projects/$PROJECT_ID/merge_requests/$MR_IID"     --jq '{sha,detailed_merge_status,has_conflicts,head_pipeline}'
  sleep 2
done

In production automation, use bounded retries/backoff, explicit terminal states, and a timeout. Do not spin indefinitely or hammer the API.

8. Security-sensitive and destructive operations: what this chapter does not use

  • No force-push or history rewrite is needed to repair a target-branch mistake.
  • No protected-branch bypass is needed to make the lab merge.
  • No project/group deletion or transfer is part of diagnosis.
  • No real token/variable is printed into an MR, job log, issue, or screenshot.
  • No self-hosted runner or parent-context fork pipeline is required.
  • No permanent deletion of review notes is required; preserving diagnostic evidence is usually more useful.

9. Reliability and performance: review the evidence surface, not just the browser

Large MRs cost reviewer attention, diff storage/rendering, CI time, and merge-conflict risk. GitLab limits/collapses diffs partly to protect service performance. The production answer is usually smaller changes, generated-file policy, targeted local inspection, and appropriate CI—not weakening review requirements or increasing global limits without measurement.

10. Compact MR failure runbook

Symptom Preserve first Likely scope Least-destructive next step
Wrong target MR JSON + source/target SHAs + current diff Hosted MR target metadata Retarget, then re-review recalculated diff/checks
Resolved but unfixed Discussion JSON + head SHA + file diff Discussion state versus Git source state Reopen/request corrective commit
Some changes hidden UI message + file list + SHAs Diff rendering limits/generated classification Inspect locally/API; split future changes
Fork needs parent secret Fork CI diff + pipeline source/project + resource assumptions Project/runner/variable trust boundary Keep secretless source-side validation or explicitly review before parent run
Issue stayed open MR state/target + default branch + issue state Closing semantics/workflow intent Trace commit to default branch; correct metadata/policy, do not fake delivery

Knowledge check

After retargeting an MR from release/old to main, can the old review be reused without inspection?

A thread says resolved and the head SHA did not change. What should you inspect next?

The UI says “Some changes are not shown.” What is the wrong response?

Why is running fork code in a parent pipeline potentially dangerous before merge?

An issue did not close after an MR merged to a non-default release branch. Is GitLab necessarily broken?

Summary

MR failures become understandable when you keep Git identity, hosted metadata, discussion state, CI trust, and work-item semantics separate. You repaired a wrong target without destructive Git operations, proved that “resolved” can be stale evidence, handled collapsed/large diffs with independent inspection, protected parent resources from fork code, and diagnosed issue-closing assumptions from actual branch state.

Official references

Next lesson

Integrate the chapter in one evidence-rich checkpoint

Lesson 5 runs one small change from synthetic issue through draft MR, review suggestion, current-head verification, merge, linked-issue proof, stale-thread diagnosis, and targeted cleanup.

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.