← Back to Git series
🔀
Git & GitHub Deep Dive
clone · push · pull · fetch · credentials

Chapter 4 — Remote Repositories

A "remote" is another copy of your repository — usually on GitHub. `git push`, `git pull`, `git fetch` and `git clone` are the four verbs you need. Add credentials (PAT or SSH) once and you're set.

clonepushpullPAT/SSH
Duration
1-2 hours
Level
📊 Beginner
Prerequisite
🎯 Chapter 3
Outcome
Sync local and remote repos with credentials set up once

What you'll learn

  • 1`git clone` a remote to start working.
  • 2`git push` your commits up, `git pull` to bring others' down.
  • 3Add, rename, remove remotes with `git remote`.
  • 4Authenticate via Personal Access Token (HTTPS) or SSH key.

Core Concepts

1) The four verbs

CommandWhat
`git clone <url>`Download a remote into a new directory
`git fetch`Pull remote changes into refs (no merge)
`git pull``fetch` + `merge` (or `rebase`) into current branch
`git push`Send local commits to the remote

2) Managing remotes

bash
git remote                # list remotes (usually 'origin')
git remote -v             # with URLs
git remote add upstream <url>
git remote rename origin gh
git remote remove upstream
git remote set-url origin <new-url>

3) HTTPS vs SSH

AuthURL formatSetup
**HTTPS + PAT**`https://github.com/user/repo.git`Create Personal Access Token on GitHub; use as password
**SSH** (recommended)`git@github.com:user/repo.git``ssh-keygen` then add `~/.ssh/id_ed25519.pub` to GitHub

4) Credential storage

bash
# Cache for an hour
git config --global credential.helper "cache --timeout=3600"

# OS keyring (Mac/Windows)
git config --global credential.helper osxkeychain     # macOS
git config --global credential.helper manager         # Windows

Examples

Example 1 — `ex01_clone.sh`: clone a local "remote"

**Output**

text
=== Creating remote-role repository ===
=== Remote clone ===
Cloning into 'workspace'...

Key: any directory with `--bare` flag can play the role of "remote" for learning.

Example 2 — `ex02_remote.sh`: add / rename / remove remotes

**Output**

text
=== Add remote ===
=== Change remote ===
=== Remove remote ===
origin updated

Key: `git remote -v` shows both fetch & push URLs.

Example 3 — `ex03_push_pull.sh`: two workers via push/pull

**Output**

text
=== Worker 1 ===
initial commit then push
=== Worker 2 ===
clone, edit, then push
=== Worker 1 ===
pull → conflict-free merge

Key: pull regularly to keep up; push when ready.

Example 4 — `ex04_credentials.sh`: how to set up PAT / SSH

**Output**

text
=== Credential help ===
=== HTTPS vs SSH ===
=== PAT setup ===
=== SSH key setup ===

Key: SSH key once → push/pull forever without typing a password.

Common mistakes

  1. **Pushing without setting upstream** — first push needs `git push -u origin main`.
  2. **Forgetting to pull** — your `push` is rejected because the remote moved.
  3. **Token expired** — HTTPS push suddenly fails; regenerate the PAT or switch to SSH.
  4. **Pushing the wrong branch** — `git push origin main` not `feature`.
  5. **`git pull` on dirty working tree** — uncommitted changes block the merge. Stash or commit first.

Recap

  • `clone` → `push`/`pull` are the daily verbs.
  • One remote is usually called `origin`; you can add more.
  • SSH key setup is one-time pain, one-life convenience.
  • `git pull --rebase` keeps history linear.

Try it

bash
cd src
chmod +x ex0*.sh
./ex01_clone.sh
./ex02_remote.sh
./ex03_push_pull.sh
./ex04_credentials.sh

💻 Examples

Runnable examples — see the output yourself.

ex01_clone.shclone a local "remote"
CODE
#!/usr/bin/env bash
set -euo pipefail

WORKDIR=$(mktemp -d)
echo "Working directory: $WORKDIR"

# remote role of bare repository create
git init --bare -b main "$WORKDIR/remote.git" -q
echo "bare repository create done"

