8000 bug #43704 [Finder] Fix .gitignore infinite loop (julienfalque) · symfony/symfony@29f42aa · GitHub
[go: up one dir, main page]

Skip to content

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 29f42aa

Browse files
bug #43704 [Finder] Fix .gitignore infinite loop (julienfalque)
This PR was merged into the 5.4 branch. Discussion ---------- [Finder] Fix .gitignore infinite loop | Q | A | ------------- | --- | Branch? | 5.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | #43239 (comment) | License | MIT | Doc PR | - Extracted from #43239. Commits ------- dcdc67f [Finder] Fix .gitignore infinite loop
2 parents 0e24210 + dcdc67f commit 29f42aa

File tree

8 files changed

+71
-18
lines changed

8 files changed

+71
-18
lines changed

src/Symfony/Component/Finder/Iterator/VcsIgnoredFilterIterator.php

Lines changed: 50 additions & 11 deletions
8000
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ final class VcsIgnoredFilterIterator extends \FilterIterator
2727

2828
public function __construct(\Iterator $iterator, string $baseDir)
2929
{
30-
$this->baseDir = $baseDir;
30+
$this->baseDir = $this->normalizePath($baseDir);
3131

3232
parent::__construct($iterator);
3333
}
@@ -36,35 +36,65 @@ public function accept(): bool
3636
{
3737
$file = $this->current();
3838

39-
$fileRealPath = $file->getRealPath();
40-
if ($file->isDir()) {
39+
$fileRealPath = $this->normalizePath($file->getRealPath());
40+
if ($file->isDir() && !str_ends_with($fileRealPath, '/')) {
4141
$fileRealPath .= '/';
4242
}
4343

44-
$parentDirectory = $fileRealPath;
45-
46-
do {
47-
$parentDirectory = \dirname($parentDirectory);
48-
$relativeFilePath = substr($fileRealPath, \strlen($parentDirectory) + 1);
44+
foreach ($this->parentsDirectoryDownward($fileRealPath) as $parentDirectory) {
45+
$fileRelativePath = substr($fileRealPath, \strlen($parentDirectory) + 1);
4946

5047
$regex = $this->readGitignoreFile("{$parentDirectory}/.gitignore");
5148

52-
if (null !== $regex && preg_match($regex, $relativeFilePath)) {
49+
if (null !== $regex && preg_match($regex, $fileRelativePath)) {
5350
return false;
5451
}
55-
} while ($parentDirectory !== $this->baseDir);
52+
53+
if (0 !== strpos($parentDirectory, $this->baseDir)) {
54+
break;
55+
}
56+
}
5657

5758
return true;
5859
}
5960

61+
/**
62+
* @return list<string>
63+
*/
64+
private function parentsDirectoryDownward(string $fileRealPath): array
65+
{
66+
$parentDirectories = [];
67+
68+
$parentDirectory = $fileRealPath;
69+
70+
while (true) {
71+
$newParentDirectory = \dirname($parentDirectory);
72+
73+
// dirname('/') = '/'
74+
if ($newParentDirectory === $parentDirectory) {
75+
break;
76+
}
77+
78+
$parentDirectory = $newParentDirectory;
79+
80+
if (0 !== strpos($parentDirectory, $this->baseDir)) {
81+
break;
82+
}
83+
84+
$parentDirectories[] = $parentDirectory;
85+
}
86+
87+
return array_reverse($parentDirectories);
88+
}
89+
6090
private function readGitignoreFile(string $path): ?string
6191
{
6292
if (\array_key_exists($path, $this->gitignoreFilesCache)) {
6393
return $this->gitignoreFilesCache[$path];
6494
}
6595

6696
if (!file_exists($path)) {
67-
return null;
97+
return $this->gitignoreFilesCache[$path] = null;
6898
}
6999

70100
if (!is_file($path) || !is_readable($path)) {
@@ -73,4 +103,13 @@ private function readGitignoreFile(string $path): ?string
73103

74104
return $this->gitignoreFilesCache[$path] = Gitignore::toRegex(file_get_contents($path));
75105
}
106+
107+
private function normalizePath(string $path): string
108+
{
109+
if ('\\' === \DIRECTORY_SEPARATOR) {
110+
return str_replace('\\', '/', $path);
111+
}
112+
113+
return $path;
114+
}
76115
}

src/Symfony/Component/Finder/Tests/FinderTest.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -433,14 +433,18 @@ public function testIgnoreVCSIgnored()
433433
->ignoreVCSIgnored(true)
434434
);
435435

436-
copy(__DIR__.'/Fixtures/gitignore/b.txt', __DIR__.'/Fixtures/gitignore/a.txt');
437-
copy(__DIR__.'/Fixtures/gitignore/dir/a.txt', __DIR__.'/Fixtures/gitignore/dir/b.txt');
436+
copy(__DIR__.'/Fixtures/gitignore/search_root/b.txt', __DIR__.'/Fixtures/gitignore/search_root/a.txt');
437+
copy(__DIR__.'/Fixtures/gitignore/search_root/b.txt', __DIR__.'/Fixtures/gitignore/search_root/c.txt');
438+
copy(__DIR__.'/Fixtures/gitignore/search_root/dir/a.txt', __DIR__.'/Fixtures/gitignore/search_root/dir/b.txt');
439+
copy(__DIR__.'/Fixtures/gitignore/search_root/dir/a.txt', __DIR__.'/Fixtures/gitignore/search_root/dir/c.txt');
438440

439441
$this->assertIterator($this->toAbsoluteFixtures([
440-
'gitignore/b.txt',
441-
'gitignore/dir',
442-
'gitignore/dir/a.txt',
443-
]), $finder->in(__DIR__.'/Fixtures/gitignore')->getIterator());
442+
'gitignore/search_root/b.txt',
443+
'gitignore/search_root/c.txt',
444+
'gitignore/search_root/dir',
445+
'gitignore/search_root/dir/a.txt',
446+
'gitignore/search_root/dir/c.txt',
447+
]), $finder->in(__DIR__.'/Fixtures/gitignore/search_root')->getIterator());
444448
}
445449

446450
public function testIgnoreVCSCanBeDisabledAfterFirstIteration()
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
/a.txt
1+
c.txt
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/a.txt

src/Symfony/Component/Finder/Tests/Iterator/VcsIgnoredFilterIteratorTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,15 @@ public function getAcceptData(): iterable
202202
];
203203
}
204204

205+
public function testAcceptAtRootDirectory()
206+
{
207+
$inner = new InnerNameIterator([__FILE__]);
208+
209+
$iterator = new VcsIgnoredFilterIterator($inner, '/');
210+
211+
$this->assertIterator([__FILE__], $iterator);
212+
}
213+
205214
private function toAbsolute(array $files): array
206215
{
207216
foreach ($files as &$path) {

0 commit comments

Comments
 (0)
0