Skip to content
beginner

Version Control for Python Projects: Track Changes and Restore Earlier Work

It looks like main.py, main_v2.py, main_final.py, and main_final_REAL.py. It works right up until the moment you can't remember which copy is current, what…

Published 2026-10-02Updated 2026-10-0411 min read
Stunning sunset with a crescent moon, rich orange and red hues create a dramatic sky view.
Stunning sunset with a crescent moon, rich orange and red hues create a dramatic sky view. Photo by Oleksiy Yeshtokyn,🌻🇺🇦🌻 on Pexels.

Your folder already has a version control system. It's just a bad one.

It looks like main.py, main_v2.py, main_final.py, and main_final_REAL.py. It works right up until the moment you can't remember which copy is current, what changed between them, or why you made two of them. And when the version that runs stops running, you have no clean way back.

Real version control fixes exactly that. For a Python project, it gives you three things: a record of every change, a way to read that record, and a way to return to the last state that worked. In this tutorial, you'll set that up on your own small project, make a save point, break something on purpose, and restore it.

Why Your Folder of Copies Is Not Version Control

The copy-and-rename habit fails in three predictable ways.

First, you lose the why. main_v2.py tells you a change happened, not what it was or what problem it solved. Second, you lose the when. File timestamps lie the moment you copy a file or move it between folders. Third, you lose the comparison. To see what changed between two copies, you have to open both and read them side by side, line by line, by eye.

A version control system stores a full history of your project and lets you move between saved states. That's the whole idea. You take a snapshot when the code works, and later you can look back at any snapshot or return to one.

Git is the tool most Python projects use for this, and it runs locally on your machine. You don't need an account, a server, or an internet connection to start. Those come later, when you want your history to survive a dead laptop.

This tutorial assumes you already have a small project you can run from its root folder. If your files are still scattered across your desktop, organize them into one folder first, then come back. Everything here builds on that single project root.

Set Up Git Once, Then Initialize Your Project

You only install and configure Git once per machine. After that, every project is a two-minute setup.

Check whether Git is installed

Open your terminal and run:

git --version

If Git is installed, you'll see something like:

git version 2.43.0

The exact number doesn't matter. If you instead see command not found, install Git from the official Git website or your system's package manager, then run the check again.

Set your identity once

Git labels every save point with a name and email. Set them once, globally:

git config --global user.name "Your Name"
git config --global user.email "[email protected]"

These don't need to be real-world accurate for a personal project. They just need to be consistent, because they'll appear next to every commit you make.

Initialize the project

Navigate to your project root — the folder that contains your main script — and run:

git init

You'll see a short confirmation:

Initialized empty Git repository in /path/to/your/project/.git/

That hidden .git folder is the repository. It holds your entire history. The folder around it is just your working files. This distinction matters later: deleting .git deletes your history, even though your code files stay put.

Now check the state of things:

git status

You'll see something like:

On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        main.py

nothing added to commit but untracked files present

"Untracked" means Git can see the file but isn't watching it yet. That's expected. You haven't made your first save point.

Knowledge check

Check your understanding

Answer this question before you continue.

After you run `git init` from your project root, what does Git create there?
Single Choice

Focus: Initialize a project root as a local Git repository without confusing repository history with project files.

Add a .gitignore before your first commit

Before you commit anything, create a file named .gitignore in the project root. It tells Git which files to ignore forever.

__pycache__/
.venv/
.env
*.pyc

This keeps Python's cache folders, your virtual environment, and any secret files out of your history. Do this now, not later. Cleaning a virtual environment out of your history after the fact is annoying; preventing it takes ten seconds.

Your First Save Point: Stage and Commit

Three connected states show working files flowing to a staging selection, then to a committed snapshot in project history.
Staging selects what goes into the next save point; committing adds that selection to history.

Git has a two-step save model, and beginners trip on it constantly. Learn it once and it stops being strange.

  • git add chooses what goes into the next save point.
  • git commit writes that selection into history with a message.

Staging is a selection step, not a formality. It's how you decide what the snapshot contains.

Let's make a tiny example. Create a file called greet.py:

def greet(name):
    return f"Hello, {name}!"

print(greet("world"))

Run it:

python greet.py

Expected output:

Hello, world!

It works. Now save this state. Stage both the script and the ignore rules you just created:

git add greet.py .gitignore

Check what you're about to save before you save it:

git status

Expected output:

On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   .gitignore
        new file:   greet.py

Both files are staged. Now commit them with a message:

git commit -m "Add greet script and ignore rules"

You'll see a summary line:

