Git & GitHub track

Lesson 04 · Lessons

Remote Repositories

clone, push, pull, fetch, remotes and PAT/SSH authentication

Lessons6 min readFree lesson

Local Git is your version control alone. By connecting a remote repository such as GitHub, you can safely back up your code. You can download it anywhere and work with your team. This section clearly distinguishes the differences between clone, push, pull, and fetch. Set up PAT (Personal Access Token) and SSH key authentication.

Learning Objectives

  • You can clone remote storage with git clone.
  • The difference in operation between git push·git pull·git fetch can be explained.
  • You can manage remote connections with git remote add/rename/remove.
  • Authentication can be set up with GitHub PAT or SSH key.
  • Understand the relationship between the local branch and the remote branch (origin/main).

Key concepts

1) git clone

git clone https://github.com/user/repo.git         # HTTPS
git clone [email protected]:user/repo.git             # SSH
git clone https://github.com/user/repo.git mydir   #by another name
git clone --depth=1 https://github.com/user/repo.git  #shallow replication

2) push / pull / fetch

local remote (origin)
main ──push──▶  origin/main
main ◀──pull──  origin/main  (fetch + merge)
──fetch──▶ origin/main (no effect on local branches)
git push origin main          #Local main → remote main
git push -u origin main       #Push after configuring upstream (afterwards, only git push is possible)
git pull origin main          # fetch + merge
git fetch origin              #Update remote information only, no merge
git fetch origin main         #Only certain branches

3) remote management

git remote -v                     #Remote list + URL verification
git remote add origin URL         #add remote
git remote rename origin upstream #change name
git remote remove origin          #remote wipe
git remote set-url origin NEW_URL #Change URL

4) Remote branch tracking

git branch -vv              #Check local and remote tracked branches
git switch -c feature origin/feature  #Check out remote branch locally
git push origin --delete feature      #Delete remote branch

5) Credential settings

PAT (Personal Access Token) — HTTPS method

# GitHub → Settings → Developer settings → Personal access tokens → Generate
#When cloning/pushing, enter token instead of password.
git config --global credential.helper store  #Save (be careful of plain text)

SSH key method

ssh-keygen -t ed25519 -C "[email protected]"   #key generation
cat ~/.ssh/id_ed25519.pub                    #Copy public key
# GitHub → Settings → SSH and GPG keys → New SSH key
ssh -T [email protected]                        #Connection test

See as example

Example 1 — ex01_clone.sh: Clone a local bare repository to simulate two repositories

#!/usr/bin/env bash
#The GitHub lab requires a network, so we simulate it with a local bare repository.
WORKDIR=$(mktemp -d)
cd "$WORKDIR"

#Create a bare repository for the “remote” role
git init --bare remote.git
echo "Create a bare repository: $WORKDIR/remote.git"

#Create a local work repository and push
git init local_a
cd local_a
git config user.name "Worker A" && git config user.email "[email protected]"
echo "Hello" > README.md
git add . && git commit -q -m "init"
git remote add origin "$WORKDIR/remote.git"
git push -u origin main

echo ""
echo "=== clone ==="
cd "$WORKDIR"
git clone remote.git local_b
cd local_b
git log --oneline

rm -rf "$WORKDIR"

Execution result

Create bare repository: /tmp/tmp.XXX/remote.git

=== clone ===
Cloning into 'local_b'...
a1b2c3d init

Key point: git clone copies the entire history of the remote repository and automatically configures the origin remote.


Example 2 — ex02_remote.sh: Add/Confirm/Change remote

#!/usr/bin/env bash
REPO=$(mktemp -d)
cd "$REPO"
git init -q
git config user.name "For practice" && git config user.email "[email protected]"

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

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

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

echo ""
echo "=== remove upstream ==="
git remote remove upstream
git remote -v

rm -rf "$REPO"

Execution result

=== Add remote ===

=== remote list ===
origin    https://github.com/example/repo.git (fetch)
origin    https://github.com/example/repo.git (push)
upstream  https://github.com/original/repo.git (fetch)
...

=== Change origin URL ===
origin    https://github.com/example/new-repo.git (fetch)
...

Key point: You can check all remote repositories and URLs connected to git remote -v.


Example 3 — ex03_push_pull.sh: Simulating push/pull with local bare repository

#!/usr/bin/env bash
WORKDIR=$(mktemp -d)

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

#Storage A
git clone "$WORKDIR/remote.git" "$WORKDIR/repo_a" -q
cd "$WORKDIR/repo_a"
git config user.name "Worker A" && git config user.email "[email protected]"
echo "A's file" > a.txt
git add . && git commit -q -m "feat: A adds file"
git push origin main -q

