Git, Commits, Branches, and Pull Requests

This repository is built using a branch-based workflow.

The goal is not only to create a clean Python project, but also to show how a project can grow through small, understandable changes.

Why workflow matters

Writing code is only one part of software development.

A maintainable project also needs a clear workflow for:

  • making changes,
  • reviewing changes,
  • understanding project history,
  • protecting the main branch,
  • collaborating with other people.

Even when working alone, a good workflow helps keep the project organized.

The basic workflow

This repository uses the following workflow:

update main
create a branch
make a small change
commit the change
push the branch
open a pull request
review the pull request
merge into main
delete the branch

In command form:

git switch main
git pull
git switch -c docs/example-change

# edit files

git status
git add path/to/file.md
git commit -m "docs: describe example change"
git push -u origin docs/example-change

After the pull request is merged:

git switch main
git pull
git branch -d docs/example-change

The main branch

The main branch should represent the current stable version of the project.

Do not commit directly to main.

Every meaningful change should go through a pull request.

This keeps the project history easier to understand and gives CI a chance to check changes before they are merged.

Branches

A branch is an isolated place for one change.

Good branches are small and focused.

Recommended branch name format:

<type>/<short-description>

Examples:

docs/add-pytest-guide
docs/add-git-workflow-guide
chore/configure-ruff
test/add-text-statistics-tests
ci/add-github-actions-workflow
feat/add-text-toolkit-package

The branch name should describe the purpose of the change.

Good branch names

Good branch names are clear and specific:

docs/add-ruff-guide
test/add-text-stats-tests
ci/add-github-actions-workflow
chore/initialize-uv-project

These names make it easy to understand what the branch is about.

Bad branch names

Avoid vague names:

changes
updates
work
stuff
fixes
new-version
final

These names do not explain the purpose of the branch.

They may be understandable for a few minutes, but they become confusing later.

Commits

A commit should represent one logical change.

Good commits make project history readable.

This repository uses commit messages in this format:

<type>: <short description>

Examples:

docs: add project roadmap
docs: explain pytest
chore: configure ruff
test: add text statistics tests
ci: add GitHub Actions workflow
feat: add text toolkit package

Common commit types

Useful commit types include:

docs      documentation changes
chore     configuration, setup, maintenance
feat      new functionality
test      tests
fix       bug fixes
ci        continuous integration changes
refactor  code changes that do not change behavior

The goal is not to follow a complicated standard perfectly.

The goal is to make the project history easy to scan.

Good commit messages

Good commit messages are specific:

docs: explain github actions
test: add text statistics tests
chore: initialize uv project
ci: add GitHub Actions workflow

A person reading the history can understand what happened.

Bad commit messages

Avoid unclear messages:

update
fix
changes
work
final
final fix
really final

These messages do not explain the change.

They make history harder to understand.

Small changes

Small changes are easier to review.

A pull request should usually focus on one idea.

Good examples:

Add pytest configuration and first tests.
Add Ruff configuration.
Add GitHub Actions workflow.
Explain project structure.

Poor examples:

Add tests, CI, Ruff, documentation, refactoring, and new features at once.

Large pull requests are harder to review because many decisions are mixed together.

Small pull requests make it easier to discuss one thing at a time.

Pull requests

A pull request is a proposal to merge a branch into main.

A good pull request should answer three questions:

What changed?
Why was it changed?
How was it checked?

The pull request description should be helpful to the reviewer.

It should not force the reviewer to guess the intention of the change.

Pull request title

The pull request title should use the same style as commit messages.

Examples:

docs: explain ruff
test: add text statistics tests
ci: add GitHub Actions workflow

Avoid vague titles:

Update files
New changes
Some fixes

Pull request description

A good pull request description may include:

  • summary,
  • motivation,
  • list of changes,
  • type of change,
  • local checks,
  • notes for reviewers.

Example:

## Summary

Adds tests for the text statistics utilities.

## Why

The project now contains a small example package, so it should include automated tests that verify its behavior.

## Changes

- Adds pytest as a development dependency
- Adds tests for whitespace normalization
- Adds tests for word counting
- Adds tests for character counting

## Notes

Local check:

```bash
uv run pytest
```

Review mindset

A pull request is not only a technical step.

It is also a communication tool.

When preparing a pull request, think about the person who will review it.

A good pull request should make the reviewer's job easier.

That means:

  • avoid unrelated changes,
  • keep the diff small,
  • explain non-obvious decisions,
  • include checks that were run,
  • mention follow-up work when needed.

Reviewing your own pull request

Before asking someone else to review a pull request, review it yourself.

Useful questions:

  • Is the pull request focused?
  • Is the title clear?
  • Does the description explain the change?
  • Are unrelated files included by accident?
  • Did local checks pass?
  • Is the diff easy to read?

Self-review catches many simple mistakes.

Local checks before opening a pull request

For code changes, run:

uv run ruff check .
uv run ruff format --check .
uv run rumdl check .
uv run mkdocs build --strict
uv run pytest --cov=text_toolkit --cov-report=term-missing
uv run mypy

For documentation-only changes, code checks may not be necessary.

However, it is still useful to review the rendered Markdown on GitHub after opening the pull request.

CI checks

After opening a pull request, GitHub Actions runs automated checks.

A green CI check means:

  • linting passed,
  • formatting check passed,
  • tests passed.

A green CI check does not mean the pull request is perfect.

It only means the automated checks passed.

Human review is still needed for clarity, design, documentation quality, and project direction.

Protected main branch

The main branch is protected on GitHub.

Changes must go through a pull request. Before merging:

  • the branch must include the latest changes from main,
  • the Quality checks CI check must pass,
  • review conversations must be resolved.

Force pushes and deletion of main are blocked.

These rules turn the documented workflow into a repository safeguard. They help prevent accidental direct changes without making an approval from another person mandatory for this learning project.

Merge strategy

This repository uses merge commits.

On GitHub, merge a completed pull request with:

Create a merge commit

This strategy keeps the focused commits made on the branch and adds a merge commit that marks the pull request boundary. It fits this repository because the history is intended to show how the project was built through small pull requests.

Squash merging and rebase merging are disabled. Using one strategy consistently makes the history easier to explain.

After merging

After the pull request is merged, update the local main branch:

git switch main
git pull

Then delete the local branch:

git branch -d docs/example-change

GitHub deletes the remote branch automatically after merging.

Deleting merged branches keeps the repository clean.

Common beginner mistakes

Common mistakes include:

  • committing directly to main,
  • making one huge pull request,
  • using unclear commit messages,
  • mixing unrelated changes,
  • forgetting to pull the latest main,
  • ignoring failing CI,
  • not reading the diff before opening a pull request,
  • using branches with vague names.

These mistakes are normal when learning.

The purpose of this workflow is to avoid them gradually.

Rule of thumb

A good workflow should make the project easier to understand.

Use branches to isolate work.

Use commits to record meaningful steps.

Use pull requests to explain and review changes.

Use CI to check repetitive things automatically.

The goal is not bureaucracy.

The goal is clarity.

Continue