diff --git a/src/workingcopy/ArcanistSubversionWorkingCopy.php b/src/workingcopy/ArcanistSubversionWorkingCopy.php --- a/src/workingcopy/ArcanistSubversionWorkingCopy.php +++ b/src/workingcopy/ArcanistSubversionWorkingCopy.php @@ -46,6 +46,29 @@ return new self(); } + protected function selectFromNestedWorkingCopies(array $candidates) { + // To select the best working copy in Subversion, we first walk up the + // tree looking for a working copy with an ".arcconfig" file. If we find + // one, this anchors us. + + foreach (array_reverse($candidates) as $candidate) { + $arcconfig = $candidate->getPath('.arcconfig'); + if (Filesystem::pathExists($arcconfig)) { + return $candidate; + } + } + + // If we didn't find one, we select the outermost working copy. This is + // because older versions of Subversion (prior to 1.7) put a ".svn" file + // in every directory, and all versions of Subversion allow you to check + // out any subdirectory of the project as a working copy. + + // We could possibly refine this by testing if the working copy was made + // with a recent version of Subversion and picking the deepest working copy + // if it was, similar to Git and Mercurial. + + return head($candidates); + } } diff --git a/src/workingcopy/ArcanistWorkingCopy.php b/src/workingcopy/ArcanistWorkingCopy.php --- a/src/workingcopy/ArcanistWorkingCopy.php +++ b/src/workingcopy/ArcanistWorkingCopy.php @@ -11,16 +11,10 @@ ->setAncestorClass(__CLASS__) ->execute(); - // Find the outermost directory which is under version control. We go from - // the top because: - // - // - This gives us a more reasonable behavior if you embed one repository - // inside another repository. - // - This handles old Subversion working copies correctly. Before - // SVN 1.7, Subversion put a ".svn/" directory in every subdirectory. - $paths = Filesystem::walkToRoot($path); $paths = array_reverse($paths); + + $candidates = array(); foreach ($paths as $path_key => $ancestor_path) { foreach ($working_types as $working_type) { @@ -34,10 +28,28 @@ $working_copy->path = $ancestor_path; $working_copy->workingDirectory = $path; - return $working_copy; + $candidates[] = $working_copy; } } + // If we've found multiple candidate working copies, we need to pick one. + // We let the innermost working copy pick the best candidate from among + // candidates of the same type. The rules for Git and Mercurial differ + // slightly from the rules for Subversion. + + if ($candidates) { + $deepest = last($candidates); + + foreach ($candidates as $key => $candidate) { + if (get_class($candidate) != get_class($deepest)) { + unset($candidates[$key]); + } + } + $candidates = array_values($candidates); + + return $deepest->selectFromNestedWorkingCopies($candidates); + } + return null; } @@ -92,4 +104,10 @@ )); } + protected function selectFromNestedWorkingCopies(array $candidates) { + // Normally, the best working copy in a stack is the deepest working copy. + // Subversion uses slightly different rules. + return last($candidates); + } + }