# commit (local_a at push)
git clone "$WORKDIR/remote.git" "$WORKDIR/local_a" -q
cd "$WORKDIR/local_a"
git config user.name "workerA" && git config user.email "a@example.com"
 git config commit.gpgsign false
echo "# remote repository " > README.md
git add . && git commit -q -m "docs: initial README"
git push -u origin main -q
echo "local_a at push done"

# local_b at clone
echo ""
echo "=== git clone ==="
git clone "$WORKDIR/remote.git" "$WORKDIR/local_b"

cd "$WORKDIR/local_b"
echo ""
echo "=== clone (after) log ==="
git log --oneline

echo ""
echo "=== remote check ==="
git remote -v

rm -rf "$WORKDIR"
▶ Output
=== Creating remote-role repository ===
=== Remote clone ===
Cloning into 'workspace'...
ex02_remote.shadd / rename / remove remotes
CODE
#!/usr/bin/env bash
set -euo pipefail

REPO=$(mktemp -d)
cd "$REPO"
git init -q -b main
git config user.name "Demo User" && git config user.email "demo@example.com"
 git config commit.gpgsign false

echo "=== remote add ==="
git remote add origin https://github.com/example/my-repo.git
git remote add upstream https://github.com/original/my-repo.git
git remote -v

echo ""
echo "=== origin URL change ==="
git remote set-url origin https://github.com/example/new-name.git
git remote -v

echo ""
echo "=== upstream name → source using change ==="
git remote rename upstream source
git remote -v

echo ""
echo "=== source removed ==="
git remote remove source
git remote -v

rm -rf "$REPO"
▶ Output
=== Add remote ===
=== Change remote ===
=== Remove remote ===
origin updated
ex03_push_pull.shtwo workers via push/pull
CODE
#!/usr/bin/env bash
set -euo pipefail

WORKDIR=$(mktemp -d)

git init --bare -b main "$WORKDIR/remote.git" -q

setup_repo() {
 local path="$1" name="$2" email="$3"
 git clone "$WORKDIR/remote.git" "$path" -q
 git -C "$path" config user.name "$name"
 git -C "$path" config user.email "$email"
 git -C "$path" config commit.gpgsign false
}

# repository A: initial commit + push
setup_repo "$WORKDIR/repo_a" "workerA" "a@example.com"
cd "$WORKDIR/repo_a"
echo "A of first commit" > a.txt
git add . && git commit -q -m "feat: A's first commit"
git push origin main -q
echo "A → push done"

# repository B: B at commit add + push
setup_repo "$WORKDIR/repo_b" "workerB" "b@example.com"
cd "$WORKDIR/repo_b"
git pull origin main -q
echo "B of commit" > b.txt
git add . && git commit -q -m "feat: B's commit"
git push origin main -q
echo "B → push done"

# repository A: pull then B of change fetching
cd "$WORKDIR/repo_a"
echo ""
echo "=== A pull (before) log ==="
git log --oneline

git pull origin main -q
echo ""
echo "=== A pull (after) log (B of commit(included) ==="
git log --oneline

rm -rf "$WORKDIR"
▶ Output
=== Worker 1 ===
initial commit then push
=== Worker 2 ===
clone, edit, then push
=== Worker 1 ===
pull → conflict-free merge
ex04_credentials.shhow to set up PAT / SSH
CODE
#!/usr/bin/env bash
set -euo pipefail

