Git basics for a Python project
Git is version control: a record of every change, and the ability to go back. This is enough Git to work on your own projects confidently and to not be lost on your first day somewhere.
It is not a complete Git course. It is the fifteen commands that cover almost everything, plus the parts specific to Python projects.
Why
Three reasons, in order of how often they matter.
You can go back. Yesterday it worked, today it does not, and you cannot remember what you changed. With Git you can see exactly, or return to yesterday.
You stop keeping main_final_v2_working.py. That folder full of near-copies
is version control done by hand, badly.
You can work with other people, and with your future self, who will want to know why a line exists.
Starting
git init
Creates a .git folder in your project. That is the whole repository.
Set who you are, once per machine:
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
The three places
Git has three areas, and understanding them makes every command obvious:
Working directory — your files as they are now. Staging area — changes you have marked for the next commit. Repository — committed history.
working directory → staging area → repository
git add git commit
The staging area exists so you can commit some of your changes. Fixed a bug and half-wrote a feature? Stage and commit only the bug fix.
The daily commands
git status
What has changed, what is staged, which branch you are on. Run it constantly. When unsure what Git thinks, this answers it.
git add file.py one file
git add . everything changed
git diff unstaged changes
git diff --staged what you are about to commit
git diff --staged before committing is a good habit — it is the last chance to
notice a debug print or a hard-coded password.
git commit -m "Add expense validation"
Records everything staged.
git log --oneline compact history
git log -p file.py history of one file, with changes
Commit messages
You will read these when something has broken and you are trying to find when.
Poor:
update
fix
asdf
changes
Better:
Add validation to reject negative expense amounts
Fix GST calculation rounding to two decimal places
Remove unused imports from reporting module
The convention: imperative mood, under about 50 characters, saying what the change does. "Add", not "Added" — it completes the sentence "this commit will…".
When the why is not obvious, add a body after a blank line:
Reject expenses over 1,00,000 without approval
Finance found three mis-keyed entries last month, all with an extra
zero. The limit can be raised per user later if needed.
Six months later that paragraph is the only record of the reason.
Commit size
One logical change per commit. Not one file, not one day's work.
If your message needs "and", it is probably two commits. Small commits are easier to review, easier to understand later, and easy to revert individually.
Commit when something works, not when everything is finished.
.gitignore for Python
From module 8, and worth repeating because the consequences are real:
.venv/
__pycache__/
*.pyc
.env
*.db
.pytest_cache/
.ruff_cache/
.coverage
.DS_Store
.env is the important one. Committing an API key to a public repository is
among the easier ways to leak a credential — automated scanners find them within
minutes, and rotating a leaked key is the only real fix.
Committing a secret and then deleting it does not help. It stays in the history. If it happens, assume the key is compromised and rotate it.
Create .gitignore before your first commit, not after.
Undoing things
The reason Git is worth learning, and the commands people look up every time:
git restore file.py discard unstaged changes to a file
git restore --staged file.py unstage, keeping the changes
git commit --amend redo the last commit message or contents
git revert <commit> a new commit undoing an old one
git revert is the safe undo. It adds a commit rather than rewriting history,
so it works on anything already shared.
You will see git reset --hard suggested online. It discards work
permanently. Be certain before running it, and never on commits you have
pushed.
Branches
A branch is a separate line of work:
git switch -c feature/add-csv-export create and switch
git switch main go back
git merge feature/add-csv-export bring the work in
git branch -d feature/add-csv-export delete when merged
The value: main keeps working while you experiment. If the experiment fails,
delete the branch and nothing was lost.
Even alone, branch for anything more than a small change. It is a habit that transfers directly to working in a team, where it is not optional.
Remotes
git remote add origin https://github.com/you/project.git
git push -u origin main
git pull
git clone https://github.com/you/project.git
push sends commits up, pull brings them down, clone copies a repository.
-u on the first push remembers the destination.
Pushing is also a backup. A laptop that dies takes everything not pushed with it.
Merge conflicts
When two changes touch the same lines:
<<<<<<< HEAD
tax_rate = 0.18
=======
tax_rate = 0.20
>>>>>>> feature/update-gst
Not an error — Git is telling you it cannot decide. Edit the file to what it
should be, delete the three marker lines, then git add and commit.
The markers are plain text, so a file with conflict markers left in is a file
that will not run. git status lists conflicted files.
A realistic session
git switch -c feature/expense-validation
# ... write code and tests ...
ruff format . && ruff check --fix . && pytest
git status
git diff
git add src/expense_tracker/models.py tests/test_models.py
git commit -m "Reject negative expense amounts"
git push -u origin feature/expense-validation
Format, lint and test before committing. Commit what works.
Check your work
The three areas. Working directory → staging area → repository, moved by
git add and git commit. The staging area exists so you can commit some of
your changes — the bug fix without the half-written feature.
Better commit messages.
update → Add validation to reject negative expense amounts
fix → Fix GST rounding to two decimal places
changes → Remove unused imports from the reporting module
Imperative mood, under about 50 characters, saying what the change does — "Add", not "Added", so it completes "this commit will…".
Committing two changes separately means using git add on individual files
rather than git add .. Each commit is then revertable on its own.
git restore discards unstaged changes to a file. git restore --staged
unstages while keeping the changes. Neither is reset --hard, which discards
work permanently.
git revert adds a new commit undoing an old one, so both appear in the
log. That is the safe undo, and it works on anything already pushed.
The conflict.
<<<<<<< HEAD
tax_rate = 0.18
=======
tax_rate = 0.20
>>>>>>> feature/update-gst
Not an error — Git is saying it cannot decide. Edit the file to what it should
be, delete the three marker lines, git add, commit. A file with markers left
in will not run, which is at least obvious.
On GitHub, check .venv and .env are absent. If a secret was committed,
deleting it later does not help — it stays in history. Assume the key is
compromised and rotate it.
Practice
- Create a folder,
git init, and write a.gitignorebefore anything else. - Add a Python file,
git status,git add,git commit. Readgit log. - Change the file. Run
git diff, stage it, thengit diff --staged. - Make two unrelated changes and commit them separately using
git addon individual files. - Write three bad commit messages and rewrite them properly.
- Change a file and discard it with
git restore. - Stage something, then unstage it with
git restore --staged. - Make a commit, then
git revertit. Look at the log and note that both commits are present. - Create a branch, commit on it, switch back to
main, and confirm the change is absent. Then merge. - Create a conflict on purpose: change the same line on two branches and merge. Resolve it by hand.
- Put a project on GitHub and push it. Confirm
.venvand.envare absent from the web view.
That is module ten. Your code can be tested automatically, checked for the mistakes this course has warned about, and kept in a history you can return to.
Next module: the capstone — building a complete, tested, version-controlled program from a brief.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship