Ran Wei/Git Series
中文
Git and Version Control — Ran Wei

Remotes

Share a repository: put it on a server, clone it, and exchange work with push, fetch and pull, first with a folder on your own computer and then on GitHub.

3 hours3 study sessions4 command sessionsGit 2.50.1 (Apple Git-155)

By the end you can

  • Explain what a remote is, and what origin and origin/main mean.
  • Create a stand-in server, connect a repository to it and push with git push -u.
  • Clone a repository, and read git remote -v and git branch -a.
  • Bring in other people's work with git fetch and git pull, and read what git status says about it.
  • Recover from a rejected push, and put a repository on GitHub with secure sign-in.

Before you start

Modules 4 and 5: branches, merging and resolving conflicts.

Contents

Study plan

3 hours

Times are estimates and include typing the commands. Progress is saved in this browser and shared by both language editions.

Session 160 min
A remote of your own
Session 260 min
Exchanging work
Session 360 min
GitHub and practice
1

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 clone makes a new local copy of a remote repository, with all its history.
  • git push sends your new commits to the remote.
  • git fetch brings the remote’s new commits into your repository, without changing your own branches or files.
  • git pull fetches, 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 and a remote repository Your computer Your repository main: your work origin/main: the server’s main, as of your last fetch The server Remote repository: origin main git push git fetch git push sends your commits; git fetch brings the server’s commits; git pull = git fetch + git merge
Figure 6.1

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.

2

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 name origin. git remote -v lists your remotes and their addresses.
  • git push -u origin main sends main there. -u (short for --set-upstream) also records origin/main as the upstream of your main, so that later a plain git push or git pull knows 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.

Try itPut your repository on a server
git init --bare /srv/git/recipes.git
Output
Initialized empty Git repository in /srv/git/recipes.git/
git remote add origin /srv/git/recipes.git
git remote -v
Output
origin	/srv/git/recipes.git (fetch)
origin	/srv/git/recipes.git (push)
git push -u origin main
Output
To /srv/git/recipes.git
 * [new branch]      main -> main
branch 'main' set up to track 'origin/main'.
git status
Output
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
Output from git version 2.50.1 (Apple Git-155), run while this page was built. Other versions of Git may word some messages differently.

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.

Pitfall

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.

3

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.

Note — Two people on one computer

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.

Try itClone a repository
Now acting as Sam Lee, in Sam's own folder.
git clone /srv/git/recipes.git
Output
Cloning into 'recipes'...
done.
cd recipes
ls
Output
README.md  pancakes.md
git log --oneline
Output
8bf3c2d Add a pancake recipe
907a979 Add a README
git remote -v
Output
origin	/srv/git/recipes.git (fetch)
origin	/srv/git/recipes.git (push)
git branch -a
Output
* main
  remotes/origin/HEAD -> origin/main
  remotes/origin/main
* 8bf3c2d (HEAD -> main, origin/main, origin/HEAD) Add a pancake recipe
* 907a979 Add a README
Output from git version 2.50.1 (Apple Git-155), run while this page was built. Other versions of Git may word some messages differently.

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.

4

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.

Try itFetch and pull someone else's work
Create or edit 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"
Output
[main d5392e7] Add a lemon cake recipe
 1 file changed, 4 insertions(+)
 create mode 100644 cake.md
git push
Output
To /srv/git/recipes.git
   8bf3c2d..d5392e7  main -> main
Now acting as Sam Lee, in Sam's own folder.
git status
Output
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean
git fetch
Output
From /srv/git/recipes
   8bf3c2d..d5392e7  main       -> origin/main
git status
Output
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
Output
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
Output
Updating 8bf3c2d..d5392e7
Fast-forward
 cake.md | 4 ++++
 1 file changed, 4 insertions(+)
 create mode 100644 cake.md
ls
Output
README.md  cake.md  pancakes.md
Output from git version 2.50.1 (Apple Git-155), run while this page was built. Other versions of Git may word some messages differently.

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.

Tip

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.

5

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.

Try itWhen a push is rejected
Now acting as Sam Lee, in Sam's own folder.
Create or edit soup.md so that it contains:
# Tomato soup

