12 — Git, LFS, commit conventions¶
TL;DR¶
- Branch = feature; PR = single PR per branch with many commits.
- Never
git pushwithout explicit user permission. Commit locally as much as you want; ask before pushing. - LFS for any file >1MB. Configured via
.gitattributes. - Stage in batches (
git add -A && git commit), not file-by-file. Rungit statusONCE to sanity-check before adding. - Don't force-push to
main— or to ANY branch that exists on the remote. Add commits on top of the remote branch, never rewrite it.git fetchand check the remote branch state BEFORE starting dev. - Don't
--no-verify. Don'tgit reset --hardwithout confirming with the user.
Commit messages¶
Follow the pattern visible in git log --oneline:
feat(<scope>): <imperative summary>for new featuresfix(<scope>): <imperative summary>for bug fixesdocs(<scope>): <imperative summary>for doc-only changestest(<scope>): ...for test-only changesci(<scope>): ...for CI config changesrefactor(<scope>): ...for code-cleanup-only changes
Body explains WHY (constraint, prior incident, design decision) — not WHAT (the diff already shows that).
Commits in this repo carry no AI-attribution trailer. If your tooling appends a
Co-Authored-By: line naming a model, strip it before committing.
LFS¶
The .gitattributes already tracks these patterns via LFS:
*.pth,*.onnx,*.tflite,*.h5,*.weights,*.pb— note*.ptis not in.gitattributes; a.ptoutsideartifacts/models/commits raw — ML model weights*.wav,*.flac,*.aiff,*.ogg— audio samples (underartifacts/audio-samples/)*.mp4,*.avi,*.mov— video samples*.zip,*.tar.gz— datasetsartifacts/models,artifacts/models/*— by-path tracking. Not/**: gitattributes globs do not match nested subdirectories
If you add a new large file:
1. Verify the path/extension is in .gitattributes (git check-attr
filter <file>).
2. If not, add a pattern to .gitattributes BEFORE staging the file.
3. Commit the file normally; Git LFS handles the rest.
4. git push will upload the LFS object. Watch for the
Uploading LFS objects: ... done line.
On Jetson / fresh clone: git lfs pull to fetch all LFS objects.
Stage in batches, not file-by-file¶
The pattern that wastes the least tokens AND catches the most bugs:
# 1. Make your changes. Build, lint, test as you go.
# 2. ONE git status — verify the directory is in the state you want.
# Is anything weird showing up? Stray editor files? .DS_Store?
# Investigate before assuming it's fine. The .gitignore Python
# `lib/` rule has silently dropped new frontend src/lib/*.ts
# files in the past — git add -A WILL skip them and you won't
# notice without looking.
git status
# 3. Batch add + commit.
git add -A
git commit -m "..."
Do NOT git add path1 && git commit -m "..." && git add path2 && git
commit -m "..." && ... across 10 rounds. Past agents have done this
and burnt the user's tokens for no reason. ONE coherent commit per
unit of work; the diff itself tells the story.
If a git add -A does drop a file silently, git check-ignore -v <file>
tells you which rule caught it.
Never push without permission¶
Every push triggers CI which costs the user real money. Past sessions have pushed at the wrong moment (after every commit, or at a self- declared "natural shipping point" that was 1/20th of where the user actually wanted to ship). Don't guess.
The pattern: - Commit locally as you work, freely. - When you reach what FEELS like a stopping point, ask: "Want me to push these N commits?" or similar. Don't just push. - The user will say "push it," "open the PR," or "yes" → then push. - If the user says "keep going" or doesn't reply, keep committing locally.
If you've been working a while and have a stack of local commits,
that's fine. The user can see them with git log whenever they
want; they're not lost.
Check the remote before starting dev¶
Before you begin work on any branch that exists on the remote, sync your view of it:
git fetch origin
git log --oneline <branch>..origin/<branch> # commits on the remote you don't have?
If the remote branch has commits you don't have, start FROM them
(git pull --ff-only, or branch off origin/<branch>). The owner may
have advanced the branch between sessions — local work must never
silently diverge from the remote. A past session skipped this check,
developed on a stale local copy, and overwrote the owner's commits on
the remote: "absolutely dont overwrite the remote branch. you should
be adding ON TOP OF IT. you should have checked before you started
dev."
Branch + PR conventions¶
One branch = one PR. Many commits per PR is fine and encouraged. Each commit should be coherent (the diff matches the message).
The PR's branch name describes the work. PR numbers are just the next available number in the repo — don't read meaning into the number.
When a PR is open and you push new commits to the branch, CI re-runs automatically. Don't open a second PR for the same branch.
When CI is failing because of a small problem¶
Don't push a series of "try fix" commits to see if CI passes. Each push runs the entire CI matrix. Costs the user real money. Instead:
- Reproduce the failure locally (
make lint-<x>/make test-<x>). - Fix it.
- Verify locally.
- Push ONCE with the fix.
If you can't reproduce locally, that itself is a problem — local and CI must match. Investigate the discrepancy before pushing again.
Destructive operations¶
Never without explicit user confirmation:
git push --forcetomainor to ANY branch that exists on the remote (feature/working branches included)git reset --hard(always offer the safe alternative)git checkout -- <file>(discards uncommitted work)git branch -D <branch>git clean -fdrm -rfon anything under git control
Once a branch exists on the remote — even "your own" feature branch — treat its history as shared: add commits ON TOP of it, never rewrite it. The owner may have advanced the branch since you last looked (see "Check the remote before starting dev" above). Rebase-then-force-push is reserved for branches that have never been pushed; after the first push, history is append-only.
See also¶
13-ci-cd.mdfor what CI actually runs.99-gotchas.mdfor the.gitignorelib/ pattern and other landmines.