Skip to content

Git Lesson Plan

A progressive curriculum to master git through hands-on practice.

Goal: Understand the basic commit workflow.

Git tracks changes to files. The workflow is:

  1. Make changes to files
  2. Stage changes (select what to commit)
  3. Commit (save a snapshot with a message)
  1. Initialize a repository

    Terminal window
    mkdir learn-git && cd learn-git
    git init
    git status # See the empty repo
  2. Create and stage a file

    Terminal window
    echo "# My Project" > README.md
    git status # Untracked file
    git add README.md
    git status # Staged for commit
  3. Make your first commit

    Terminal window
    git commit -m "Add README"
    git log # See your commit
  4. Modify and commit again

    Terminal window
    echo "This is a demo." >> README.md
    git diff # See changes
    git add README.md
    git commit -m "Add description to README"
    git log --oneline # Compact history

You can init, add, commit, and view history. Run git log --oneline and see 2 commits.


Goal: Work on features without affecting main.

Branches are independent lines of development. main is the default branch. Create branches for features, merge when done.

main: A---B---C
\
feature: D---E
  1. Create and switch branches

    Terminal window
    git branch # List branches
    git branch feature # Create branch
    git switch feature # Switch to it
    # Or combined:
    git switch -c feature2 # Create and switch
  2. Make commits on a branch

    Terminal window
    git switch -c add-license
    echo "MIT License" > LICENSE
    git add LICENSE
    git commit -m "Add MIT license"
    git log --oneline # See commit on branch
  3. Compare branches

    Terminal window
    git switch main
    ls # No LICENSE file
    git log --oneline --all --graph # Visualize branches
  4. Delete a branch

    Terminal window
    git branch -d add-license # Fails: not merged
    git branch -D add-license # Force delete

Create a branch, commit to it, switch back to main. The commit only exists on your branch.


Goal: Combine branch work back into main.

Merge brings changes from one branch into another. Fast-forward merges move the pointer. Three-way merges create a merge commit.

  1. Fast-forward merge

    Terminal window
    git switch -c docs
    echo "## Usage" >> README.md
    git add README.md
    git commit -m "Add usage section"
    git switch main
    git merge docs # Fast-forward
    git log --oneline # Linear history
  2. Three-way merge

    Terminal window
    # Create diverging branches
    git switch -c feature-a
    echo "Feature A" > a.txt
    git add a.txt && git commit -m "Add feature A"
    git switch main
    git switch -c feature-b
    echo "Feature B" > b.txt
    git add b.txt && git commit -m "Add feature B"
    git switch main
    git merge feature-a # Fast-forward
    git merge feature-b # Creates merge commit
    git log --oneline --graph
  3. Handle a merge conflict

    Terminal window
    git switch -c conflict-demo
    echo "Line from branch" >> README.md
    git add README.md && git commit -m "Branch change"
    git switch main
    echo "Line from main" >> README.md
    git add README.md && git commit -m "Main change"
    git merge conflict-demo
    # CONFLICT! Edit README.md to resolve
    # Remove <<<<<<<, =======, >>>>>>> markers
    git add README.md
    git commit -m "Resolve merge conflict"

Merge two branches. Resolve at least one conflict manually.


Goal: Push and pull from GitHub/GitLab.

Remotes are copies of your repo on a server. origin is the conventional name for your primary remote. Push sends commits; pull fetches and merges.

  1. Clone an existing repo

    Terminal window
    git clone https://github.com/octocat/Hello-World.git
    cd Hello-World
    git remote -v # See origin
  2. Add a remote to existing repo

    Terminal window
    cd ~/learn-git
    # Create a repo on GitHub first, then:
    git remote add origin git@github.com:YOU/learn-git.git
    git remote -v
  3. Push your work

    Terminal window
    git push -u origin main # -u sets upstream
    git push # Future pushes are simple
  4. Pull changes

    Terminal window
    # After someone else pushes:
    git fetch # Download without merging
    git status # See "behind by N commits"
    git pull # Fetch and merge
  5. Track remote branches

    Terminal window
    git branch -r # List remote branches
    git switch -c feature origin/feature # Track remote

Clone a repo. Push a new branch. Pull changes from origin.


Goal: Clean up commits before sharing.

