芝麻web文件管理V1.00
编辑当前文件:/home/sditechnicalteam/public_html/vendor/gitonomy/gitlib/src/Gitonomy/Git/Repository.php
* (c) Julien DIDIER
* * This source file is subject to the MIT license that is bundled * with this source code in the file LICENSE. */ namespace Gitonomy\Git; use Gitonomy\Git\Diff\Diff; use Gitonomy\Git\Exception\InvalidArgumentException; use Gitonomy\Git\Exception\ProcessException; use Gitonomy\Git\Exception\RuntimeException; use Psr\Log\LoggerInterface; use Symfony\Component\Process\Process; /** * Git repository object. * * Main entry point for browsing a Git repository. * * @author Alexandre Salomé
*/ class Repository { const DEFAULT_DESCRIPTION = "Unnamed repository; edit this file 'description' to name the repository.\n"; /** * Directory containing git files. * * @var string */ protected $gitDir; /** * Working directory. * * @var string */ protected $workingDir; /** * Cache containing all objects of the repository. * * Associative array, indexed by object hash * * @var array */ protected $objects; /** * Reference bag associated to this repository. * * @var ReferenceBag */ protected $referenceBag; /** * Logger (can be null). * * @var LoggerInterface */ protected $logger; /** * Path to git command. */ protected $command; /** * Debug flag, indicating if errors should be thrown. * * @var bool */ protected $debug; /** * Environment variables that should be set for every running process. * * @var array */ protected $environmentVariables; /** * @var bool */ protected $inheritEnvironmentVariables; /** * Timeout that should be set for every running process. * * @var int */ protected $processTimeout; /** * Constructs a new repository. * * Available options are: * * * working_dir : specify where working copy is located (option --work-tree) * * * debug : (default: true) enable/disable minimize errors and reduce log level * * * logger : a logger to use for logging actions (Psr\Log\LoggerInterface) * * * environment_variables : define environment variables for every ran process * * @param string $dir path to git repository * @param array $options array of options values * * @throws InvalidArgumentException The folder does not exists */ public function __construct($dir, $options = []) { $options = array_merge([ 'working_dir' => null, 'debug' => true, 'logger' => null, 'command' => 'git', 'environment_variables' => [], 'inherit_environment_variables' => false, 'process_timeout' => 3600, ], $options); if (null !== $options['logger'] && !$options['logger'] instanceof LoggerInterface) { throw new InvalidArgumentException(sprintf('Argument "logger" passed to Repository should be a Psr\Log\LoggerInterface. A %s was provided', is_object($options['logger']) ? get_class($options['logger']) : gettype($options['logger']))); } $this->logger = $options['logger']; $this->initDir($dir, $options['working_dir']); $this->objects = []; $this->command = $options['command']; $this->debug = (bool) $options['debug']; $this->processTimeout = $options['process_timeout']; if (defined('PHP_WINDOWS_VERSION_BUILD') && isset($_SERVER['PATH']) && !isset($options['environment_variables']['PATH'])) { $options['environment_variables']['PATH'] = $_SERVER['PATH']; } $this->environmentVariables = $options['environment_variables']; $this->inheritEnvironmentVariables = $options['inherit_environment_variables']; if (true === $this->debug && null !== $this->logger) { $this->logger->debug(sprintf('Repository created (git dir: "%s", working dir: "%s")', $this->gitDir, $this->workingDir ?: 'none')); } } /** * Initializes directory attributes on repository:. * * @param string $gitDir directory of a working copy with files checked out * @param string $workingDir directory containing git files (objects, config...) */ private function initDir($gitDir, $workingDir = null) { $realGitDir = realpath($gitDir); if (false === $realGitDir) { throw new InvalidArgumentException(sprintf('Directory "%s" does not exist or is not a directory', $gitDir)); } elseif (!is_dir($realGitDir)) { throw new InvalidArgumentException(sprintf('Directory "%s" does not exist or is not a directory', $realGitDir)); } elseif (null === $workingDir && is_dir($realGitDir.'/.git')) { $workingDir = $realGitDir; $realGitDir = $realGitDir.'/.git'; } $this->gitDir = $realGitDir; $this->workingDir = $workingDir; } /** * Tests if repository is a bare repository. * * @return bool */ public function isBare() { return null === $this->workingDir; } /** * Returns the HEAD resolved as a commit. * * @return Commit|null returns a Commit or ``null`` if repository is empty */ public function getHeadCommit() { $head = $this->getHead(); if ($head instanceof Reference) { return $head->getCommit(); } return $head; } /** * @throws RuntimeException Unable to find file HEAD (debug-mode only) * * @return Reference|Commit|null current HEAD object or null if error occurs */ public function getHead() { $file = $this->gitDir.'/HEAD'; if (!file_exists($file)) { $message = sprintf('Unable to find HEAD file ("%s")', $file); if (null !== $this->logger) { $this->logger->error($message); } if (true === $this->debug) { throw new RuntimeException($message); } } $content = trim(file_get_contents($file)); if (null !== $this->logger) { $this->logger->debug('HEAD file read: '.$content); } if (preg_match('/^ref: (.+)$/', $content, $vars)) { return $this->getReferences()->get($vars[1]); } elseif (preg_match('/^[0-9a-f]{40}$/', $content)) { return $this->getCommit($content); } $message = sprintf('Unexpected HEAD file content (file: %s). Content of file: %s', $file, $content); if (null !== $this->logger) { $this->logger->error($message); } if (true === $this->debug) { throw new RuntimeException($message); } } /** * @return bool */ public function isHeadDetached() { return !$this->isHeadAttached(); } /** * @return bool */ public function isHeadAttached() { return $this->getHead() instanceof Reference; } /** * Returns the path to the git repository. * * @return string A directory path */ public function getPath() { return $this->workingDir === null ? $this->gitDir : $this->workingDir; } /** * Returns the directory containing git files (git-dir). * * @return string */ public function getGitDir() { return $this->gitDir; } /** * Returns the work-tree directory. This may be null if repository is * bare. * * @return string path to repository or null if repository is bare */ public function getWorkingDir() { return $this->workingDir; } /** * Instanciates a revision. * * @param string $name Name of the revision * * @return Revision */ public function getRevision($name) { return new Revision($this, $name); } /** * @return WorkingCopy */ public function getWorkingCopy() { return new WorkingCopy($this); } /** * Returns the reference list associated to the repository. * * @param bool $reload Reload references from the filesystem * * @return ReferenceBag */ public function getReferences($reload = false) { if (null === $this->referenceBag || $reload) { $this->referenceBag = new ReferenceBag($this); } return $this->referenceBag; } /** * Instanciates a commit object or fetches one from the cache. * * @param string $hash A commit hash, with a length of 40 * * @return Commit */ public function getCommit($hash) { if (!isset($this->objects[$hash])) { $this->objects[$hash] = new Commit($this, $hash); } return $this->objects[$hash]; } /** * Instanciates a tree object or fetches one from the cache. * * @param string $hash A tree hash, with a length of 40 * * @return Tree */ public function getTree($hash) { if (!isset($this->objects[$hash])) { $this->objects[$hash] = new Tree($this, $hash); } return $this->objects[$hash]; } /** * Instanciates a blob object or fetches one from the cache. * * @param string $hash A blob hash, with a length of 40 * * @return Blob */ public function getBlob($hash) { if (!isset($this->objects[$hash])) { $this->objects[$hash] = new Blob($this, $hash); } return $this->objects[$hash]; } /** * @return Blame */ public function getBlame($revision, $file, $lineRange = null) { if (is_string($revision)) { $revision = $this->getRevision($revision); } return new Blame($this, $revision, $file, $lineRange); } /** * Returns log for a given set of revisions and paths. * * All those values can be null, meaning everything. * * @param array $revisions An array of revisions to show logs from. Can be * any text value type * @param array $paths Restrict log to modifications occurring on given * paths. * @param int $offset Start from a given offset in results. * @param int $limit Limit number of total results. * * @return Log */ public function getLog($revisions = null, $paths = null, $offset = null, $limit = null) { return new Log($this, $revisions, $paths, $offset, $limit); } /** * @return Diff */ public function getDiff($revisions) { if (null !== $revisions && !$revisions instanceof RevisionList) { $revisions = new RevisionList($this, $revisions); } $args = array_merge(['-r', '-p', '-m', '-M', '--no-commit-id', '--full-index'], $revisions->getAsTextArray()); $diff = Diff::parse($this->run('diff', $args)); $diff->setRepository($this); return $diff; } /** * Returns the size of repository, in kilobytes. * * @return int A sum, in kilobytes */ public function getSize() { $totalBytes = 0; $path = realpath($this->gitDir); if ($path && file_exists($path)) { foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS)) as $object) { $totalBytes += $object->getSize(); } } return (int) ($totalBytes / 1000 + 0.5); } /** * Executes a shell command on the repository, using PHP pipes. * * @param string $command The command to execute */ public function shell($command, array $env = []) { $argument = sprintf('%s \'%s\'', $command, $this->gitDir); $prefix = ''; foreach ($env as $name => $value) { $prefix .= sprintf('export %s=%s;', escapeshellarg($name), escapeshellarg($value)); } proc_open($prefix.'git shell -c '.escapeshellarg($argument), [STDIN, STDOUT, STDERR], $pipes); } /** * Returns the hooks object. * * @return Hooks */ public function getHooks() { return new Hooks($this); } /** * Returns description of repository from description file in git directory. * * @return string The description */ public function getDescription() { $file = $this->gitDir.'/description'; $exists = is_file($file); if (null !== $this->logger && true === $this->debug) { if (false === $exists) { $this->logger->debug(sprintf('no description file in repository ("%s")', $file)); } else { $this->logger->debug(sprintf('reading description file in repository ("%s")', $file)); } } if (false === $exists) { return static::DEFAULT_DESCRIPTION; } return file_get_contents($this->gitDir.'/description'); } /** * Tests if repository has a custom set description. * * @return bool */ public function hasDescription() { return static::DEFAULT_DESCRIPTION !== $this->getDescription(); } /** * Changes the repository description (file description in git-directory). * * @return Repository the current repository */ public function setDescription($description) { $file = $this->gitDir.'/description'; if (null !== $this->logger && true === $this->debug) { $this->logger->debug(sprintf('change description file content to "%s" (file: %s)', $description, $file)); } file_put_contents($file, $description); return $this; } /** * This command is a facility command. You can run any command * directly on git repository. * * @param string $command Git command to run (checkout, branch, tag) * @param array $args Arguments of git command * * @throws RuntimeException Error while executing git command (debug-mode only) * * @return string Output of a successful process or null if execution failed and debug-mode is disabled. */ public function run($command, $args = []) { $process = $this->getProcess($command, $args); if ($this->logger) { $this->logger->info(sprintf('run command: %s "%s" ', $command, implode(' ', $args))); $before = microtime(true); } $process->run(); $output = $process->getOutput(); if ($this->logger && $this->debug) { $duration = microtime(true) - $before; $this->logger->debug(sprintf('last command (%s) duration: %sms', $command, sprintf('%.2f', $duration * 1000))); $this->logger->debug(sprintf('last command (%s) return code: %s', $command, $process->getExitCode())); $this->logger->debug(sprintf('last command (%s) output: %s', $command, $output)); } if (!$process->isSuccessful()) { $error = sprintf("error while running %s\n output: \"%s\"", $command, $process->getErrorOutput()); if ($this->logger) { $this->logger->error($error); } if ($this->debug) { throw new ProcessException($process); } return; } return $output; } /** * Set repository logger. * * @param LoggerInterface $logger A logger * * @return Repository The current repository */ public function setLogger(LoggerInterface $logger) { $this->logger = $logger; return $this; } /** * Returns repository logger. * * @return LoggerInterface the logger or null */ public function getLogger() { return $this->logger; } /** * Clones the current repository to a new directory and return instance of new repository. * * @param string $path path to the new repository in which current repository will be cloned * @param bool $bare flag indicating if repository is bare or has a working-copy * * @return Repository the newly created repository */ public function cloneTo($path, $bare = true, array $options = []) { return Admin::cloneTo($path, $this->gitDir, $bare, $options); } /** * This internal method is used to create a process object. * * Made private to be sure that process creation is handled through the run method. * run method ensures logging and debug. * * @see self::run */ private function getProcess($command, $args = []) { $base = [$this->command, '--git-dir', $this->gitDir]; if ($this->workingDir) { $base = array_merge($base, ['--work-tree', $this->workingDir]); } $base[] = $command; $process = new Process(array_merge($base, $args)); if ($this->inheritEnvironmentVariables) { $process->setEnv(array_replace($_SERVER, $this->environmentVariables)); } else { $process->setEnv($this->environmentVariables); } $process->setTimeout($this->processTimeout); $process->setIdleTimeout($this->processTimeout); return $process; } }