Aller au contenu

Remote repositories

Until now, the homelab repository lived on a single computer: Sam's desktop. If its disk fails, the whole history is gone, and editing the inventory from the laptop is impossible. Remote repositories solve both problems.

Local and remote repositories

Local repository Remote repository
Where On your computer, in the project folder (.git/) Somewhere else: an online hosting service, a server, another disk
Who uses it You Everyone working on the project
How you use it Edit files, commit, branch, merge Copy it, receive changes from it, send changes to it

A remote is simply another copy of the same repository, that your local repository knows about. Remotes are generally hosted on an online service such as GitHub, GitLab, or Bitbucket, but any repository Git can reach works: a folder on a server over SSH, or even a path on the same machine.

Benefits of a remote repository:

  • Everything is backed up: the full history exists in several places.
  • Collaboration, regardless of location: everyone works on their own local copy and synchronizes through the remote.
flowchart TB
    server[("Remote repository<br/>/srv/git/homelab.git")]
    desktop["Desktop<br/>local repository"]
    laptop["Laptop<br/>local repository"]

    desktop <-- "push / pull" --> server
    laptop <-- "push / pull" --> server

Git is distributed: every clone holds the complete history, not just the latest files. The remote is a copy that everyone agrees to use as the shared reference, not a special kind of repository.

The homelab Git server

For the examples, the homelab repository is published on a small Git server on the local network, at /srv/git/homelab.git. It was created from the desktop's repository with:

git clone --bare ~/homelab /srv/git/homelab.git

A bare repository contains only the Git data (the content of .git/), without a working directory. Nobody edits files directly on the server, so none is needed: this is the usual form for a shared remote. On GitHub, this part is handled for you when you create a repository.

Cloning a repository: git clone

Making a local copy of a repository is called cloning:

git clone <path-or-URL> [directory]

Cloning from a path

On the laptop, Sam clones the repository from the server:

$ git clone /srv/git/homelab.git
Cloning into 'homelab'...
done.

Git creates a homelab folder, named after the repository (without .git), with the full history and the files of the default branch. A second argument chooses another folder name:

$ git clone /srv/git/homelab.git homelab-copy
Cloning into 'homelab-copy'...
done.

Cloning from a hosting service

For a repository on a hosting service, use its URL. The two common forms are:

Protocol Example URL Authentication
HTTPS https://github.com/sam-rivera/homelab.git None for public repositories; for private ones or to push, a personal access token or a credential manager
SSH [email protected]:sam-rivera/homelab.git An SSH key registered on your account
git clone https://github.com/sam-rivera/homelab.git

Anyone can clone a public repository. A private repository requires an account with access to it. The URL is shown on the repository page, under the Code button.

Identifying remotes: git remote

When cloning, Git remembers where the original came from: it stores a remote in the new repository's configuration, and names it origin by default. git remote lists the remotes:

$ git remote
origin

-v (verbose) adds the URL of each remote:

$ git remote -v
origin  /srv/git/homelab.git (fetch)
origin  /srv/git/homelab.git (push)

Each remote has two lines: the URL used to fetch (receive changes) and the URL used to push (send changes). They are almost always the same.

A repository created with git init has no remote at all: git remote prints nothing.

Remote-tracking branches

A clone also contains remote-tracking branches, named <remote>/<branch>, which record where each branch of the remote was at the last synchronization. git branch -a (all) shows them next to the local branches:

$ git branch -a
* main
  remotes/origin/HEAD -> origin/main
  remotes/origin/main
  • main is your local branch: you commit on it.
  • origin/main is Git's memory of main on origin. You do not commit on it; Git updates it when it communicates with the remote.
  • origin/HEAD -> origin/main records the default branch of the remote.

git branch -r shows only the remote-tracking branches.

Adding a remote: git remote add

A repository can have several remotes. To add one:

git remote add <name> <URL>

Sam keeps an extra copy of the repository on a USB drive, as an offline backup. The drive contains an empty bare repository, created with git init --bare /mnt/usb/homelab.git:

$ git remote add usb /mnt/usb/homelab.git
$ git remote -v
origin  /srv/git/homelab.git (fetch)
origin  /srv/git/homelab.git (push)
usb /mnt/usb/homelab.git (fetch)
usb /mnt/usb/homelab.git (push)

The name is a short alias for the URL, used in every later command (git fetch usb, git push usb main...). Choose names that say what the remote is. A common convention when contributing to someone else's project: origin for your own copy, and upstream for the original project.

The desktop repository, created with git init, gets its origin the same way: git remote add origin /srv/git/homelab.git.

Managing remotes

