3.10. Branches and Commits
Table of Contents
Why Branches and Commits Matter
As a backend developer, you almost never work alone or in a single straight line. You experiment, fix bugs, review code, and deploy features. Git branches and commits are the core tools that let you do all that without turning your codebase into chaos.
In this chapter you will:
- Understand what commits are and how they create project history.
- Learn how branches work and why they are so powerful.
- Practice the most common commands for everyday backend development.
- See realistic workflows you will actually use on real projects.
You should already know what Git is and have a repository created, because those basics belong to earlier chapters.
Commits: Saving Checkpoints in Your Project
A commit is a snapshot of your project at a specific point in time. Think of it as a "save point" in a game. Every time you commit, Git stores:
- Which files changed
- What changed inside those files
- Who made the change
- When the change was made
- A message describing the change
All commits together form the history of your project.
Basic Commit Workflow
Typical steps when working with commits:
- Change some files.
- Stage the changes.
- Commit them with a message.
Step 1: Check Status
git statusExample output:
On branch main
Changes not staged for commit:
modified: app.py
Untracked files:
tests/test_app.pyThis tells you:
- Which branch you are on
- Which files are modified
- Which files are new (untracked)
Step 2: Stage Changes
You stage what you want to include in the next commit.
# Stage a single file
git add app.py
# Stage multiple specific files
git add app.py tests/test_app.py
# Stage everything that changed
git add .Staging lets you prepare a commit carefully. For example, you can stage only part of your work and commit it with a clear message.
Step 3: Create a Commit
git commit -m "Add basic request handler for /health endpoint"
The -m flag provides a commit message.
Good commit messages are:
- Short but clear
- In present tense
- Describe what the change does, not how you felt
Examples of good messages:
"Fix 500 error when user ID is missing""Add pagination to GET /tasks endpoint""Document environment variables in README"
Important rule
Always write meaningful commit messages. Future you, your teammates, and your reviewers will rely on them to understand why a change exists.
Viewing Commit History
See the history of commits:
git logTypical output:
commit 4d2b7a8c1234567890abcdef1234567890abcdef (HEAD -> main)
Author: Alice <alice@example.com>
Date: Tue Aug 27 14:03:52 2026 +0000
Add logging to failed login attempts
commit f1e2d3c4b5a697887766554433221100ffeeddcc
Author: Alice <alice@example.com>
Date: Tue Aug 27 13:45:10 2026 +0000
Implement /login endpointUseful variations:
# More compact, one line per commit
git log --oneline
# Show a simple graph with branches
git log --oneline --graph --allExample with graph:
* 4d2b7a8 Add logging to failed login attempts
* f1e2d3c Implement /login endpoint
* a1b2c3d Initial commitInspecting a Commit’s Changes
To see what changed in the last commit:
git showTo see changes for a specific commit:
git show <commit_hash>
# Example:
git show 4d2b7a8To see just which files changed, not the full diff:
git show --name-only 4d2b7a8This is very useful during code review or debugging to understand where a bug might have been introduced.
Branches: Parallel Lines of Development
A branch is a named line of commits. The default branch in many repositories is called main or master. When you create a new branch, Git creates a new pointer to a commit, so you can add new commits there without touching other branches.
Visually:
main: A --- B --- C
\
feature: D --- EA,B,C,D,Eare commits.mainpoints toC.featurepoints toE.
Both branches share commits A, B, C, then feature has its own commits D and E.
Why branches are useful for backend work:
- Develop features separately from stable code.
- Work on bug fixes without interrupting ongoing features.
- Experiment safely. If it fails, you can delete the branch.
Creating and Switching Branches
You very rarely commit directly to main on a serious backend project. Instead, you:
- Create a branch from
main. - Work and commit in that branch.
- Push the branch.
- Open a Pull Request / Merge Request to merge back into
main.
Check Which Branch You Are On
git branchExample:
* main
feature/login
The * indicates the current branch.
Create a New Branch
git branch feature/loginNow list again:
git branch
# Output:
# * main
# feature/login
The new branch exists, but you are still on main.
Switch to a Branch
git checkout feature/login
Now feature/login is active.
You can also create AND switch in one command:
git checkout -b feature/loginThis is probably the most common way you will create branches.
Example: Starting a Feature Branch
Imagine you are adding a /tasks endpoint to a task management API.
# Start from main
git checkout main
# Make sure main is up to date
git pull
# Create and switch to a new feature branch
git checkout -b feature/tasks-endpoint
Now all new commits will belong to feature/tasks-endpoint instead of main.
Working with Branches in Practice
Making Commits on a Branch
Once you are on a branch:
# Edit files:
# app/routes.py
# tests/test_tasks.py
git status
# Shows modified files
git add app/routes.py tests/test_tasks.py
git commit -m "Add basic GET /tasks endpoint"
All these commits live in feature/tasks-endpoint. main is unaffected.
Listing All Branches
Local branches:
git branchRemote branches (branches that exist on the server, for example GitHub):
git branch -rBoth local and remote:
git branch -aExample output:
* feature/tasks-endpoint
main
remotes/origin/main
remotes/origin/feature/loginorigin/mainis themainbranch on the remote namedorigin.feature/tasks-endpointis your local feature branch.
Pushing a Branch to Remote
To share your work or create a Pull Request:
git push -u origin feature/tasks-endpointoriginis the default name of your remote server.-usetsorigin feature/tasks-endpointas the "upstream" for this local branch, so later you can just rungit pushwithout arguments.
Next time:
git push # Now this will push feature/tasks-endpointMerging: Bringing Branches Together
When your feature branch is ready and reviewed, you merge it into another branch, usually main.
Basic Merge Workflow
- Switch to the branch you want to merge into.
- Run
git merge <other_branch>.
Example: Merge a Feature into Main
# Make sure you are on main
git checkout main
# Update main from remote
git pull
# Merge the feature branch into main
git merge feature/tasks-endpointIf Git can combine the changes automatically, you get a fast-forward or simple merge. If both branches touched the same lines, you might get conflicts.
Fast-Forward vs Merge Commit
If main had no new commits since you created feature/tasks-endpoint, Git can "fast-forward" main to point at the same commit as feature/tasks-endpoint.
Visually before:
main: A --- B
\
feature: C --- DAfter fast-forward merge:
main: A --- B --- C --- D
feature: ^If both branches moved independently, Git may create a merge commit that has two parents.
You do not need to fully master the difference right now. Just know:
- Use
git mergeto bring branch changes together. - Your hosting platform (GitHub, GitLab) often does it for you when you click "Merge".
Handling Merge Conflicts (Basic Idea)
A merge conflict happens when the same part of a file was changed differently in two branches.
Example:
- On
main, you changedapp.pyline 10. - On
feature/login, you also changedapp.pyline 10 in a different way.
When you merge, Git cannot decide which change to keep.
You will see markers in the file like:
<<<<<<< HEAD
def login():
return "Login v1"
=======
def login():
return "Login v2 with security check"
>>>>>>> feature/loginRough conflict resolution steps:
- Open the file, choose or combine the correct version.
- Remove the
<<<<<<<,=======,>>>>>>>lines. - Stage the fixed file:
git add app.py- Continue the merge (if Git asks) with:
git commitGit will usually pre-fill a merge commit message.
You will get more detailed practice with conflicts in team workflows, but at this stage you should understand what conflicts are and not panic when you see them.
Comparing Branches and Commits
You often want to see what changed between two points in time.
Compare Working Directory With Last Commit
git diffShows line by line differences for changed but not yet staged files.
Compare staged changes with last commit:
git diff --cachedCompare Two Branches
git diff main..feature/tasks-endpoint
This shows changes that feature/tasks-endpoint introduces compared to main.
Useful to review your own work before pushing or opening a Pull Request.
Compare Specific Commits
git diff <commit1> <commit2>
# Example:
git diff a1b2c3d 4d2b7a8Common Everyday Commands Cheat Sheet
Here is a quick table you can refer to while working:
| Task | Command example |
|---|---|
| Show current branch | git branch |
| Create and switch to new branch | git checkout -b feature/name |
| Switch to existing branch | git checkout main |
| See changes since last commit | git diff |
| Stage a file | git add app.py |
| Stage all changes | git add . |
| Commit staged changes | git commit -m "Describe change" |
| See history compactly | git log --oneline |
| Push current branch to remote | git push -u origin feature/name |
| Merge a branch into current branch | git merge feature/name |
| Delete local branch (after merge) | git branch -d feature/name |
| Delete remote branch | git push origin --delete feature/name |
Important workflow rule
For almost all team backend projects:
- Do not commit directly to
main. - Create a feature branch, commit there, then open a Pull Request / Merge Request to merge into
main.
Example: Realistic Backend Feature Workflow
Let us walk through a full mini-scenario that mirrors real backend work.
Goal
Add a new GET /health endpoint to an existing FastAPI backend.
Step 1: Start from Latest Main
git checkout main
git pullStep 2: Create a Feature Branch
git checkout -b feature/health-endpointStep 3: Implement the Feature
Edit app/main.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/health")
def health_check():
return {"status": "ok"}Step 4: Check Status and Stage Files
git status
# modified: app/main.py
git add app/main.pyStep 5: Commit With a Clear Message
git commit -m "Add basic GET /health endpoint"Step 6: Push the Branch
git push -u origin feature/health-endpoint
Now you can create a Pull Request on GitHub or a Merge Request on GitLab to merge feature/health-endpoint into main.
Later, after it is merged and no longer needed locally:
git checkout main
git pull
git branch -d feature/health-endpointGood Practices for Branches and Commits
Some habits will make you a much more effective backend developer:
- Make small, focused commits
A commit should ideally do one logical thing: - "Add token expiration validation"
- "Add index on users.email"
- "Log failed login attempts"
- Commit often, but not every keystroke
After you complete a small unit of work that compiles and makes sense, commit. - Use feature branches per task or ticket
For example, if you have tasks in a system like Jira or GitHub Issues: feature/JIRA-123-add-redis-cachebugfix/JIRA-456-fix-500-on-login- Update your feature branch from main regularly
To avoid big merge conflicts:
git checkout main
git pull
git checkout feature/your-branch
git merge main- Never commit secrets
Do not commit.envfiles with database passwords or API keys. Use.gitignorefor such files.
By understanding branches and commits, you now have the basic tools to:
- Develop multiple features in parallel.
- Keep a clean project history.
- Collaborate safely on backend code.
In later chapters, you will see these tools used in more complex workflows such as pull requests, code reviews, and CI/CD pipelines, but the core mechanics you learned here remain the same.
Views: 8
KAHIBARO