Chapter 04Lesson 04~125 minutes

Branches, Forks, Remotes, Synchronization, and Contribution Workflows: Diagnostics, Failure Modes, Security, and Performance

Diagnose wrong-remote pushes, wrong authenticated identities, polluted fork default branches, stale bases, confused local-versus-hosted synchronization, and unsafe branch deletion from preserved ref/permission/API evidence before correcting anything.

DiagnosticsWrong remoteStale baseBranch safety

Learning objectives

  • Apply one repeatable diagnostic sequence to remote, fork, ref, permission, and synchronization failures.
  • Diagnose a wrong-target push from remote configuration and dry-run evidence before a hosted branch is changed.
  • Recognize when authentication succeeds as the wrong GitHub account or SSH identity and repair account selection without widening permissions.
  • Repair the habit of developing on a fork default branch by isolating work into topic branches without deleting evidence.
  • Separate hosted fork synchronization from local tracking state and explain stale-base contribution failures.
  • Inventory pull-request and automation dependencies before deleting remote branches.
  • Distinguish authentication/authorization/policy errors from local Git topology errors and select the least destructive correction.
Availability: All failure drills use disposable repositories or local configuration and are GitHub Free-compatible. Branch protection/rulesets, private forks, SSO, and private-fork Actions policy are mentioned only when they can causally explain a rejection; availability varies by plan/visibility/organization/deployment.

1. Diagnostic sequence: preserve → scope → inspect → correct → verify

  1. Preserve evidence. Save stderr/exit status, git status, remotes, branch tracking, relevant OIDs, and hosted API/CLI fields. Do not reset/rebase/delete first.
  2. Identify scope. Which local repository? Which GitHub owner/repo? Which local ref, remote-tracking ref, or hosted branch? Which identity and permission? Which PR/workflow depends on it?
  3. Inspect controls. URLs/push URLs, branch tracking, GitHub viewer permission, fork parent/source, branch protection/rules, PR state, workflow logs/approvals, and API status/body where relevant.
  4. Choose least destructive correction. Fix one remote URL, fetch, create a new topic branch, merge/rebase deliberately, or request the correct permission. Do not force-update or delete merely to make an error disappear.
  5. Verify independently. Compare exact hosted/local OIDs and re-run the failing read/dry-run operation.
Security principle: a denied push may be a correct control. Do not weaken rules, create broader tokens, grant admin rights, or bypass policy until you have proven that authorization—not topology—is the actual problem.

2. Failure A — the topic is about to be pushed to upstream

In a real fork workflow, this mistake often fails because the contributor lacks upstream write permission. But a maintainer or the owner of both repositories may have enough permission for the mistake to succeed, which is more dangerous. Diagnose before sending data.

git status --short --branch
git remote -v
git branch -vv
git rev-parse HEAD

# Safe evidence: ask Git to simulate the ref update only.
git push --dry-run upstream HEAD:refs/heads/topic/accidental-target
echo "dry-run exit=$?"

A dry run that says the branch would be created proves upstream is a writable target for your current identity. That is not success—it is evidence that the human-factor guardrail failed. The least-destructive correction is to push to origin instead and optionally disable the upstream push URL locally.

git push origin HEAD:refs/heads/topic/accidental-target
git remote set-url --push upstream DISABLED
git remote -v
Do not create the upstream branch just to demonstrate the mistake. The --dry-run evidence is enough.

3. Intentionally broken example — upstream push is locally disabled

Now the upstream push URL is DISABLED. Run a controlled push attempt. This is expected to fail before changing GitHub state.

git push upstream HEAD:refs/heads/topic/should-not-exist
      RC=$?
      printf 'exit=%s
' "$RC"
      git remote -v

Interpret the result: Git will report that DISABLED does not appear to be a Git repository (exact wording can vary). The nonzero exit is not an authentication problem and not a GitHub branch-policy rejection. git remote -v proves the push URL was deliberately replaced. Repair by using origin for publication; do not “fix” the guardrail by restoring upstream write unless the workflow requires it.

git push --dry-run origin HEAD:refs/heads/topic/should-not-exist
echo "origin dry-run exit=$?"

This satisfies the diagnostic rule: original cause remains visible, correction changes only the intended target, and verification uses a dry run.

4. Failure B — authentication succeeds as the wrong GitHub identity

A confusing failure can begin with a message that looks successful. Your SSH agent may offer a valid key for a different GitHub account, or GitHub CLI may have another account active for github.com. Authentication has succeeded, but authorization for the intended repository can still fail because GitHub sees the wrong actor.

