Git for beginners: branch, commit, and PR on one real task

Git ships with well over a hundred commands, and a normal working week uses about six of them. You don't find out which six from a git course. You find out by having a change to make and needing somewhere safe to make it. So this post has one small bug, one fix, and every command the fix needs — with the output your terminal will print back.

Git for beginners: learn it on a task, not in a course

Git for beginners comes down to one loop: create a branch, change the code, commit the change, push the branch, open a pull request. A course teaches those five steps as vocabulary. A task teaches them as answers to questions you already have — "where do I put this so I don't break what works?" is a branch, "how do I save this state so I can get back to it?" is a commit. Git records changes, so without a change you care about there is nothing to practise on.

The task

Say you followed the first-week plan and pushed the result to GitHub: a total_of function that sums a list of prices, plus two tests. Then someone passes it a list with "free" in it:

    total = total + price
            ~~~~~~^~~~~~~
TypeError: unsupported operand type(s) for +: 'float' and 'str'

The decision: entries that aren't numbers get skipped. Three lines of Python. The Python is the easy part today.

Look before you touch anything

$ git status
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

git status tells you which branch you're on and what has changed since the last commit. It modifies nothing, so run it whenever you're unsure — experienced engineers run it constantly, and not out of nervousness.

Make a branch

$ git switch -c fix/skip-non-numeric
Switched to a new branch 'fix/skip-non-numeric'

git switch -c creates a new branch and moves you onto it. A branch is a movable label on a commit, nothing heavier: from now on your new commits land under this label, and main stays exactly as it was. If the fix turns out to be a bad idea, you delete the label and nothing happened.

Older tutorials write git checkout -b here. It does the same thing; switch arrived in Git 2.23 because checkout had been given too many unrelated jobs.

Now make the fix, add a third test that pins the new behaviour, and ask git what you did:

$ git diff prices.py
diff --git a/prices.py b/prices.py
index b466e71..e362d7b 100644
--- a/prices.py
+++ b/prices.py
@@ -1,5 +1,7 @@
 def total_of(prices):
     total = 0
     for price in prices:
+        if not isinstance(price, (int, float)):
+            continue
         total = total + price
     return total

git diff shows the lines that differ between your files and the last commit — + for added, - for removed. Read it before every commit. The cheapest code review you'll ever get is your own, thirty seconds before anyone else sees the change.

$ pytest -q
...                                                                      [100%]
3 passed in 0.01s

Stage, then commit

$ git add prices.py test_prices.py
$ git commit -m "Skip non-numeric entries in total_of"
[fix/skip-non-numeric 1ead82b] Skip non-numeric entries in total_of
 2 files changed, 6 insertions(+)

git add puts the current state of those two files into the staging area — the list of what goes into the next commit. git commit records that staged snapshot permanently, with your message and an ID (1ead82b here) you can return to later.

The two-step dance confuses everyone at first, and it exists for a reason: your working directory might hold three unrelated edits, and a commit should hold one. Staging is how you pick. That's also why naming the files beats git add . — the dot stages everything, including the debug print you forgot about.

The message is written in the imperative and says what the change does. "Skip non-numeric entries" will still make sense in eight months. "fix" and "changes" won't, and you will be the person reading them.

Push, then open the pull request

$ git push -u origin fix/skip-non-numeric
remote:
remote: Create a pull request for 'fix/skip-non-numeric' on GitHub by visiting:
remote:      https://github.com/you/prices/pull/new/fix/skip-non-numeric
remote:
 * [new branch]      fix/skip-non-numeric -> fix/skip-non-numeric
branch 'fix/skip-non-numeric' set up to track 'origin/fix/skip-non-numeric'.

git push uploads your branch's commits to the remote repository named origin. The -u remembers the pairing between your local branch and the remote one, so next time a bare git push is enough.

Notice who printed the link: the lines starting with remote: come from GitHub, not from git. A pull request is not a git command. Git has no idea what a PR is — which is a strange thing to learn about the most famous word in its vocabulary. A PR is a feature of the hosting platform (GitLab calls the same thing a merge request): a page that says "I'd like this branch merged into main," with the diff attached and room to argue about it. Open the link, write two sentences on what changed and why, and submit.

That page is where code review happens. Once the PR is merged, you come home:

$ git switch main
$ git pull
$ git branch -d fix/skip-non-numeric

Switch back, download the merged result, delete the label you no longer need. That's the loop. The next task is the same loop with a different branch name.

What the course would have taught you instead

Rebase, cherry-pick, reflog, and detached HEAD — in week one, to someone with nothing to rebase. All four are real and you'll need them. You'll need them on the day a task demands them, and on that day the explanation will stick, because it will be attached to a problem you have.

What actually goes wrong in your first month is smaller. You'll start editing on main by accident — run git switch -c anyway, because uncommitted changes travel with you to the new branch. You'll hit your first merge conflict, and it will look worse than it is. Neither needs a syllabus. Both need a repository you care about enough to fix.

This is the same argument as katas versus the engineering process, one level down: git is part of the process, and the process is learned by running it, badly at first, on real changes.

Where ProdQuest fits, without overselling it

Today, real git commands run in the browser terminal from minute one, next to the editor and Python — no install. What is not there yet is the full cycle: a project repository, pull requests, the agent reviewing your PR the way a senior engineer would, CI/CD pipelines. That is process mode, a later phase on the public roadmap, and we'll move to it once the data from the earlier phases says they work.

You don't have to wait for it. Everything above runs on any machine with git and a free GitHub account. Pick the smallest bug you have and make the branch first. If you'd rather practise where the terminal is already open and the tasks are picked for your level, try the first task as a guest.