[main (root-commit) 3f9a1c2] Add greet script and ignore rules
 2 files changed, 8 insertions(+)

That's your first save point. Notice the short ID 3f9a1c2 — Git uses IDs like this to reference specific commits later.

Write messages for future-you

A commit message is documentation you write for the person who will read it in a panic at 11 p.m. That person is you.

"Add greet script and ignore rules" is fine. "stuff" and "fix" are not, because they tell future-you nothing. A good beginner rule: describe what changed and why, in one short line. "Fix off-by-one in total calculation" beats "fix bug" every time.

Knowledge check

Check your understanding

Answer this question before you continue.

You have changed `greet.py` and want that file included in your next saved snapshot. What is the role of `git add greet.py`?
Single Choice

Focus: Distinguish selecting files for the next save point from recording that save point in history.

Read Your History Before You Need It

Most beginners only learn to read history when something is already broken. That's the worst time to learn. Make it a habit now, while nothing is on fire.

Three commands cover almost everything.

git status answers "what changed since my last save point?" You'll run this more than any other command.

git diff shows the actual line-by-line change before you commit it. This is how you catch a stray edit you forgot about.

git log --oneline gives you a compact list of save points:

git log --oneline

Expected output:

3f9a1c2 (HEAD -> main) Add greet script and ignore rules

Newest commit first, each with a short ID and your message. As your project grows, this list becomes a readable timeline of your work.

Here's the judgment call I'd make if I were you: read the diff before you commit, not after. It's cheaper to catch a mistake at the staging step than to untangle it from history. Run git diff after editing a file and before git add. You'll see exactly what you're about to save.

Knowledge check

Check your understanding

Answer this question before you continue.

You want a compact timeline of saved commits, newest first, with their short IDs and messages. Which command from the tutorial should you run?
Single Choice

Focus: Use Git's compact history command to identify prior save points and their messages.

Restore an Earlier Working State

This is the payoff. You edited a working script, it now raises an error or prints the wrong thing, and you want the last good version back.

There are three paths, and which one you reach for depends on whether the bad change is already committed.

Path 1: Discard uncommitted changes

If you broke the file but haven't committed yet, this is the safest and simplest move:

git restore greet.py

This throws away your uncommitted edits and returns the file to its last committed state. Run git status afterward and you'll see a clean tree:

On branch main
nothing to commit, working tree clean

Use this when you know the change was wrong and you don't want to keep it. Be aware: git restore discards work permanently. If there's any chance you want the edit, commit it first, then revert.

Path 2: Pull a file from an older commit

If the last commit is also wrong, you need a specific file from an earlier save point. Get the commit ID from git log --oneline, then:

git checkout 3f9a1c2 -- greet.py

This copies greet.py from that commit into your working files. You can then review it and commit the restored version.

Path 3: Undo a commit that's already in history

This path only makes sense once you have a bad commit to undo. So let's create one. Edit greet.py so it prints the wrong thing:

def greet(name):
    return f"Goodbye, {name}!"

print(greet("world"))

Run it:

python greet.py

Expected output:

Goodbye, world!

That's wrong. Commit the mistake anyway, so you have something to undo:

git add greet.py
git commit -m "Change greeting to Goodbye"

Now git log --oneline shows two save points:

b7e4d91 (HEAD -> main) Change greeting to Goodbye
3f9a1c2 Add greet script and ignore rules

You want history to stay honest — meaning you don't rewrite the past, you add a correction — so use git revert on the bad commit:

git revert b7e4d91

This creates a new commit that undoes the earlier one. Run git log --oneline and you'll see all three:

c2f8a04 (HEAD -> main) Revert "Change greeting to Goodbye"
b7e4d91 Change greeting to Goodbye
3f9a1c2 Add greet script and ignore rules

Run the script again and the original output returns:

python greet.py

Expected output:

Hello, world!

The history now tells the truth: you made a change, then you undid it. That's often exactly what you want.

Knowledge check

Check your understanding

Answer this question before you continue.

A bad change is already committed, and you want to undo it while keeping the history honest. Which statement is accurate?
Misconception Check

Focus: Choose a history-preserving way to undo a change that has already been committed.

Which command should you reach for?

SituationCommandWhat it does
Broke a file, haven't committedgit restore <file>Discards uncommitted edits
Last commit is wrong, want an older filegit checkout <id> -- <file>Copies a file from an older commit
Bad change is committed, want history intactgit revert <id>Adds a new commit that undoes the old one

A good beginner instinct: start with git restore. It's the least destructive and covers the most common case.

Common Beginner Mistakes and How to Recover

