Skip to content

Commit

Permalink
[1.x] Adds --diff option (#327)
Browse files Browse the repository at this point in the history
* add [diff] option to default command

* add path resolver for git diff

* add [diff] method to PathsRepository contract. (Breaking?)

* extract method for future re-use

* implement new [diff] method

* lint

* skip duplicates

* include untracked files in diff list

* add tests

* fixes

* make stan happy
  • Loading branch information
ProjektGopher authored Jan 6, 2025
1 parent 0d8f830 commit cc904c0
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 2 deletions.
1 change: 1 addition & 0 deletions app/Commands/DefaultCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ protected function configure()
new InputOption('test', '', InputOption::VALUE_NONE, 'Test for code style errors without fixing them'),
new InputOption('bail', '', InputOption::VALUE_NONE, 'Test for code style errors without fixing them and stop on first error'),
new InputOption('repair', '', InputOption::VALUE_NONE, 'Fix code style errors but exit with status 1 if there were any changes made'),
new InputOption('diff', '', InputOption::VALUE_REQUIRED, 'Only fix files that have changed since branching off from the given branch', null, ['main', 'master', 'origin/main', 'origin/master']),
new InputOption('dirty', '', InputOption::VALUE_NONE, 'Only fix files that have uncommitted changes'),
new InputOption('format', '', InputOption::VALUE_REQUIRED, 'The output format that should be used'),
new InputOption('cache-file', '', InputArgument::OPTIONAL, 'The path to the cache file'),
Expand Down
8 changes: 8 additions & 0 deletions app/Contracts/PathsRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,12 @@ interface PathsRepository
* @return array<int, string>
*/
public function dirty();

/**
* Determine the files that have changed since branching off from the given branch.
*
* @param string $branch
* @return array<int, string>
*/
public function diff($branch);
}
21 changes: 21 additions & 0 deletions app/Project.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ public static function paths($input)
return static::resolveDirtyPaths();
}

if ($diff = $input->getOption('diff')) {
return static::resolveDiffPaths($diff);
}

return $input->getArgument('path');
}

Expand Down Expand Up @@ -46,4 +50,21 @@ public static function resolveDirtyPaths()

return $files;
}

/**
* Resolves the paths that have changed since branching off from the given branch, if any.
*
* @param string $branch
* @return array<int, string>
*/
public static function resolveDiffPaths($branch)
{
$files = app(PathsRepository::class)->diff($branch);

if (empty($files)) {
abort(0, "No files have changed since branching off of {$branch}.");
}

return $files;
}
}
47 changes: 45 additions & 2 deletions app/Repositories/GitPathsRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use App\Contracts\PathsRepository;
use App\Factories\ConfigurationFactory;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Symfony\Component\Process\Process;

Expand Down Expand Up @@ -41,14 +42,56 @@ public function dirty()
->mapWithKeys(fn ($file) => [substr($file, 3) => trim(substr($file, 0, 3))])
->reject(fn ($status) => $status === 'D')
->map(fn ($status, $file) => $status === 'R' ? Str::after($file, ' -> ') : $file)
->values();

return $this->processFileNames($dirtyFiles);
}

/**
* {@inheritDoc}
*/
public function diff($branch)
{
$files = [
'committed' => tap(new Process(['git', 'diff', '--name-only', '--diff-filter=AM', "{$branch}...HEAD", '--', '**.php']))->run(),
'staged' => tap(new Process(['git', 'diff', '--name-only', '--diff-filter=AM', '--cached', '--', '**.php']))->run(),
'unstaged' => tap(new Process(['git', 'diff', '--name-only', '--diff-filter=AM', '--', '**.php']))->run(),
'untracked' => tap(new Process(['git', 'ls-files', '--others', '--exclude-standard', '--', '**.php']))->run(),
];

$files = collect($files)
->each(fn ($process) => abort_if(
boolean: ! $process->isSuccessful(),
code: 1,
message: 'The [--diff] option is only available when using Git.',
))
->map(fn ($process) => $process->getOutput())
->map(fn ($output) => explode(PHP_EOL, $output))
->flatten()
->filter()
->unique()
->values()
->map(fn ($s) => (string) $s);

return $this->processFileNames($files);
}

/**
* Process the files.
*
* @param \Illuminate\Support\Collection<int, string> $fileNames
* @return array<int, string>
*/
protected function processFileNames(Collection $fileNames)
{
$processedFileNames = $fileNames
->map(function ($file) {
if (PHP_OS_FAMILY === 'Windows') {
$file = str_replace('/', DIRECTORY_SEPARATOR, $file);
}

return $this->path.DIRECTORY_SEPARATOR.$file;
})
->values()
->all();

$files = array_values(array_map(function ($splFile) {
Expand All @@ -58,6 +101,6 @@ public function dirty()
->files()
)));

return array_values(array_intersect($files, $dirtyFiles));
return array_values(array_intersect($files, $processedFileNames));
}
}
86 changes: 86 additions & 0 deletions tests/Feature/DiffTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

use App\Contracts\PathsRepository;

it('determines diff files', function () {
$paths = Mockery::mock(PathsRepository::class);

$paths
->shouldReceive('diff')
->with('main')
->once()
->andReturn([
base_path('tests/Fixtures/without-issues-laravel/file.php'),
]);

$this->swap(PathsRepository::class, $paths);

[$statusCode, $output] = run('default', ['--diff' => 'main']);

expect($statusCode)->toBe(0)
->and($output)
->toContain('── Laravel', ' 1 file');
});

it('ignores the path argument', function () {
$paths = Mockery::mock(PathsRepository::class);

$paths
->shouldReceive('diff')
->once()
->andReturn([
base_path('tests/Fixtures/without-issues-laravel/file.php'),
]);

$this->swap(PathsRepository::class, $paths);

[$statusCode, $output] = run('default', [
'--diff' => 'main',
'path' => base_path(),
]);

expect($statusCode)->toBe(0)
->and($output)
->toContain('── Laravel', ' 1 file');
});

it('does not abort when there are no diff files', function () {
$paths = Mockery::mock(PathsRepository::class);

$paths
->shouldReceive('diff')
->once()
->andReturn([]);

$this->swap(PathsRepository::class, $paths);

[$statusCode, $output] = run('default', [
'--diff' => 'main',
]);

expect($statusCode)->toBe(0)
->and($output)
->toContain('── Laravel', ' 0 files');
});

it('parses nested branch names', function () {
$paths = Mockery::mock(PathsRepository::class);

$paths
->shouldReceive('diff')
->with('origin/main')
->once()
->andReturn([
base_path('tests/Fixtures/without-issues-laravel/file.php'),
]);

$this->swap(PathsRepository::class, $paths);

[$statusCode, $output] = run('default', [
'--diff' => 'origin/main',
]);

expect($statusCode)->toBe(0)
->and($output)
->toContain('── Laravel', ' 1 file');
});

0 comments on commit cc904c0

Please sign in to comment.