Live Demo Script: Git & GitHub, first command to last#

Run this top to bottom in one terminal. Everything happens in a throwaway folder, and the “remote” is a local bare repo, so you only need GitHub for Part 7b. Each step has a Say line (what to tell the audience) and a Show line (what to point at).

Tip: keep a second terminal open running watch -n1 git log --oneline --graph --all so the audience sees the history change live.

Part 0 · Setup (2 min)#

git --version
git config --global user.name  "Your Name"
git config --global user.email "you@example.com"
git config --global init.defaultBranch main
git config --global core.editor "nano"
git config --list --show-origin | grep -E "user|defaultBranch"

rm -rf ~/git-demo && mkdir ~/git-demo && cd ~/git-demo

Say: Git needs to know who you are because every commit is signed with a name and email.

Part 1 · First repository (5 min)#

git init
ls -a                      # show the hidden .git folder
git status                 # "No commits yet"

echo "# Demo App" > README.md
git status                 # Untracked
git add README.md
git status                 # Staged (green)
git commit -m "Add README"
git log

Show: the three states of a file: untracked, staged, committed.

echo "print('hello')" > app.py
echo "Built with Python" >> README.md
git status                 # one new, one modified
git add .
git commit -m "Add app.py and describe the stack"
git log --oneline

Say: edit, add, commit. That is the whole rhythm.

Part 2 · Looking back (4 min)#

echo "print('hello, world')" > app.py
git diff                   # working folder vs staging
git add app.py
git diff                   # empty now
git diff --staged          # staging vs last commit
git commit -m "Greet the whole world"

git log --oneline --graph
git show HEAD              # open the last commit
git show HEAD~1            # the one before
git blame app.py

.gitignore#

echo "SECRET=123" > .env
echo "debug" > debug.log
git status                 # both show as untracked
printf ".env\n*.log\n" > .gitignore
git status                 # only .gitignore remains
git add .gitignore && git commit -m "Ignore secrets and logs"

Part 3 · Undoing things (6 min)#

# 1. Discard edits to a file
echo "oops" >> app.py
git restore app.py
cat app.py

# 2. Un-stage
echo "print('draft')" >> app.py
git add app.py
git restore --staged app.py
git restore app.py

# 3. Amend the last commit
echo "License: MIT" >> README.md
git add README.md
git commit -m "Add licence"
git commit --amend -m "Add license note"        # fixed the typo

# 4. Reset (local only)
echo "a" > a.txt && git add a.txt && git commit -m "Add a"
echo "b" > b.txt && git add b.txt && git commit -m "Add b"
git reset --soft HEAD~1      # commit gone, b.txt still staged
git status
git reset --mixed HEAD       # unstage
git status
git add b.txt && git commit -m "Add b"
git reset --hard HEAD~1      # DANGER: b.txt deleted
ls

# 5. Reflog: the safety net
git reflog
git reset --hard HEAD@{1}    # b is back
ls

# 6. Revert: safe undo for shared history
git revert HEAD --no-edit
git log --oneline

Say: reset rewrites history (private only). revert adds a new commit (safe for shared work).

Part 4 · Branches (5 min)#

git branch
git switch -c feature/login
echo "def login(): pass" > login.py
git add login.py && git commit -m "Add login stub"
echo "# login docs" > docs.md
git add docs.md && git commit -m "Add login docs"
git log --oneline --graph --all

git switch main
ls                           # login.py is gone: it lives on the other branch
git switch feature/login
ls                           # and it is back

Show: ls flipping between branches; HEAD moving in the graph terminal.

Part 5 · Merging and conflicts (7 min)#

Fast-forward#

git switch main
git merge feature/login      # "Fast-forward"
git branch -d feature/login

Three-way merge#

git switch -c feature/footer
echo "footer" > footer.txt && git add . && git commit -m "Add footer"
git switch main
echo "header" > header.txt && git add . && git commit -m "Add header"
git merge feature/footer -m "Merge footer"
git log --oneline --graph

Conflict#

git switch -c feature/greeting-a
echo "Hello from A" > greeting.txt && git add . && git commit -m "Greeting A"
git switch main
git switch -c feature/greeting-b
echo "Hello from B" > greeting.txt && git add . && git commit -m "Greeting B"
git switch main
git merge feature/greeting-a            # fine
git merge feature/greeting-b            # CONFLICT
git status
cat greeting.txt                        # show <<<<<<< ======= >>>>>>>
echo "Hello from A and B" > greeting.txt
git add greeting.txt
git commit -m "Resolve greeting conflict"
git merge --abort 2>/dev/null; git log --oneline --graph