Local commits can be modified. Published commits should not be rewritten (breaks collaborators). Use rebase and amend for unpushed work.

  1. Amend the last commit

    Terminal window
    # Forgot a file:
    git add forgotten-file.txt
    git commit --amend --no-edit
    # Fix the message:
    git commit --amend -m "Better message"
  2. Interactive rebase

    Terminal window
    # Reword, squash, or reorder last 3 commits
    git rebase -i HEAD~3
    # In editor:
    # pick abc123 First commit
    # squash def456 Fixup commit <- squash into previous
    # reword ghi789 Bad message <- edit this message
  3. Rebase onto main

    Terminal window
    git switch feature
    git rebase main # Replay feature on top of main
    # Resolve conflicts if needed
    git rebase --continue
  4. Abort a rebase

    Terminal window
    # If it goes wrong:
    git rebase --abort

Squash 3 commits into 1. Rebase a feature branch onto updated main.


Goal: Recover from mistakes.

SituationCommand
Unstage a filegit restore --staged file
Discard local changesgit restore file
Undo last commit (keep)git reset --soft HEAD~1
Undo last commit (lose)git reset --hard HEAD~1
Revert a pushed commitgit revert <sha>
Find lost commitsgit reflog
  1. Unstage and restore

    Terminal window
    echo "mistake" >> README.md
    git add README.md
    git restore --staged README.md # Unstage
    git restore README.md # Discard changes
  2. Reset commits

    Terminal window
    # Make a bad commit
    echo "oops" > oops.txt
    git add oops.txt && git commit -m "Oops"
    git reset --soft HEAD~1 # Undo commit, keep staged
    git status # oops.txt still staged
    git reset HEAD oops.txt # Unstage
    rm oops.txt
  3. Revert a pushed commit

    Terminal window
    git revert HEAD # Creates a new commit undoing HEAD
    git log --oneline # See revert commit
  4. Recover with reflog

    Terminal window
    git reflog # History of HEAD movements
    git reset --hard abc123 # Go back to any state

Accidentally reset —hard, then recover using reflog.


Goal: Flexible workflows for real situations.

  1. Stash work in progress

    Terminal window
    # Working on something, need to switch branches
    echo "wip" >> README.md
    git stash # Save and clean
    git switch other-branch
    # Do work...
    git switch main
    git stash pop # Restore WIP
  2. Manage multiple stashes

    Terminal window
    git stash list
    git stash save "description"
    git stash apply stash@{1} # Apply without removing
    git stash drop stash@{0} # Remove from list
  3. Cherry-pick a commit

    Terminal window
    # Grab one commit from another branch
    git log other-branch --oneline
    git cherry-pick abc123
  4. Find bug with bisect

    Terminal window
    git bisect start
    git bisect bad # Current is broken
    git bisect good v1.0 # This version worked
    # Git checks out middle commit
    # Test it, then:
    git bisect good # or bad
    # Repeat until git finds the culprit
    git bisect reset

Stash changes, switch branches, pop the stash. Cherry-pick one commit.


Goal: Understand team collaboration patterns.

WorkflowDescription
Feature branchBranch per feature, merge to main
GitHub FlowBranch, PR, review, merge
Trunk-basedShort-lived branches, frequent integration
Gitflowdevelop/release/hotfix branches (heavyweight)
  1. Feature branch workflow

    Terminal window
    git switch -c feature/add-search
    # Work...
    git push -u origin feature/add-search
    # Open PR on GitHub
    # After review, merge and delete branch
  2. Keep branch updated

    Terminal window
    git switch feature/add-search
    git fetch origin
    git rebase origin/main # Or merge
    git push --force-with-lease # Safe force push
  3. Clean up merged branches

    Terminal window
    git branch --merged main | grep -v main | xargs git branch -d
    git fetch --prune # Remove stale remote refs

Complete a full cycle: branch → commits → push → PR → merge → delete branch.


Create a repo. Make 10 commits across 3 branches. Practice rebasing one branch onto another. Squash related commits. Push to GitHub.

Clone your own repo to a second directory (simulating a collaborator). Make conflicting changes in both. Push from one, pull and resolve in the other.

Clone a large open-source project. Use git log, git blame, and git bisect to understand how a feature was implemented.


StageMust Know
Beginnerinit add commit status log diff
Branchingbranch switch merge rebase
Remoteclone remote push pull fetch
Undoingrestore reset revert reflog
Powerstash cherry-pick bisect rebase -i