Create and maintain a private development mirror of a public upstream repository without using the hosting platform's Fork button. Use when a user needs to secondary-develop an open-source project privately, keep proprietary changes out of a public fork, set up origin/upstream remotes, sync upstream changes, or resolve conflicts during upstream merges and rebases on GitHub, GitLab, or similar platforms.
Set up a private mirror workflow when the user needs both of these outcomes:
Do not use the platform Fork button for this workflow. Create a new empty private repository and mirror the public upstream into it manually.
Collect the missing private repository name before running commands. Default the rest of the workflow to the house convention unless the repository state proves otherwise.
Use this skill for either of these cases:
origin and upstream setup.If the user only wants a normal public fork and does not care about private derivative work, this skill is unnecessary.
Ask only this when it is missing:
Do not ask for platform, owner, transport, or branch names by default.
Use these defaults unless the repository state or user instruction says otherwise:
gh account or its target org chosen by the user in the active workflow.gh default and then read back the actual URL from gh repo view.sync-upstream.main.If the existing repository layout contradicts these defaults, inspect the real state and adapt instead of asking speculative questions.
If gh is available, prefer GitHub for repository creation and remote validation.
Check first:
gh auth status
If authentication is missing or invalid, instruct the user to authenticate before continuing.
Resolve the owner automatically first:
gh api user --jq .login
Create the empty private repository with the resolved owner and confirmed name:
gh repo create <owner>/<repo-name> --private --disable-issues --disable-wiki --confirm
Then capture the resulting remote URL that matches the user's preferred transport:
gh repo view <owner>/<repo-name> --json sshUrl,url
Use the returned URL that matches the local authenticated setup. Do not ask the user to choose SSH versus HTTPS unless there is an actual auth problem that requires switching transport.
If the repository already exists, verify it instead of recreating it:
gh repo view <owner>/<repo-name> --json nameWithOwner,isPrivate,sshUrl,url,defaultBranchRef
Reject the workflow if the target repository is not private.
Aim for this final state:
origin points to the user's private repository.upstream points to the official public repository.Verify with:
git remote -v
If the remote layout is wrong, fix it before any sync work.
Use this sequence when starting from scratch:
upstream.Commands:
git clone --bare <upstream-url>
cd <repo-name>.git
git push --mirror <private-origin-url>
cd ..
rm -rf <repo-name>.git
git clone <private-origin-url>
cd <working-copy>
git remote add upstream <upstream-url>
git remote -v
Rules:
git push --mirror.git clone --bare plus git push --mirror so branches, tags, and history stay intact.gh repo create instead of asking the user to click through the UI.If the user already clicked Fork on the hosting platform:
origin to the new private repository.upstream still points to the official public repository.Typical commands:
git remote rename origin old-origin
git remote add origin <private-origin-url>
git remote add upstream <upstream-url> # only if missing
git push -u origin <main-branch>
git push origin --tags
If multiple long-lived branches matter, push them explicitly or mirror from a clean clone.
When the private mirror already exists, prefer this baseline flow:
git fetch upstream
git checkout sync-upstream
git merge upstream/<upstream-default-branch>
git push origin sync-upstream
git checkout main
git merge sync-upstream
git push origin main
Use sync-upstream as the upstream integration branch and main as the internal development branch by default.
Before syncing:
git status --short.git branch --show-current.When merge or rebase conflicts appear:
git status.git add <files> and git commit.git add <files> and git rebase --continue.If the conflict is broad:
Before declaring the workflow complete, verify all of these:
origin is private and writable by the user.upstream is the official public source.git remote -v shows the expected mapping.git branch -a includes the expected local and remote branches.git fetch upstream succeeds.When helping the user, be explicit about:
origin.upstream.sync-upstream is the branch used to receive upstream merges by default.main is the internal development branch by default.Use concrete command sequences with the actual branch names when they are known. Default to sync-upstream and main unless the repository already uses a different layout.
When the user has not yet provided the private repository name, ask only for that value instead of expanding into a multi-question intake.