#Repository B
git clone "$WORKDIR/remote.git" "$WORKDIR/repo_b" -q
cd "$WORKDIR/repo_b"
git config user.name "Worker B" && git config user.email "[email protected]"

echo "=== Log before pull from B ==="
git log --oneline

git pull origin main -q
echo ""
echo "=== Log after pull from B ==="
git log --oneline

rm -rf "$WORKDIR"

Execution result

=== Log before pull from B ===
(none)

=== Log after pull from B ===
a1b2c3d feat: A adds file

Key point: git pull performs fetch + merge at once.


Example 4 — ex04_credentials.sh: SSH key generation and instructions

#!/usr/bin/env bash
set -euo pipefail

echo "=== Instructions on how to create an SSH key ==="
echo ""
echo "1. Generate key:"
echo "   ssh-keygen -t ed25519 -C \"[email protected]\""
echo ""
echo "2. Verify public key:"
echo "   cat ~/.ssh/id_ed25519.pub"
echo ""
echo "3. GitHub registration:"
echo "   GitHub → Settings → SSH and GPG keys → New SSH key"
echo "Paste the public key copied above"
echo ""
echo "4. Test connection:"
echo "   ssh -T [email protected]"
echo "   # Hi username! You've successfully authenticated..."
echo ""

if [ -f "$HOME/.ssh/id_ed25519.pub" ]; then
    echo "=== Current public key ==="
    cat "$HOME/.ssh/id_ed25519.pub"
else
    echo "(The ~/.ssh/id_ed25519.pub file does not exist. Create it using command 1 above)"
fi

Execution result

=== Instructions on how to create an SSH key ===
1. Generate key: ssh-keygen -t ed25519 ...
...
(The ~/.ssh/id_ed25519.pub file does not exist. Create it using command 1 above)

Bottom line: The SSH key method allows secure authentication without having to enter PAT every time.


Seeing from a different perspective

Git commands Metaphor
git clone Copy original document to USB
git push Upload my edits to the server
git fetch Preview server change history
git pull Receive server change details and apply them immediately
origin Server alias associated with my repository

Common Mistakes

  1. --force abuse after git push failure — Team member's commits may disappear; Use --force-with-lease.
  2. Forget git fetch instead of git pull — Checking the diff after fetch can reduce unexpected conflicts.
  3. Use passwords for HTTPS URLs — As of August 2021, GitHub does not support password authentication; Use PAT or SSH.
  4. Tracking branches remaining locally after deleting remote branches — Organize with git fetch --prune.
  5. First push without -u — git push does not work afterward because upstream is not set.

Cleanup

  • Clone the remote repository to git clone URL and origin is automatically set.
  • If you set the upstream to git push -u origin <branch>, you only need to enter git push afterwards.
  • git fetch does not change the local branch, and git pull performs fetch + merge at once.
  • The SSH key method is more convenient and safer than HTTPS PAT.

Try it yourself

cd 04_remote_storage/src
chmod +x ex01_clone.sh ex02_remote.sh ex03_push_pull.sh ex04_credentials.sh
./ex01_clone.sh
./ex02_remote.sh
./ex03_push_pull.sh
./ex04_credentials.sh

Application:

  1. Check the difference with the remote using git diff HEAD origin/main after git fetch origin.
  2. Try deleting the remote branch with git push origin --delete <branch name>.

Example files

The runnable files used in this lesson. Copy them into your own project and experiment.

src/ex01_clone.shShell
#!/usr/bin/env bash
set -euo pipefail

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

#Create a bare repository of remote roles
git init --bare -b main "$WORKDIR/remote.git" -q
echo "bare repository creation completed"

#First commit (push from local_a)
git clone "$WORKDIR/remote.git" "$WORKDIR/local_a" -q
cd "$WORKDIR/local_a"
git config user.name "Worker A" && git config user.email "[email protected]"
    git config commit.gpgsign false
echo "# Remote repository practice" > README.md
git add . && git commit -q -m "docs: initial README"
git push -u origin main -q
echo "Push completed in local_a"

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

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

echo ""
echo "=== Check remote ==="
git remote -v

rm -rf "$WORKDIR"
src/ex02_remote.shShell
#!/usr/bin/env bash
set -euo pipefail

REPO=$(mktemp -d)
cd "$REPO"
git init -q -b main
git config user.name "For practice" && git config user.email "[email protected]"
    git config commit.gpgsign false

echo "=== Add remote ==="
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 "=== Change origin URL ==="
git remote set-url origin https://github.com/example/new-name.git
git remote -v

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

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

rm -rf "$REPO"
src/ex03_push_pull.shShell
#!/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" "Worker A" "[email protected]"
cd "$WORKDIR/repo_a"
echo "A's first commit" > a.txt
git add . && git commit -q -m "feat: A's first commit"
git push origin main -q
echo "A → push complete"

