RizTech Academy logo
RizTech Academy
Testing and Code QualityLesson 5 of 530 min

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

  1. Create a folder, git init, and write a .gitignore before anything else.
  2. Add a Python file, git status, git add, git commit. Read git log.
  3. Change the file. Run git diff, stage it, then git diff --staged.
  4. Make two unrelated changes and commit them separately using git add on individual files.
  5. Write three bad commit messages and rewrite them properly.
  6. Change a file and discard it with git restore.
  7. Stage something, then unstage it with git restore --staged.
  8. Make a commit, then git revert it. Look at the log and note that both commits are present.
  9. Create a branch, commit on it, switch back to main, and confirm the change is absent. Then merge.
  10. Create a conflict on purpose: change the same line on two branches and merge. Resolve it by hand.
  11. Put a project on GitHub and push it. Confirm .venv and .env are 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