Lesson 5 - Guided Project: Clean Up a Messy Repo

Welcome to the Guided Project

A teammate has handed you the SkyLog repository and asked you to “tidy it up before we share it.” When you open it, the mess is immediately visible: the most recent commit message has a typo, an earlier commit added a half-baked “experimental color mode” that crashes on some machines, a secret config file was committed straight into history, and git status is cluttered with files that should never have been tracked in the first place.

This is a realistic situation. Repositories drift into this state all the time, especially early in a project’s life. The good news is that everything you need to fix it comes from this module: amending a commit, reverting a commit, and using git rm --cached together with a .gitignore. In this guided project you’ll apply those skills in sequence and watch a messy repo turn into a clean one.

By the end of this project, you will be able to:

  • Inspect a repository’s history and working tree to identify what’s wrong
  • Fix a mistake in the latest commit message with git commit --amend
  • Safely undo a bad commit with git revert
  • Stop tracking a committed file with git rm --cached and prevent future clutter with .gitignore

We’ll work through it stage by stage, running real commands and reading the real output each one produces. Let’s get the repository in shape.


Stage 1: Set Up and Inspect the Mess

First, build the messy repository so you have something to clean. Run the following in an empty folder. It creates the SkyLog project, an API config file containing a (fake) secret, a broken experimental commit, and a typo’d README commit — exactly the situation you’ve inherited:

git init skylog && cd skylog

# Commit 1: the project
printf 'import sys\n\ndef add(o):\n    open("observations.md","a").write(f"- {o}\\n")\n' > skylog.py
printf '# SkyLog\n' > README.md
git add skylog.py README.md
git commit -m "Add SkyLog project"

# Commit 2: a committed secret (clearly fake placeholder)
printf 'API_TOKEN=sk-demo-not-a-real-secret-123\n' > config.env
git add config.env
git commit -m "Add API config"

# Commit 3: a broken experimental feature
printf 'import sys\n\nUSE_COLOR = True  # experimental, breaks on Windows\n\ndef add(o):\n    open("observations.md","a").write(f"- {o}\\n")\n' > skylog.py
git add skylog.py
git commit -m "Add experimental color mode"

# Commit 4: a README update with a typo in the message
printf '# SkyLog\n\nUsage: python skylog.py "your observation"\n' > README.md
git add README.md
git commit -m "Updaet README with usage"

# And some untracked clutter that should never be in Git
mkdir __pycache__ && touch __pycache__/skylog.cpython-311.pyc
touch skylog.debug.log

Now inspect what you’re dealing with. Start with the history:

$ git log --oneline
0295bee Updaet README with usage
f187525 Add experimental color mode
d030441 Add API config
a47fb61 Add SkyLog project

Two problems jump out from the history alone. The newest commit message says “Updaet” instead of “Update”, and there’s a commit titled “Add experimental color mode” that we’ve been told is broken. Now check the working tree:

$ git status --short
?? __pycache__/
?? skylog.debug.log

Those ?? marks mean Git sees two untracked things — a __pycache__/ directory and a skylog.debug.log file — that are noise. They shouldn’t be committed, but right now they clutter every git status. And although it doesn’t show here, the config.env file with a secret in it is already tracked from commit d030441.

So your cleanup list is clear:

  1. Fix the typo in the latest commit message.
  2. Undo the broken experimental commit.
  3. Stop tracking the secret and add a .gitignore so the clutter disappears.

(Your commit hashes will differ from the ones shown here — every hash is computed from the commit’s content and the moment it was made. Read the messages to identify each commit, then use your own hashes.)


Stage 2: Fix the Typo in the Latest Commit Message

The mistake is in the message of the most recent commit, and we haven’t shared this history with anyone yet. That’s exactly the situation git commit --amend is built for: it replaces the latest commit instead of adding a new one. To fix only the message, pass the corrected message with -m:

$ git commit --amend -m "Update README with usage"

Confirm the latest commit now reads correctly:

$ git log --oneline -1
4af789d Update README with usage

The typo is gone. Notice the short hash changed (here from 0295bee to 4af789d): amending doesn’t edit the old commit in place — it builds a brand-new commit to replace it. That’s why amending is safe only for commits you haven’t pushed yet. Since this history is still local, we’re in the clear.


Stage 3: Revert the Broken Commit

Next, the “Add experimental color mode” commit. That commit added a USE_COLOR line to skylog.py that breaks on some systems, and we want it gone. We could not simply amend or delete it — it’s not the latest commit, and other commits sit on top of it. The right tool is git revert, which creates a new commit that undoes the changes of a target commit, leaving the original commit in place in history.

First find the commit’s hash from the log — it’s f187525 in this example (yours will differ; match the message “Add experimental color mode”). Then revert it. The --no-edit flag accepts Git’s default revert message so it doesn’t open an editor:

$ git revert --no-edit f187525
[main f8a471d] Revert "Add experimental color mode"
 Date: Thu Mar 5 09:00:00 2026 +0330
 1 file changed, 2 deletions(-)

Git reports a new commit, “Revert "Add experimental color mode"”, that removed two lines — the blank line and the USE_COLOR line the bad commit had added. After this, skylog.py no longer contains the experimental USE_COLOR line; the broken behavior is undone, while the original commit remains in the history as an honest record of what happened.

This is the key difference from amending: revert is additive and safe. It doesn’t rewrite anything, so it’s the correct way to undo a commit that’s already buried in history (or already shared with others).


Stage 4: Untrack the Secret and Add a .gitignore

