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); }