Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild

Git Clone and Push to a New Repo: Mirror, Fresh Start, or One Branch, and the Command for Each

Sean

Platform Writer

Sep 16, 2026
8 min read

To clone a repository and push it to a new one with everything intact, clone it bare and mirror-push: git clone —bare OLD_URL, cd into it, git push —mirror NEW_URL. That copies every branch, tag and ref. If you want the new repository to start with a clean history, clone normally, delete the .git directory, git init, commit, and push. If you want just one branch, clone with —single-branch and push that branch to the new remote. Those are the three shapes; the mistake is using the first when you meant the second.

Git Clone and Push to a New Repo: Mirror, Fresh Start, or One Branch, and the Command for Each

The highest-voted answer for this question gives the mirror commands and nothing else, and every week someone runs them, pushes fifteen stale branches and a leaked secret from 2019 into a repository that was supposed to be a clean start. The commands are easy. Choosing the shape is the actual decision, so this post starts there.

Table of contents

First decide which shape you want

Three different intentions get typed as the same search.

  • Mirror. Moving a repository from one host to another, or making a full backup. Every branch, tag and commit should arrive. History is the point.
  • Fresh start. Using an existing project as the base for a new one: a template, a fork you do not want linked, a client project cloned from your boilerplate. The files should arrive; the history should not.
  • One branch. Extracting a feature branch or a release branch into its own repository, with its own history but not the rest.

Ask one question: should the new repository’s git log show the old commits? If yes, mirror or one branch. If no, fresh start. The fresh-start case is where people accidentally ship the whole history, including everything that was ever committed and later deleted.

Shape 1: mirror everything

A bare clone has no working directory, just the repository data, which is what you want for a straight copy. The —mirror push sends all refs, including branches you never checked out and tags, and deletes refs on the target that do not exist in the source.

git clone --bare https://github.com/old-owner/old-repo.git
cd old-repo.git
git push --mirror https://github.com/new-owner/new-repo.git
cd ..
rm -rf old-repo.git

The new repository must exist and be empty before the push. Create it on the host without a README, licence or .gitignore, because any initial commit there will conflict with the mirror.

Then clone the new repository normally to work in it. Do not keep working in the bare clone; it has no working tree.

If the repository uses Git LFS, the mirror push moves the pointers but not the large files. Fetch and push them separately from inside the bare clone before deleting it.

git lfs fetch --all
git lfs push --mirror https://github.com/new-owner/new-repo.git

Shape 2: fresh start with no history

Clone, throw away the history, start again. This is the shape for templates and for any case where the old commits should not follow the code.

git clone https://github.com/old-owner/template.git my-project
cd my-project
rm -rf .git
git init -b main
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/new-owner/my-project.git
git push -u origin main

Two things to do before that first commit. Check .gitignore is still in place, because rm -rf .git does not touch it but a template sometimes ships without one. And search the tree for anything that should not be in the new project: .env files, the old project’s name in package.json, a CI config that deploys to the old host.

The GitHub way to do this is the Use this template button on a template repository, which produces a new repository with a single initial commit. It is the same result without the terminal, and it does not link the two repositories the way a fork does.

Shape 3: one branch to its own repository

Clone only the branch you want, then push it as the new repository’s main branch. The commits on that branch come along; other branches do not.

git clone --single-branch --branch release-v2 https://github.com/old-owner/old-repo.git v2-repo
cd v2-repo
git remote set-url origin https://github.com/new-owner/v2-repo.git
git push -u origin release-v2:main

The refspec release-v2:main pushes the local branch under a different name on the remote, so the new repository has a main branch without a rename step. If you want the local branch renamed too, git branch -m release-v2 main first.

Note that —single-branch limits what was fetched, not what is reachable. Commits shared with other branches are included because they are ancestors. If the branch was cut from main, its history includes main up to that point, which is usually what you want.

The remote after the push

In shapes 1 and 3, the clone still has origin pointing at the old repository until you change it. Forgetting this is how a week of commits ends up pushed to the wrong place.

git remote -v
git remote set-url origin https://github.com/new-owner/new-repo.git
git remote -v

If you want to keep pulling from the old repository while pushing to the new one, add it as a second remote named upstream rather than overwriting origin. That is the fork pattern, done by hand.

Wiring the new repository to a deploy

Most of the time the reason for the new repository is that something is going to build from it: a client site, a new environment, a project that has outgrown a personal account. The repository is not done until a push produces a deploy.

On RunxBuild, connecting the new repository is the same flow as any other: pick it in the create form, set the branch, and the first push after that produces a build log and a live route. Rollback to the previous deploy is there from the second push. If you used shape 2, remember the environment variables did not come with the clone; set them on the service before the first build, or the first build is a failed build. Deploying from GitHub on RunxBuild covers the connection.

How this fits the rest of the stack

The three shapes are one decision, whether history should follow the code, and three short command sequences. Whichever you used, fix the remote, confirm with git remote -v, and connect the repository to whatever will build it. If the new repository is going to run as a service with a database beside it, the RunxBuild hosting calculator shows what that pair costs before the first push, and the dashboard is where the repository gets connected.

Useful related references:

FAQ

How do I copy a git repository to a new remote with all history?

Clone it bare and mirror-push: git clone —bare OLD_URL, cd into the .git directory, git push —mirror NEW_URL. The new repository must be empty. Then clone the new repository normally to work in it.

How do I push a cloned repo to a new repo without the history?

Clone it, delete the .git directory, run git init, commit everything as an initial commit, add the new remote as origin and push. Check the tree for .env files and old project names before that first commit.

What is the difference between git clone —bare and —mirror?

Both produce a repository without a working tree. —mirror also sets up the remote-tracking configuration so that git fetch and git push —mirror keep the copy in sync with the source, which is what you want for an ongoing mirror rather than a one-time move.

Does git push —mirror copy Git LFS files?

No. It pushes the LFS pointer files that live in git, not the large objects they point to. Run git lfs fetch —all in the clone, then git lfs push —mirror NEW_URL, before deleting the clone.

Why does git push to the new repo say the remote contains work I do not have?

The new repository was created with an initial commit, usually a README. Either create it empty, or pull that commit first with —allow-unrelated-histories, or force-push if the initial commit is disposable.

#git clone and push to new repo#git push --mirror#git clone --bare#move git repository#duplicate github repository