Skip to main content
RunBook Academy

Git, CI/CD & GitOpsXX · RemotesRemotes

What a remote is — a named pointer to another repository

Intermediate⏱ ~18 mingit

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

Not yet marked complete on this device.

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 by git fetch. A remote-tracking ref is read-only in normal use (you cannot git checkout into 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 main branch and the remote-tracking origin/main ref 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 the Authorization header. 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

  1. The default remote name origin is convention, not requirement. It can be renamed with git remote rename origin github; nothing about the underlying repository changes. Scripts that depend on origin being the right name are brittle in multi-remote workflows.
  2. 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).
  3. The URL is the contract. If a remote’s URL is wrong, every fetch and push is wrong. Verify with git remote get-url and git ls-remote before assuming a fetch is hitting the right server.
  4. 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 application whose 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

  1. Q1. What is a Git remote, strictly speaking?

  2. Q2. The `https://` and `ssh://` URL schemes authenticate with different mechanisms and use different transports, but they fetch and push the same repository content.

  3. Q3. Name the three fields a remote entry stores in .git/config, and explain what each one is for.

  4. 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.