Inspect identity without printing credentials. For GitHub CLI, ask which account is active. For SSH, the test response names the GitHub username associated with the key that authenticated:

gh auth status --active --hostname github.com

      # SSH intentionally does not provide a shell. Read the username in its response.
      ssh -T git@github.com
      SSH_RC=$?
      printf 'ssh_test_exit=%s
' "$SSH_RC"

GitHub documents exit status 1 for a successful ssh -T authentication test because shell access is not provided. Therefore, do not classify that exit code alone as failure; verify that the response says Hi EXPECTED-USERNAME!. If it names a different account, the problem is identity selection, not missing repository permissions.

For multiple accounts, use an explicit account/SSH-host strategy. gh auth switch --hostname github.com --user EXPECTED-USERNAME can select a known CLI account. For SSH, GitHub documents separate keys/host aliases or an explicit GIT_SSH_COMMAND approach. Inspect the repository remote after any fix so the selected identity and target repository form one coherent path.

Credential safety: do not run gh auth status --show-token, gh auth token, or verbose commands that expose private key material merely to identify the account. Identity can be diagnosed without printing secrets.

5. Failure C — work was committed directly on the fork default branch

A contributor repeatedly commits work on their fork’s main. Later, syncing upstream main becomes awkward because the fork default branch contains contributor-only commits. Do not erase those commits to “make Sync fork green.” First preserve them with a topic ref.

git fetch origin
git fetch upstream
git switch main
git log --oneline --left-right --graph main...upstream/main -12

# Preserve contributor-only work before changing default-branch strategy.
git branch topic/recovered-default-work main
git push -u origin topic/recovered-default-work

Now the work has an explicit topic branch. If the desired policy is for fork main to track upstream exactly, you can plan a fast-forward or controlled reset only after verifying no unique work remains unreferenced. The repair is “separate work from synchronization branch,” not “force first, inspect later.”

History rewrite/reset warning: resetting a published fork default branch can discard unique commits from that ref. In this course, any such reset is optional and only after a preservation branch plus exact OID verification.

6. Failure D — GitHub says the fork is synced, but local Git is still behind

This is a scope error, not a broken sync. A hosted sync updated the fork branch on GitHub; your local clone still has the old origin/main remote-tracking ref. Preserve the three OIDs and compare.

LOCAL_MAIN=$(git rev-parse main)
      LOCAL_ORIGIN=$(git rev-parse origin/main)
      HOSTED_FORK=$(git ls-remote origin refs/heads/main | cut -f1)
      printf 'local_main=%s
local_origin=%s
hosted_fork=%s
'         "$LOCAL_MAIN" "$LOCAL_ORIGIN" "$HOSTED_FORK"

      git fetch origin main
      NEW_LOCAL_ORIGIN=$(git rev-parse origin/main)
      printf 'origin_after_fetch=%s
' "$NEW_LOCAL_ORIGIN"

If hosted fork and fetched origin/main now match, GitHub sync worked. Whether local main should fast-forward, merge, or remain independent is a separate local-history decision.

7. Failure E — contribution branch is based on stale upstream state

A stale base is not automatically invalid. But it increases the chance that the PR diff includes obsolete assumptions, conflicts, or checks against old code. The failure is opening or updating a contribution without first inspecting how the base moved.

git fetch upstream
git merge-base topic/navigation upstream/main
git log --oneline --left-right --cherry-pick         topic/navigation...upstream/main -20
git diff --stat upstream/main...topic/navigation
git diff upstream/main...topic/navigation

The merge base tells you where the lines diverged. The triple-dot diff shows the topic change relative to that base. If upstream contains important changes, merge or rebase according to your policy, rerun tests, then push the updated topic. Do not assume “GitHub will sort it out when I open the PR.”

8. Failure F — someone wants to delete a hosted topic branch too early

Branch deletion is a ref deletion. It can remove the named head that a pull request, workflow, deployment script, or external system expects. Current GitHub documentation states that branches associated with open pull requests cannot be deleted through the PR branch-deletion workflow; after PRs are closed/merged, GitHub can expose delete/restore controls. Treat that guardrail as one signal, not a complete dependency inventory.

Before deleting any hosted branch, gather:

  • Open pull requests whose head uses the branch, including cross-repository PRs.
  • Workflow runs, deployment/environment processes, or external CI that target the branch name.
  • Release scripts, Pages/source configuration, badges, webhooks, or scheduled jobs that reference the branch.
  • Whether the branch is protected/locked or governed by a ruleset that disallows deletion.
  • Exact branch OID and whether the commit remains reachable from another durable ref if recovery is needed.
