Git is a powerful tool — but with power comes the ability to create chaos. This final lesson distils the practices that distinguish mature Git users from beginners, and summarises the major team workflows.
Commit Hygiene
- Commit one logical change at a time. A commit should be reviewable and revertable as a unit. Mixing unrelated changes in one commit makes both reviews and reverts painful.
- Commit early, commit often. Small commits on a feature branch are cheap and safe — you can always squash before merging.
- Never commit broken code to a shared branch. It is fine to have failing tests on a feature branch; never push them to main.
- Write commit messages in the imperative mood: "Add retry logic" not "Added retry logic" or "Adding retry logic."
Conventional Commits
The Conventional Commits specification adds a structured prefix to commit messages that enables automated tooling:
feat: add password reset via email
fix: prevent crash when user list is empty
chore: update dependencies
docs: clarify README installation steps
refactor: extract auth logic into its own module
test: add unit tests for payment calculator
ci: add CodeQL security scanning workflow
Tools like semantic-release read Conventional Commit messages to automatically bump version numbers and generate changelogs. The feat: prefix triggers a minor version bump; fix: triggers a patch bump; a footer of BREAKING CHANGE: triggers a major bump.
git bisect — Finding Bugs Automatically
git bisect uses binary search to find the exact commit that introduced a bug:
git bisect start
git bisect bad # Current commit is broken
git bisect good v1.2.0 # This version was working
# Git checks out a middle commit
# Test the build — mark good or bad
git bisect good # or: git bisect bad
# Repeat until Git identifies the first bad commit
git bisect reset # Return to original HEAD when done
For a repository with 1,000 commits between good and bad, bisect needs only about 10 checks to find the culprit.
Git LFS for Large Files
Git is not designed for large binary files (images, videos, datasets, compiled binaries). Every commit of a large file is stored forever in the repository history, bloating clone times for everyone. Git LFS (Large File Storage) stores the actual file content in a separate store and commits only a pointer:
git lfs install
git lfs track "*.psd"
git lfs track "*.mp4"
git add .gitattributes
git add design/mockup.psd
git commit -m "Add design mockup"
GitHub, GitLab, and most Git hosts support LFS. Free accounts have storage limits — check your plan.
Signed Commits
Anyone can set user.email to any address — Git does not verify identity. Signed commits use GPG or SSH to cryptographically prove that a commit came from you. GitHub shows a "Verified" badge on signed commits.
# Sign commits using SSH (simpler, requires Git 2.34+)
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
Choosing a Team Workflow
| Workflow | How it works | Best for |
|---|---|---|
| GitHub Flow | Short-lived feature branches off main; PR to merge; deploy from main | Web apps with continuous deployment |
| GitFlow | Long-lived main + develop + feature/release/hotfix branches | Software with versioned releases |
| Trunk-based development | All developers commit to main (or very short-lived branches); feature flags control release | High-velocity teams with strong CI |
| Forking workflow | Each contributor works from their own fork; PRs to upstream | Open-source projects |
Repository Hygiene Checklist
- ✓
.gitignorecommitted to the repository root - ✓
README.mdwith setup and usage instructions - ✓ Branch protection on
main— require PR review and CI to pass - ✓ No secrets in the repository history — use a secrets manager
- ✓ Delete merged branches to keep the branch list clean
- ✓ Large binaries in Git LFS or a blob store, not in the repository
- ✓ Consistent commit message style enforced by a linter (e.g.,
commitlint)