# Keeping Git Repos Clean

> Prevents, detects, and remediates files that should never be committed — secrets (.env, API tokens, hardcoded credentials) and dev artifacts (build output, scratch databases, editor/OS files). Covers .gitignore (and why it does not untrack), git rm --cached, auditing tracked files, history scrubbing, and credential rotation. Use when a repo has committed secrets or junk, when setting up a new rep…

- **Type:** Skill
- **Install:** `agentstack add skill-wdm0006-python-skills-git-hygiene`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [wdm0006](https://agentstack.voostack.com/s/wdm0006)
- **Installs:** 0
- **Category:** [Security](https://agentstack.voostack.com/c/security)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [wdm0006](https://github.com/wdm0006)
- **Source:** https://github.com/wdm0006/python-skills/tree/main/skills/git-hygiene

## Install

```sh
agentstack add skill-wdm0006-python-skills-git-hygiene
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Keeping Git Repos Clean

Two classes of files keep ending up in repos: **secrets** and **dev artifacts**.
Both are cheap to prevent and expensive to clean up after the fact, because git
history is forever and public repos publish everything.

## The one rule everyone forgets

**`.gitignore` does NOT untrack files that are already committed.** Adding a path
to `.gitignore` only prevents *future untracked* files from being staged. A file
git is already tracking keeps getting committed regardless. This bites repeatedly:
a `.env` is listed in `.gitignore` but was committed before the rule existed, so
it keeps shipping.

To actually stop tracking a file while keeping your local copy:

```bash
git rm --cached path/to/file        # untrack, leave working-tree copy in place
git rm -r --cached some/dir/        # for a directory
echo "path/to/file" >> .gitignore   # then ignore it so it doesn't come back
git commit -m "Stop tracking ; add to .gitignore"
```

`--cached` is the important flag — plain `git rm` deletes the working copy too.

## Audit what a repo actually tracks

Don't trust `.gitignore` to tell you what's clean — read the index directly:

```bash
git ls-files | grep -iE '\.(env|pem|key|p12|profraw|log|bak|db|sqlite3?)$'
git ls-files | grep -iE '(^|/)(\.DS_Store|~\$|todo\.db|node_modules/|__pycache__/)'
git ls-files '*.db' '*.sqlite*'                 # scratch databases
git ls-files | xargs -I{} du -h {} | sort -rh | head   # surprisingly large tracked files
```

Usual suspects seen across real repos:

- **Secrets:** `.env` with a live token, hardcoded `AWS_*`/DB creds in a settings
  module, `SECRET_KEY = "CHANGEME"`/`"foobar"` placeholders shipped to prod.
- **Build artifacts:** LaTeX `.aux/.toc/.log/.synctex.gz/.pdf`, LLVM `*.profraw`,
  compiled binaries, `htmlcov/`, `dist/`, `*.egg-info/`.
- **Scratch / personal artifacts:** `todo.db` and other tool-local SQLite scratch
  DBs, editor backups (`*.backup`, `*.bak`, `~$*.docx` Word lock files), stray
  `*.log`.
- **OS noise:** `.DS_Store`, `Thumbs.db`.

## Secrets need more than `git rm`

Removing a secret from `HEAD` does **not** remove it from history — `git log -p`
and the commit that introduced it still expose it. Three things must happen, in
order, and the first is the only one that actually protects you:

1. **Rotate the credential.** Treat any secret that ever touched a remote as
   compromised. Issue a new token/key/password and revoke the old one. Do this
   first — the leaked value is public the moment it was pushed.
2. **Untrack going forward** (`git rm --cached` + `.gitignore` + `.env.example`
   documenting which vars are needed, with placeholder values only).
3. **Scrub history** if required (`git filter-repo --invert-paths --path .env`,
   or BFG) and force-push. This rewrites SHAs and disrupts collaborators, so it's
   usually a deliberate maintainer step done after rotation — not an automated PR.

A PR that does (2) and (3) but skips (1) gives false comfort: the value is still
valid and still in history clones/forks. Always call out rotation as the required
human follow-up.

## "Committed" means "published"

For public repos — and especially static sites deployed with `path: '.'`
(GitHub Pages uploads the entire repo) — every tracked file is fetchable at a
public URL. A scratch `todo.db` at the repo root of a brochure site is served at
`/todo.db`. Before committing to any public repo, assume anyone can download it.

## Prevent it: global ignore + a secret scanner

Per-developer noise (editor files, OS files, tool scratch DBs like `todo.db`)
should be ignored **globally**, not in every project's `.gitignore` — that way it
never lands anywhere:

```bash
git config --global core.excludesFile ~/.gitignore_global
printf '%s\n' '.DS_Store' '*.swp' 'todo.db' '*.profraw' >> ~/.gitignore_global
```

Block secrets at commit time with a pre-commit hook so they never reach history:

```yaml
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks: [{id: gitleaks}]
  - repo: https://github.com/Yelp/detect-secrets
    rev: v1.5.0
    hooks: [{id: detect-secrets, args: ["--baseline", ".secrets.baseline"]}]
```

A starter project `.gitignore` (commit this):

```gitignore
# Secrets / local config
.env
.env.*
!.env.example
*.pem
*.key

# Python build/test artifacts
__pycache__/
*.py[cod]
build/
dist/
*.egg-info/
.coverage
htmlcov/
.pytest_cache/

# Scratch / OS / editor
*.db
*.sqlite
*.sqlite3
*.profraw
*.log
*.bak
*.backup
.DS_Store
~$*
```

## Checklist

```
Audit:
- [ ] `git ls-files` reviewed for secrets, build output, scratch DBs, OS files
- [ ] No live credentials in tracked source or .env
- [ ] No surprisingly large/binary tracked files

Remediate (if dirty):
- [ ] Secret rotated/revoked FIRST (history is public the moment it was pushed)
- [ ] `git rm --cached` + .gitignore entry for each offending file
- [ ] .env.example documents required vars with placeholder values only
- [ ] History scrub flagged as a maintainer follow-up if the secret is in history

Prevent:
- [ ] Project .gitignore covers secrets, build artifacts, OS/editor noise
- [ ] Global core.excludesFile catches per-developer scratch files
- [ ] gitleaks / detect-secrets pre-commit hook installed
```

For scanning source code for vulnerabilities and hardcoded-secret *patterns*, see
the **auditing-python-security** skill — this skill is about what git *tracks*.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [wdm0006](https://github.com/wdm0006)
- **Source:** [wdm0006/python-skills](https://github.com/wdm0006/python-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-wdm0006-python-skills-git-hygiene
- Seller: https://agentstack.voostack.com/s/wdm0006
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
