Skip to content

Skip .git, .gitignore and .gitattributes export-ignore files when enumerating#297

Merged
BrianHenryIE merged 1 commit into
masterfrom
ignore-git-gitignore-gitattributes
Jun 27, 2026
Merged

Skip .git, .gitignore and .gitattributes export-ignore files when enumerating#297
BrianHenryIE merged 1 commit into
masterfrom
ignore-git-gitignore-gitattributes

Conversation

@BrianHenryIE

Copy link
Copy Markdown
Owner

Fixes #254

Summary

When a package is installed from source (a git clone) rather than a dist zip, FileEnumerator previously copied every file in the package directory — including .git/ internals, build/test artifacts listed in .gitignore, and files the package author marked export-ignore in .gitattributes (which git archive / Composer dist would have stripped).

This change makes FileEnumerator mimic git archive / dist behaviour, per package root:

  • skips the .git directory;
  • skips files matched by the package's .gitignore, using inmarelibero/gitignore-checker;
  • skips files marked export-ignore in the package's .gitattributes, via a new BrianHenryIE\Strauss\Helpers\GitAttributes parser (parse() + isExportIgnored()).

Filtering is gated behind a new exclude_git_files config option which defaults to true; set it to false to restore the previous copy-everything behaviour.

Changes

  • composer.json — added inmarelibero/gitignore-checker.
  • src/Helpers/GitAttributes.php (new) — minimal .gitattributes parser + gitignore-style path matching (anchored vs unanchored, directory-prefix matching, * wildcards, last-match-wins so -export-ignore can re-include).
  • src/Config/FileEnumeratorConfig.php + src/Composer/Extra/StraussConfig.php — new getExcludeGitFiles() / exclude_git_files option (default true).
  • src/Pipeline/FileEnumerator.php — builds the gitignore/gitattributes checkers once per base path and filters discovered files; degrades gracefully (try/catch) when the gitignore library cannot read a path.
  • README.md — documented the new option.

Tests

  • Unit GitAttributesTest — parsing and export-ignore matching against the in-memory filesystem.
  • Unit StraussConfigTestexclude_git_files default-true and false mapping.
  • Integration GitFilesFeatureTest — builds a real package dir with .git/, .gitignore and .gitattributes and asserts the right files are kept/skipped, plus a flag-off case proving backward compatibility.

Verification

  • phpcs (PSR-2): clean on all new/changed code.
  • phpstan (level 7): no errors on all new src code.
  • New unit + integration tests pass; full Unit suite and existing FileEnumeratorIntegrationTest pass.

Note: inmarelibero/gitignore-checker reads the real filesystem (not the Flysystem abstraction), so .gitignore filtering is exercised via integration tests rather than in-memory unit tests; it degrades gracefully on non-local paths.

🤖 Generated with Claude Code

…merating

FileEnumerator now mimics `git archive` / Composer dist behaviour by skipping
files that would not be part of a package's distributed archive: the `.git`
directory, files matched by the package's `.gitignore` (via
inmarelibero/gitignore-checker), and files marked `export-ignore` in the
package's `.gitattributes` (via a new BrianHenryIE\Strauss\Helpers\GitAttributes
parser). Filtering is per package root and gated behind the new
`exclude_git_files` config option, which defaults to true.

Fixes #254

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Project Code Coverage
project coverage report 45% @ a39df63

Generated: Sat, Jun 27, 2026, 3:33:39 UTC.

Lines Functions and Methods Classes and Traits
Total 🟥🟥🟥🟥⬜⬜⬜⬜⬜⬜ 48.77% (179 / 367) 🟥🟥🟥🟥⬜⬜⬜⬜⬜⬜ 42.47% (31 / 73) ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0.00% (0 / 3)
Composer/Extra/StraussConfig.php 🟧🟧🟧🟧🟧🟧⬜⬜⬜⬜ 69.12% (141 / 204) 🟥🟥🟥🟥⬜⬜⬜⬜⬜⬜ 49.21% (31 / 63) ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0.00% (0 / 1)
Config/FileEnumeratorConfig.php n/a (0 / 0) n/a (0 / 0) n/a (0 / 0)
Helpers/GitAttributes.php 🟧🟧🟧🟧🟧⬜⬜⬜⬜⬜ 55.74% (34 / 61) ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0.00% (0 / 4) ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0.00% (0 / 1)
Pipeline/FileEnumerator.php ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 3.92% (4 / 102) ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0.00% (0 / 6) ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 0.00% (0 / 1)

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a feature to exclude Git-related files (such as the .git directory, .gitignore-matched files, and .gitattributes export-ignore files) during file enumeration, controlled by the new exclude_git_files configuration option. Feedback on the changes highlights two critical issues in FileEnumerator.php related to cross-platform compatibility: prepending a slash to the base path breaks path resolution on Windows and with stream wrappers, and a lack of directory separator normalization on $relativePath prevents proper exclusion of .git and parent directories on Windows environments.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

/**
* TODO: use {@see FileSystem::prefixPath()} when #278 is merged.
*/
$gitIgnoreChecker = new GitIgnoreChecker('/' . $normalizedBasePath);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Prepending '/' to $normalizedBasePath breaks path resolution on Windows (e.g., /C:/path) and prevents the GitIgnoreChecker from working with registered stream wrappers like mem:// (which becomes /mem://...). This causes .gitignore detection to be silently skipped during dry-runs and in-memory unit tests. Pass $normalizedBasePath directly instead.

                    $gitIgnoreChecker = new GitIgnoreChecker($normalizedBasePath);

protected function isGitExcluded(string $sourceAbsolutePath, array $repositories): bool
{
foreach ($repositories as $basePath => $checkers) {
$relativePath = $this->filesystem->getRelativePath($basePath, $sourceAbsolutePath);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

On Windows, $relativePath may contain backslashes (e.g., ..\\ or .git\\). This will cause the checks for ../ and .git/ to fail, meaning files outside the repository or inside the .git directory won't be correctly identified and excluded. Normalize the directory separators of $relativePath immediately after retrieving it.

            $relativePath = FileSystem::normalizeDirSeparator($this->filesystem->getRelativePath($basePath, $sourceAbsolutePath));

@github-actions

Copy link
Copy Markdown

strauss.phar.zip @ 151bbf8 \n composer require brianhenryie/strauss:"dev-master#151bbf8028065e5f9bc60ecbf3937e911a96f8bd" --dev

@BrianHenryIE BrianHenryIE merged commit c732a5b into master Jun 27, 2026
10 checks passed
@BrianHenryIE BrianHenryIE deleted the ignore-git-gitignore-gitattributes branch June 27, 2026 04:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ignore vendor file inside symlinked packages ala .gitattributes

1 participant