Command Effect
git remote rename usb backup Rename a remote
git remote set-url origin <new-URL> Change the URL of a remote, for example after moving the repository
git remote remove usb Remove a remote from the configuration; the remote repository itself is not affected

Summary

Command Result
git clone /srv/git/homelab.git Clone a repository from a path into the folder homelab
git clone /srv/git/homelab.git homelab-copy Clone into the folder homelab-copy
git clone https://github.com/sam-rivera/homelab.git Clone a repository from a hosting service
git remote List the names of the remotes
git remote -v List the remotes with their fetch and push URLs
git remote add usb /mnt/usb/homelab.git Add a remote called usb
git branch -a List local and remote-tracking branches

Common mistakes

  • Expecting a remote to be named after the project. A cloned repository's remote is called origin by default, whatever the URL.
  • Committing on origin/main. It is a remote-tracking branch, updated only by communicating with the remote. Commit on your local main.
  • Thinking git remote remove deletes the remote repository. It only removes the alias from your configuration.
  • Cloning inside an existing repository. Run git clone from a folder that is not already a Git working directory, to avoid nesting repositories.

Hands-on labs

Three labs, from guided to more autonomous. Each one has its own setup, so you can do them in any order and repeat them as often as you like. Type the commands rather than pasting them, and read every output before moving on: the goal is to build reflexes, not to reach the end.

Lab 1: set up remotes

Objective: create a shared repository, clone it, inspect its remotes, and add a second remote.

Prerequisites and initial state: Git installed. No GitHub account needed: the "server" is a folder in ~/git-practice/remotes. The setup creates a project and publishes it as a bare repository.

Setup:

mkdir -p ~/git-practice/remotes && cd ~/git-practice/remotes
git init -q -b main project
cd project
git config user.name "Practice User"
git config user.email "[email protected]"
printf 'hostname,ip,role\npi-dns,192.168.1.10,dns\n' > inventory.csv
git add . && git commit -q -m "Create inventory"
cd ..
git clone -q --bare project server.git
git init -q --bare backup.git

Tasks:

  1. From ~/git-practice/remotes, clone server.git into a folder named laptop.
  2. In laptop, list the remotes, then display their URLs.
  3. List all branches, local and remote-tracking. Which one is the default branch of the remote?
  4. Add ~/git-practice/remotes/backup.git as a remote named backup.
  5. In project (the original repository), check whether a remote is configured.

Expected result and verification:

  • laptop/inventory.csv exists and git log --oneline in laptop shows Create inventory.
  • In laptop, git remote -v shows origin and backup, each with a (fetch) and a (push) line.
  • git branch -a shows remotes/origin/HEAD -> origin/main: main is the default branch.
  • In project, git remote prints nothing.
Solution
# 1. Clone into a chosen folder name
cd ~/git-practice/remotes
git clone server.git laptop       # Cloning into 'laptop'...
cd laptop

# 2. Remotes
git remote                        # origin
git remote -v                     # origin  .../server.git (fetch) and (push)

# 3. Local and remote-tracking branches
git branch -a                     # * main, remotes/origin/HEAD -> origin/main, remotes/origin/main

# 4. Add a second remote
git remote add backup ~/git-practice/remotes/backup.git
git remote -v                     # backup and origin, fetch and push each

# 5. The original repository was created with git init
cd ../project
git remote                        # (no output)
  • A relative path (server.git) works for cloning; Git stores it as an absolute path in the clone's configuration.
  • project was created with git init and never connected to anything: remotes are added by cloning or by git remote add.

Clean up when you are done: rm -rf ~/git-practice/remotes.

Lab 2: fix a broken remote

Objective: diagnose a remote with a wrong URL, fix it, then rename and remove remotes.

Prerequisites and initial state: Git installed. The setup creates a "server" repository, a clone called desktop, and an empty bare repository usb.git standing for a USB drive, in ~/git-practice/broken-remote.

Setup:

mkdir -p ~/git-practice/broken-remote && cd ~/git-practice/broken-remote
git init -q -b main project
cd project
git config user.name "Practice User"
git config user.email "[email protected]"
printf 'hostname,ip,role\npi-dns,192.168.1.10,dns\n' > inventory.csv
git add . && git commit -q -m "Create inventory"
cd ..
git clone -q --bare project server.git
git init -q --bare usb.git
git clone -q server.git desktop
cd desktop

Tasks: (all from ~/git-practice/broken-remote/desktop)

  1. Add a remote called usb with a typo in its path: ~/git-practice/broken-remote/ubs.git. List the remotes with their URLs.
  2. Fetch from usb. Read the error.
  3. Fix the URL of usb without removing it, and fetch again.
  4. Rename usb to backup, and list the remotes.
  5. You no longer use this drive: remove the backup remote. Does usb.git still exist on disk?