Say: a conflict is not an error, it is Git asking a human to decide.

Part 6 · Remotes (6 min)#

Simulate GitHub with a local bare repo first (no account needed):

cd ~ && rm -rf fake-github.git && git init --bare fake-github.git
cd ~/git-demo
git remote add origin ~/fake-github.git
git remote -v
git push -u origin main
git branch -a

Simulate a teammate:

cd ~ && rm -rf teammate && git clone ~/fake-github.git teammate
cd teammate
git config user.name "Teammate"; git config user.email "t@example.com"
echo "teammate was here" > team.txt
git add . && git commit -m "Teammate adds a file"
git push

cd ~/git-demo
git fetch                       # download only
git log --oneline origin/main -3
git status                      # "behind by 1"
git pull                        # fetch + merge

Rejected push:

echo "mine" > mine.txt && git add . && git commit -m "My change"
cd ~/teammate && echo "theirs" > theirs.txt && git add . && git commit -m "Their change" && git push
cd ~/git-demo
git push                        # REJECTED
git pull --no-rebase --no-edit
git push                        # works

Part 6b · Real GitHub (3 min)#

ssh-keygen -t ed25519 -C "you@example.com"
cat ~/.ssh/id_ed25519.pub        # paste into GitHub > Settings > SSH keys
ssh -T git@github.com
# create an empty repo "git-demo" on github.com, then:
git remote set-url origin git@github.com:YOUR-USER/git-demo.git
git push -u origin main

Part 7 · Pull request (live on GitHub, 5 min)#

git switch -c fix/readme-typo
echo "Contributions welcome" >> README.md
git add . && git commit -m "Add contributing note"
git push -u origin fix/readme-typo

On github.com: Compare & pull request → write title and description → Create pull request → add a review comment → Squash and merge → Delete branch.

git switch main
git pull
git branch -d fix/readme-typo
git fetch --prune

Fork flow (show on the website only): Fork → clone your fork → git remote add upstream <original-url> → git fetch upstream → git merge upstream/main.

Part 8 · Power tools (6 min)#

# Stash
echo "half done" >> app.py
git switch -c temp                 # may be refused if conflicts; otherwise fine
git switch main
git stash
git status                         # clean
git stash list
git stash pop

git restore app.py

# Cherry-pick
git switch -c hotfix
echo "fix" > fix.txt && git add . && git commit -m "Hotfix"
git switch main
git cherry-pick hotfix
git log --oneline -3

# Rebase
git switch -c feature/rebase-demo
echo "x" > x.txt && git add . && git commit -m "Feature x"
git switch main
echo "y" > y.txt && git add . && git commit -m "Main moves on"
git switch feature/rebase-demo
git rebase main
git log --oneline --graph --all

# Tags
git switch main
git tag v1.0.0
git tag -a v1.1.0 -m "Second release"
git tag
git push --tags

Say: never rebase commits other people already have.

Part 9 · Workflows hands-on (6 min)#

# Feature-branch workflow in 6 commands
git switch main && git pull
git switch -c feature/search
echo "search" > search.py && git add . && git commit -m "Add search"
git push -u origin feature/search
# open PR on GitHub, merge, then:
git switch main && git pull && git branch -d feature/search

# Trunk-based: tiny change straight to main
echo "tweak" >> README.md && git commit -am "Tweak README" && git push

Walk through the six workflow diagrams (centralized, feature branch, GitHub flow, Git Flow, forking, trunk-based) and ask the audience which fits their team.

Part 10 · Rescue missions (6 min)#

# Committed on main by mistake
echo "wip" > wip.txt && git add . && git commit -m "Oops on main"
git branch rescue-branch
git reset --hard HEAD~1
git switch rescue-branch            # the commit lives on

# Detached HEAD
git switch main
git switch --detach HEAD~2
git switch -c saved-work            # keep anything done here
git switch main

# Deleted a branch
git branch -D saved-work
git reflog | head
git branch saved-work <commit-id-from-reflog>

# Wrong author
git commit --amend --reset-author --no-edit

# Bisect: which commit broke it?
git bisect start
git bisect bad
git bisect good HEAD~5
# test, then mark good/bad until Git names the commit
git bisect reset

Wrap-up (2 min)#

git log --oneline --graph --all

Say: the daily loop is git pull → work → git add → git commit → git push. When in doubt, run git status.

Cleanup#

cd ~ && rm -rf git-demo fake-github.git teammate