Menu for Per-Arne's Git Good!
Courses and project Per-Arne's Git Good!

Remotes and clone: three things called main

Module 3: Collaboration · 10 min

The interactive lesson needs JavaScript. The text below is the complete lesson.

After this lesson you can

  • explain that a remote is just another repository, and that origin is only a local name for its address understand
  • connect a repository to a remote with git remote add, and copy a repository with git clone apply
  • tell apart your main, your remote-tracking branch origin/main and main inside origin understand
  • set a branch's upstream, and read with git branch -vv which upstream it follows and how far ahead or behind it is apply

Understand: One more repository

So far you have worked in one repository on your own machine. To collaborate, the team needs a repository everyone can reach. A repository that yours knows about is called a remote. It can live on GitHub, on a school server or in an ordinary folder.

Your repository keeps a short list of remotes, each with a name and an address. The usual name is origin. It is only a local nickname, not a special server. In the figure, main inside origin is labelled origin:main, a label only the figure uses.

git clone copies a whole repository: the whole history its branches and tags reach, not just the latest files. Clone also adds the remote called origin and checks out the default branch, here main. Afterwards both repositories know the same commits.

Now there are three things called main. main is your own branch. origin:main is the branch inside origin. And origin/main is a remote-tracking branch in your repository: Git's note of where main in origin was the last time you talked to origin.

Watch what happens when you commit C. Only your main moves. Origin knows nothing until you send the commit there, and origin/main still remembers B. You are one commit ahead.

Your main also remembers which remote-tracking branch it follows, here origin/main. That is the branch's upstream. That is how git status and git push know what to compare with. git branch -vv shows the upstream in square brackets, with how far ahead or behind you are.

Watch: Create a shared repository and clone it

We have a repository with one commit. First we create a shared repository the team can use. A bare repository holds only history and no working files. That is what a server keeps.

$ git init --bare /course/origin.git

Now we give the address a name in our repository. We call it origin, which is the convention. The graph does not change. We have only written a name and a path in a list.

$ git remote add origin /course/origin.git

git remote -v shows the list. We see one address for fetch and one for push. Here it is just a folder on the same machine.

$ git remote -v

Now we send main to origin. The -u option also records that main should follow main in origin. Before the push, origin is empty and you have no origin/main.

$ git push -u origin main

Ada joins the project. She clones the shared repository into her own folder. Her main appears in the graph as ada:main, on the same commit. She has received the whole history.

$ git clone /course/origin.git /course/ada

Back in your repository we make a new commit locally. We do not push it.

$ echo "Draft" > notes.txt && git add notes.txt && git commit -m "Add notes"

git branch -vv shows the upstream in the brackets, origin/main, and that you are one commit ahead. In the graph, origin/main and origin:main still sit on the old commit.

$ git branch -vv

Tasks

  1. Create an empty bare repository at /course/origin.git to be the team's shared repository.
  2. Register the shared repository as a remote named origin.
  3. Show which remotes the repository knows, with their addresses.
  4. Publish main to origin, and set origin/main as its upstream.
  5. Make Ada's copy of the shared repository in /course/ada.
  6. Show your branches with their upstream and how far ahead you are.

Practise: Transfer: a new colleague and a missing upstream

A shared repository already exists, and main has been pushed there, but without -u. Ada has no copy yet.

Tasks

  1. Your main has no upstream, so git status compares with nothing. Make main follow origin/main, without making new commits.
  2. Ada joins the team. Give her her own copy of the shared repository in /course/ada.
  3. Back in your repository: the team would rather call the shared repository shared. Rename the remote, and confirm that the upstream you set for main is now called shared/main.

Check yourself: Recall it

What is origin?

  • GitHub's central server
  • A name in your repository for the address of another repository
  • A special branch that always has the latest version

Ada clones the shared repository and goes offline. Can she see the very first commit with git log?

  • No. Clone copies only the latest files, so older commits must be fetched from origin.
  • Yes. Clone copied the history, so log reads it from her own repository.
  • Only if she ran git pull before going offline

git branch -vv shows [origin/main: behind 2]. What does it mean?

  • Right now the server has exactly two commits you are missing
  • You have two commits that are not pushed
  • origin/main has two commits your main is missing, as of the last time you talked to origin