Expected result and verification:

  • Task 1: git remote -v shows origin and usb, the latter with the wrong path. Git accepted it without checking.
  • Task 2 fails with fatal: '.../ubs.git' does not appear to be a git repository.
  • Task 3: after the fix, git fetch usb prints nothing and succeeds: the drive's repository is empty.
  • Task 4: git remote lists backup and origin.
  • Task 5: git remote lists only origin, and ls .. still shows usb.git.
Solution
# 1. Git stores the URL as given
git remote add usb ~/git-practice/broken-remote/ubs.git
git remote -v

# 2. The problem appears only when Git uses the remote
git fetch usb                     # fatal: '.../ubs.git' does not appear to be a git repository

# 3. Change the URL
git remote set-url usb ~/git-practice/broken-remote/usb.git
git remote -v
git fetch usb                     # no output: success, nothing to download

# 4. Rename
git remote rename usb backup
git remote                        # backup, origin

# 5. Remove the alias, not the repository
git remote remove backup
git remote                        # origin
ls ..                             # desktop  project  server.git  usb.git
  • git remote add does not contact the remote: a typo goes unnoticed until the first fetch, pull, or push.
  • git remote set-url keeps the name and only changes the address; removing and re-adding the remote would work too, but loses its settings.
  • A remote is just a name and a URL in .git/config. Run cat .git/config to see it.

Clean up when you are done: rm -rf ~/git-practice/broken-remote.

Lab 3: two clones, two worlds

Objective: check that clones are independent copies, and discover how a remote branch becomes a local one.

Prerequisites and initial state: Git installed. The setup creates ~/git-practice/two-clones with a "server" repository holding two branches, main and monitoring.

Setup:

mkdir -p ~/git-practice/two-clones && cd ~/git-practice/two-clones
git init -q -b main project
cd project
git config user.name "Practice User"
git config user.email "[email protected]"
printf 'hostname,ip,role\npi-dns,192.168.1.10,dns\n' > inventory.csv
git add . && git commit -q -m "Create inventory"
git switch -q -c monitoring
echo "mon01,192.168.1.50,monitoring" >> inventory.csv
git commit -q -am "Add monitoring server"
git switch -q main
cd ..
git clone -q --bare project server.git
rm -rf project

Tasks:

  1. Clone server.git twice, into desktop and laptop.
  2. In desktop, list the local branches, then all branches. Where is monitoring?
  3. In desktop, set a name and email for this repository, add nas,192.168.1.20,storage to the inventory, and commit with the message Add NAS. Run git status.
  4. Look at the history of laptop, and at the history of the server (git log works inside server.git too). Do they know about Add NAS?
  5. In laptop, switch to monitoring. Read the message, and list the local branches again.

Expected result and verification:

  • Task 2: git branch shows only * main; git branch -a also shows remotes/origin/monitoring.
  • Task 3: git status says Your branch is ahead of 'origin/main' by 1 commit.
  • Task 4: neither laptop nor server.git has Add NAS: a commit stays in the clone where it was made until it is pushed.
  • Task 5 prints branch 'monitoring' set up to track 'origin/monitoring'. and Switched to a new branch 'monitoring'; git branch now shows main and * monitoring.
Solution
# 1. Two clones of the same server
cd ~/git-practice/two-clones
git clone server.git desktop
git clone server.git laptop

# 2. Only main is created locally
cd desktop
git branch                        # * main
git branch -a                     # also remotes/origin/HEAD, remotes/origin/main, remotes/origin/monitoring

# 3. A local commit
git config user.name "Practice User"
git config user.email "[email protected]"
echo "nas,192.168.1.20,storage" >> inventory.csv
git commit -am "Add NAS"
git status                        # ahead of 'origin/main' by 1 commit

# 4. Nobody else knows about it
cd ../laptop
git log --oneline                 # Create inventory
cd ../server.git
git log --oneline                 # Create inventory

# 5. Switching to a name that only exists on origin creates the local branch
cd ../laptop
git switch monitoring             # branch 'monitoring' set up to track 'origin/monitoring'.
git branch                        # main, * monitoring
  • A clone copies every commit and every branch of the server, but creates a local branch only for the default one; the others are visible as origin/<name>.
  • git switch <name> creates the local branch from origin/<name> when no local branch has that name.
  • Each clone is a complete, independent repository. Sharing commits always goes through an explicit exchange, covered on the next pages: fetch, pull, and push.

Clean up when you are done: rm -rf ~/git-practice/two-clones.