public static function newAPIFromWorkingCopyIdentity(ArcanistWorkingCopyIdentity $working_copy)
 {
     $root = $working_copy->getProjectRoot();
     if (!$root) {
         throw new ArcanistUsageException("There is no readable '.arcconfig' file in the working directory or " . "any parent directory. Create an '.arcconfig' file to configure arc.");
     }
     // check if we're in an svn working copy
     list($err) = exec_manual('svn info');
     if (!$err) {
         $api = newv('ArcanistSubversionAPI', array($root));
         $api->workingCopyIdentity = $working_copy;
         return $api;
     }
     if (Filesystem::pathExists($root . '/.hg')) {
         $api = newv('ArcanistMercurialAPI', array($root));
         $api->workingCopyIdentity = $working_copy;
         return $api;
     }
     $git_root = self::discoverGitBaseDirectory($root);
     if ($git_root) {
         if (!Filesystem::pathsAreEquivalent($root, $git_root)) {
             throw new ArcanistUsageException("'.arcconfig' file is located at '{$root}', but working copy root " . "is '{$git_root}'. Move '.arcconfig' file to the working copy root.");
         }
         $api = newv('ArcanistGitAPI', array($root));
         $api->workingCopyIdentity = $working_copy;
         return $api;
     }
     throw new ArcanistUsageException("The current working directory is not part of a working copy for a " . "supported version control system (svn, git or mercurial).");
 }
 protected function executeUpdate(PhabricatorRepository $repository, $local_path)
 {
     // Run a bunch of sanity checks to detect people checking out repositories
     // inside other repositories, making empty directories, pointing the local
     // path at some random file or path, etc.
     list($err, $stdout) = $repository->execLocalCommand('rev-parse --show-toplevel');
     if ($err) {
         // Try to raise a more tailored error message in the more common case
         // of the user creating an empty directory. (We could try to remove it,
         // but might not be able to, and it's much simpler to raise a good
         // message than try to navigate those waters.)
         if (is_dir($local_path)) {
             $files = Filesystem::listDirectory($local_path, $include_hidden = true);
             if (!$files) {
                 throw new Exception("Expected to find a git repository at '{$local_path}', but there " . "is an empty directory there. Remove the directory: the daemon " . "will run 'git clone' for you.");
             }
         }
         throw new Exception("Expected to find a git repository at '{$local_path}', but there is " . "a non-repository directory (with other stuff in it) there. Move or " . "remove this directory (or reconfigure the repository to use a " . "different directory), and then either clone a repository yourself " . "or let the daemon do it.");
     } else {
         $repo_path = rtrim($stdout, "\n");
         if (empty($repo_path)) {
             throw new Exception("Expected to find a git repository at '{$local_path}', but " . "there was no result from `git rev-parse --show-toplevel`. " . "Something is misconfigured or broken. The git repository " . "may be inside a '.git/' directory.");
         }
         if (!Filesystem::pathsAreEquivalent($repo_path, $local_path)) {
             throw new Exception("Expected to find repo at '{$local_path}', but the actual " . "git repository root for this directory is '{$repo_path}'. " . "Something is misconfigured. The repository's 'Local Path' should " . "be set to some place where the daemon can check out a working " . "copy, and should not be inside another git repository.");
         }
     }
     // This is a local command, but needs credentials.
     $future = $repository->getRemoteCommandFuture('fetch --all --prune');
     $future->setCWD($local_path);
     $future->resolvex();
 }
 /**
  * @task git
  */
 private function executeGitUpdate()
 {
     $repository = $this->getRepository();
     list($err, $stdout) = $repository->execLocalCommand('rev-parse --show-toplevel');
     $message = null;
     $path = $repository->getLocalPath();
     if ($err) {
         // Try to raise a more tailored error message in the more common case
         // of the user creating an empty directory. (We could try to remove it,
         // but might not be able to, and it's much simpler to raise a good
         // message than try to navigate those waters.)
         if (is_dir($path)) {
             $files = Filesystem::listDirectory($path, $include_hidden = true);
             if (!$files) {
                 $message = pht("Expected to find a git repository at '%s', but there " . "is an empty directory there. Remove the directory: the daemon " . "will run '%s' for you.", $path, 'git clone');
             } else {
                 $message = pht("Expected to find a git repository at '%s', but there is " . "a non-repository directory (with other stuff in it) there. Move " . "or remove this directory (or reconfigure the repository to use a " . "different directory), and then either clone a repository " . "yourself or let the daemon do it.", $path);
             }
         } else {
             if (is_file($path)) {
                 $message = pht("Expected to find a git repository at '%s', but there is a " . "file there instead. Remove it and let the daemon clone a " . "repository for you.", $path);
             } else {
                 $message = pht("Expected to find a git repository at '%s', but did not.", $path);
             }
         }
     } else {
         $repo_path = rtrim($stdout, "\n");
         if (empty($repo_path)) {
             // This can mean one of two things: we're in a bare repository, or
             // we're inside a git repository inside another git repository. Since
             // the first is dramatically more likely now that we perform bare
             // clones and I don't have a great way to test for the latter, assume
             // we're OK.
         } else {
             if (!Filesystem::pathsAreEquivalent($repo_path, $path)) {
                 $err = true;
                 $message = pht("Expected to find repo at '%s', but the actual git repository root " . "for this directory is '%s'. Something is misconfigured. " . "The repository's 'Local Path' should be set to some place where " . "the daemon can check out a working copy, " . "and should not be inside another git repository.", $path, $repo_path);
             }
         }
     }
     if ($err && $repository->canDestroyWorkingCopy()) {
         phlog(pht("Repository working copy at '%s' failed sanity check; " . "destroying and re-cloning. %s", $path, $message));
         Filesystem::remove($path);
         $this->executeGitCreate();
     } else {
         if ($err) {
             throw new Exception($message);
         }
     }
     $retry = false;
     do {
         // This is a local command, but needs credentials.
         if ($repository->isWorkingCopyBare()) {
             // For bare working copies, we need this magic incantation.
             $future = $repository->getRemoteCommandFuture('fetch origin %s --prune', '+refs/heads/*:refs/heads/*');
         } else {
             $future = $repository->getRemoteCommandFuture('fetch --all --prune');
         }
         $future->setCWD($path);
         list($err, $stdout, $stderr) = $future->resolve();
         if ($err && !$retry && $repository->canDestroyWorkingCopy()) {
             $retry = true;
             // Fix remote origin url if it doesn't match our configuration
             $origin_url = $repository->execLocalCommand('config --get remote.origin.url');
             $remote_uri = $repository->getRemoteURIEnvelope();
             if ($origin_url != $remote_uri->openEnvelope()) {
                 $repository->execLocalCommand('remote set-url origin %P', $remote_uri);
             }
         } else {
             if ($err) {
                 throw new CommandException(pht('Failed to fetch changes!'), $future->getCommand(), $err, $stdout, $stderr);
             } else {
                 $retry = false;
             }
         }
     } while ($retry);
 }