- 1 kg tomatoes
git add soup.md
git commit -m "Add a tomato soup recipe"
Output
[main a128fc7] Add a tomato soup recipe
 1 file changed, 3 insertions(+)
 create mode 100644 soup.md
git push
Output
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
Output
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
Output
Merge made by the 'ort' strategy.
 cake.md | 4 ++++
 1 file changed, 4 insertions(+)
 create mode 100644 cake.md
git push
Output
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
Output from git version 2.50.1 (Apple Git-155), run while this page was built. Other versions of Git may word some messages differently.

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 false merges, as you have done since Module 4.
  • pull.rebase true rebases, which Module 8 explains.
  • pull.ff only refuses 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.

Note — Windows

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.

6

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.

  1. 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.
  2. 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.
  3. 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.
  4. Point origin at GitHub and push. First run git pull in your recipes folder, so that it has Sam’s work from Section 5. GitHub shows the repository’s address on its page, in the form https://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.

Note — If you want to keep the stand-in

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.

7

Exercises

Exercise 1★★★conceptual4 min

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 fetch moves only origin/main, to where main is on the remote.
  • git pull moves origin/main (the fetch), then main (the merge).
  • git commit moves only main.
  • git push moves main on the remote, and therefore your origin/main too, which records it. Your own main stays where it is.
Exercise 2★★★coding4 min

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:

SolutionClone into a folder of your choice
Now acting as Sam Lee, in Sam's own folder.
git clone /srv/git/recipes.git family-recipes
Output
Cloning into 'family-recipes'...
done.
ls
Output
family-recipes
cd family-recipes
git log --oneline
Output
8bf3c2d Add a pancake recipe
907a979 Add a README
Output from git version 2.50.1 (Apple Git-155), run while this page was built. Other versions of Git may word some messages differently.

The folder name is only local: the remote is still called origin, and its address is unchanged.

Exercise 3★★★conceptual5 min

Read the status. What does each message mean, and what would you run next?

  1. Your branch is ahead of ‘origin/main’ by 2 commits.
  2. Your branch is behind ‘origin/main’ by 1 commit, and can be fast-forwarded.
  3. Your branch and ‘origin/main’ have diverged, and have 1 and 1 different commits each, respectively.
Show solution
  1. You have two commits that the remote does not have, as of your last fetch. Run git push.
  2. The remote has one commit that you do not have, and you have none of your own. Run git pull, which fast-forwards.
  3. Both sides have a commit the other lacks. Run git pull, which merges (resolve any conflict as in Module 5), then git push.

All three compare with origin/main as your repository last saw it, so run git fetch first for an up-to-date answer.

Exercise 4★★★coding5 min

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:

SolutionShare a new branch
git switch -c soups
Output
Switched to a new branch 'soups'
Create or edit soup.md so that it contains:
# Tomato soup

- 1 kg tomatoes
git add soup.md
git commit -m "Add a tomato soup recipe"
Output
[soups 995e901] Add a tomato soup recipe
 1 file changed, 3 insertions(+)
 create mode 100644 soup.md
git push -u origin soups
Output
To /srv/git/recipes.git
 * [new branch]      soups -> soups
branch 'soups' set up to track 'origin/soups'.
git branch -vv
Output
  main  8bf3c2d [origin/main] Add a pancake recipe
* soups 995e901 [origin/soups] Add a tomato soup recipe
Output from git version 2.50.1 (Apple Git-155), run while this page was built. Other versions of Git may word some messages differently.

git branch -vv (two vs) shows each branch’s upstream in square brackets: soups now tracks origin/soups.

Exercise 5★★★conceptual3 min

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.

Exercise 6★★★conceptual4 min

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.

8

Self-check quiz

1
What is origin?
2
What does git fetch change in your repository?
3
With pull.rebase set to false, what does git pull do?
4
A push fails with ! [rejected] main -> main (fetch first). What does it mean?
5
What does -u add to git push -u origin main?
6
git status says Your branch is up to date with ‘origin/main’. What do you know for certain?
7
Which is a good way to let Git sign in to GitHub?
9

Further reading