Working with GitHub: clone, remotes, push, and fetch
The previous lesson separated local Git from GitHub. This lesson connects them. A local repository has its own commits and branch pointers; a hosted repository has its own. Git transfers data between them only when you ask it to.
GitHub repository ← push / fetch → local Git repositoryWe will use an ASP.NET Core project called order-api. The example URL is illustrative; use your own repository URL in real commands.
1. Clone an existing GitHub repository
Section titled “1. Clone an existing GitHub repository”Use clone when the repository already exists online and you want a local working copy:
git clone https://github.com/acme-learning/order-api.gitcd order-apigit statusgit remote -vBy default, this creates an order-api directory containing checked-out project files and a hidden .git directory. Git downloads the repository’s objects and history, configures a remote called origin, creates remote-tracking references for the advertised branches, and normally checks out the remote’s default branch as a local branch that tracks it.
GitHub: main ── C1 ── C2 ── C3 clone ↓Your machine: main ── C1 ── C2 ── C3 origin/main points to the last fetched mainThe diagram shows the initial state, not automatic synchronization. Each repository can advance independently afterward. A normal clone downloads history, but options such as --depth deliberately make a shallow clone. Clone also does not create a checked-out local branch for every remote branch.
Public repositories can normally be cloned by anyone. A private repository requires permission and authentication. Authentication for pushing is separate from the fact that your repository has a URL.
2. Two ways to start a project on GitHub
Section titled “2. Two ways to start a project on GitHub”Choose the starting point that matches the work you already have. Do not create two unrelated initial histories and expect Git to combine them automatically.
Option A: an existing local project with commits
Section titled “Option A: an existing local project with commits”Create a new empty repository on GitHub. For this route, do not ask GitHub to initialize it with a README, license, or .gitignore; those would create a separate initial commit. Copy its URL, then run inside the existing local repository:
git statusgit log --oneline -3git remote add origin https://github.com/acme-learning/order-api.gitgit remote -vgit branch --show-currentIf the intended branch should be named main, rename the current branch only if needed:
git branch -M mainThen publish it and set its upstream:
git push -u origin mainremote add registers a destination; it does not upload anything. -u sets origin/main as the local main branch’s upstream for later commands such as git push, git pull, and ahead/behind status. Rename only when main is really the desired branch name; -M can overwrite an existing local branch name, so inspect first.
If the directory has files but is not yet a Git repository, first initialize it, review what will be committed, and make an initial commit:
git initgit statusgit add .git diff --cached --statgit commit -m "Initial project setup"Before git add ., configure .gitignore and inspect for credentials, generated bin/ and obj/ folders, and local settings. This is especially important in .NET projects.
Option B: start on GitHub, then clone
Section titled “Option B: start on GitHub, then clone”Create a repository on GitHub and clone it:
git clone https://github.com/acme-learning/order-api.gitcd order-apiIf it contains an initial README or other commit, edit or add the project files, then commit and push:
git statusgit add .git diff --cached --statgit commit -m "Initial ASP.NET Core project setup"git pushThis route already has origin from cloning. A normal clone of an existing default branch also configures upstream tracking. For a completely empty remote repository, check the newly created branch and its upstream; the first push may need git push -u origin main instead of bare git push.
| Your starting point | Recommended route |
|---|---|
| Local Git history already exists | Create an empty GitHub repository, add origin, push with -u |
| No local history and starting online | Create on GitHub, clone, add files, commit, push |
| Local files but no Git history | Initialize and commit locally, then follow Option A; or clone first and copy the files into the clone |
Do not run git init inside an already cloned repository; it already has .git.
3. What is a remote?
Section titled “3. What is a remote?”A remote is a named entry in the local repository’s configuration containing fetch and push URLs plus related refspec settings. It is a connection name and destination, not the hosted repository itself.
git remotegit remote -vgit remote add origin https://github.com/acme-learning/order-api.gitgit remote rename origin companygit remote remove companyThe examples above demonstrate separate operations, not a sequence to run on your project. Removing a remote removes its local configuration and associated remote-tracking references; it does not delete the GitHub repository or your local commits.
origin is a convention, not a reserved keyword. git clone normally creates that name; git init does not. A repository can have several remotes and each may use HTTPS or SSH URLs.
Inspect before editing a real remote:
git remote -vgit remote get-url origin4. Multiple remotes: origin and upstream
Section titled “4. Multiple remotes: origin and upstream”An open-source contributor may fork acme-learning/order-api into their own account. After cloning their fork, they could use:
origin → https://github.com/your-name/order-api.gitupstream → https://github.com/acme-learning/order-api.gitRegister the original project as another remote:
git remote add upstream https://github.com/acme-learning/order-api.gitgit remote -vgit fetch upstreamIn this convention, origin is your fork, so you usually push your feature branches there. upstream is the original project, so you fetch its new commits. These names are only local labels: another team could choose different names. Two remotes are useful for fork-based contributions, not required for every project.
5. Push local commits
Section titled “5. Push local commits”push sends commits and updates a selected branch on a remote, subject to access permissions and server rules:
git push origin maingit push origin feature/loginIn these simple forms, the named local branch is pushed to a branch of the same name on origin. You can name a branch even when it is not the one currently checked out:
git push origin feature/loginThis does not push uncommitted working-tree edits or every local branch. It also does not guarantee the server accepts the push: permissions, protected-branch rules, hooks, or newer remote commits can reject it.
For a newly created feature branch, set its upstream on the first push:
git switch feature/logingit push -u origin feature/loginAfterward, git push normally uses that upstream and your configured push behavior. Check before publishing:
git statusgit log --oneline --decorate -5git branch -vvgit status helps identify staged, unstaged, and ahead/behind state when tracking is configured. It does not by itself list every commit that will be pushed, and its remote information can be stale until you fetch.
6. Three different branch references
Section titled “6. Three different branch references”These names can point to different commits:
| Name | Where it lives | What it represents |
|---|---|---|
main |
Your local repository | Your writable local branch |
origin/main |
Your local repository | Local remote-tracking reference: your last known position of main on origin |
main on GitHub |
Hosted repository | The branch actually stored on the server |
origin/main is not a live connection to GitHub. If a teammate pushes, your local reference remains at its old commit until a fetch or pull updates it. You do not normally commit directly on origin/main; commit on a local branch.
Local main: A──BLocal origin/main: A──BGitHub main: A──B──C ← teammate pushed C
After git fetch origin:Local main: A──BLocal origin/main: A──B──CGitHub main: A──B──CThe local main and your working files have not changed yet. The remote-tracking reference is a local bookmark, not a separate checked-out working copy.
7. Fetch without integrating
Section titled “7. Fetch without integrating”Fetch contacts a remote, downloads available objects, and updates matching remote-tracking references according to the repository’s fetch configuration:
git fetch origingit statusgit log --oneline main..origin/mainThe last command lists commits reachable from origin/main but not from local main. It is useful before deciding how to integrate.
To request a particular branch:
git fetch origin mainWith a normal clone’s fetch refspec, this updates origin/main as well as FETCH_HEAD. Custom configurations and command options can change which remote-tracking references update. git fetch origin does not necessarily fetch every possible server-side ref, and git fetch origin main does not mean “merge main into my current branch.”
Fetch does not merge, rebase, or update the checked-out branch’s working files. It does update your local Git metadata and can download new history; it is not a command that leaves the entire local repository byte-for-byte untouched.
fetch → learn about remote historyintegrate → deliberately bring that history into the current branchFor example, after reviewing the new commits, you could fast-forward local main when possible:
git switch maingit merge --ff-only origin/maindotnet testgit pull combines fetching with a configured integration step. Its exact merge/rebase behavior depends on configuration, so it is not a synonym for git fetch.
A complete backend workflow
Section titled “A complete backend workflow”Suppose a teammate pushed a fix to the hosted main while you worked on a local payment branch:
git statusgit fetch origingit log --oneline main..origin/mainInspect the new work. When your working tree is ready, update local main:
git switch maingit merge --ff-only origin/maindotnet testContinue your feature on its own branch. After committing and testing it, publish only that branch:
git switch feature/paymentgit statusdotnet testgit push -u origin feature/paymentThe push can still fail if the server’s branch moved or permissions deny it. Do not force-push merely to make an error disappear; inspect the rejection first.
Common mistakes
Section titled “Common mistakes”- Treating
originas GitHub itself rather than a local remote name. - Assuming a clone creates local branches for every remote branch.
- Assuming
git commitorgit remote adduploads work. - Running
git initinside an existing clone. - Creating separate initial commits locally and online, then expecting a simple first push.
- Assuming
git push origin mainincludes every local branch or uncommitted edit. - Treating
origin/mainas a live view of GitHub rather than a cached local ref. - Assuming fetch merges or changes working files.
- Assuming a bare
git pushworks before an upstream is configured. - Force-pushing after a non-fast-forward rejection without understanding what happened.
Summary
Section titled “Summary”- Clone when the hosted repository already exists; it creates a local repository and usually an
originremote. - To publish existing local history, connect it to a new empty hosted repository and push with upstream tracking.
- A remote is a named local connection; one repository can have several.
- Push sends selected committed history; fetch receives history and updates remote-tracking references without integrating it into your local branch.
- Distinguish local
main, localorigin/main, and the actual hostedmain.
Knowledge check
Section titled “Knowledge check”- What does
git clonecreate locally, and does it create a local branch for every remote branch? - Why should the GitHub repository be empty when you publish an existing local history through Option A?
- Does
git remote add origin <url>upload any commits? - What does
-uadd to the first push? - Which of
main,origin/main, and GitHub’smaincan be stale after a teammate pushes? - What changes after
git fetch originand what stays untouched in your working tree?