Section 3 · Collaborating on GitHub
Remotes: Push, Fetch, Pull and Tracking Branches
16 min4 min readFill in the blanks
Lesson at a glance
Connect your portfolio to a GitHub repository, push branches with upstream tracking, and keep in sync using fetch and pull while understanding what origin/main really is.
- 1Put the portfolio on GitHub
- 2Remote-tracking branches: Git’s memory of the remote
- 3Upstreams and git push -u
- 4Fetch, then pull
- 5A daily rhythm
- 6When push is rejected
No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.
So far the portfolio exists in one place, your laptop. If the disk dies, it’s gone, and nobody else can see or review it. A remote is another copy of the repository somewhere else, usually on GitHub. Git’s job is to keep the two in sync, and it does that with four main commands: clone, fetch, pull and push.
Put the portfolio on GitHub
On github.com, create a new repository named portfolio. Leave “Add a README” and the other initialisation options unticked: your local repository already has history, and an extra commit on GitHub would give you two unrelated histories to reconcile. GitHub then shows the commands to connect an existing repository. They boil down to:
git remote add origin https://github.com/maya-okafor/portfolio.git
git remote -v
git push -u origin maingit remote add gives a URL a short name. origin is only a convention, the name Git uses automatically when you clone, but everyone uses it, so do too. The push uploads your commits and creates main on GitHub. The first time, Git will ask you to authenticate; the next lesson covers setting that up properly with SSH keys or a credential manager.
If you use the GitHub CLI, gh repo create portfolio --public --source=. --remote=origin --push does the whole thing in one go.
Starting from the other side, with a repository that already exists on GitHub, you clone it instead:
git clone https://github.com/maya-okafor/portfolio.gitClone creates the folder, downloads every commit, adds a remote called origin and checks out main with tracking already set up.
Remote-tracking branches: Git’s memory of the remote
After the push, git branch -a shows something new:
* main
remotes/origin/mainorigin/main is a remote-tracking branch: your repository’s note of where main was on origin the last time you talked to it. You never commit to it. It moves only when you fetch, pull or push. That’s why git status can say “Your branch is up to date with ‘origin/main’” while a teammate pushed five minutes ago: Git hasn’t asked GitHub since.
Upstreams and git push -u
The -u in git push -u origin main (long form --set-upstream) records that your local main tracks origin/main. From then on:
- a bare
git pushorgit pullonmainknows where to go; git statusreports “ahead 2” or “behind 1” againstorigin/main;git branch -vvlists each branch with its upstream and ahead/behind counts.
Every new branch needs it once, on its first push: git push -u origin footer.
Fetch, then pull
git fetch asks the remote what’s new, downloads the commits and moves your remote-tracking branches. It never touches your own branches or files, so it’s always safe:
git fetch
git status # "Your branch is behind 'origin/main' by 1 commit"
git log --oneline main..origin/main # what's new over theregit pull is git fetch followed by integrating the upstream into your current branch. If you have no local commits of your own, that’s a fast-forward. If both sides have new commits, Git has to merge or rebase, and recent Git versions refuse to guess:
fatal: Need to specify how to reconcile divergent branches.Make the choice once:
git config --global pull.rebase true # replay my unpushed commits on top (my default)
# or: git config --global pull.rebase false (create a merge commit)
# or: git config --global pull.ff only (refuse unless it's a fast-forward)I recommend pull.rebase true. It only rewrites your own unpushed commits, which the golden rule allows, and it keeps history free of “Merge branch ‘main’ of github.com:…” commits that say nothing.
A daily rhythm
Put these together and a working session on the portfolio looks the same every time. Start by switching to main and pulling, so you begin from what’s actually on GitHub rather than what was there last week. Create a branch for the change. Commit as you go, and push the branch early, even unfinished: a pushed branch is a backup, and it’s visible to anyone helping you. Before you open it for review, fetch again and rebase onto the fresh origin/main if it has moved. That habit alone prevents most of the rejected pushes and surprise conflicts people blame on Git.
git switch main && git pull
git switch -c projects-filter
# …work and commit…
git push -u origin projects-filterWhen push is rejected
If someone pushed to main since your last fetch, your push is refused:
! [rejected] main -> main (fetch first)
error: failed to push some refs to 'https://github.com/maya-okafor/portfolio.git'The remote has commits you don’t. Pull them in, resolve any conflict with the routine from section 2, run your checks, and push again.
Your first push asked for credentials. Next you’ll set up authentication properly, so Git stops asking and GitHub knows it’s really you.
Key takeaways
- A remote is a named URL for another copy of the repository;
originis the conventional name for the one you cloned from or push to. origin/mainis your local record of wheremainwas on the remote at your last fetch; it moves only when you fetch, pull or push.git push -u origin <branch>uploads a branch and sets it as the upstream, so later a baregit pushorgit pullknows where to go.git fetchdownloads without touching your branches;git pullfetches and then merges or rebases.- A rejected push means the remote has commits you don’t; pull them in, then push again, never force.