Explains every line of a .gitignore, tests it against paths that must be ignored or must stay committable, names the rule that decides each path, and flags mistakes such as negations blocked by an excluded parent folder, inline comments, leading spaces, backslashes, stray **, secrets not covered and .env.example ignored. Returns a fixed file. Includes a stdlib Python checker with --self-test,…
--- name: gitignore-pattern-tester-and-reviewer description: Explains every line of a .gitignore, tests it against paths that must be ignored or must stay committable, names the rule that decides each path, and flags mistakes such as negations blocked by an excluded parent folder, inline comments, leading spaces, backslashes, stray **, secrets not covered and .env.example ignored. Returns a fixed file. Includes a stdlib Python checker with --self-test, matched against real git. --- # Gitignore Pattern Tester and Reviewer You help developers understand and fix `.gitignore` files. You explain what each pattern really matches, prove it with path tests, and find the mistakes that make git ignore source files or commit secrets and build output. You return a corrected file with tests that pass, not just advice. ## Files in this skill - `scripts/gitignore_check.py` - explains each rule, runs "+ path" (must be ignored) and "- path" (must be kept) tests, names the deciding line for each path, and reports HIGH, MEDIUM, LOW and INFO findings (Python 3 standard library only) - `references/gitignore-rules.md` - how git reads ignore files: sources and priority, line syntax, anchoring, wildcards, the parent-directory rule, tracked files and commands to confirm - `references/common-pitfalls.md` - every finding code with its fix, pitfalls the script cannot see (broad names, tracked files, nested files, case sensitivity, lock files, .dockerignore differences) and good starting blocks - `templates/gitignore-review.md` - the review to hand back - `examples/webapp.gitignore` - a web app ignore file with six real bugs - `examples/webapp-fixed.gitignore` - the corrected file (all tests pass) - `examples/webapp-paths.txt` - 19 path tests used with both files ## Workflow ### 1. Collect the inputs Ask for the `.gitignore` (and any nested ones), the stack, and the paths that matter: what must be ignored (build output, dependencies, logs, secrets) and what must stay committable (source folders with common names like `build/` or `lib/`, `.gitkeep` files, `.env.example`, lock files). If the user only asks "why is X ignored?" ask for the path and use `git check-ignore -v X` output if they have it. ### 2. Run the checker ``` python3 scripts/gitignore_check.py examples/webapp.gitignore --test examples/webapp-paths.txt python3 scripts/gitignore_check.py .gitignore --test paths.txt --json python3 scripts/gitignore_check.py --self-test ``` Paths file: `+ path` must be ignored, `- path` must be kept, end directories with `/`. Exit 1 means a HIGH finding or a failed test, 2 means unreadable input. The checker models one ignore file at the repo root; for a nested file, give paths relative to its folder. ### 3. Explain and fix - Walk through the Rules table in plain words, especially anchoring (slash at start or middle = relative to the .gitignore folder) and the parent-directory rule (see `references/gitignore-rules.md`). - For each FAIL, quote the deciding line from the "Decided by" column and give the smallest change that fixes it. - Fix every HIGH and MEDIUM finding; explain any INFO you leave in place. - Add `- path` tests for source folders with common names so over-broad rules show up. - Re-run the checker on the fixed file until all tests pass, and show the diff. ### 4. Deliver Fill `templates/gitignore-review.md`: what the current file does, the test table, findings, the full fixed file, a diff, and the commands to confirm (`git check-ignore -v`, `git status --ignored`) and to untrack files that were already committed (`git rm --cached`). ## Example result For `examples/webapp.gitignore` with `examples/webapp-paths.txt`: 13/19 tests pass, 1 HIGH, 4 MEDIUM, 2 LOW, 1 INFO. HIGH NEGATION_BLOCKED: `logs/` excludes the folder, so `!logs/.gitkeep` can never apply (fix: `logs/*`). MEDIUM: `config/secrets.yml # never commit` is one pattern, so the secrets file would be committed; ` Thumbs.db` has a leading space; `src\generated` is read as `srcgenerated`; and `*.key`, `*.pem` and `id_rsa` files are not covered. LOW: `.env*` hides `.env.example`; `*.log` is listed twice. The unanchored `build` also hides `src/build/README.md` (fix: `/build/`). `examples/webapp-fixed.gitignore` passes 19/19 with no HIGH, MEDIUM or LOW findings. ## Rules - Last matching line wins; a file inside an excluded folder can never be re-included. Say this whenever a `!` line is involved. - Never suggest ignoring lock files (`package-lock.json`, `poetry.lock`, `go.sum`, app `Cargo.lock`) or shared config the team commits on purpose. - If a secret was already committed, say that ignoring it is not enough: rotate it and consider rewriting history. - Prefer anchored root patterns (`/dist/`, `/build/`) for output folders, and put personal editor and OS files in the global ignore file when the team agrees. - Confirm on the real repository with `git check-ignore -v`: the script follows git's documented rules for one file, but nested files, `.git/info/exclude`, the global file and `core.ignorecase` can change the result. FILE:references/gitignore-rules.md # How git reads .gitignore Source: the gitignore(5) manual (`git help gitignore`). The checker implements these rules and was compared with real git 2.47 on 6,956 path checks with no differences. ## Where rules come from (highest priority first) 1. Command-line `--exclude` options (rare; used by some tools). 2. `.gitignore` files: the one in the same folder as the path wins over the ones above it. Patterns are relative to the folder that holds that `.gitignore`. 3. `.git/info/exclude` - local to one clone, never committed. Good for personal scratch files. 4. The global file in `core.excludesFile` (default `~/.config/git/ignore`) - good for editor and OS files such as `.DS_Store` and `*.swp`. Inside one file, the LAST matching line decides. ## Line syntax | Line | Meaning | |---|---| | empty | ignored, use as a separator | | `# text` | comment; only at the very start of a line. `foo # bar` is the pattern `foo # bar` | | `\#file` | a name that starts with `#` | | `!pattern` | re-include something an earlier line excluded | | `\!file` | a name that starts with `!` | | `name ` | trailing spaces are dropped unless escaped (`name\ `) | | ` name` | leading spaces are KEPT and become part of the name | | `\` | escapes the next character; it is never a path separator | ## Slashes decide where a pattern matches | Pattern | Matches | Does not match | |---|---|---| | `build` | `build`, `src/build`, `a/b/build/x.js` (any file or folder named build) | `build.sh` | | `build/` | folders named build at any depth | a FILE named build | | `/build` | `build` next to the .gitignore only | `src/build` | | `doc/frotz` | `doc/frotz` next to the .gitignore (a slash in the middle anchors like a leading slash) | `a/doc/frotz` | | `**/doc/frotz` | `doc/frotz` at any depth | | Rule of thumb: no slash (or only a trailing one) = any depth; a slash at the start or middle = relative to the .gitignore folder. ## Wildcards | Wildcard | Meaning | |---|---| | `*` | any characters except `/` (`*.log`, `/a*z` does not match `ab/cz`) | | `?` | exactly one character except `/` | | `[abc]`, `[a-z]`, `[!0-9]`, `[^0-9]` | one character from (or not from) the set; `[[:digit:]]` style classes work | | `**/name` | `name` in any folder, including the top | | `dir/**` | everything inside `dir`, but not `dir` itself | | `a/**/b` | `a/b`, `a/x/b`, `a/x/y/b` | | `foo**`, `**bar` | `**` not next to slashes acts like a single `*` | ## The parent-directory rule (the most common surprise) "It is not possible to re-include a file if a parent directory of that file is excluded." Git does not even list the contents of an excluded folder, so a later `!` line never gets a chance. ```gitignore # does NOT work: logs/ is excluded as a folder logs/ !logs/.gitkeep # works: exclude the folder's contents, keep the folder visible logs/* !logs/.gitkeep # works for deeper files: re-include each folder level /assets/* !/assets/icons/ /assets/icons/* !/assets/icons/app.svg ``` ## Whitelist style Ignore everything, then allow what you want: ```gitignore * !*/ !*.md !src/** ``` `!*/` re-includes every folder so the later `!` lines can reach files inside them. ## Already tracked files .gitignore only affects untracked files. A file that is already committed stays tracked: ``` git rm --cached path/to/file # keep it on disk, stop tracking it git rm -r --cached build/ # same for a folder ``` If it was a secret, removing it from tracking is not enough: rotate the secret, because it is still in history. ## Commands to confirm ``` git check-ignore -v path/to/file # prints the file, line and pattern that ignores it (nothing = not ignored) git check-ignore -v --no-index path # also for files that are tracked git status --ignored --short # lists ignored files as "!!" git ls-files --others --ignored --exclude-standard # every ignored untracked file ``` FILE:references/common-pitfalls.md # Common .gitignore pitfalls and the checker's codes | Code | Severity | What goes wrong | Fix | |---|---|---|---| | NEGATION_BLOCKED | HIGH | `!path/file` after a rule that excludes a parent folder (`logs/`, `dist`, `a/*` for `a/b/c`) never takes effect, so the file you meant to commit stays ignored | Exclude contents (`logs/*` or `logs/**`) instead of the folder, or re-include each folder level (`!a/b/`) | | SECRETS_NOT_COVERED | MEDIUM | typical secret files (`.env`, `*.pem`, `*.key`, `id_rsa`) would be committed by `git add .` | Add `.env`, `.env.*`, `*.pem`, `*.key`, `id_rsa*`; also use a secret scanner in CI | | INLINE_COMMENT | MEDIUM | `secrets.yml # never commit` is one pattern including the comment text, so `secrets.yml` is NOT ignored | Put comments on their own line | | LEADING_SPACE | MEDIUM | ` Thumbs.db` matches only a name that starts with a space | Remove the space | | BACKSLASH_SEPARATOR | MEDIUM | `src\generated` means "src" + escaped "g" + "enerated" = `srcgenerated` | Always use `/`, also on Windows | | ENV_EXAMPLE_IGNORED | LOW | `.env*` also hides `.env.example`, the template other developers need | Add `!.env.example` after it | | DUPLICATE | LOW | the same line twice; harmless but a sign of copy-paste merges | Delete one | | DOUBLE_STAR | LOW | `foo**` is just `foo*`; the author probably meant `foo/**` | Use `**` only as `**/x`, `x/**` or `a/**/b` | | TRAILING_SPACE | LOW | trailing spaces are silently dropped; if they were meant, the file will not match | Remove or escape them | | CRLF | LOW | Windows line endings in .gitignore | Save as LF; add `* text=auto` to .gitattributes | | ANCHORED_BY_SLASH | INFO | `src/gen` only matches next to the .gitignore, not `packages/a/src/gen` | Add `/` to show intent or `**/` to match at any depth | ## Pitfalls the script cannot see (check them by hand) - **Unanchored names that are too broad.** `build` also hides `src/build/` and `docs/build/`; `lib` hides every `lib` folder including source code. Use `/build/` for the root output folder. Write a `- path` test for any source folder with a common name. - **Tracked files.** Adding a rule does not untrack files already committed. Run `git ls-files -ci --exclude-standard` to list tracked files that match the ignore rules, then `git rm --cached`. - **Nested .gitignore files.** A `.gitignore` in a subfolder adds rules relative to that subfolder and can re-include what the root file excluded (unless a parent folder is excluded). Run the checker on each file with paths relative to its folder, or confirm with `git check-ignore -v`. - **Case sensitivity.** On macOS and Windows `core.ignorecase=true` makes `*.JPG` match `photo.jpg`; on Linux CI it does not. Write both cases or use `*.[jJ][pP][gG]`. - **Personal files in the shared file.** `.idea/`, `.vscode/`, `*.swp`, `.DS_Store` belong in the global ignore file (`core.excludesFile`), though many teams keep them in the repo file for convenience. Do not ignore `.
출처 · prompts.chat 공개 프롬프트 모음 · 작성 Fatih Kadir Akın — CC0(저작권 포기)로 공개된 원문을 그대로 싣습니다. 한국어 정리 · 실행 결과는 아직 없습니다.