.gitignore, good commit messages and viewing history
A clean repository is easy to understand, safe to share and simple to fix. Three habits get you there.
1. Keep things out with .gitignore
Some files should never be committed:
- Secrets: passwords, API keys, M-Pesa credentials,
.envfiles, config files with database passwords. - Generated files:
node_modules/, build folders,__pycache__/. - Personal/system files:
.DS_Store,Thumbs.db, editor settings. - Big files: videos, database dumps, logs.
Create a file called .gitignore in the project's root folder:
# Secrets: never commit these
.env
config.php
mpesa-config.php
*.key
# Dependencies and builds
node_modules/
vendor/
dist/
__pycache__/
# Logs and system files
*.log
.DS_Store
Thumbs.db
.vscode/| Pattern | Matches |
|---|---|
config.php | That file anywhere |
/config.php | Only in the root folder |
logs/ | Any folder called logs |
*.log | Every file ending in .log |
!keep.log | Exception: do include this one |
Tip: commit a safe example instead, like
config.example.phpwith empty values, so others know what to fill in.
Oops, I already committed a secret
.gitignore only affects untracked files. If a file is already committed:
git rm --cached config.php # stop tracking it (keeps your local copy)
echo "config.php" >> .gitignore
git commit -m "Stop tracking config.php"The secret is still in the history, and if you pushed to GitHub, assume it's been seen. Change the password or key immediately. That's the only real fix.
2. Write good commit messages
A commit message explains why a change was made, for your future self and your team.
Bad: update, fix, changes, asdf
Good:
Add M-Pesa STK push to the checkout page
Customers can now pay from their phone without leaving the site.
The callback saves the receipt code on the order.Rules that work:
- A short summary line (about 50 characters), in the imperative: "Add", "Fix", "Remove" (as if giving an order: "this commit will... Add M-Pesa").
- A blank line, then more detail if needed.
- One logical change per commit. Don't mix "fix login bug" with "redesign footer".
- Commit often: small commits are easier to review and undo.
Many teams use prefixes: feat: add receipts page, fix: phone validation, docs: update README.
3. Explore history
git log # full history
git log --oneline --graph --all # compact, with branches drawn
git log -p index.html # every change to one file
git log --author="Amina"
git log --since="2 weeks ago"
git show a1b2c3d # one commit in detail
git diff # unstaged changes
git diff --staged # what you're about to commit
git blame index.html # who last changed each line, and whenTag releases
git tag -a v1.0 -m "First launch"
git push origin v1.0Tags mark important points (a launch, a client handover) you can return to.
A good README
Every repository should have a README.md explaining what the project is, how to run it and how to configure it (without real secrets). GitHub shows it on the repo's front page. See the Markdown example in our practice editor.
Why commit habits matter
Your Git history is a diary of the project. Good commits make it easy to find when a bug appeared, understand why a change was made, undo one feature without losing others, and review a teammate's work. A clean history full of clear messages also impresses employers who look at your GitHub. A messy one ("update", "fix", "asdf") makes debugging and teamwork much harder. And a single committed password or API key can lead to a hacked server or a drained payment account.
.gitignore for common project types
# Node.js / React / Vite
node_modules/
dist/
build/
.env
.env.*
!.env.example
npm-debug.log*
# Python
__pycache__/
*.pyc
.venv/
venv/
# PHP / Laravel
/vendor/
/storage/*.key
.env
# Flutter / Android
.dart_tool/
build/
*.jks
key.properties
local.properties
# Editors and OS
.vscode/
.idea/
.DS_Store
Thumbs.dbPatterns: folder/ ignores a folder, *.log ignores by extension, !file makes an exception, and a leading / anchors to the repository root. GitHub's gitignore templates (github.com/github/gitignore) have ready lists for most languages.
Commit an .env.example with variable names but no real values, so teammates know which settings they need:
MPESA_CONSUMER_KEY=
MPESA_CONSUMER_SECRET=
DATABASE_URL=Checking why a file is ignored (or not)
git check-ignore -v config/secret.php # shows which .gitignore rule matches
git status --ignored # list ignored files
git rm --cached .env # stop tracking a file that was committed before being ignoredAdding a file to .gitignore doesn't remove it from Git if it was already committed; git rm --cached does (and the old version stays in history).
Committed a secret? Act in this order
- Revoke/rotate the secret immediately: generate a new API key, change the password, regenerate the M-Pesa Daraja credentials. Assume the old one is compromised the moment it was pushed (bots scan GitHub for keys within minutes).
- Remove it from the code and move it to environment variables or a config file outside the repository.
- Optionally clean history with tools like
git filter-repoor BFG, then force-push and ask collaborators to re-clone. Cleaning history alone isn't enough; rotation is the real fix. - Turn on secret scanning and push protection in GitHub settings where available.
Small, focused commits
| One big commit | Several small commits |
|---|---|
| "Update website" (40 files: header, payments, typo fixes, new page) | "Add M-Pesa payment button to checkout" |
| "Fix typo on About page" | |
| "Add Services page with pricing table" |
Small commits are easier to review, revert and understand. Use git add -p to stage only some changes in a file:
git add -p index.html # Git shows each change ("hunk") and asks: stage this? y/n/s(plit)Commit message format
Add M-Pesa STK push to checkout ← summary: imperative, about 50 characters
Customers can now pay with an STK push instead of typing the till
number. The callback updates the order status to "paid".
Closes #42 ← links and closes an issueMany teams use Conventional Commits prefixes:
| Prefix | Use | Example |
|---|---|---|
feat: | New feature | feat: add booking calendar |
fix: | Bug fix | fix: correct VAT rounding on invoices |
docs: | Documentation | docs: add setup steps to README |
style: | Formatting only | style: format CSS with Prettier |
refactor: | Code change without new behaviour | refactor: split cart logic into module |
test: | Tests | test: add tests for phone validation |
chore: | Maintenance | chore: update dependencies |
Searching history like a detective
git log --oneline --graph --all # visual history
git log -p index.html # every change to one file
git log --author="Wanjiru" --since="2 weeks ago"
git log -S "calculateVat" # commits that added or removed this text
git log --grep="payment" # commits whose message mentions "payment"
git show a1b2c3d # what one commit changed
git blame checkout.js # who last changed each line, and in which commit
git bisect start # binary search for the commit that introduced a buggit bisect asks you to mark commits as good or bad and finds the exact commit that broke something, even among hundreds.
Tags and releases
git tag -a v1.0.0 -m "First public release"
git push origin v1.0.0
git tag # list tagsSemantic versioning (MAJOR.MINOR.PATCH): increase PATCH for bug fixes (1.0.1), MINOR for new features (1.1.0), MAJOR for breaking changes (2.0.0). On GitHub, create a Release from a tag with notes describing changes.
Writing a strong README
# Duka Bora Online Shop
A mobile-friendly shop for a Nakuru grocery with WhatsApp ordering and M-Pesa payments.

## Features
- Product catalogue with search
- Cart and WhatsApp checkout
- Admin page to update prices
## Run locally
1. `git clone https://github.com/you/duka-bora.git`
2. `cp .env.example .env` and fill in the values
3. `npm install && npm run dev`
## Live demo
https://duka-bora.example.com
## Tech
HTML, CSS, JavaScript, PHP, MySQLPractice
- Create a
.gitignorefor a Node project and check thatnode_modulesand.envare ignored. - Make three small commits using Conventional Commit prefixes.
- Use
git add -pto commit only part of a file's changes. - Use
git log -Sto find when a function name first appeared. - Tag a release
v1.0.0and push the tag.
Think about it: A developer pushes a Daraja consumer secret to a public repository, then deletes it in the next commit. Is the problem solved?Show answer
No. The secret remains in the Git history and may already have been copied by bots that scan GitHub. The developer must revoke and regenerate the credentials immediately, store the new ones outside the repository (environment variables), and optionally scrub the history. Deleting it in a later commit doesn't remove it from earlier commits.
Check yourself
What is the name of the file that lists files Git should ignore?
Show answer
.gitignore
Which pattern ignores every file ending in .log?
Show answer
*.log
A password was committed and pushed. What is the only real fix?
Show answer
change the password
Which command stops tracking a file but keeps your local copy?
Show answer
git rm --cached
Should a commit summary say "Added feature" or "Add feature"?
Show answer
Add feature
Which command shows who last changed each line of a file?
Show answer
git blame
Which command binary-searches history to find the commit that introduced a bug?
Show answer
git bisect
In semantic versioning, which number increases for a bug fix: major, minor or patch?
Show answer
patch
Which Conventional Commit prefix marks a new feature?
Show answer
feat