Git, CI/CD & GitOpsXX · RemotesRemotes
What a remote is — a named pointer to another repository
What you'll learn
- Define a remote as a named entry in .git/config that pairs a name with a fetch and push URL
- Distinguish the remote name from the remote URL and from the remote-tracking refs in .git/refs/remotes
- Identify the four common URL schemes (https, ssh, git, file) and how each handles authentication and transport
- Read git remote -v and git remote show output to recover the configured URLs of every remote
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
A Git “remote” is one of the most overloaded words in the version
control vocabulary. It can mean the remote server, the remote
repository, the remote URL, the remote name as it appears in
.git/config, or the local mirror refs under .git/refs/remotes/.
Each of those is a different thing, and Git’s behaviour depends on
which one the current command is operating on. This lesson pins
down what a remote is, on disk, and what the other related things
are not.
What a remote is, on disk
A remote is a named entry in .git/config. The entry has three
fields: a name (a single token chosen by the engineer), a fetch
URL, and a push URL. The fetch and push URLs are typically the
same; they can be configured independently with git remote set-url --push.
[remote "origin"]
url = git@github.com:acme/infra.git
fetch = +refs/heads/*:refs/remotes/origin/*
The url field is the address of the remote repository. The
fetch field is the refspec used when git fetch origin is
run with no further arguments: the + means “force update”, and
refs/heads/*:refs/remotes/origin/* means “every branch on the
remote becomes a remote-tracking ref under refs/remotes/origin/”.
That refspec is why, after a clone, you have a local branch named
main and a remote-tracking ref named origin/main.
git remote
# origin
git remote -v
# origin git@github.com:acme/infra.git (fetch)
# origin git@github.com:acme/infra.git (push)
git remote (no arguments) lists the names of every configured
remote. git remote -v adds the URLs, separated into fetch and
push columns. The output is a flat text representation of the
config; every name listed is guaranteed to have an entry under
[remote "<name>"] in .git/config.
What a remote is not
Three things that look like remotes but are not:
- The remote repository itself. The remote repository lives on a server (GitHub, GitLab, an internal Gitea, a USB stick) and is not part of your local clone. A remote points at it; a remote is not it. Deleting a remote does not delete the remote repository.
- A remote-tracking branch. A remote-tracking ref lives at
refs/remotes/<remote-name>/<branch-name>and is a local copy of a branch on the remote, last updated bygit fetch. A remote-tracking ref is read-only in normal use (you cannotgit checkoutinto one and commit on it). It is the cache the remote fetch wrote into your local clone. - The remote-tracking branch’s local counterpart. Your local
mainbranch and the remote-trackingorigin/mainref are two separate refs with no automatic link between them. The link is the upstream configuration on the local branch — a separate entry under[branch "main"]in.git/config— not a property of either ref.
flowchart LR
A["origin (name in .git/config)"] --> B["remote URL\ngit@github.com:acme/infra.git"]
A --> C["refspec\n+refs/heads/*:refs/remotes/origin/*"]
C --> D["refs/remotes/origin/main\n(remote-tracking ref, local cache)"]
E["local main branch\nrefs/heads/main"] -.->|"branch.main.remote=origin\nbranch.main.merge=refs/heads/main"| A
This separation is what lets one local clone talk to many remotes and one remote serve many clones. The remote is the name and address; the repository lives elsewhere; the remote-tracking ref is the local cache; the local branch is yours.
URL schemes: https, ssh, git, file
The url field of a remote can use one of four schemes. Each
scheme has a different authentication model and a different
transport:
https://— Sends a plain HTTP request to the server over TLS. Authentication is by username and personal access token (PAT) sent in theAuthorizationheader. The PAT is held by the credential helper or cached in~/.git-credentials. No SSH key required. Suitable for CI runners that do not have an SSH agent, and for engineers behind firewalls that block SSH. Example:https://github.com/acme/infra.git.ssh://or[user@]host:path— Speaks the Git protocol over an SSH channel. Authentication is by SSH key (the key must be loaded into the agent or referenced in~/.ssh/config). No credential helper required; the key is the credential. Suitable for interactive use and for any host that exposes SSH. Example:git@github.com:acme/infra.git.git://— Speaks the unauthenticated Git smart protocol on port 9418. Reads only, no authentication, no push support. Rare on the public internet; mostly seen inside private networks and as a read-only mirror. Example:git://git.internal/acme/infra.git.file://— A path on the local filesystem. No network, no authentication, no daemon. Useful for mirroring a repository to a local disk before a planned offline session, for sharing a repository between two working trees on the same host, and for bootstrap scripts that seed a fresh clone from a local tarball. Example:file:///srv/git/infra.git.
# Add the same repository with three different URL schemes
git remote add origin-ssh git@github.com:acme/infra.git
git remote add origin-https https://github.com/acme/infra.git
git remote add origin-file file:///srv/git/infra.git
In production, the choice between https and ssh is mostly a
choice between credential helpers and SSH keys. CI runners that
do not run an SSH agent typically use https with a PAT injected
by the runner’s secret manager. Engineers on interactive shells
typically use ssh with a key in their agent. The protocol does
not change the content of what is fetched; only the transport and
authentication change.
How a remote is created
A remote is created in exactly two ways: explicitly with git remote add, and implicitly by git clone. git clone adds one
remote named origin whose URL is the clone URL. git remote add adds a remote with a chosen name and a chosen URL. There is
no third path; if a remote appears in git remote output, it was
put there by one of those two commands (or by a script that ran
one of them).
# What git clone did for you
git clone git@github.com:acme/infra.git ~/work/infra
cd ~/work/infra
git remote -v
# origin git@github.com:acme/infra.git (fetch)
# origin git@github.com:acme/infra.git (push)
The clone wrote the [remote "origin"] block into
.git/config, fetched every branch on the remote into the
refs/remotes/origin/ namespace, created a local branch matching
the remote’s default branch (typically main), and set that
local branch’s upstream to origin/main. None of those steps are
magic — they are exactly what git remote add + git fetch +
git checkout --track would have produced by hand.
Production discipline
- The default remote name
originis convention, not requirement. It can be renamed withgit remote rename origin github; nothing about the underlying repository changes. Scripts that depend onoriginbeing the right name are brittle in multi-remote workflows. - One remote per repository is the minimum, not the maximum. A clone has one remote by default; an engineer contributing to an upstream open-source project has three or more (origin for their fork, upstream for the upstream, possibly a vendor-specific remote for the deployed copy).
- The URL is the contract. If a remote’s URL is wrong,
every fetch and push is wrong. Verify with
git remote get-urlandgit ls-remotebefore assuming a fetch is hitting the right server. - HTTPS with a PAT is the CI default; SSH with a key is the interactive default. Mixing them across the same repository is fine; mixing them within a single script is asking for the credential helper to be loaded in the wrong environment.
Cross-course references
- GitOps with Argo CD - Part II (RepoLayout) discusses
multiple remotes in the context of an
applicationwhose source of truth is one remote and whose read-only mirror is another; the controller needs both URLs and chooses per operation. - Terraform for Production Sysadmins - Part XIV (ModuleSources) uses HTTPS URLs to fetch Terraform modules from a private registry; the authentication model is the same HTTPS + PAT pattern.
- Linux for Production Sysadmins - Part XXVII (MirrorLayout)
discusses the
file://URL scheme as a way to seed a local mirror of an upstream repository before a planned offline maintenance window.
Quiz
Knowledge check · 4 questions
Q1. What is a Git remote, strictly speaking?
Q2. The `https://` and `ssh://` URL schemes authenticate with different mechanisms and use different transports, but they fetch and push the same repository content.
Q3. Name the three fields a remote entry stores in .git/config, and explain what each one is for.
Q4. Diagnose why a CI job that ran successfully yesterday is now hitting what looks like the wrong repository, and recommend a fix.
A CI job runs `git fetch origin` and then a deploy step that uses the fetched refs. Yesterday the job succeeded. Today the deploy step fails because the fetched commits look stale (the latest commit on the remote's `main` branch is two weeks old, but the team's GitHub UI shows commits from this morning). The on-call engineer suspects the URL is wrong.
Passing score: 75%. Answers are checked in this browser.