Lesson 10 · Everyday work
Connect a remote without confusing it with an upstream
Add a local bare origin, fetch its main branch and deliberately choose what local main tracks. Prove that a remote name, a remote-tracking ref and an upstream setting are three different things.
- Level: Intermediate
- 11 min read
- 6 min video
- Updated
- Before this lesson: Create a feature branch and retire only the disposable ref
Progress saved in this browser.
Playing loads the video from YouTube (privacy-enhanced mode).
What you will learn
- Distinguish origin, origin/main and main's upstream configuration.
- Explain what adding a remote, fetching and selecting an upstream each change.
- Connect GitTree to a real local bare repository without provider credentials.
- Prove that the feature branch remains local and unpublished.
Before you start
Start where lesson 9 ended, in the original gittree-features: checked-out feature/pricing-cards has its workflow-card commit F1; main has five commits through the history document H5. Both are clean, no remote exists and dist remains ignored. These are demo refs, never instructions to change this website's branch.
The video was recorded with Git 2.52.0 and GitTree 2.0 on Ubuntu 24.04: the document, the remote, the fetch and the upstream were done in the app; the bare destination, the add-only state and the checks were run in the terminal. Nothing was run on macOS or Windows, and Ubuntu is the only Linux system recorded; the Command-key chords below come from the app's keymap. GitTree's inspected Git floor is 2.39.0. Once you are signed in, local Git transport and upstream configuration are on the Free plan; provider integration is a different gated feature.
Reserve one empty sibling destination gittree-features-origin.git outside every source checkout. Do not replace an existing directory. The bare repository will be seeded from this project's main; it is not a GitLab installation, GitHub repository, backup service or CI runner. If the clone destination exists, stop and identify it rather than deleting it.
For the add-only prediction, background fetch must be off in this recording repository. Open Preferences with Command+, (Ctrl+, on Ubuntu), choose the gittree-features page under the repository's own name, and in Auto-fetch interval enter 0, then Apply, wait for Saved, and Exit Preferences. The app stores this in its own per-repository settings, not in Git's config. Note the previous value to restore it later; do not change the app-wide interval or another repository.
Concept
A remote is repository-local configuration naming a transport address and fetch rules. origin is a convention, not a built-in central authority. Adding it writes this demo's config; it does not contact the address, create the destination or upload a branch.
A remote-tracking ref such as refs/remotes/origin/main is a local record updated by fetching. It is not the server branch refs/heads/main itself. Immediately after adding origin, there is no origin/main here. Fetch obtains the objects/refs but does not switch HEAD, merge, rebase or replace the working page.
An upstream is a relationship stored for a local branch. Here branch.main.remote = origin and branch.main.merge = refs/heads/main. Selecting it tells later commands which fetched branch main tracks. It does not create a commit, fetch missing history or publish feature/pricing-cards.
The analogy is an address book entry, the last message you collected, and a choice of which conversation to follow. They are related, but saving an address is not receiving or sending a message.
State effects
| Action | Working tree / index | Local refs/config | Server |
|---|---|---|---|
| Switch to main | Select H5's recorded files | HEAD attaches to main | Unchanged |
| Commit the document | Index/work agree with H6 | main advances to H6 | Unchanged |
| Prepare the bare copy | Original unchanged | Original has no origin yet | Separate local main starts at H6 |
| Add Remote… | Unchanged | origin URL/fetch config only | Unchanged |
| Fetch All | Unchanged | origin/main becomes H6; objects obtained | Read, not modified |
| Set Upstream… | Unchanged | main's remote/merge config set | Unchanged |
F1 stays on the local feature. The main/feature graph now has one distinct commit on each side of H5: documentation on main, workflow cards on feature. Neither is a merge.
Full project change
Switch to main before creating this file. Create docs/remotes.md with the full UTF-8/LF body and final newline:
# GitTree Features remotes
origin names a separate local bare repository used only for this course.
Its file:// URL is a transport address, not a GitLab project or a login.
main tracks origin/main after an explicit fetch and upstream selection.
origin/main is a local record of the last fetched remote main.
Adding a remote does not fetch, push or publish any branch.
feature/pricing-cards remains local and has no upstream in this checkpoint.
No credentials or machine-specific absolute paths are stored in this document.Commit subject: docs: record remotes-and-upstreams practice. Author/committer: GitTree Demo <demo@example.com>. No second practice notebook or extra commit belongs in this checkpoint.
In GitTree
- In the original tab, right-click local main → Checkout main. Expect the history-document-era page, no workflow section, and clean status. If a dirty-switch dialog appears, Cancel and inspect the unexpected work.
- Open the palette with Command+Shift+P (Ctrl+Shift+P on Ubuntu). Choose New File…. In Path from the project folder, type docs/remotes.md, then Create. Paste the full body above and Save / Command+S. Wait for disk-save completion.
- Choose Back to Graph, then palette Show Working Changes. Review docs/remotes.md and activate Stage File. Enter the exact subject in Summary, leave Description empty and ordinary optional behaviors off. Click Commit Changes to 1 File once, or Command+Enter as an alternative. Verify the actual sixth main commit.
- Prepare the separate bare repository using terminal segment T1 below. This is an explicitly labeled native preparation step: no GitTree “create hosting project” button is invented.
- Run T2 to obtain the actual file:// URL. The inspected GitTree URL parser accepts file URLs, not a plain bare filesystem path even though native Git can accept one. Use the generated URL, with any spaces percent-encoded. Never paste a shell command into the remote URL field.
- Palette Add Remote… opens Add a remote. Enter Name = origin and URL = the measured file URL. Choose Add Remote. The add form has no separate Push URL field; that belongs to Edit Remote…. Expect origin configured, but no fetched origin/main yet.
- Fetch from the palette with Fetch All (Command+Shift+F, Ctrl+Shift+F on Ubuntu). Before this first fetch the side panel's Remote group does not list origin at all, so there is no row to right-click; with exactly one remote, Fetch All fetches it. Wait for the actual result. Expect origin/main at the same real SHA as main; HEAD and files stay put.
- Right-click local main, open the Upstream submenu and choose Set Upstream…. In What should main track?, select Upstream branch = origin/main, then Track. This named-ref action is not a generic palette command. Cancel leaves the relationship unchanged; do not choose Track Nothing.
- Verify with T3 and T4. Leave main checked out, feature/pricing-cards present but unpublished, and no in-progress operation. Do not press Push, Pull, Force Push… or a remote-delete command to “finish” connecting.
Ordinary options off means Amend Previous Commit unchecked; Skip Hooks, Add Signed-off-by, Different Author, Push After Commit and Allow Empty Commit off; no co-authors. Wait for confirmed completion rather than clicking twice.
In the terminal
Run all commands inside the isolated gittree-features, not a source checkout. App/native mutations are alternatives, not steps to repeat after app success.
T1 — record the document and prepare only main on the local origin
After saving the full document on main:
git switch --quiet main
git add -- docs/remotes.md
git commit --quiet -m "docs: record remotes-and-upstreams practice"
git clone --quiet --bare --single-branch --branch main --no-local . ../gittree-features-origin.gitAll commands succeeded silently in the native fixture. The clone creates a new, separate bare repository from main only; it does not configure origin in the original. --no-local avoids a hardlinked local object-copy shortcut. There is no push in this preparation transcript.
If the app already committed the document, omit the first three commands. Never make the same document commit twice.
T2 — add, then fetch, then select upstream
Generate a real platform-correct, percent-encoded URL:
ORIGIN_URL="$(node --input-type=module -e "import path from 'node:path'; import {pathToFileURL} from 'node:url'; console.log(pathToFileURL(path.resolve('../gittree-features-origin.git')).href);")"
printf '%s\n' "$ORIGIN_URL"The actual output is a machine-specific file URL ending /gittree-features-origin.git. Its root varies; file://<lab-root>/gittree-features-origin.git is explanatory notation, not fabricated terminal output or a literal URL to paste. ORIGIN_URL is a shell variable, not a credential.
git remote add origin "$ORIGIN_URL"
git config --get remote.origin.fetch
git for-each-ref --format='%(refname)' refs/remotes/originThe add is silent. The config query prints:
+refs/heads/*:refs/remotes/origin/*The final for-each-ref prints nothing: adding a remote did not fetch one.
git fetch --quiet origin
git branch --set-upstream-to=origin/main mainFetch succeeded silently. Actual upstream output:
branch 'main' set up to track 'origin/main'.T3 — prove the original's final state
git symbolic-ref --short HEAD
git rev-parse --abbrev-ref 'main@{upstream}'
git rev-list --left-right --count main...origin/main
git log -1 --format=%s origin/main
git rev-list --count main
git status --shortOutputs from the first five queries, in order:
main
origin/main
0 0
docs: record remotes-and-upstreams practice
6The final short status is empty, exit 0. The tab between the two zeros separates commits reachable only from main and only from origin/main; matching tips are not proof future fetches are unnecessary.
T4 — distinguish the actual bare repository from its local tracking ref
git --git-dir=../gittree-features-origin.git rev-parse --is-bare-repository
git --git-dir=../gittree-features-origin.git for-each-ref --format='%(refname)' refs/heads
git ls-remote --heads origin refs/heads/feature/pricing-cards
git config --get branch.feature/pricing-cards.remoteThe first query prints true; the second prints refs/heads/main. The third is silent, exit 0: feature/pricing-cards was not published. The fourth is silent, exit 1: no feature upstream setting exists. That expected absence is not a failed fetch.
Independently resolve the three tips:
git rev-parse refs/heads/main
git rev-parse refs/remotes/origin/main
git --git-dir=../gittree-features-origin.git rev-parse refs/heads/mainAll three actual SHAs agree. Never substitute H6 for their real SHA output.
Result
Where you end: checked-out main has six commits, including only the new docs/remotes.md. A separate local bare origin has main at the same H6. The original has origin configured and refs/remotes/origin/main fetched; main tracks origin/main. Index/work clean; ignored dist may remain. Local feature/pricing-cards still points at F1 and has no upstream or server branch. No merge, pull, hosted project, sign-in or CI run happened.
Common mistakes
- Treating origin as a special server: it is a chosen local name.
- Expecting Add Remote to create a server or fetch its branches.
- Pasting a plain path into GitTree's URL field: generate the file URL instead.
- Choosing a real company's remote, using a token-bearing URL, or reusing an existing bare directory.
- Creating the document while still on the feature: it would record the wrong branch's history.
If something goes wrong
Before fetching, native upstream selection fails because origin/main does not exist. The isolated changed-input replay actually ran:
git -c advice.setUpstreamFailure=false branch --set-upstream-to=origin/main mainObserved stderr, exit 128:
fatal: the requested upstream branch 'origin/main' does not existThe branch tip and work remain unchanged. According to the app's source (not filmed), the upstream form instead has no fetched options and its Track control is unavailable with There are no remote-tracking branches yet. Fetch first. Do not draw the native fatal message as an app dialog. Fetch the intended remote, then deliberately select origin/main. In this lesson that is Fetch All.
A missing local directory, wrong URL or permissions failure is not fixed by forcing a push or disabling security. Cancel, inspect Edit Remote…, verify the actual destination and retry the intended fetch only. If there is already an origin, inspect it; do not overwrite or remove it blindly. Removing a remote changes local config/tracking, not the server itself, and is unnecessary for the canonical lesson.
Check your understanding
Answer each question, then read why. Questions marked “Required” count toward completing the lesson.
0 of 6 correct
Answer the required questions correctly to complete the lesson.
Checked in this browser.
Practice
Explain why origin/main is absent immediately after Add Remote. In a separate same-project replay, try selecting it before fetching, then fetch and select it successfully. Verify main's SHA and complete index.html bytes did not change across either configuration action. At the original, prove main/origin/main agree and the feature is still absent from the bare server.
Your own confirmation: the site cannot see your computer.
To complete the lesson, answer the required questions correctly and confirm the practical exercise.
Recap
- A remote configures an address; fetch obtains its refs; an upstream chooses a branch relationship.
- origin/main is local fetched knowledge, not the server branch itself.
- Connecting main did not publish the feature or change its working page.
Related
Glossary terms introduced
Remote — مستودع بعيد; remote-tracking ref — مرجع تتبُّع بعيد; upstream — الفرع المتتبَّع; bare repository — مستودع مجرد; fetch — جلب; transport — وسيلة نقل.
Video transcript
The narration of the video, cue by cue.
Show the transcript
- Connect a remote, then choose an upstream deliberately.
- The feature exists locally; it has not been published.
- Disable background fetching only in this recording repository.
- Switch to main before writing the transport document.
- Save the complete document without machine-specific paths or credentials.
- Stage the transport document on main, not on the feature.
- The document adds the sixth commit on main.
- Prepare a separate bare copy of main for local transport.
- This destination has main, but no feature branch.
- The original and bare destination are separate repositories.
- Generate a file URL for the intended local destination.
- Saving the address does not fetch branches or upload the feature.
- Open the remote dialog in the original repository.
- Name it origin and verify the complete destination.
- The remote is configured; its branch has not been fetched.
- The fetch rule exists while the tracking-ref query is empty.
- Selecting a missing upstream fails without moving main.
- Does choosing an upstream replace fetching? It does not.
- Fetch the remote's main without switching your current branch.
- Origin slash main is your local fetched record.
- Fetching is not merging, rebasing or replacing the page.
- Choose the relationship for the named local main branch.
- Selecting the option alone is not the confirmed relationship.
- Confirm main's upstream without moving its commit.
- Verify main, its upstream, matching tips and clean work.
- An empty server feature query does not mean the local feature was lost.
- Address, fetched record and upstream setting remain different things.
- Correct an upstream choice without resetting the history.
- Verify the original and server separately before continuing.
- Explain why the missing ref required a fetch, not a push.
- A local bare remote is not a hosted project or a pipeline.
- Next, fetch a teammate's update while keeping your own files unchanged.