CSCI 4513
Week 6, Lecture 12
Before diving into advanced concepts, let's SEE how Git works!
We'll use an interactive game to visualize branching, commits, and merges. This hands-on exploration builds intuition!
Nobody is perfect! Sometimes you need to fix mistakes in your commit history.
Use git commit --amend to fix your most recent commit:
# Forgot to add a file?
git add forgotten_file.js
git commit --amend
# Want to change the commit message?
git commit --amend -m "New commit message"
--amend - Replaces the last commit entirely (creates a new commit hash)
Forgot a file? - Stage it with git add first, then --amend adds it to the previous commit
-m - Provide a new message inline; without it, Git opens your editor
Only safe for commits that haven't been pushed yet!
--amend doesn't edit the commitโit replaces it with a new one. The old commit is destroyed and a new one is created.
Use git rebase -i to interactively modify multiple commits:
# Edit the last 3 commits
git rebase -i HEAD~3
# Edit all commits back to root
git rebase -i --root
-i - Interactive mode; opens an editor where you choose what to do with each commit
HEAD~3 - Go back 3 commits from HEAD (the current commit)
--root - Go all the way back to the very first commit
The editor lists commits oldest-first โ the opposite of git log!
pick - Use the commit as-isedit - Stop to amend the commitsquash - Combine with previous commitreword - Change the commit messagedrop - Remove the commit entirelyLet's say you want to fix a typo in a commit message:
# Start the rebase
git rebase -i HEAD~2
# In the editor, change from:
pick eacf39d Create send file
pick 92ad0af Create third file
# To:
edit eacf39d Create send file
pick 92ad0af Create third file
# Then amend the commit
git commit --amend -m "Create second file"
# Finish the rebase
git rebase --continue
Step 1: Start interactive rebase for the last 2 commits
Step 2: Change pick โ edit for the commit you want to fix
Step 3: Git pauses at that commit โ now amend it with the corrected message
Step 4: --continue finishes replaying the remaining commits
Notice "send file" was a typo โ this workflow fixes it to "second file"
git log (newest first).
Squashing combines multiple commits into one, keeping your Git history clean and readable.
# In interactive rebase:
pick e30ff48 Create first file
squash 92aa6f3 Create second file
squash 05e5413 Create third file
# All three commits become one!
pick - Keep this commit as the base; squashed commits merge into it
squash - Merge this commit into the one above it (the picked commit)
Result: Three commits โ one commit with a combined message you can edit
The first commit must be pick โ you need a base to squash into!
Time to practice what we just learned!
Use git reset to split a commit that does too much:
# In interactive rebase, mark commit as 'edit'
git rebase -i HEAD~1
# Reset to commit before HEAD
git reset HEAD~
# Now add and commit files separately
git add file1.js && git commit -m 'Add feature A'
git add file2.js && git commit -m 'Add feature B'
# Continue the rebase
git rebase --continue
Step 1: Mark the commit as edit so Git pauses there
git reset HEAD~ - Undoes the commit but keeps all files in your working directory (mixed reset)
Step 3: Selectively add and commit files into separate, focused commits
Step 4: --continue finishes the rebase with your new split commits
What git reset HEAD~ does:
git reset --soft HEAD~1
Moves HEAD only. Staging area and working directory stay the same.
git reset HEAD~1
Moves HEAD and updates staging area. Working directory unchanged.
git reset --hard HEAD~1
โโโโโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโโฌโโโโโโโโโโ โ Mode โ HEAD โ Staging โ Working โ โโโโโโโโโโโโโโผโโโโโโโโโผโโโโโโโโโโผโโโโโโโโโโค โ --soft โ Moves โ Same โ Same โ โ (mixed) โ Moves โ Resets โ Same โ โ --hard โ Moves โ Resets โ Resets โ โโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโดโโโโโโโโโโ
Each level resets one more thing โ soft is safest, hard is most destructive
A branch is just a pointer to a single commit. That commit points to the one before it, forming a chain.
git rebase -i HEAD~2, Git starts at HEAD, follows the pointer to the previous commit, then follows that pointer to edit the last 2 commits.
When collaborating, you'll work with remote repositories on GitHub.
Git only lets you push if your local branch is up-to-date with the remote. This prevents you from overwriting others' work!
# This will fail if remote has new commits
git push origin main
# Update your local branch first
git fetch origin
git merge origin/main
git push origin main
git fetch - Downloads new commits from the remote without merging them
git merge origin/main - Integrates the fetched remote changes into your local branch
The pattern: fetch โ merge โ push ensures you never overwrite teammates' work
git pull is a shortcut that does fetch + merge in one step!
Let's practice working with remotes!
git push --force overwrites the remote repository with your local history. This can destroy your teammates' work!
# DON'T DO THIS without good reason!
git push --force origin main
--force - Overwrites the remote history with your local history, no questions asked
Safer alternative: --force-with-lease only forces if no one else has pushed since you last fetched
git push --force-with-lease for a safety check!
Instead of forcing, use git revert to undo commits safely:
# Destroys history
git reset HEAD~1
git push --force
# Creates new commit
git revert HEAD
git push origin main
git reset + --force - Erases the commit from history and forces the remote to match โ teammates lose work!
git revert HEAD - Creates a new commit that undoes the changes, preserving the full history
Key difference: revert adds to history; reset + force destroys history
Always prefer revert on shared branches โ it's safe for everyone!
--force-with-lease instead of --force-f, have a really good reason!More practice with advanced techniques!
A standard for commit messages gaining popularity in collaborative projects.
feat: add user authentication
fix: resolve login button bug
docs: update API documentation
style: format code with prettier
refactor: simplify error handling
test: add tests for login flow
chore: update dependencies
feat - New feature for the user
fix - Bug fix
docs / style - Documentation or formatting only (no logic changes)
refactor / test / chore - Internal changes that don't affect users directly
Format: type: description โ always lowercase, no period at the end!
Most open source projects use a fork workflow where you don't have write access to the main repo.
You can pull from upstream but can only push to origin. To contribute, you push to origin and create a Pull Request to upstream.
# 1. Fork the repo on GitHub (use the Fork button)
# 2. Clone YOUR fork to your local machine
git clone git@github.com:your_username/curriculum.git
cd curriculum
# 3. Add the original repo as 'upstream'
git remote add upstream git@github.com:TheOdinProject/curriculum.git
# 4. Verify your remotes
git remote -v
# origin git@github.com:your_username/curriculum.git
# upstream git@github.com:TheOdinProject/curriculum.git
Step 1: Fork creates your own copy of the repo on GitHub
git clone - Downloads your fork; Git automatically names it origin
git remote add upstream - Links the original repo so you can pull updates from it
git remote -v - Verify you have both origin (your fork) and upstream (original)
You can push to origin but only pull from upstream!
Practice collaborative Git workflows!
git checkout -b fix-typogit fetch upstreamgit checkout main && git merge upstream/maingit checkout fix-typo && git merge maingit push origin fix-typoPractice the contribution workflow!
Seems backwards, right? Here's why it's correct:
One last round of practice before we wrap up!
Click each card to reveal the answer!
How can you amend your last commit?
Answer: Use git commit --amend to replace the last commit. You can add forgotten files or change the commit message. Remember: never amend commits that have been pushed to shared repositories!
What are some different ways to rewrite history?
Answer: git commit --amend (change last commit), git rebase -i (edit/squash/reorder multiple commits), git reset (move HEAD and optionally update staging/working directory). All are dangerous when used on shared branches!
What does it mean for branches to be pointers?
Answer: A branch is just a pointer to a single commit. Each commit is a snapshot that also points to its parent commit. HEAD is a special pointer showing which branch you're currently on. This pointer chain is how Git tracks history!
What name is typically given for a Git remote that points to a forked repo's original source?
Answer: upstream - Your fork is origin, and the original repository you forked from is called upstream.
What should you do immediately before merging main into your feature branch?
Answer: First git fetch upstream to get the latest changes, then git checkout main && git merge upstream/main to update your local main branch. This ensures your main is current before merging it into your feature branch.
๐ Next Class: Testing JavaScript with Jest
โ ๏ธ Remember: Never rewrite shared history without good reason!