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:
parent
3d06bd4c56
commit
2e02332216
2 changed files with 52 additions and 0 deletions
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue