Learning git basics is the moment your code stops living in scary folders like final_v2_REAL_fixed.py and starts living in a proper time machine you can step through, compare, and rewind. Git is version control: it records snapshots of your project so you can see what changed, undo a mistake, and work with confidence instead of fear.
“A language that doesn’t affect the way you think about programming is not worth knowing.”
Alan Perlis, Epigrams on Programming
Last Updated: July 2026 | Tested on: Git 2.45 | Difficulty: Beginner | Reading Time: 16 minutes
Every job listing for a Python role, from a small startup to Amazon or Meta, quietly assumes you know Git. It sits in the minimum qualifications next to “writes Python,” not because interviewers love trivia, but because no team ships code without it. The good news: git basics come down to about six daily commands, and you can learn them in one sitting. We will practice on the small bank account project from the OOP tutorial, run every command for real, and read the actual output together.
Table of Contents
Why Version Control Exists
Picture a kitchen where every cook keeps their own copy of the recipe on loose paper. One scribbles a change, another photocopies an old version, and soon nobody knows which sheet is correct. That is a codebase without version control. You have seen the folder: report.py, report_final.py, report_final_v2.py, report_final_REAL_use_this.py. It feels like a backup, but it is really just confusion you are paying for later.
Git replaces that whole mess with one folder that remembers its own past. Instead of copies, you take snapshots called commits. Each commit says “here is exactly what every file looked like at this moment, and here is a note about why.” You can jump between snapshots, compare any two, and recover a version from last Tuesday without keeping ten files around. That single idea, a project that carries its own history, is why every professional team on the planet uses it. The git basics that follow are simply the commands that move you around that history.
Git Basics: Your First Commit with init, status, add, commit
Let us turn the bank project into a Git repository. We have one file, bank.py, sitting in a folder. Four commands take it from “just some files” to “tracked history”: git init creates the repository, git status shows what Git currently sees, git add stages the files you want to save, and git commit records the snapshot. Think of add as loading items onto a tray and commit as taking the photo of that tray.
📄 Terminal: create the repo and check status
git init -b main git status
▶ Output
Initialized empty Git repository in .../bankdemo/.git/ On branch main No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) bank.py nothing added to commit but untracked files present (use "git add" to track)
What happened here: git init -b main made a hidden .git folder, which is the entire repository, history and all. The -b main just names the starting branch main. Then git status, the command you will run more than any other, reports the truth: we are on branch main, there are no commits yet, and bank.py is “untracked,” meaning Git sees it but is not yet recording it. Git even tells you the next move, git add. It is a chatty, helpful tool once you start reading what it says.
📄 Terminal: stage and commit the first snapshot
git add bank.py git commit -m "Add Account class with deposit and withdraw"
▶ Output
[main (root-commit) 6d10ca8] Add Account class with deposit and withdraw 1 file changed, 23 insertions(+) create mode 100644 bank.py
What happened here: the snapshot is saved. The short code 6d10ca8 is the commit’s fingerprint, a unique id you can use to refer back to this exact state forever. The -m flag attaches the message inline so Git does not pop open an editor. It even calls this the “root-commit” because it is the very first one, the trunk that every future commit grows from. Run git status again now and it says “nothing to commit, working tree clean,” which is the calm state you want to leave your project in at the end of a session.
Reviewing Your Work: log and diff
A history you cannot read is useless, so Git gives you two windows into it. git log is the table of contents, the list of every commit with who made it and when. git diff is the highlighter, showing the exact lines that changed. Let us make a real change first: we add a history list to the account so every deposit is recorded, then look at the diff before we commit.
📄 Terminal: git diff after editing bank.py
diff --git a/bank.py b/bank.py
index 7c4801d..134ab17 100644
--- a/bank.py
+++ b/bank.py
@@ -5,9 +5,11 @@ class Account:
def __init__(self, owner, balance=0):
self.owner = owner
self.balance = balance
+ self.history = []
def deposit(self, amount):
self.balance += amount
+ self.history.append(("deposit", amount))
return self.balance
def withdraw(self, amount):
What happened here: the two lines starting with + are what we added, and Git shows a few unchanged lines around them for context so you can see where the change lands. If we had deleted anything, those lines would start with -. The @@ -5,9 +5,11 @@ marker is a location hint, roughly “this chunk starts near line 5.” You do not need to memorize the header format. The habit that matters is running git diff before every commit, so you always know exactly what you are about to save. Now we stage and commit it, then read the log.
📄 Terminal: commit, then git log –oneline
git add bank.py git commit -m "Record deposits in a transaction history" git log --oneline
▶ Output
8ad4f1a Record deposits in a transaction history 6d10ca8 Add Account class with deposit and withdraw
What happened here: git log --oneline compresses each commit to its short id and message, newest at the top. Plain git log shows the full author, email, and date for each one, which is handy when you are hunting for who changed what. This tiny two line history is already doing real work: you can see the project grew from an Account class into one that tracks transactions, and you could return to either state at any time.
The Three Trees Mental Model
Here is the one picture that makes git basics click. Git juggles three versions of your project, often called the three trees. The working directory is the real files you edit. The staging area (also called the index) is a holding tray for changes you have chosen for the next commit. The repository history is the permanent record of committed snapshots inside .git. A change flows left to right: you edit, you git add to stage, you git commit to record. The undo commands simply move a change back to the left.
The reason Git separates staging from committing is control. It lets you edit five files but commit only the two that belong together, keeping each snapshot focused. The diagram below traces one edit to bank.py through all three trees, with the dotted arrows showing how the restore commands walk it back.
Tip: click the diagram to open it full screen, then use the zoom and pan controls for a closer look.
Undo Safely: restore, revert, and reset
The whole point of version control is that mistakes stop being scary. But Git has three different “undo” commands, and picking the wrong one is how beginners lose work. Here is the plain English version of when each is safe.
- git restore <file> throws away uncommitted edits in your working directory, snapping the file back to the last commit. Safe, because it only touches changes you never saved.
- git revert <commit> undoes an already committed change by adding a new commit that reverses it. Safe for shared history, because it never rewrites the past, it just appends an “undo” entry.
- git reset moves the branch pointer backward, erasing commits from your current branch. Powerful and useful on your own local, unpushed work, but dangerous on anything you have shared, because it rewrites history other people may already have.
The rule of thumb: restore for edits you have not committed, revert to undo a commit that others might have, and reset only on private local commits when you are sure. Let us see revert in action. Say we committed a bad “quick fix” that disabled the balance check in withdraw, and now we want it gone without pretending it never happened.
📄 Terminal: undo a committed mistake with git revert
git revert --no-edit a0f9a02 git log --oneline
▶ Output
[main 06f3d01] Revert "Temporarily disable balance check" 1 file changed, 2 insertions(+), 2 deletions(-) 06f3d01 Revert "Temporarily disable balance check" a0f9a02 Temporarily disable balance check 8ad4f1a Record deposits in a transaction history 6d10ca8 Add Account class with deposit and withdraw
What happened here: git revert did not delete the bad commit a0f9a02. It created a brand new commit 06f3d01 that undoes exactly what the bad one did, and the --no-edit flag accepted Git’s default message. Both commits stay in the log, which is honest and safe: anyone reading the history sees the mistake and its correction. That is why revert is the command you reach for on shared branches, while reset, which would have quietly rewritten the history, stays reserved for local work only.
Ignoring the Junk with .gitignore
Not everything in your project folder belongs in version control. Python creates a __pycache__ folder of compiled files, your virtual environment lives in .venv, and a .env file often holds secrets like Application Programming Interface (API) keys. None of that should be committed: it is machine specific, huge, or private. A .gitignore file is a simple list of patterns Git should pretend it cannot see. Watch how much noise it removes from git status.
📄 .gitignore: the essentials for any Python project
# Virtual environments .venv/ venv/ # Python cache and compiled files __pycache__/ *.pyc # Secrets and local config .env
▶ git status –short before and after adding .gitignore
# before .gitignore ?? .env ?? .venv/ ?? __pycache__/ # after .gitignore ?? .gitignore
What happened here: the ?? marks in git status --short mean “untracked.” Before the ignore file, Git nagged about the cache folder, the virtual environment, and the secret .env. After we saved the .gitignore, those three vanished from the report, and the only untracked item left is .gitignore itself, which you do commit so your teammates inherit the same rules. Lines starting with # are comments, a trailing / means a folder, and *.pyc uses a wildcard to match every compiled file.
Commit Hygiene and What Never to Commit
Commit hygiene is the part of git basics that separates a readable history from noise. A commit history is a message to your future self and your teammates, so it pays to keep it tidy. Two habits do most of the work: write clear messages, and keep each commit small and focused on one change. A good message finishes the sentence “If applied, this commit will…” So Add balance check to withdraw reads well, while stuff or fixed it tells nobody anything six months from now.
- Keep the first line short (around 50 characters) and in the imperative mood, like a command: “Fix”, “Add”, “Remove”.
- Make one commit do one thing. A commit that “fixes a bug and renames three files and adds a feature” is impossible to revert cleanly.
- Commit often. Small, frequent snapshots give you more points to safely rewind to.
The most important rule, though, is about what you must never commit: secrets. Passwords, API keys, and tokens do not belong in a repository, ever. Once a secret is committed and pushed, treat it as leaked, because deleting it later does not remove it from history. This is exactly why .env goes in your .gitignore from the very first commit. If a key does slip in, the safe move is to rotate it (generate a new one and revoke the old) rather than assume nobody saw it.
Drill: Break the Bank, Then Recover
Reading about safety nets is one thing, falling into one is another. So let us deliberately break the bank project and rescue it with the tools you just met. Imagine a user named Anvi is editing bank.py late at night and fat-fingers a variable name in deposit, changing amount to amnt. She runs the program and it explodes.
📄 Terminal: run the accidentally broken program
File "bank.py", line 11, in deposit
self.balance += amnt
^^^^
NameError: name 'amnt' is not defined. Did you mean: 'amount'?
Instead of squinting at the file trying to spot what she changed, Anvi asks Git. git diff shows the exact damage, and git restore undoes it in one move.
📄 Terminal: diagnose with diff, recover with restore
git diff git restore bank.py python bank.py
▶ Output
diff --git a/bank.py b/bank.py
@@ -8,7 +8,7 @@ class Account:
self.history = []
def deposit(self, amount):
- self.balance += amount
+ self.balance += amnt
self.history.append(("deposit", amount))
return self.balance
Aditi 150
What happened here: git diff put the mistake right under her nose, showing that the good line self.balance += amount had been replaced by the broken self.balance += amnt. Because the change was never committed, git restore bank.py simply pulled the last committed version back into the file, and running the program printed Aditi 150 again, healthy. No panic, no lost work, no bank_backup_final.py. That is the everyday payoff of version control: an undo button for your entire project.
Will Git Still Be Here in Ten Years?
It is fair to ask whether learning a tool is worth it if it might vanish. Git is about as safe a bet as exists in software. It was created in 2005 and has been the dominant version control system for well over a decade, running under nearly every professional codebase, at the time of writing. The git basics in this post, init, add, commit, diff, log, restore, have been stable for years and are not going anywhere.
One thing to keep straight: Git is the tool, and a host is where you put your repository online to share it. GitHub is the most popular host, but GitLab and Bitbucket do the same job, and you can even self host. They compete on features and can rise or fall, but they all speak the same Git underneath, which is why we learned the command line first. Master the Command-Line Interface (CLI) and no amount of website redesign or button-moving can rot your skills. Whatever host wins, git commit still means git commit.
Common Mistakes
Mistake 1: Committing secrets or the virtual environment
Beginners often run git add . on day one and accidentally commit their .venv folder (thousands of files) or a .env with real keys. Add a .gitignore before your first commit. If a secret sneaks in, rotate the key immediately, because it stays in the history even after you delete the file.
Mistake 2: Reaching for git reset –hard to undo everything
git reset --hard throws away uncommitted work with no confirmation and no easy recovery. It has its uses, but a beginner rarely needs it. For discarding one file use git restore, and to undo a commit that has been shared use git revert, which is reversible because it only adds history.
Mistake 3: Giant commits with vague messages
Saving a week of work in one commit called update makes the history useless. You cannot revert one piece without the others, and nobody can tell what changed. Commit small and often, and write a message that says what the change does, not just that you changed something.
Best Practices
- DO run
git statusandgit diffbefore every commit, so you always know exactly what you are saving - DO add a
.gitignorefor.venv,__pycache__, and.envas your very first step - DO write commit messages in the imperative mood: “Add”, “Fix”, “Remove”
- DO commit small, focused changes often rather than one huge dump
- DON’T commit secrets, and if you do, rotate them rather than just deleting the file
- DON’T use
git reset --hardto fix a mistake until you understand exactly what it discards
Conclusion
You just took a plain folder and gave it a memory. With init, add, and commit you saved snapshots, with log and diff you read the story of what changed, and with restore and revert you learned to undo mistakes without fear. That is the daily rhythm of every working developer, and it is most of the git basics you will ever need. The three trees picture, working directory to staging to history, ties it all together: everything you do is just moving a change between those three places.
From here, put a real project under Git today, even a tiny one, and commit as you go until the habit sticks. Next in the series we build on this with branching and pull requests, which is how teams work on the same code without stepping on each other. And if you want to see everything this series covers, from first steps to AI and machine learning, browse the Python + AI/ML tutorial series home.
Frequently Asked Questions
What is the difference between Git and GitHub?
Git is the version control tool that runs on your machine and records your commits. GitHub is a website that hosts Git repositories online so you can share and back them up. GitLab and Bitbucket are alternative hosts that do the same job. You can use Git with no host at all, and the git basics work the same everywhere.
When should I use git revert instead of git reset?
Use git revert to undo a commit that has been shared or pushed, because it adds a new commit that reverses the change and never rewrites history. Use git reset only on local commits you have not shared yet, since it rewrites history and can confuse teammates who already have those commits.
What should a Python .gitignore contain?
At minimum: .venv/ and venv/ for virtual environments, __pycache__/ and *.pyc for compiled files, and .env for secrets. Add editor folders like .idea/ or .vscode/ if you use those. GitHub publishes a ready made Python .gitignore template you can copy.
How do I undo changes to a file I have not committed yet?
Run git restore followed by the filename. It replaces the file with the version from your last commit, discarding your uncommitted edits. If you already staged the change with git add, run git restore –staged first to unstage it.
What happens if I accidentally commit a password?
Assume it is compromised the moment it is pushed, because it stays in the commit history even if you delete the file later. Rotate the secret immediately by generating a new key and revoking the old one, then add the file to .gitignore so it does not happen again.
Interview Questions on Git
Try each one aloud first. The phrasing you produce under mild pressure is what interviews measure.
Q: What are the three areas (or “trees”) Git tracks, and how does a change move between them?
The working directory holds your actual files, the staging area (index) holds changes selected for the next commit, and the repository holds committed history. A change moves right with git add (working to staging) and git commit (staging to history), and the restore commands move it back left. Separating staging from committing lets you commit only part of your changes at a time.
Q: Explain the difference between git revert and git reset.
git revert creates a new commit that undoes a previous one, so history is preserved and it is safe on shared branches. git reset moves the branch pointer backward, effectively removing commits from the branch and rewriting history, which is fine for local unshared work but dangerous once others have those commits. Revert is additive and safe, reset is destructive to history.
Q: Why should secrets never be committed, and what do you do if one is?
A commit is permanent in history, so a secret stays retrievable even after you delete the file in a later commit. Anyone with repository access, or anyone who cloned it, can recover it. The correct response is to rotate the secret immediately, generating a new key and revoking the old one, then prevent recurrence by keeping secrets in a .env file listed in .gitignore.
Q: A new teammate says they do not need to learn git because the company uses GitHub. Untangle that for them.
Git is the distributed version control tool that runs locally and manages commits, branches, and history. GitHub, GitLab, and Bitbucket are hosting services that store Git repositories on a server for sharing, backup, and collaboration features like pull requests. Git works entirely offline without any of them, and switching hosts does not change the underlying Git commands.
Q: How would you find out exactly what changed in a file before committing?
Run git diff to see unstaged changes line by line, with + for additions and - for removals. After staging, use git diff --staged to review what is about to be committed. This review habit catches accidental edits, leftover debug prints, and secrets before they become part of history.
Further reading: for the full reference, see Official Git documentation.
Related Posts
Previous: Python OOP Project: Build a Bank Account Manager (Capstone)
Next: Git Branching and Pull Requests: The GitHub Team Workflow
Series Home: Python + AI/ML Tutorial Series

No comment