Destructive operation: git push origin --delete branch deletes a hosted ref if authorization/policy permits. Do not use it in a valuable repository and do not use it merely to clean the branch list.

9. Permission denial: distinguish four layers before changing credentials

Evidence Likely layer Next inspection
repository not found / 404 for private repo Visibility/auth/authorization may be intentionally masked Authenticated account, repo owner/name, SSO/policy, token repository access
Permission to OWNER/REPO denied Authenticated identity lacks push permission or wrong account/key/token gh auth status, SSH identity, viewer permission, remote URL
GH006 / protected branch / ruleset rejection Server policy denied a ref update Branch/ruleset settings, required PR/checks, actor role
Local DISABLED does not appear... Local remote push URL guardrail git remote -v / pushurl
Fork exists but private fork workflow does not run Actions/fork policy Repository/org Actions settings and plan/policy, not Git transport

A larger token or admin permission is not the first repair. Identify the denied resource and required operation. Least privilege means the correction should grant only the missing capability—or change the workflow so the capability is unnecessary.

10. Security: fork-based automation failures can be intentional protections

A workflow triggered from a public fork may wait for maintainer approval depending on contributor/repository settings. Fork pull_request contexts intentionally restrict secrets and token permissions. A contributor should not “repair” that by printing environment variables, switching to a privileged event, or asking for broad secrets.

For private forks, repository/organization policy can decide whether workflows run and whether write tokens/secrets are sent. Those settings increase trust and should be reviewed by administrators, not casually changed to satisfy one failing contribution.

Preserve the security failure. A log saying “awaiting approval” or a read-only token failure may be proof the platform is enforcing the intended boundary.

11. Reliability/performance: stale state is more important than speed here

Most Chapter 04 problems are correctness problems, not raw Git throughput problems. Avoid repeated blind clone/fetch loops in automation, but do not optimize away the one fetch that gives you fresh evidence. When fleet tooling inventories branches/forks through REST, paginate and inspect rate-limit responses. When a human operates two repositories, clarity beats micro-optimization.

Use commit OIDs in logs when diagnosing synchronization. Branch names move; OIDs let two operators confirm they are discussing the same state.

12. Least-destructive repair matrix

Failure Avoid Prefer
Wrong remote target Force/delete after accidental push Dry-run first; correct remote; disable upstream push locally if suitable
Wrong authenticated identity Broaden token scopes or grant repo access blindly Confirm active gh/SSH username; select the intended account/key; re-test authorization
Contributor commits on fork default branch Hard reset immediately Create preservation topic branch, push it, then repair default branch
Hosted fork synced, local stale Repeat hosted sync / force Fetch origin and compare OIDs
Stale topic base Open PR blindly or reset to upstream Fetch, inspect merge-base/diff, then merge/rebase by policy
Branch appears “unused” Delete based on age Inventory PR/automation/ref dependencies, preserve OID, then delete if safe
Push rejected by policy Grant admin/bypass Use PR/check/review path or request minimal role needed

13. Diagnostic verification checklist

  • Original stderr/exit status and OIDs were preserved before correction.
  • Repository owner/name and local working directory were confirmed.
  • Remote fetch and push URLs were inspected explicitly.
  • The active GitHub CLI/SSH identity was confirmed without printing tokens or private keys.
  • Local branch, remote-tracking ref, and hosted branch were compared as separate state.
  • Permission/policy failure was not misdiagnosed as bad credentials.
  • No force push, hard-reset sync, branch delete, policy bypass, or admin escalation was used to hide the cause.
  • Fork/Actions denial was treated as a potential security control, not automatically a malfunction.
  • Verification compared the exact intended branch OID in the intended repository.

Knowledge check

A dry-run push to upstream would create the branch successfully. Why is that a warning rather than good news?

After clicking Sync fork, origin/main is old. What is the least destructive fix?

You accidentally committed three changes on fork main. What should you do before any reset?

A fork workflow is waiting for approval. Should you expose a secret to make it run?

What evidence should exist before deleting a remote topic branch?

ssh -T git@github.com says authentication succeeded but names the wrong username. What should you fix first?

Next lesson

Integrate the whole workflow under test

Lesson 05 creates independent upstream and contributor changes, predicts ref movements, synchronizes safely, verifies hosted/local ownership, and cleans up only after dependency checks.

Authoritative references

 Syncing a fork
 About permissions and visibility of forks
 Deleting and restoring branches in a pull request
 Approving workflow runs from forks
 Managing GitHub Actions settings for a repository
 gh repo sync
 gh auth status
 Testing your SSH connection
 Managing multiple accounts
 REST API endpoints for branches
 git-remote
 git-fetch
 git-push

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
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.