#Repository B: add commit from B + push
setup_repo "$WORKDIR/repo_b" "Worker B" "[email protected]"
cd "$WORKDIR/repo_b"
git pull origin main -q
echo "B's commit" > b.txt
git add . && git commit -q -m "feat: B's commit"
git push origin main -q
echo "B → push complete"

#Repository A: pull to receive changes in B
cd "$WORKDIR/repo_a"
echo ""
echo "=== Log before A pulls ==="
git log --oneline

git pull origin main -q
echo ""
echo "=== Log after A pull (contains B's commit) ==="
git log --oneline

rm -rf "$WORKDIR"
src/ex04_credentials.shShell
#!/usr/bin/env bash
set -euo pipefail

echo "============================================"
echo "Instructions for setting up GitHub credentials"
echo "============================================"
echo ""
echo "--- Method 1: SSH Key (Recommended) ---"
echo ""
echo "1. Generate key:"
echo "   ssh-keygen -t ed25519 -C \"[email protected]\""
echo ""
echo "2. Register with ssh-agent:"
echo "   eval \"\$(ssh-agent -s)\""
echo "   ssh-add ~/.ssh/id_ed25519"
echo ""
echo "3. Copy public key:"
echo "   cat ~/.ssh/id_ed25519.pub"
echo "(Paste in GitHub → Settings → SSH and GPG keys → New SSH key)"
echo ""
echo "4. Test connection:"
echo "   ssh -T [email protected]"
echo ""
echo "--- Method 2: HTTPS + PAT ---"
echo ""
echo "1. Create PAT:"
echo "   GitHub → Settings → Developer settings"
echo "   → Personal access tokens → Tokens (classic)"
echo "   → Generate new token"
echo "(Check repo permissions)"
echo ""
echo "2. Save credentials:"
echo "   git config --global credential.helper store"
echo "(Enter user name and PAT for first push/pull → saved)"
echo ""
echo "--- Current SSH key status ---"
if [ -f "$HOME/.ssh/id_ed25519.pub" ]; then
    echo "Discover public key:"
    cat "$HOME/.ssh/id_ed25519.pub"
elif [ -f "$HOME/.ssh/id_rsa.pub" ]; then
    echo "Discover RSA public key:"
    cat "$HOME/.ssh/id_rsa.pub"
else
    echo "(No SSH public key — create one using method 1 above)"
fi

Assignment

Problem 1

Implement a two-repository (repo_a, repo_b) scenario with a local bare repository as the "remote".

  • File name: hw01.sh
  • Requirements:
  1. Create a working directory as mktemp -d.
  2. Create a bare repository with git init --bare.
  3. In repo_a, perform clone → add file → commit → git push.
  4. In repo_b, click clone → git log --oneline to check whether the commit of repo_a is visible.
  5. Add file in repo_b → Commit → Push.
  6. Check the remote branch from repo_a to git fetch and then to git log --oneline --all → reflect to git merge.
#Expected output (core)
=== After repo_a fetch + merge ===
b2c3d4e (HEAD -> main, origin/main) feat: B's file
a1b2c3d feat: A's file

Problem 2

Write the git remote management script.

  • File name: hw02.sh
  • Requirements:
  1. Initialize empty storage.
  2. Add the origin remote as https://github.com/example/project.git.
  3. Add the backup remote as https://backup.example.com/project.git.
  4. Print both remotes as git remote -v.
  5. Change the origin URL to https://github.com/example/project-v2.git.
  6. Delete the backup remote.
  7. Print the final git remote -v.

Check the correct answer

After writing it yourself, compare it with the reference solution below.

Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

hw01.shShell
#!/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 "Worker A"
git -C "$WORKDIR/repo_a" config user.email "[email protected]"
git -C "$WORKDIR/repo_a" config commit.gpgsign false
echo "A's 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 completed"

#repo_b: Check A's commit after clone
git clone "$WORKDIR/remote.git" "$WORKDIR/repo_b" -q
git -C "$WORKDIR/repo_b" config user.name "Worker B"
git -C "$WORKDIR/repo_b" config user.email "[email protected]"
git -C "$WORKDIR/repo_b" config commit.gpgsign false

echo ""
echo "=== Log from repo_b (A's commit should be visible) ==="
git -C "$WORKDIR/repo_b" log --oneline

echo "B's 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 completed"

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

rm -rf "$WORKDIR"
hw02.shShell
#!/usr/bin/env bash
set -euo pipefail

REPO=$(mktemp -d)
cd "$REPO"
git init -q -b main
git config user.name "For practice" && git config user.email "[email protected]"
    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"

Learn this lesson with a mentor

Work through the Git & GitHub track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now