1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-12-23 14:00:55 +01:00

Add trailing tabs when generating synthetic Git diffs for files with spaces

Summary:
Fixes T8768. See PHI294. See that task for more details.

Git, Mercurial, `diff`, and `patch` have conspired to make things weird. To correctly handle files with spaces in the way everything else does and expects, we need to emit semantic trailing whitespace literals.

Test Plan:
- Created a file with spaces in it in a Mercurial repositroy, committed it, diffed it into a revision.
- Used `arc patch` to apply the change to a clean copy of the repository.
- Before patch: Mercurial incorrectly creates a file named `X`, not a file named `X Y.txt`.
- After patch: `arc patch` commit is identical to genuine commit.
- Also added test coverage. The other general behaviors here are fairly well covered already.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T8768

Differential Revision: https://secure.phabricator.com/D18869
This commit is contained in:
epriestley 2018-01-16 08:15:08 -08:00
parent 3d06bd4c56
commit 2e02332216
2 changed files with 52 additions and 0 deletions

View file

@ -424,6 +424,9 @@ final class ArcanistBundle extends Phobject {
$cur_target = 'b/'.$cur_path; $cur_target = 'b/'.$cur_path;
} }
$old_target = $this->encodeGitTargetPath($old_target);
$cur_target = $this->encodeGitTargetPath($cur_target);
$result[] = "diff --git {$old_index} {$cur_index}".$eol; $result[] = "diff --git {$old_index} {$cur_index}".$eol;
if ($type == ArcanistDiffChangeType::TYPE_ADD) { if ($type == ArcanistDiffChangeType::TYPE_ADD) {
@ -591,6 +594,24 @@ final class ArcanistBundle extends Phobject {
return $results; return $results;
} }
private function encodeGitTargetPath($path) {
// See T8768. If a target path contains spaces, it must be terminated with
// a tab. If we don't do this, Mercurial has the wrong behavior when
// applying the patch. This results in a semantic trailing whitespace
// character:
//
// +++ b/X Y.txt\t
//
// Everyone is at fault here and there are no winners.
if (strpos($path, ' ') !== false) {
$path = $path."\t";
}
return $path;
}
private function getOldPath(ArcanistDiffChange $change) { private function getOldPath(ArcanistDiffChange $change) {
$old_path = $change->getOldPath(); $old_path = $change->getOldPath();
$type = $change->getType(); $type = $change->getType();

View file

@ -33,6 +33,37 @@ final class ArcanistBundleTestCase extends PhutilTestCase {
return ArcanistBundle::newFromDiff($diff); return ArcanistBundle::newFromDiff($diff);
} }
public function testTabEncoding() {
// See T8768. Test that we add semantic trailing tab literals to diffs
// touching files with spaces in them. This is a pain to encode using the
// support toolset here so just do it manually.
// Note that the "b/X Y.txt" line has a trailing tab literal.
$diff = <<<EODIFF
diff --git a/X Y.txt b/X Y.txt
new file mode 100644
--- /dev/null
+++ b/X Y.txt\t
@@ -0,0 +1 @@
+quack
EODIFF;
$bundle = ArcanistBundle::newFromDiff($diff);
$changes = $bundle->getChanges();
$this->assertEqual(1, count($changes));
// The path should parse as "X Y.txt" despite the trailing tab.
$change = head($changes);
$this->assertEqual('X Y.txt', $change->getCurrentPath());
// The tab should be restored when the diff is output again.
$this->assertEqual($diff, $bundle->toGitPatch());
}
/** /**
* Unarchive a saved git repository and apply each commit as though via * Unarchive a saved git repository and apply each commit as though via
* "arc patch", verifying that the resulting tree hash is identical to the * "arc patch", verifying that the resulting tree hash is identical to the