Almost every Git mistake is recoverable — as long as you haven't deleted .git. Here are the ones I see most.

Committing everything blindly with git add . This sweeps in virtual environments, cache folders, and secret files. Fix it with a .gitignore and a careful git status check before every commit. Read what you're about to stage.

Panicking at a scary Git message and deleting .git. This destroys the history you were trying to protect. Git's messages are verbose, not fatal. Read the last line — it usually tells you the exact command to fix things.

Committing too rarely. If one save point contains a week of unrelated changes, restoring becomes guesswork. Commit whenever the script runs correctly.

Writing commit messages that say nothing. "Update" and "fix" make git log useless exactly when you need it most.

Common mistake: Treating a Git error as a dead end. Most errors are recoverable. The one action that isn't is deleting .git.

Practice: Break It on Purpose, Then Restore It

Reading about restore is not the same as doing it. Run the full loop once and it becomes muscle memory.

  1. Commit your current working script so you have a known-good save point.
  2. Deliberately introduce a bug that changes the output — flip a comparison, change a string, whatever's easy.
  3. Run the script and confirm the wrong output.
  4. Run git diff to see exactly what you changed.
  5. Run git restore <your-file> and run the script again. The original output should return.

Then extend it: make a second commit, use git revert <id> to undo it, and watch the new revert commit appear in git log --oneline.

What to memorize now: status, add, commit, log, diff, restore. These six cover 90% of daily work.

What to look up later: revert, checkout, branching, and remotes. You'll know when you need them.

Where This Leads

The point of version control was never ceremony. It's the freedom to experiment because you can always get back to a working state. That freedom changes how you write code — you try the risky edit, because the cost of being wrong just dropped to one command.

One decision rule to carry forward: commit whenever the script runs correctly, and read the diff before you commit. That's the whole habit.

Your next step is to push this project to a hosted remote so your history survives a dead laptop. That's also the same history you'll point to when you document and present the project to someone else.

Knowledge check

Final check

Finish the article by checking the ideas you just learned.

You changed a working script, have not committed the change, and have decided you do not want to keep it. Which action matches the tutorial's recovery workflow?
Question 1 of 2Debugging

Focus: Restore a file's last committed contents when its uncommitted edits are known to be unwanted.

You have edited a script and want to catch an accidental change before saving it in a commit. Which habit does the tutorial recommend?
Question 2 of 2Single Choice

Focus: Inspect the actual edits before staging and committing them.

References

  1. Version Control with giteducation.molssi.org
Practical resource

Want a more structured Python path?

Use the Python Starter Pack to turn scattered tutorials into a focused practice path.

View the bundle
Coming soon

Python for Artificial Intelligence Starter Pack

Build a Python foundation you can actually use. The Python for AI Starter Pack brings together a guided path through setup, core programming concepts, data structures, files, JSON, APIs, debugging, and practical projects—so you can move quickly from running your first program to understanding and building useful software.

$9
PDF BundlePythonAIBeginner
  • 264-page illustrated PDF
  • 12 guided Python chapters
  • Visual concept diagrams
  • Self-assessment quizzes
  • Bonus deep-dive sections
  • Files, JSON, APIs, debugging & projects
  • Foundation for data, automation & AI

Coming soon

Free Python bundle

Get the LearnPyFast Python for Artificial Intelligence Starter Bundle

Build a Python foundation you can actually use. The Python for Artificial Intelligence Starter Pack brings together a guided path through setup, core programming concepts, data structures, files, JSON, APIs, debugging, and practical projects—so you can move quickly from running your first program to understanding and building useful software.

You’ll receive the bundle by email. You can unsubscribe anytime.

No spam. You can unsubscribe anytime. See our Privacy policy.

Related sites

Continue beyond Python

Explore related Worldmonger sites when you want to move from Python basics into JavaScript or LLM application building.

JavaScript tutorialstutorial

LearnJSFast

Beginner-friendly JavaScript tutorials for practical web development and self-taught developers.

JavaScriptFrontendWeb development
Visit LearnJSFast
LLM tutorialstutorial

LearnLLMFast

Practical LLM tutorials for builders who want to understand prompting, workflows, agents, and AI applications.

LLMAIBuilders
Visit LearnLLMFast

Keep learning

Related tutorials

Continue with nearby Python topics and beginner-friendly explanations.

Captivating view of a stormy sea under dark clouds, showcasing powerful ocean waves.
beginner
6 min read

Beginner Python Project Ideas

You finished the syntax tutorials. You know what a loop does, you can write a function, and you understand what a dictionary is for. Then you close the…

Read tutorial