From 3ef22ab89b834d75c4fbcd255a96174f7492c3e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Mon, 7 Sep 2026 18:02:46 +0200 Subject: [PATCH] Support git's mnemonic diff prefixes in DiffParser diff.mnemonicPrefix replaces the default "a/"/"b/" prefixes with context-specific ones ("c/", "i/", "o/", "w/"), which DiffParser's regexp didn't account for, causing a RuntimeException for tools like GrumPHP that enable it. Fixes #114 --- src/Gitonomy/Git/Parser/DiffParser.php | 5 ++++- tests/Gitonomy/Git/Tests/DiffTest.php | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Gitonomy/Git/Parser/DiffParser.php b/src/Gitonomy/Git/Parser/DiffParser.php index 3e95d1a..93de0a9 100644 --- a/src/Gitonomy/Git/Parser/DiffParser.php +++ b/src/Gitonomy/Git/Parser/DiffParser.php @@ -45,7 +45,10 @@ protected function doParse(): void $fileIndex = 0; while (!$this->isFinished()) { // 1. title - $vars = $this->consumeRegexp("/diff --git \"?(a\\/.*?)\"? \"?(b\\/.*?)\"?\n/"); + // The prefix is normally "a" and "b", but git's diff.mnemonicPrefix option + // (used by tools such as GrumPHP) can produce other single-letter prefixes + // ("c"ommit, "i"ndex, "o"bject, "w"ork tree), or "1"/"2" with --no-index. + $vars = $this->consumeRegexp("/diff --git \"?([^\\/\\s]\\/.*?)\"? \"?([^\\/\\s]\\/.*?)\"?\n/"); $oldName = $vars[1]; $newName = $vars[2]; // Get indexes from raw if it exists diff --git a/tests/Gitonomy/Git/Tests/DiffTest.php b/tests/Gitonomy/Git/Tests/DiffTest.php index a79fd4b..47624ac 100644 --- a/tests/Gitonomy/Git/Tests/DiffTest.php +++ b/tests/Gitonomy/Git/Tests/DiffTest.php @@ -224,6 +224,26 @@ public function testThrowErrorOnBlobGetWithoutIndex(): void $this->assertSame('', $file->getNewIndex()); } + public function testMnemonicPrefix(): void + { + $this->expectUserDeprecationMessage('Using Diff::parse without raw information is deprecated. See https://github.com/gitonomy/gitlib/issues/227.'); + + // With `diff.mnemonicPrefix` enabled, git replaces the default "a/" and "b/" + // prefixes with context-specific ones, e.g. "c/" (commit) and "i/" (index) + // for `git diff --cached`. See https://github.com/gitonomy/gitlib/issues/114. + $diff = Diff::parse(<<<'DIFF' + diff --git c/composer.json i/composer.json + index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 + --- c/composer.json + +++ i/composer.json + + DIFF); + $firstFile = $diff->getFiles()[0]; + + $this->assertSame('composer.json', $firstFile->getOldName()); + $this->assertSame('composer.json', $firstFile->getNewName()); + } + public function testEmptyNewFile(): void { $this->expectUserDeprecationMessage('Using Diff::parse without raw information is deprecated. See https://github.com/gitonomy/gitlib/issues/227.');