echo "============================================"
echo " GitHub credential setup guide"
echo "============================================"
echo ""
echo "--- way 1: SSH key (recommended) ---"
echo ""
echo "1. key create:"
echo " ssh-keygen -t ed25519 -C \"your@email.com\""
echo ""
echo "2. ssh-agent at register:"
echo " eval \"\$(ssh-agent -s)\""
echo " ssh-add ~/.ssh/id_ed25519"
echo ""
echo "3. Copy the public key:"
echo " cat ~/.ssh/id_ed25519.pub"
echo " (GitHub → Settings → SSH and GPG keys → New SSH key (paste here)"
echo ""
echo "4. Connection test:"
echo " ssh -T git@github.com"
echo ""
echo "--- way 2: HTTPS + PAT ---"
echo ""
echo "1. PAT create:"
echo " GitHub → Settings → Developer settings"
echo " → Personal access tokens → Tokens (classic)"
echo " → Generate new token"
echo " (repo permission check)"
echo ""
echo "2. credential Save:"
echo " git config --global credential.helper store"
echo " (first push/pull use username and PAT input → saved)"
echo ""
echo "--- current SSH key status ---"
if [ -f "$HOME/.ssh/id_ed25519.pub" ]; then
 echo "Public key found:"
 cat "$HOME/.ssh/id_ed25519.pub"
elif [ -f "$HOME/.ssh/id_rsa.pub" ]; then
 echo "RSA Public key found:"
 cat "$HOME/.ssh/id_rsa.pub"
else
 echo "(SSH key none — 1 way using createplease)"
fi
▶ Output
=== Credential help ===
=== HTTPS vs SSH ===
=== PAT setup ===
=== SSH key setup ===

📝 Exercises

Try them yourself first, then open the solution to compare.

Exercise 1

Problem 1 (hw01.sh)

Goal: Set up two local directories ("worker A" and "worker B") cloned from a single bare repo. Demonstrate a push-pull cycle between them.

Requirements
  • Filename: hw01.sh
Toggle solution
SOLUTION
#!/usr/bin/env bash
set -euo pipefail

WORKDIR=$(mktemp -d)

git init --bare -b main "$WORKDIR/remote.git" -q

# repo_a
git clone "$WORKDIR/remote.git" "$WORKDIR/repo_a" -q
git -C "$WORKDIR/repo_a" config user.name "workerA"
git -C "$WORKDIR/repo_a" config user.email "a@example.com"
git -C "$WORKDIR/repo_a" config commit.gpgsign false
echo "A of file" > "$WORKDIR/repo_a/a.txt"
git -C "$WORKDIR/repo_a" add .
git -C "$WORKDIR/repo_a" commit -q -m "feat: A's file"
git -C "$WORKDIR/repo_a" push origin main -q
echo "repo_a push done"

# repo_b: clone (after) A of commit check
git clone "$WORKDIR/remote.git" "$WORKDIR/repo_b" -q
git -C "$WORKDIR/repo_b" config user.name "workerB"
git -C "$WORKDIR/repo_b" config user.email "b@example.com"
git -C "$WORKDIR/repo_b" config commit.gpgsign false

echo ""
echo "=== repo_b at log (A of commit ) ==="
git -C "$WORKDIR/repo_b" log --oneline

echo "B of file" > "$WORKDIR/repo_b/b.txt"
git -C "$WORKDIR/repo_b" add .
git -C "$WORKDIR/repo_b" commit -q -m "feat: B's file"
git -C "$WORKDIR/repo_b" push origin main -q
echo "repo_b push done"

# repo_a: fetch + merge
git -C "$WORKDIR/repo_a" fetch origin -q
echo ""
echo "=== repo_a fetch + merge (after) ==="
git -C "$WORKDIR/repo_a" merge origin/main -q
git -C "$WORKDIR/repo_a" log --oneline

rm -rf "$WORKDIR"
Exercise 2

Problem 2 (hw02.sh)

Goal: Add a second remote called `upstream`, rename `origin` to `gh`, then list remotes with their URLs.

Requirements
  • Filename: hw02.sh
Toggle solution
SOLUTION
#!/usr/bin/env bash
set -euo pipefail

REPO=$(mktemp -d)
cd "$REPO"
git init -q -b main
git config user.name "Demo User" && git config user.email "demo@example.com"
 git config commit.gpgsign false

git remote add origin https://github.com/example/project.git
git remote add backup https://backup.example.com/project.git

echo "=== initial remote list ==="
git remote -v

git remote set-url origin https://github.com/example/project-v2.git
git remote remove backup

echo ""
echo "=== final remote list ==="
git remote -v

rm -rf "$REPO"
Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub ↗