Reading paths from stdin
gnaw can take its file list from standard input instead of walking a directory: pipe one repo-relative path per line and gnaw sources exactly those files. This page is the precise contract; the how-to covers the day-to-day workflows.
git diff --name-only | gnawWhen stdin mode activates
gnaw reads stdin as a path list only when all of these hold:
| Condition | Why |
|---|---|
| stdin is not a terminal (it's piped or redirected) | A human at a prompt isn't sending a list |
| no path argument was given | An explicit path means "walk this"; it always wins |
not in TUI mode (--tui) | The TUI drives its own selection |
| not the internal clipboard daemon | The daemon uses stdin for its own payload |
If any condition fails, gnaw behaves as before. In particular, a bare gnaw at
an interactive terminal prints help, and gnaw . (or any explicit path) walks
the tree even inside a pipeline.
How paths are resolved
Each non-blank line is trimmed, joined onto the root, and canonicalized:
- Relative paths resolve against the root (the path argument, default
.). - Paths that resolve outside the root are dropped. After canonicalization, a
path that doesn't sit under the root is discarded โ a piped
../../etc/passwdcan't escape the allowed root. - Paths that don't exist are dropped. Deleted files (which still appear in
git diff --name-only) fail to canonicalize and are skipped silently. - Binary and empty files are dropped during extraction, identical to a normal walk.
Blank lines are ignored, so trailing newlines and empty input are harmless; empty input yields an empty selection rather than an error.
Ordering
The surviving files are sorted by path, not kept in the order they arrived on stdin. This keeps output byte-stable for snapshot tests and matches the other sources. If you need a specific order, it has to come from the rendered content, not the pipe order.
Interaction with other features
Filtering is bypassed. The piped list is treated as authoritative โ you
named exactly these files โ so --include / --exclude and .gitignore rules
do not apply to it. Binary/empty dropping and secret scanning still run.
Secret scanning still applies. The scrubber stage runs as normal, so
--secret-scan=block will still halt a stdin run that hits a finding, and
redact still masks.
Stdin wins over the git source axes. If a path list is present, it takes
precedence over --git-diff-shas and the git-narrative source selection. A run
is either "these piped files" or "this git range," not both.
--full-directory-tree is ignored. The source tree is always derived from
the piped files, so it lists exactly those paths and never expands to the whole
repository.
Templates resolve normally. Because a stdin run has no --git-diff-shas,
the default template (or your --template) is used. The git-narrative templates'
{{git_diff}} section stays empty unless you also pass --diff, which loads the
working-tree diff as chrome alongside the piped contents.
See also
- Pipe a file list into gnaw โ workflows and examples.
- Git diffs and logs โ folding diffs and logs into the
prompt, and the per-file
--git-diff-shasview. - Output formats โ
-Fand the rendered structure.