Creating Repositories, Templates, Settings, Visibility, Topics, and Default Branches: Diagnostics, Failure Modes, Security, and Performance
Diagnose repository-creation and lifecycle mistakes from evidence before changing state, including wrong ownership/visibility, unrelated histories, stale remotes after rename, and unsafe transfer or deletion assumptions.
Learning objectives
- Apply a repeatable evidence-first diagnostic sequence to repository creation, identity, and lifecycle failures.
- Diagnose wrong owner/visibility without immediately transferring or changing visibility.
- Engineer and repair an unrelated-history push rejection without using force push to hide the cause.
- Explain GitHub repository rename redirects while still updating local remotes and dependent integrations deliberately.
- Explain why changing the hosted default branch does not rename local branches automatically.
- Inventory Actions, Pages, packages, webhooks, forks, permissions, and backups before archive/transfer/delete operations.
1. Diagnostic sequence: preserve → scope → inspect → correct → verify
- Preserve evidence. Save non-secret command output, owner/name, remote URLs, branch/ref names, commit OIDs, HTTP status/body, and timestamps before changing anything.
- Identify scope. Is the failure about local Git history, the GitHub repository resource, owner/role, visibility, default branch, integration, Actions, package, fork network, or enterprise policy?
-
Inspect effective state. Use
git status/log/remote,gh repo view --json, documented REST GETs, and relevant settings/logs. - Choose the least destructive correction. Prefer fetching/reconciling or updating metadata/remotes over force push, delete/recreate, transfer, or visibility changes.
- Verify independently. Re-run read-only inspection from both Git and GitHub layers; confirm the original symptom is gone for the right reason.
2. Failure mode: repository created under the wrong owner
Symptom: a service intended for
acme-platform/atlas exists as
developer/atlas. Git operations may work perfectly, so
this is not a Git transport failure. It is a hosted
ownership/governance error.
gh repo view OWNER/REPO \
--json nameWithOwner,isInOrganization,owner,viewerPermission,visibility \
--jq '{nameWithOwner,isInOrganization,owner:.owner.login,viewerPermission,visibility}'
Do not transfer immediately. First inventory the target organization’s repository-creation permission, plan/policy, existing same-name repositories/forks, team access, Pages, packages, Actions, webhooks, and whether transfer is allowed. A disposable repository can simply be deleted/recreated if no useful history or hosted metadata exists; a production repository requires a migration plan.
3. Failure mode: repository created with the wrong visibility
Symptom: a repository intended to be private is public, or a learner selected private but expected public discovery. If confidential material was exposed publicly, this is an incident, not merely a settings correction. Preserve evidence, remove/rotate exposed credentials first, and follow incident response.
For ordinary non-secret lab data, inspect before changing:
gh repo view OWNER/REPO --json nameWithOwner,visibility,isPrivate,viewerPermission
Current gh repo edit --visibility requires an explicit
flag accepting visibility-change consequences. That requirement is
useful design feedback: GitHub treats visibility change as
consequential. In this course, recreate a disposable repository with
the correct visibility instead of using visibility changes as a
practice toggle.
4. Intentionally broken example: remote README versus unrelated local history
This failure is safe to engineer in a disposable repository and teaches the most common initialization mistake. The remote gets one README commit; the local repository independently gets one different initial commit. Neither history is “wrong,” but a direct push cannot fast-forward the remote branch.
Setup
OWNER=$(gh api /user --jq .login)
BAD="github-unrelated-history-lab-$(date +%Y%m%d%H%M%S)"
gh repo create "$OWNER/$BAD" --public --add-readme
DEFAULT=$(gh repo view "$OWNER/$BAD" --json defaultBranchRef --jq .defaultBranchRef.name)
mkdir "$BAD-local"
cd "$BAD-local"
git init -b "$DEFAULT"
git config user.name "Learner Example"
git config user.email "learner@example.invalid"
printf '%s\n' 'service=local-first' > service.txt
git add service.txt
git commit -m "feat: create local independent history"
git remote add origin "https://github.com/$OWNER/$BAD.git"
git push -u origin "$DEFAULT"
Interpret the rejection
! [rejected] main -> main (fetch first)
error: failed to push some refs ...
hint: Updates were rejected because the remote contains work that you do not have locally.
The exact wording/branch name can vary, but the causal evidence is:
remote branch already points at a commit not reachable from your
local branch. The correct next step is inspection, not
--force.
Preserve and inspect both histories
git fetch origin
git log --oneline --decorate --graph --all --boundary
git show --stat HEAD
git show --stat "origin/$DEFAULT"
You should see two root histories. The remote README commit and local service commit have no common ancestor because they were created independently.
Least-destructive repair for this synthetic case
git merge "origin/$DEFAULT" --allow-unrelated-histories --no-edit
git log --oneline --decorate --graph --all -8
git push -u origin "$DEFAULT"
gh api -H "X-GitHub-Api-Version: 2026-03-10" \
"/repos/$OWNER/$BAD/commits/$DEFAULT" --jq .sha
git rev-parse HEAD
Because the two roots contain different files, this small example should merge without a content conflict and create a merge commit joining the histories. In a real project, decide whether the remote seed or local history is authoritative; sometimes recreating an unused remote is cleaner. The lesson is to preserve evidence and choose deliberately—not to normalize unrelated-history merges everywhere.
5. Failure mode: repository renamed but dependencies still use the old coordinate
GitHub redirects most web and Git traffic from the old repository
name to the new one, so a stale origin can appear to
work. That compatibility is not permission to leave the estate
inconsistent. GitHub specifically warns that calls to an
action hosted in a renamed repository are not redirected. Pages URLs and external systems can also need deliberate updates.
Before rename: inspect dependencies
git remote -v
gh repo view OWNER/OLD --json nameWithOwner,url,homepageUrl,isArchived
# Search your own code/config for OWNER/OLD before changing it.
If you intentionally rename a disposable lab repository, update the local clone afterward:
git remote set-url origin https://github.com/OWNER/NEW.git
git remote -v
gh repo view OWNER/NEW --json nameWithOwner,url
6. Failure mode: changing the GitHub default branch and expecting local clones to rename
Suppose GitHub’s default changes from main to
trunk. Existing clones still have whatever local branch
names and upstream configuration they had. Fetching can update the
remote-tracking view, but GitHub cannot rewrite branch names on
every developer workstation.
git branch -vv
git remote show origin
git remote set-head origin -a
git symbolic-ref refs/remotes/origin/HEAD
git remote set-head origin -a asks Git to update the
local symbolic origin/HEAD based on the remote’s
advertised default. That is still different from renaming your local
checked-out branch. If a local rename is part of a migration, plan
and execute it explicitly with the correct upstream tracking—not as
an assumed side effect of the GitHub setting.
7. Before transfer/archive/delete: inventory the hosted resource graph
A Git repository is not the whole GitHub repository resource. Before a lifecycle operation, capture both Git and hosted dependencies. The exact endpoints/features available depend on permission and plan; absence of a field is not proof that the integration does not exist.
| Inventory item | Why it matters before lifecycle change | Example evidence |
|---|---|---|
| Git refs/tags/default branch | Defines source/history/release identities. |
git ls-remote --heads --tags, repository
default-branch field.
|
| Actions/workflows/secrets/environments | Repository coordinate, permissions, logs, secrets, OIDC trust may be bound to repo. | Workflow list/settings; never export secret values. |
| Pages | Rename/transfer can affect URLs/domains/source. | Pages settings + custom-domain inventory. |
| Packages/releases/assets | Ownership/linkage/consumer URLs may change or transfer differently. | Package/release metadata and dependency inventory. |
| Webhooks/Apps/deploy keys | External integrations may target old repository ID/name or receive events under new owner. | Webhook/App inventory without secret material. |
| Fork network | Visibility/delete/transfer constraints can affect forks. | Fork/network metadata. |
| Teams/collaborators/policies | Transfer changes governing owner context. | Non-secret role/team/policy inventory. |
| Backup/restore evidence | Deletion restore is conditional; Git backup alone omits hosted metadata. | Verified mirror + separate metadata/runbook where required. |
8. Archive is not delete; restore is not backup
Archiving makes the repository read-only on GitHub until it is unarchived. GitHub documents that code, issues, pull requests, releases, branches, comments, permissions, and other repository content become read-only. This is useful for retired projects that must remain referenceable.
Deletion removes the repository and permissions. GitHub currently allows restoration of some deleted repositories within 90 days, but fork-network conditions can prevent restoration. A conditional restoration window is not a disaster-recovery design. If recovery matters, maintain an appropriate Git backup and a separate plan for hosted metadata/integrations.
9. Hosted API errors: distinguish resource, permission, and policy
| Observation | Possible cause | Next inspection |
|---|---|---|
| 404 for private repository | Wrong owner/name, repository moved/deleted, or authenticated caller lacks access. | Confirm authenticated login, exact coordinate, and expected repository ownership. |
| 403 on settings mutation | Authenticated but insufficient repository permission, token permission, organization/enterprise policy, or rate/policy restriction. | Inspect viewer permission, token model, response headers/body, and governing policy. |
| 422 on repository creation | Validation conflict such as invalid/occupied name or request parameters. | Read response body; do not blind-retry with new mutations. |
| Git push rejected non-fast-forward | Remote ref points at history not contained in local branch. | Fetch and inspect commit graph before deciding integration strategy. |
REST automation later in the course will add pagination/rate-limit/retry discipline. In this chapter, the important point is that HTTP status plus body and target coordinate are evidence. Never solve a 403 by immediately granting admin or widening a token.
10. Cleanup the intentionally broken repository safely
After you preserve the non-secret commit graph and confirm the
repaired branch OID, return to the repository’s
Settings → General → Danger Zone and delete only
the disposable $BAD repository. GitHub requires
destructive confirmation in the UI.
Then verify from the CLI:
gh repo view "$OWNER/$BAD"
printf 'expected gh exit=%s\n' "$?"
A not-found/access failure confirms the hosted resource is gone.
Your local $BAD-local directory remains because local
Git state is independent; remove it only after you have preserved
any teaching evidence you need.
11. Diagnostic verification checklist
- Every failure is classified as local Git, hosted repository metadata, identity/permission, integration, policy, or lifecycle scope before correction.
- Wrong owner/visibility is inspected before transfer/visibility mutation.
- The unrelated-history rejection remains visible in the evidence and was repaired without force push.
- Rename guidance updates local remotes and inventories dependencies rather than trusting redirects forever.
- Default-branch guidance distinguishes hosted setting, remote HEAD, and local branch name.
- Transfer/archive/delete planning inventories non-Git hosted resources and fork/package/Pages/integration consequences.
- No production repository is used for destructive demonstrations.
Knowledge check
A push to a newly created GitHub repository is rejected with “fetch first.” What should you inspect before considering force push?
Fetch the remote and inspect the commit graph/ref OIDs. A remotely initialized README may have created an independent first history that your local branch does not contain.
After renaming a repository, git fetch still works
using the old URL. Is your migration finished?
No. GitHub redirects much old repository traffic, but local remotes and dependent integrations should be updated deliberately; action references hosted in renamed repositories are a documented exception to redirect behavior.
Why does changing the GitHub default branch not rename a developer’s local branch?
The default-branch selection is hosted repository metadata. Each local clone owns its own branch refs/configuration.
What is safer for a retired repository that must remain referenceable: archive or delete?
Archive, because it makes the GitHub repository read-only and can be reversed by unarchiving. Deletion removes the hosted resource and has only conditional restoration.
A repository transfer “preserves the Git commits.” Why can production still break?
Ownership/policy, collaborator/team access, Pages, packages, Actions/integrations, webhooks, URLs, plan entitlements, and external consumers can change even when Git object history is preserved.
12. Summary
Repository failures are easier when you keep the layers separate. A non-fast-forward rejection is Git/ref history evidence; wrong ownership and visibility are hosted resource problems; a stale remote after rename is local configuration/integration debt; default-branch changes are hosted settings with local consequences; transfer/archive/delete are lifecycle operations over a graph of hosted dependencies.
The diagnostic discipline is consistent with earlier chapters: preserve evidence, identify scope, inspect structured state, make the least destructive correction, and independently verify.
Authoritative references
Creating a new repository
Setting repository visibility
Renaming a repository
Transferring a repository
Archiving repositories
Deleting a repository
Restoring a deleted repository
Backing up a repository
Changing the default branch
gh repo edit
REST API endpoints for repositories
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.