Recall from module 2: what is a branch, such as main?

  • A copy of the project folder
  • A name that points to one commit, and moves when you commit on it
  • A list of the changes made since the branch was created

Common misconceptions

  • “origin is GitHub” or “origin is the server”. origin is just the name your repository uses for an address. The address could just as well be a folder on the same disk, and the name can be changed.
  • “origin/main shows the server right now.” origin/main is a remote-tracking branch in your repository. It shows where main in origin was the last time you talked to origin.
  • “clone only fetches the latest files.” An ordinary clone copies the whole history its branches and tags reach.

Read more

A remote is a name for an address

A remote is a small block in your repository's configuration: a short name, a URL and a rule for what fetch stores. git remote add origin /course/origin.git downloads nothing and contacts nobody. It only writes this into .git/config:

[remote "origin"]
    url = /course/origin.git
    fetch = +refs/heads/*:refs/remotes/origin/*

The URL can be https://…, an SSH address such as git@example.org:team/project.git, or a plain folder path like in this course. Git treats all of them the same way; only the transport differs. Logging in to a hosting service is a matter for that transport, not for git config user.name, which only labels your commits.

Managing remotes

Command What it does
git remote -v List every remote with its fetch and push URL.
git remote add upstream URL Add one more remote under a new name.
git remote rename origin shared Rename a remote. Remote-tracking branches and upstream settings are renamed too.
git remote set-url origin URL Change the address, for example when a project moves or you switch from HTTPS to SSH.
git remote remove upstream Forget the name and its remote-tracking branches. The other repository is not touched.
git remote show origin Contact the remote and summarise its default branch, tracked branches and what git pull and git push will do.

What git clone actually does

Clone is a short script of steps you already know:

  1. Create the folder and run git init in it.
  2. Add a remote called origin with the URL you gave.
  3. Fetch every branch and tag, with all the commits they reach. Each branch in the remote becomes a remote-tracking branch such as origin/main or origin/docs.
  4. Create one local branch, the remote's default branch, set its upstream and check it out.

So after cloning a project with the branches main and docs, you have origin/docs but no local docs. That is not a problem: git switch docs notices that exactly one remote has a branch with that name, creates a local docs from origin/docs and sets the upstream for you. Clone copies history, not configuration: local settings, hooks and stashes in the original stay behind.

Refspecs: the rule behind origin/main

The fetch line above is a refspec, a mapping from names in the remote to names here. Read +refs/heads/*:refs/remotes/origin/* from left to right: take every branch in the remote and store it under refs/remotes/origin/ in my repository. The plus sign lets those notes move even when the remote branch was rewritten.

Two consequences follow. First, origin/main is just short for refs/remotes/origin/main, an ordinary ref in your repository. Second, a plain git fetch never moves your own main, because the default refspec only writes into refs/remotes/origin/. Push uses refspecs too: git push origin main means "make main over there point where my main points", and git push origin feature:review publishes your local feature as a branch called review in the remote. Deleting a remote branch is a push of nothing: git push origin :review, which is the same as git push origin --delete review.

Upstream: what a branch follows

git push -u origin main stores two settings, which you can read back:

$ git config --get-regexp '^branch\.main\.'
branch.main.remote origin
branch.main.merge refs/heads/main

Together they are the branch's upstream. git status uses it for "ahead" and "behind", and git pull and git push without arguments use it as their target. The shorthand @{u} names the upstream in any command: git log @{u}.. lists commits you have not pushed, and git log ..@{u} lists commits you have fetched but not integrated.

You want to Command
Set the upstream while publishing git push -u origin feature
Set it on an existing branch git branch -u origin/main
Remove it git branch --unset-upstream
Make the first plain push set it (Git 2.37 or newer) git config --global push.autoSetupRemote true

Ahead and behind are computed locally against origin/main. Run git fetch first when you need the truth about the remote.

Bare repositories

git init --bare creates a repository without a working tree: the folder contains what would normally live inside .git. A shared repository on a server is usually bare, because nobody edits files there.

More than one remote

In a fork workflow, origin is usually your own fork, where you may push, and upstream is the main project, which you only read from. You fork the project on the hosting service, clone your fork as origin, push branches there and open the pull request from the fork to the original:

git remote add upstream https://example.org/project/project.git
git fetch upstream
git switch -c fix-typo upstream/main

Official documentation