The last and most important fix. The config.env file holds an API token and was committed into the repository — it should never have been tracked. We want Git to stop tracking it without deleting your local copy, and we want to make sure it (and the clutter from Stage 1) can never be committed again.

Use git rm --cached to remove the file from Git’s tracking while keeping it on disk:

$ git rm --cached config.env

Now create a .gitignore so Git ignores the secret file, log files, and the Python cache directory from now on. Here are the contents to put in .gitignore:

$ cat > .gitignore   # contents:
__pycache__/
*.log
*.env

Those three patterns cover everything that was cluttering the repo: __pycache__/ ignores the cache directory, *.log ignores skylog.debug.log (and any future log), and *.env ignores config.env (and any other environment file). Stage the .gitignore and commit both changes together:

$ git add .gitignore
$ git commit -m "Remove committed secret and add .gitignore"

This single commit records two related cleanups: the secret is no longer tracked, and the ignore rules are now in place.

Untracking a secret is not the same as scrubbing it

git rm --cached stops tracking config.env going forward, but the secret still exists in the old commit (d030441 in our example). Anyone with the repository’s history can check out that commit and read the token. The only safe response to a leaked secret is to rotate it — revoke the exposed token and issue a new one — so the leaked value becomes worthless. Truly removing a value from all past history is a separate, advanced operation (rewriting history) that we’ll cover in a later module. For now: stop tracking it, add it to .gitignore, and rotate the secret.


Stage 5: Review the Clean Result

The cleanup is done. Check the history and the working tree to confirm. First the log:

$ git log --oneline
fcc83f0 Remove committed secret and add .gitignore
f8a471d Revert "Add experimental color mode"
4af789d Update README with usage
f187525 Add experimental color mode
d030441 Add API config
a47fb61 Add SkyLog project

Read it top to bottom. The typo is fixed (Update README with usage), the broken feature has been undone by a revert commit, and the secret has been untracked with a .gitignore added — all as new, honest commits on top of the original history. The broken Add experimental color mode and the original Add API config commits are still there, which is exactly right: revert and untracking add to history rather than pretending the past never happened. (Your hashes will differ throughout.)

Now the working tree:

$ git status --short

No output — a clean status. The __pycache__/ directory, skylog.debug.log, and config.env are all still on your disk, but Git no longer reports them because .gitignore now covers them. A clean git status is the goal: it means nothing unexpected is waiting to be committed, so the next time you make a change, it stands out clearly.

You’ve taken a repository with four distinct problems and turned it into one with a clean history and a clean status, using only commands from this module.


Practice Exercises

Exercise 1: Re-amend with both message and content

Suppose that right after Stage 2 you realize the README update also forgot a line. You edit README.md, then want to fold that fix into the latest commit and keep the corrected message. What two commands get you there?

Hint

Stage the edited file first with git add README.md, then run git commit --amend -m "Update README with usage". Because the file is staged, --amend rebuilds the latest commit with both the new content and the message — no extra commit needed. (Remember: only safe before pushing.)

Exercise 2: Revert the revert

A teammate decides the experimental color mode wasn’t so broken after all and wants it back. Instead of rewriting code by hand, how could you bring those changes back using a single Git command on the revert commit?

Hint

Run git revert --no-edit <hash-of-the-revert-commit> (the Revert "Add experimental color mode" commit, f8a471d in our example). Reverting a revert re-applies the original change — Git treats it like any other commit, so undoing the undo restores the USE_COLOR line.

Exercise 3: Confirm the secret is really ignored

After Stage 4, you want to prove that config.env can no longer slip into a commit. What command shows whether a path is ignored, and what does git status --short look like if the .gitignore is working?

Hint

Run git check-ignore config.env — if the file is ignored, Git prints the path back to you; if it isn’t, you get no output. And a working .gitignore means git status --short shows no ?? line for config.env (or for __pycache__/ and *.log), giving you a clean status.


Summary

In this guided project you cleaned up a realistically messy SkyLog repository using only Module 2 skills. You inspected the mess with git log --oneline and git status --short, fixed a typo in the latest commit message with git commit --amend, undid a broken commit with git revert, and stopped tracking a leaked secret with git rm --cached while adding a .gitignore to keep the clutter out. The result was a clean history that honestly records every fix, and a clean git status.

Key Concepts

  • git commit --amend -m "..." — replace the latest (unpushed) commit, e.g. to fix a message typo.
  • git revert <commit> — add a new commit that undoes a target commit, the safe way to reverse a buried or shared change.
  • git rm --cached <file> — stop tracking a file without deleting it from disk.
  • .gitignore — patterns (__pycache__/, *.log, *.env) that keep files out of Git and out of git status.
  • Untracking is not scrubbing — a committed secret stays in old history; rotate it.

Why This Matters

Repositories rarely stay tidy on their own, and the ability to clean one up calmly is a sign of real fluency with Git. Knowing which tool fits which mess — amend for the latest message, revert for a buried bad commit, rm --cached plus .gitignore for files that should never have been tracked — lets you fix problems without losing work or rewriting shared history by accident. And understanding that a leaked secret survives in old commits, so it must be rotated rather than merely deleted, is the kind of security habit that protects real projects.


Next Steps

Continue to Module 3 - Branching and Merging

Work on multiple things at once with branches, then bring your work back together.

Back to Module Overview

Return to the Working with History module overview


Continue Building Your Skills

You’ve now used every Module 2 skill together on a single realistic problem — reading history, amending, reverting, and ignoring files — and watched a messy repository become a clean one. In Module 3 you’ll learn to work on more than one thing at a time with branches, then merge that work back together, which is how teams build features in parallel without stepping on each other.