Intermediate Git

Intermediate Git

Branching, Merging & Collaboration

CSCI 4513

Week 6, Lecture 12

Today's Learning Objectives

๐ŸŽฎ Let's Visualize Git Together!

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!

How This Works:

Why Change Git History?

Nobody is perfect! Sometimes you need to fix mistakes in your commit history.

Common Scenarios:

โš ๏ธ Warning: Only change history that hasn't been pushed to a shared repository!

Changing the Last Commit

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!

What Actually Happens:

--amend doesn't edit the commitโ€”it replaces it with a new one. The old commit is destroyed and a new one is created.

โš ๏ธ Never amend commits that have been pushed! You could destroy work others are basing their changes on.

Changing Multiple Commits

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!

Options Available:

Interactive Rebase in Action

Let'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"

๐Ÿ’ก Tip: Commits appear in reverse order in rebase (oldest first) compared to git log (newest first).

Squashing Commits

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!

Why Squash?

๐ŸŽฎ Git Practice Break!

Time to practice what we just learned!

Your Task:

Splitting Up a Commit

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:

  • Moves HEAD to previous commit
  • Updates staging area to match
  • Leaves working directory unchanged

Understanding git reset

git reset --soft

git reset --soft HEAD~1

Moves HEAD only. Staging area and working directory stay the same.

git reset (mixed)

git reset HEAD~1

Moves HEAD and updates staging area. Working directory unchanged.

git reset --hard

git reset --hard HEAD~1
โš ๏ธ DANGER! Moves HEAD, updates staging, AND overwrites working directory. Can destroy data!
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  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

Branches Are Pointers

A branch is just a pointer to a single commit. That commit points to the one before it, forming a chain.

Key Concepts:

๐Ÿ’ก Example: When you run 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.

Working with Remotes

When collaborating, you'll work with remote repositories on GitHub.

The Safety Mechanism:

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!

๐ŸŽฎ Git Practice Break!

Let's practice working with remotes!

Your Task:

The Danger of Force Pushing

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

When Is It Appropriate?

๐Ÿ’ก Better Option: Use git push --force-with-lease for a safety check!

git revert: The Safe Alternative

Instead of forcing, use git revert to undo commits safely:

โŒ Dangerous Way

# Destroys history
git reset HEAD~1
git push --force

โœ… Safe Way

# 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!

Best Practices for Changing History

Golden Rule: If working on a team project, check if rewriting history is safe before doing it!

๐ŸŽฎ Git Practice Break!

More practice with advanced techniques!

Your Task:

Conventional Commits

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!

๐Ÿ’ก Benefit: Makes it crystal clear what each commit does at a glance!

Contributing to Open Source

Most open source projects use a fork workflow where you don't have write access to the main repo.

The Three Repositories:

You can pull from upstream but can only push to origin. To contribute, you push to origin and create a Pull Request to upstream.

Setting Up a Fork

# 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!

๐Ÿ’ก Remember: origin = your fork, upstream = original repo

๐ŸŽฎ Git Practice Break!

Practice collaborative Git workflows!

Your Task:

The Contribution Workflow

  1. Create a feature branch: git checkout -b fix-typo
  2. Make your changes and commit them
  3. Fetch updates: git fetch upstream
  4. Update main: git checkout main && git merge upstream/main
  5. Merge main into feature: git checkout fix-typo && git merge main
  6. Push to your fork: git push origin fix-typo
  7. Create Pull Request on GitHub

๐ŸŽฎ Git Practice Break!

Practice the contribution workflow!

Your Task:

Why Merge Main INTO Your Feature Branch?

Seems backwards, right? Here's why it's correct:

๐Ÿ’ก Rule: Always merge "senior" branches into "junior" branches to test for conflicts first!

๐ŸŽฎ Final Git Practice!

One last round of practice before we wrap up!

Your Task:

Knowledge Check

Click each card to reveal the answer!

โ“ Question 1

How can you amend your last commit?

โ“ Question 2

What are some different ways to rewrite history?

Knowledge Check (continued)

โ“ Question 3

What does it mean for branches to be pointers?

โ“ Question 4

What name is typically given for a Git remote that points to a forked repo's original source?

โ“ Question 5

What should you do immediately before merging main into your feature branch?

Today's Takeaways

You Can Now:

๐Ÿ“… Next Class: Testing JavaScript with Jest

โš ๏ธ Remember: Never rewrite shared history without good reason!