What a remote is
So far your repository has lived in one folder on one computer. That is enough to keep a history, but not to share it, or to keep a copy safe when a laptop is lost. For both, Git uses remotes: other copies of the same repository, somewhere else, that yours can exchange commits with.
Git is distributed: every copy of a repository is complete, with the whole history, and no copy is special to Git itself. In practice, a team agrees that one copy, usually on a hosting service such as GitHub, is the shared one that everybody sends work to and takes work from. To your repository, that copy is a remote with a short name. The usual name is origin: it is the name git clone gives the repository you cloned from, and the name most people choose when they add a remote themselves.
Four commands move work between your repository and a remote:
git clonemakes a new local copy of a remote repository, with all its history.git pushsends your new commits to the remote.git fetchbrings the remote’s new commits into your repository, without changing your own branches or files.git pullfetches, and then merges what it fetched into your current branch.
To remember what the remote had, your repository keeps remote-tracking branches with names such as origin/main: “where main was on origin, last time I looked”. They move only when you fetch, pull or push. Your own main moves only when you commit or merge.
Your repository holds your own branches, and remote-tracking branches such as origin/main that record what the remote had at your last fetch. git push sends commits to the remote; git fetch brings them back.
Your first push
A remote does not have to be on another computer. Any repository Git can reach works, including a folder on your own disk, and that is the easiest way to learn. In this module, a bare repository stands in for a server. A bare repository has the history but no working tree: nobody edits files in it, so others can push to it safely. By convention its folder name ends in .git.
Three commands put your repository on the stand-in server:
git init --bare <path>creates the empty bare repository.git remote add origin <path>tells your repository about it, under the nameorigin.git remote -vlists your remotes and their addresses.git push -u origin mainsendsmainthere.-u(short for--set-upstream) also recordsorigin/mainas the upstream of yourmain, so that later a plaingit pushorgit pullknows where to go.
On these pages the server lives in a folder called /srv/git. You cannot create that folder on your own computer, so make your stand-in next to your recipes folder instead. Inside recipes, run git init --bare ../server/recipes.git. Git creates the server folder and prints the full path of the new repository, for example /Users/you/server/recipes.git/ or C:/Users/you/server/recipes.git/. Wherever the pages show /srv/git/recipes.git, type that path instead. If the path contains a space, put it in quotes.
git init --bare /srv/git/recipes.git
Initialized empty Git repository in /srv/git/recipes.git/
git remote add origin /srv/git/recipes.git
git remote -v
origin /srv/git/recipes.git (fetch)
origin /srv/git/recipes.git (push)
git push -u origin main
To /srv/git/recipes.git
* [new branch] main -> main
branch 'main' set up to track 'origin/main'.
git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
* 8bf3c2d (HEAD -> main, origin/main) Add a pancake recipe * 907a979 Add a README
git init --bare reports the new, empty repository. git remote add prints nothing, and git remote -v then lists origin twice: once as the address to fetch from, once as the address to push to. They are normally the same.
git push -u origin main reports where it pushed (To /srv/git/recipes.git) and what: * [new branch] main -> main means that your main created a branch called main on the remote, which did not exist there before. The last line confirms the upstream. From now on, git status compares your branch with it, and says Your branch is up to date with ‘origin/main’. The graph shows the new label origin/main next to main, on the same commit.
If git init --bare prints a hint about the branch name master, your Git is not yet set to start repositories on main. Run git config --global init.defaultBranch main as in Module 1, delete the new server folder, and run git init --bare again.
Cloning
Now someone else joins. Sam has no copy of the recipes yet. git clone <address> creates one: it makes a folder named after the repository (here recipes, from recipes.git), copies every commit into it, checks out the default branch, and sets up origin to point back at the address. There is no need for git init, git remote add or -u; cloning does all of it.
In the session, Sam clones in Sam’s own folder. On these pages each person has a home folder, /home/alex or /home/sam, and a session can act as either of them.
To follow along as Sam, open a second terminal window. Make a folder for Sam next to your own recipes, for example a folder called sam in your home folder (cd ~, mkdir sam, cd sam), and clone there, using the path of your stand-in server. Then, inside Sam’s clone, give it Sam’s name and email, without --global so that they apply to that repository only: git config user.name "Sam Lee" and git config user.email "sam@example.com". Use the first window whenever the pages act as Alex, and the second whenever they act as Sam.
git clone /srv/git/recipes.git
Cloning into 'recipes'...
done.
cd recipes
ls
README.md pancakes.md
git log --oneline
8bf3c2d Add a pancake recipe
907a979 Add a README
git remote -v
origin /srv/git/recipes.git (fetch)
origin /srv/git/recipes.git (push)
git branch -a
* main
remotes/origin/HEAD -> origin/main
remotes/origin/main
* 8bf3c2d (HEAD -> main, origin/main, origin/HEAD) Add a pancake recipe * 907a979 Add a README
git clone says what it is doing, Cloning into 'recipes'..., and done.. Sam’s folder now holds the same files, and git log --oneline the same commits, with the same hashes as Alex’s: a commit is the same commit in every copy.
git remote -v shows that the clone already knows its origin. git branch -a (for all) lists remote-tracking branches as well as local ones. Sam has one local branch, main, and two entries under remotes/origin: the branch origin/main, and origin/HEAD, which records the remote’s default branch. You can ignore origin/HEAD; the graphs on these pages leave it out. Sam’s main already has origin/main as its upstream, so Sam can push and pull straight away.
Fetching and pulling
Alex adds a lemon cake recipe and pushes it. How does Sam get it? Not automatically: Git talks to a remote only when you run a command that does. Until then, Sam’s repository has no idea that anything has changed.
git fetch asks the remote for any commits you do not have yet. It stores them in your repository and moves the remote-tracking branches, such as origin/main, to match the remote. It changes nothing else: not your branches, not your files. You can look at what arrived before you decide what to do with it.
git pull does both steps at once: it fetches, then merges the upstream branch, here origin/main, into your current branch. When your branch has no commits of its own, the merge is a fast-forward, exactly as in Module 4.
cake.md so that it contains:# Lemon cake
- 200 g flour
- 2 lemons
git add cake.md
git commit -m "Add a lemon cake recipe"
[main d5392e7] Add a lemon cake recipe
1 file changed, 4 insertions(+)
create mode 100644 cake.md
git push
To /srv/git/recipes.git
8bf3c2d..d5392e7 main -> main
git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
git fetch
From /srv/git/recipes
8bf3c2d..d5392e7 main -> origin/main
git status
On branch main
Your branch is behind 'origin/main' by 1 commit, and can be fast-forwarded.
(use "git pull" to update your local branch)
nothing to commit, working tree clean
git log --oneline main..origin/main
d5392e7 Add a lemon cake recipe
* d5392e7 (origin/main, origin/HEAD) Add a lemon cake recipe * 8bf3c2d (HEAD -> main) Add a pancake recipe * 907a979 Add a README
git pull
Updating 8bf3c2d..d5392e7
Fast-forward
cake.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 cake.md
ls
README.md cake.md pancakes.md
Alex’s git push shows a different line from the first push: 8bf3c2d..d5392e7 main -> main means that main on the remote moved from 8bf3c2d to d5392e7.
Then look at Sam’s two git status commands. Before fetching, Git says Your branch is up to date with ‘origin/main’, although Alex’s commit is already on the server. Git compares your branch only with what your repository knows, and Sam’s repository has not asked yet. After git fetch, which reports 8bf3c2d..d5392e7 main -> origin/main, the status is Your branch is behind ‘origin/main’ by 1 commit, and can be fast-forwarded.
git log --oneline main..origin/main lists the commits that origin/main has and main does not: the two dots mean “reachable from the right-hand side, but not from the left”. The graph shows the same thing: origin/main is one commit ahead of main. Finally, git pull fast-forwards main, and ls shows cake.md in Sam’s folder.
Fetch whenever you start work, and before you push. It is always safe: it never changes your branches or your files. Then git status tells you whether you are ahead, behind or both.
When a push is rejected
Sometimes two people push in turn without fetching in between. In the next session, Alex has pushed the cake recipe, but Sam has not fetched it, and commits a soup recipe on top of the old main. Sam’s push then fails, because the remote’s main has a commit that Sam’s main does not contain. Accepting the push would throw Alex’s commit away, so Git refuses it. The remedy is to bring in Alex’s work first, merge it, and then push.
Recent versions of Git also ask you to choose, once, how git pull should combine your work with the remote’s when both have new commits. In this series you merge, as in Module 5. The session shows the question and the answer.
To follow along, commit something as Alex and push it, then, in Sam’s window, commit something else without pulling first, and push.
soup.md so that it contains:# Tomato soup
- 1 kg tomatoes
git add soup.md
git commit -m "Add a tomato soup recipe"
[main a128fc7] Add a tomato soup recipe
1 file changed, 3 insertions(+)
create mode 100644 soup.md
git push
To /srv/git/recipes.git
! [rejected] main -> main (fetch first)
error: failed to push some refs to '/srv/git/recipes.git'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
git pull
From /srv/git/recipes
8bf3c2d..d5392e7 main -> origin/main
hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint: git config pull.rebase false # merge
hint: git config pull.rebase true # rebase
hint: git config pull.ff only # fast-forward only
hint:
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.
fatal: Need to specify how to reconcile divergent branches.
git config --global pull.rebase false
git pull
Merge made by the 'ort' strategy.
cake.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 cake.md
git push
To /srv/git/recipes.git
d5392e7..642c9a6 main -> main
* 642c9a6 (HEAD -> main, origin/main, origin/HEAD) Merge branch 'main' of /srv/git/recipes |\ | * d5392e7 Add a lemon cake recipe * | a128fc7 Add a tomato soup recipe |/ * 8bf3c2d Add a pancake recipe * 907a979 Add a README
The rejected push says ! [rejected] main -> main (fetch first), and the hints explain why: the remote contains work that you do not have locally. Nothing has been lost or changed on either side.
The first git pull then fetches Alex’s commit (8bf3c2d..d5392e7 main -> origin/main) but stops with fatal: Need to specify how to reconcile divergent branches. Divergent means that both branches have commits the other lacks, as in a three-way merge. The hints list three settings:
pull.rebase falsemerges, as you have done since Module 4.pull.rebase truerebases, which Module 8 explains.pull.ff onlyrefuses unless a fast-forward is possible.
git config --global pull.rebase false chooses merging for all your repositories, so Git will not ask again. The second git pull then merges, and git push succeeds. On your computer, the merge opens your editor with the message Merge branch 'main' of followed by the remote’s address; save and close it, as in Module 4. The graph shows the result: a merge commit that joins Sam’s soup recipe and Alex’s cake recipe, with main and origin/main on the same commit.
If both people had changed the same lines, the merge would stop with a conflict. You would resolve it exactly as in Module 5, commit, and then push.
On Windows, the Git installer asks how git pull should behave and, by default, sets pull.rebase false for you. Your first git pull then merges straight away, without the fatal message; carry on from the second git pull.
Your repository on GitHub
The stand-in server taught you the commands; a hosting service makes the repository reachable from anywhere. This section puts your recipes on GitHub. Its web pages change from time to time, so follow the linked GitHub Docs for the exact buttons. The Git commands stay the same.
- Create an account at github.com, following Creating an account on GitHub. Choose a username you are happy to show: it appears in the address of every repository you own.
- Create an empty repository called
recipes, following Creating a new repository. Leave it empty: do not add a README, a licence or a.gitignore, because your local repository already has history to push. You can make it private if you prefer. - Set up sign-in for Git. GitHub does not accept your account password for Git commands. The easiest secure option is the GitHub CLI: install it, run
gh auth login, choose GitHub.com and HTTPS, and agree when it offers to sign Git in with your GitHub credentials. Git for Windows includes Git Credential Manager, which opens a browser window to sign you in the first time you push. Caching your GitHub credentials in Git describes both. Experienced users often connect with SSH instead. - Point
originat GitHub and push. First rungit pullin your recipes folder, so that it has Sam’s work from Section 5. GitHub shows the repository’s address on its page, in the formhttps://github.com/<username>/recipes.git. In your recipes folder, run:
git remote set-url origin https://github.com/<username>/recipes.git
git push -u origin main
git remote set-url changes the address of an existing remote, so origin now means GitHub instead of your stand-in folder. git push -u sends your history and sets the upstream, as before. Reload the repository’s page on GitHub: your files and commits are there. From now on, git push, git fetch and git pull talk to GitHub.
You can also keep origin as it is and add GitHub as a second remote, under another name: git remote add github https://github.com/<username>/recipes.git, then git push -u github main. A repository can have as many remotes as you like. Module 7 assumes that origin is GitHub.
Exercises
Which one moved? Your repository has main and origin/main. For each command, say which of the two can move: git fetch, git pull, git commit, git push.
Show solution
git fetchmoves onlyorigin/main, to wheremainis on the remote.git pullmovesorigin/main(the fetch), thenmain(the merge).git commitmoves onlymain.git pushmovesmainon the remote, and therefore yourorigin/maintoo, which records it. Your ownmainstays where it is.
Clone into a folder of your choice. As Sam, clone the recipes repository into a folder called family-recipes instead of recipes.
Show solution
Give git clone the folder name after the address:
git clone /srv/git/recipes.git family-recipes
Cloning into 'family-recipes'...
done.
ls
family-recipes
cd family-recipes
git log --oneline
8bf3c2d Add a pancake recipe
907a979 Add a README
The folder name is only local: the remote is still called origin, and its address is unchanged.
Read the status. What does each message mean, and what would you run next?
- Your branch is ahead of ‘origin/main’ by 2 commits.
- Your branch is behind ‘origin/main’ by 1 commit, and can be fast-forwarded.
- Your branch and ‘origin/main’ have diverged, and have 1 and 1 different commits each, respectively.
Show solution
- You have two commits that the remote does not have, as of your last fetch. Run
git push. - The remote has one commit that you do not have, and you have none of your own. Run
git pull, which fast-forwards. - Both sides have a commit the other lacks. Run
git pull, which merges (resolve any conflict as in Module 5), thengit push.
All three compare with origin/main as your repository last saw it, so run git fetch first for an up-to-date answer.
Share a new branch. As Alex, create a branch called soups, commit a soup recipe on it, and push the branch so that Sam could fetch it. Check that it has an upstream. To follow along, use a new branch name, such as salads: your repository already has a soups branch from Module 4.
Show solution
Push the branch with -u, as you did for main:
git switch -c soups
Switched to a new branch 'soups'
soup.md so that it contains:# Tomato soup
- 1 kg tomatoes
git add soup.md
git commit -m "Add a tomato soup recipe"
[soups 995e901] Add a tomato soup recipe
1 file changed, 3 insertions(+)
create mode 100644 soup.md
git push -u origin soups
To /srv/git/recipes.git
* [new branch] soups -> soups
branch 'soups' set up to track 'origin/soups'.
git branch -vv
main 8bf3c2d [origin/main] Add a pancake recipe
* soups 995e901 [origin/soups] Add a tomato soup recipe
git branch -vv (two vs) shows each branch’s upstream in square brackets: soups now tracks origin/soups.
Fetch or pull? You are in the middle of a change on main and want to know whether a colleague has pushed anything, without changing your files yet. Which command do you run, and why?
Show solution
git fetch. It brings in the colleague’s commits and updates origin/main, but leaves your branch and your files alone. git status and git log --oneline main..origin/main then show what arrived. Pull when you are ready to merge it.
A refused password. Your first push to GitHub asks for a username and password. You type your GitHub password, and the push fails with a message saying that support for password authentication was removed. What is going on, and what should you do?
Show solution
GitHub stopped accepting account passwords for Git operations in 2021. Set up a secure sign-in instead: run gh auth login with the GitHub CLI, or let Git Credential Manager sign you in through the browser, as in Section 6. Then push again. Alternatives are a personal access token, typed where the password was asked, or SSH.
Self-check quiz
origin?git clone names the remote origin, and most people use the same name with git remote add.git fetch change in your repository?origin/main; your branches and files stay as they are.pull.rebase set to false, what does git pull do?! [rejected] main -> main (fetch first). What does it mean?-u add to git push -u origin main?git push or git pull knows where to go, and git status compares with it.git status says Your branch is up to date with ‘origin/main’. What do you know for certain?git fetch to bring that record up to date.Further reading
- Scott Chacon and Ben Straub, Pro Git, section 2.5: Working with Remotes.
- The reference pages for git remote, git fetch, git pull and git push.
- GitHub Docs, Adding locally hosted code to GitHub and About remote repositories.