Clone a Git Repository Without Losing Track of It
You typed the URL, hit enter, and now there are two directories on disk that both look like the project: cloning without a plan is how that happens. This guide gets you a working copy of a Git repository, a verified origin remote, and a deliberate choice between a full, shallow, bare or local clone. The examples use Git 2.43.0 from Ubuntu package git-man 1:2.43.0-1ubuntu7.3; check your installed version if the output differs.
The route
Jump straight to the step you need, or tick off Done means at the end.
- 1. Check Git and choose an empty destination
- 2. Make the ordinary working clone
- 3. Select a branch or tag deliberately
- 4. Use a shallow clone when limited history is enough
- 5. Clone locally without accidentally sharing storage
- 6. Create a bare repository only when there are no files to check out
- 7. Add submodules only when you have reviewed their URLs
- Common traps
Allow about fifteen minutes. You need Git, a shell, network access for a remote repository, and a destination whose contents you have already checked. A normal clone is unprivileged: do not reach for sudo just because a clone failed, fix the URL, permissions or destination instead.
1. Check Git and choose an empty destination
Confirm the program before copying anything:
$ git --version
git version 2.43.0
$ command -v git
/usr/bin/git
Git creates a new directory when you give a destination name, and that directory must be empty if it already exists. Check it first:
$ test ! -e ~/src/example-project && echo 'destination does not exist'
destination does not exist
Replace ~/src/example-project and the URL in the examples with your own values. A typo in the destination can drop a second checkout right beside the one you meant to update, so keep the destination explicit whenever a script or a note will refer to it later.
Checkpoint
You know the source repository, the destination path, and whether you need its complete history. Do not go further until all three are clear.
2. Make the ordinary working clone
Clone over HTTPS or SSH, then move into the new working tree:
$ git clone https://code.example.org/team/example-project.git ~/src/example-project
Cloning into '/home/you/src/example-project'...
remote: Counting objects: 100% ...
Receiving objects: 100% ...
Resolving deltas: 100% ...
$ cd ~/src/example-project
The progress percentages and remote messages vary. A successful command creates the working files and a .git directory. Git names the remote origin, fetches remote-tracking branches, and checks out the branch named by the remote repository's HEAD, all without you asking.
Verify the result without changing it:
$ git status --short --branch
## main
$ git remote -v
origin https://code.example.org/team/example-project.git (fetch)
origin https://code.example.org/team/example-project.git (push)
$ git branch --show-current
main
Your default branch may be called master or something else entirely. The status line and remote URL are the checks that matter, not a particular branch name. A successful clone needs no undo command of its own: to remove it later, confirm the path first, then delete that clone directory as a separate, deliberate step.
3. Select a branch or tag deliberately
Use --branch when the remote's default branch is not the one you need. It accepts a branch or a tag:
$ git clone --branch release-2.4 https://code.example.org/team/example-project.git example-project-2.4
$ cd example-project-2.4
$ git status --short --branch
## release-2.4...origin/release-2.4
A tag checks out a detached HEAD. That is useful for inspecting an exact release, but any commits made there will not belong to a normal local branch unless you create one:
$ git clone --branch v2.4.0 https://code.example.org/team/example-project.git example-project-v2.4.0
$ cd example-project-v2.4.0
$ git status --short --branch
## HEAD (no branch)
$ git switch -c inspect-v2.4.0
Switched to a new branch 'inspect-v2.4.0'
Do not mistake a detached HEAD for a failed clone: it is exactly what selecting a tag does.
4. Use a shallow clone when limited history is enough
For a build workspace or a short-lived checkout, cap history with --depth:
$ git clone --depth 1 --branch main https://code.example.org/team/example-project.git example-project-ci
$ cd example-project-ci
$ git rev-parse --is-shallow-repository
true
$ git log --oneline -1
<latest commit on main>
--depth 1 fetches a truncated history and implies --single-branch unless you explicitly add --no-single-branch. It saves transfer and storage, but any command needing older commits, complete tag history or another branch will not work until you fetch more.
To turn a shallow checkout into a complete one, fetch the missing history:
$ git fetch --unshallow origin
$ git rev-parse --is-shallow-repository
false
The fetch changes only the clone, by downloading more objects; it does not touch the source repository. If you need just a fixed amount of extra history rather than all of it, use git fetch --deepen NUMBER instead of assuming the missing commits are already available locally.
5. Clone locally without accidentally sharing storage
A local path uses Git's local optimisation by default, which can mean hardlinks for objects, so the new clone is not an independent backup. Force ordinary copies with --no-hardlinks:
$ git clone --no-hardlinks /srv/git/example-project.git ~/src/example-project-copy
Cloning into '/home/you/src/example-project-copy'...
done.
Use this form when the source may be removed, or when the destination needs to stand on its own. Do not modify the source and destination concurrently during a local clone: the manpage warns this can race the same way a recursive copy can.
Warning
--shared is not a safer version of the command above. It creates an alternates file and borrows objects from the source, so if source maintenance later removes an object the clone needs, the clone can become corrupt. Use it only when that dependency is deliberate. To break the dependency afterwards, run this inside the clone:
$ git repack -a
$ test ! -e .git/objects/info/alternates && echo 'clone no longer uses an alternates file'
clone no longer uses an alternates file
Check the alternates path before trusting that verification, since a repository can have other object-storage arrangements. Never use --shared for a backup.
6. Create a bare repository only when there are no files to check out
A bare clone has no working tree; its destination directory is the Git directory itself, which suits a server-side repository or a local publishing target:
$ git clone --bare /srv/git/example-project.git /srv/git/example-project-publish.git
Cloning into bare repository '/srv/git/example-project-publish.git'...
done.
$ git -C /srv/git/example-project-publish.git rev-parse --is-bare-repository
true
There will be no project files to edit in /srv/git/example-project-publish.git, and a bare clone skips the usual remote-tracking branches and related configuration too. Do not reach for --bare if you expect to cd in and edit source files.
Warning
Writing under /srv/git may need an administrator to prepare ownership and permissions first. Run the clone as the account that will maintain the repository where you can, and do not make the whole thing world-writable as a shortcut.
7. Add submodules only when you have reviewed their URLs
A normal clone leaves submodules empty. If the project needs them:
$ git clone --recurse-submodules https://code.example.org/team/example-project.git example-project-with-deps
$ cd example-project-with-deps
$ git submodule status
This initialises and clones the submodules the superproject already recorded. Read the project's submodule configuration first: each URL is another source Git will fetch code from, and SSH or HTTPS credentials may be used along the way. For a large project, --jobs 4 fetches several submodules concurrently, at the cost of more simultaneous network and disk activity.
If you already made the ordinary clone, the equivalent recovery step is:
$ git submodule update --init --recursive
Do not combine --recurse-submodules with --bare or --no-checkout: the manpage says it is simply ignored when there is no worktree to populate.
Common traps
- Destination not empty. Inspect it before doing anything else:
Choose an empty directory, a different destination, or keep the existing files and use a separate workflow. Never delete a directory just to make$ find ~/src/example-project -mindepth 1 -maxdepth 1 -printgit clonefit; that can destroy uncommitted work. - Authentication fails. Verify the URL and your account's access independently. For SSH, check the host key and key-agent setup. For HTTPS, use the credential method your service approves. Never put a password or access token directly in the URL, shell history or a copied script.
- Clone stops part way through. The destination may hold an incomplete repository. Run
git -C PATH statusonly if you recognise that directory as disposable, then remove or repair it deliberately. Do not point a later clone at an unknown partial directory.
Done means
- Checked the installed Git version and source URL before copying anything.
- Destination holds the intended working tree, or is deliberately bare.
git status,git remote -vand the branch or tag match the plan.- Shallow history is marked as such, or expanded before a full-history task.
- A local clone does not rely on shared objects unless that is intentional and documented.
- Submodule URLs were reviewed before fetching additional repositories.