New plugin repo is cloned to /store/pluginrepos/REPONAME for analysis

This commit is contained in:
Andrew Manning
2016-05-01 22:29:51 -04:00
parent b1ae4d776c
commit c2d15e6c3b
126 changed files with 18177 additions and 15 deletions

View File

@@ -0,0 +1,123 @@
<?php
namespace PHPGit;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Process\ProcessBuilder;
/**
* Base class for git commands
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
abstract class Command
{
/**
* @var Git
*/
protected $git;
/**
* @param Git $git
*/
public function __construct(Git $git)
{
$this->git = $git;
}
/**
* Returns the combination of the default and the passed options
*
* @param array $options An array of options
*
* @return array
*/
public function resolve(array $options = array())
{
$resolver = new OptionsResolver();
$this->setDefaultOptions($resolver);
return $resolver->resolve($options);
}
/**
* Sets the default options
*
* @param OptionsResolverInterface $resolver The resolver for the options
*
* @codeCoverageIgnore
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
}
/**
* Split string by new line or null(\0)
*
* @param string $input The string to split
* @param bool $useNull True to split by new line, otherwise null
*
* @return array
*/
protected function split($input, $useNull = false)
{
if ($useNull) {
$pattern = '/\0/';
} else {
$pattern = '/\r?\n/';
}
return preg_split($pattern, rtrim($input), -1, PREG_SPLIT_NO_EMPTY);
}
/**
* Adds boolean options to command arguments
*
* @param ProcessBuilder $builder A ProcessBuilder object
* @param array $options An array of options
* @param array $optionNames The names of options to add
*/
protected function addFlags(ProcessBuilder $builder, array $options = array(), array $optionNames = null)
{
if ($optionNames) {
foreach ($optionNames as $name) {
if (isset($options[$name]) && is_bool($options[$name]) && $options[$name]) {
$builder->add('--' . $name);
}
}
} else {
foreach ($options as $name => $option) {
if ($option) {
$builder->add('--' . $name);
}
}
}
}
/**
* Adds options with values to command arguments
*
* @param ProcessBuilder $builder A ProcessBuilder object
* @param array $options An array of options
* @param array $optionNames The names of options to add
*/
protected function addValues(ProcessBuilder $builder, array $options = array(), array $optionNames = null)
{
if ($optionNames) {
foreach ($optionNames as $name) {
if (isset($options[$name]) && $options[$name]) {
$builder->add('--' . $name . '=' . $options[$name]);
}
}
} else {
foreach ($options as $name => $option) {
if ($option) {
$builder->add('--' . $name . '=' . $option);
}
}
}
}
}

View File

@@ -0,0 +1,75 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Add file contents to the index - `git add`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class AddCommand extends Command
{
/**
* Add file contents to the index
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->add('file.txt');
* $git->add('file.txt', ['force' => false, 'ignore-errors' => false);
* ```
*
* ##### Options
*
* - **force** (_boolean_) Allow adding otherwise ignored files
* - **ignore-errors** (_boolean_) Do not abort the operation
*
* @param string|array|\Traversable $file Files to add content from
* @param array $options [optional] An array of options {@see AddCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function __invoke($file, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('add');
$this->addFlags($builder, $options);
if (!is_array($file) && !($file instanceof \Traversable)) {
$file = array($file);
}
foreach ($file as $value) {
$builder->add($value);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **force** (_boolean_) Allow adding otherwise ignored files
* - **ignore-errors** (_boolean_) Do not abort the operation
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
//'dry-run' => false,
'force' => false,
'ignore-errors' => false,
//'ignore-missing' => false,
));
}
}

View File

@@ -0,0 +1,96 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Create an archive of files from a named tree - `git archive`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class ArchiveCommand extends Command
{
/**
* Create an archive of files from a named tree
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->archive('repo.zip', 'master', null, ['format' => 'zip']);
* ```
*
* ##### Options
*
* - **format** (_boolean_) Format of the resulting archive: tar or zip
* - **prefix** (_boolean_) Prepend prefix/ to each filename in the archive
*
* @param string $file The filename
* @param string $tree [optional] The tree or commit to produce an archive for
* @param string|array|\Traversable $path [optional] If one or more paths are specified, only these are included
* @param array $options [optional] An array of options {@see ArchiveCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function __invoke($file, $tree = null, $path = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('archive');
if ($options['format']) {
$builder->add('--format=' . $options['format']);
}
if ($options['prefix']) {
$builder->add('--prefix=' . $options['prefix']);
}
$builder->add('-o')->add($file);
if ($tree) {
$builder->add($tree);
}
if (!is_array($path) && !($path instanceof \Traversable)) {
$path = array($path);
}
foreach ($path as $value) {
$builder->add($value);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **format** (_boolean_) Format of the resulting archive: tar or zip
* - **prefix** (_boolean_) Prepend prefix/ to each filename in the archive
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'format' => null,
'prefix' => null
));
$resolver->setAllowedTypes(array(
'format' => array('null', 'string'),
'prefix' => array('null', 'string')
));
$resolver->setAllowedValues(array(
'format' => array('tar', 'zip')
));
}
}

View File

@@ -0,0 +1,229 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* List, create, or delete branches - `git branch`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class BranchCommand extends Command
{
/**
* Returns an array of both remote-tracking branches and local branches
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $branches = $git->branch();
* ```
*
* ##### Output Example
*
* ```
* [
* 'master' => ['current' => true, 'name' => 'master', 'hash' => 'bf231bb', 'title' => 'Initial Commit'],
* 'origin/master' => ['current' => false, 'name' => 'origin/master', 'alias' => 'remotes/origin/master']
* ]
* ```
*
* ##### Options
*
* - **all** (_boolean_) List both remote-tracking branches and local branches
* - **remotes** (_boolean_) List the remote-tracking branches
*
* @param array $options [optional] An array of options {@see BranchCommand::setDefaultOptions}
*
* @throws GitException
* @return array
*/
public function __invoke(array $options = array())
{
$options = $this->resolve($options);
$branches = array();
$builder = $this->getProcessBuilder()
->add('-v')->add('--abbrev=7');
if ($options['remotes']) {
$builder->add('--remotes');
}
if ($options['all']) {
$builder->add('--all');
}
$process = $builder->getProcess();
$this->git->run($process);
$lines = preg_split('/\r?\n/', rtrim($process->getOutput()), -1, PREG_SPLIT_NO_EMPTY);
foreach ($lines as $line) {
$branch = array();
preg_match('/(?<current>\*| ) (?<name>[^\s]+) +((?:->) (?<alias>[^\s]+)|(?<hash>[0-9a-z]{7}) (?<title>.*))/', $line, $matches);
$branch['current'] = ($matches['current'] == '*');
$branch['name'] = $matches['name'];
if (isset($matches['hash'])) {
$branch['hash'] = $matches['hash'];
$branch['title'] = $matches['title'];
} else {
$branch['alias'] = $matches['alias'];
}
$branches[$matches['name']] = $branch;
}
return $branches;
}
/**
* Creates a new branch head named **$branch** which points to the current HEAD, or **$startPoint** if given
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->branch->create('bugfix'); // from current HEAD
* $git->branch->create('patch-1', 'a092bf7s'); // from commit
* $git->branch->create('1.0.x-fix', 'v1.0.2'); // from tag
* ```
*
* ##### Options
*
* - **force** (_boolean_) Reset **$branch** to **$startPoint** if **$branch** exists already
*
* @param string $branch The name of the branch to create
* @param string $startPoint [optional] The new branch head will point to this commit.
* It may be given as a branch name, a commit-id, or a tag.
* If this option is omitted, the current HEAD will be used instead.
* @param array $options [optional] An array of options {@see BranchCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function create($branch, $startPoint = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->getProcessBuilder();
if ($options['force']) {
$builder->add('-f');
}
$builder->add($branch);
if ($startPoint) {
$builder->add($startPoint);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Move/rename a branch and the corresponding reflog
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->branch->move('bugfix', '2.0');
* ```
*
* ##### Options
*
* - **force** (_boolean_) Move/rename a branch even if the new branch name already exists
*
* @param string $branch The name of an existing branch to rename
* @param string $newBranch The new name for an existing branch
* @param array $options [optional] An array of options {@see BranchCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function move($branch, $newBranch, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->getProcessBuilder();
if ($options['force']) {
$builder->add('-M');
} else {
$builder->add('-m');
}
$builder->add($branch)->add($newBranch);
$this->git->run($builder->getProcess());
return true;
}
/**
* Delete a branch
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->branch->delete('2.0');
* ```
*
* The branch must be fully merged in its upstream branch, or in HEAD if no upstream was set with --track or --set-upstream.
*
* ##### Options
*
* - **force** (_boolean_) Delete a branch irrespective of its merged status
*
* @param string $branch The name of the branch to delete
* @param array $options [optional] An array of options {@see BranchCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function delete($branch, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->getProcessBuilder();
if ($options['force']) {
$builder->add('-D');
} else {
$builder->add('-d');
}
$builder->add($branch);
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **force** (_boolean_) Reset <branchname> to <startpoint> if <branchname> exists already
* - **all** (_boolean_) List both remote-tracking branches and local branches
* - **remotes** (_boolean_) List or delete (if used with delete()) the remote-tracking branches
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'force' => false,
'all' => false,
'remotes' => false,
));
}
/**
* @return \Symfony\Component\Process\ProcessBuilder
*/
protected function getProcessBuilder()
{
return $this->git->getProcessBuilder()
->add('branch');
}
}

View File

@@ -0,0 +1,91 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
/**
* Provide content or type and size information for repository objects - `git cat-file`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class CatCommand extends Command
{
/**
* Returns the contents of blob object
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $contents = $git->cat->blob('e69de29bb2d1d6434b8b29ae775ad8');
* ```
*
* @param string $object The name of the blob object to show
*
* @throws GitException
* @return string
*/
public function blob($object)
{
$process = $this->git->getProcessBuilder()
->add('cat-file')
->add('blob')
->add($object)
->getProcess();
return $this->git->run($process);
}
/**
* Returns the object type identified by **$object**
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $type = $git->cat->type('e69de29bb2d1d6434b8b29ae775ad8');
* ```
*
* @param string $object The name of the object to show
*
* @throws GitException
* @return string
*/
public function type($object)
{
$process = $this->git->getProcessBuilder()
->add('cat-file')
->add('-t')
->add($object)
->getProcess();
return trim($this->git->run($process));
}
/**
* Returns the object size identified by **$object**
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $type = $git->cat->size('e69de29bb2d1d6434b8b29ae775ad8');
* ```
*
* @param string $object The name of the object to show
*
* @throws GitException
* @return string
*/
public function size($object)
{
$process = $this->git->getProcessBuilder()
->add('cat-file')
->add('-s')
->add($object)
->getProcess();
return trim($this->git->run($process));
}
}

View File

@@ -0,0 +1,145 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Checkout a branch or paths to the working tree - `git checkout`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class CheckoutCommand extends Command
{
/**
* Switches branches by updating the index, working tree, and HEAD to reflect the specified branch or commit
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->checkout('develop');
* ```
*
* ##### Options
*
* - **force** (_boolean_) Proceed even if the index or the working tree differs from HEAD
* - **merge** (_boolean_) Merges local modification
*
* @param string $branch Branch to checkout
* @param array $options [optional] An array of options {@see CheckoutCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function __invoke($branch, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('checkout');
$this->addFlags($builder, $options, array('force', 'merge'));
$builder->add($branch);
$this->git->run($builder->getProcess());
return true;
}
/**
* Create a new branch and checkout
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->checkout->create('patch-1');
* $git->checkout->create('patch-2', 'develop');
* ```
*
* ##### Options
*
* - **force** (_boolean_) Proceed even if the index or the working tree differs from HEAD
*
* @param string $branch Branch to checkout
* @param string $startPoint The name of a commit at which to start the new branch
* @param array $options [optional] An array of options {@see CheckoutCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function create($branch, $startPoint = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('checkout')
->add('-b');
$this->addFlags($builder, $options, array('force', 'merge'));
$builder->add($branch);
if ($startPoint) {
$builder->add($startPoint);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Create a new orphan branch, named <new_branch>, started from <start_point> and switch to it
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->checkout->orphan('gh-pages');
* ```
*
* ##### Options
*
* - **force** (_boolean_) Proceed even if the index or the working tree differs from HEAD
*
* @param string $branch Branch to checkout
* @param string $startPoint [optional] The name of a commit at which to start the new branch
* @param array $options [optional] An array of options {@see CheckoutCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function orphan($branch, $startPoint = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('checkout');
$this->addFlags($builder, $options, array('force', 'merge'));
$builder->add('--orphan')->add($branch);
if ($startPoint) {
$builder->add($startPoint);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **force** (_boolean_) Proceed even if the index or the working tree differs from HEAD
* - **merge** (_boolean_) Merges local modification
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'force' => false,
'merge' => false
));
}
}

View File

@@ -0,0 +1,71 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Clone a repository into a new directory - `git clone`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class CloneCommand extends Command
{
/**
* Clone a repository into a new directory
*
* ``` php
* $git = new PHPGit\Git();
* $git->clone('https://github.com/kzykhys/PHPGit.git', '/path/to/repo');
* ```
*
* ##### Options
*
* - **shared** (_boolean_) Starts out without any object of its own
* - **bare** (_boolean_) Make a bare GIT repository
*
* @param string $repository The repository to clone from
* @param string $path [optional] The name of a new directory to clone into
* @param array $options [optional] An array of options {@see CloneCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function __invoke($repository, $path = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('clone')
->add('--quiet');
$this->addFlags($builder, $options);
$builder->add($repository);
if ($path) {
$builder->add($path);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **shared** (_boolean_) Starts out without any object of its own
* - **bare** (_boolean_) Make a bare GIT repository
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'shared' => false,
'bare' => false
));
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Record changes to the repository - `git commit`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class CommitCommand extends Command
{
/**
* Record changes to the repository
*
* ``` php
* $git = new PHPGit\Git();
* $git->clone('https://github.com/kzykhys/PHPGit.git', '/path/to/repo');
* $git->setRepository('/path/to/repo');
* $git->add('README.md');
* $git->commit('Fixes README.md');
* ```
*
* ##### Options
*
* - **all** (_boolean_) Stage files that have been modified and deleted
* - **reuse-message** (_string_) Take an existing commit object, and reuse the log message and the authorship information (including the timestamp) when creating the commit
* - **squash** (_string_) Construct a commit message for use with rebase --autosquash
* - **author** (_string_) Override the commit author
* - **date** (_string_) Override the author date used in the commit
* - **cleanup** (_string_) Can be one of verbatim, whitespace, strip, and default
* - **amend** (_boolean_) Used to amend the tip of the current branch
*
* @param string $message Use the given <$msg> as the commit message
* @param array $options [optional] An array of options {@see CloneCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function __invoke($message, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('commit')
->add('-m')->add($message);
$this->addFlags($builder, $options, array('all', 'amend'));
$this->addValues($builder, $options, array('reuse-message', 'squash', 'author', 'date', 'cleanup'));
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **all** (_boolean_) Stage files that have been modified and deleted
* - **reuse-message** (_string_) Take an existing commit object, and reuse the log message and the authorship information (including the timestamp) when creating the commit
* - **squash** (_string_) Construct a commit message for use with rebase --autosquash
* - **author** (_string_) Override the commit author
* - **date** (_string_) Override the author date used in the commit
* - **cleanup** (_string_) Can be one of verbatim, whitespace, strip, and default
* - **amend** (_boolean_) Used to amend the tip of the current branch
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'all' => false,
'reuse-message' => null,
'squash' => null,
'author' => null,
'date' => null,
'cleanup' => null,
'amend' => false
));
$resolver->setAllowedValues(array(
'cleanup' => array(null, 'default', 'verbatim', 'whitespace', 'strip')
));
}
}

View File

@@ -0,0 +1,132 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Get and set repository or global options - `git config`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class ConfigCommand extends Command
{
/**
* Returns all variables set in config file
*
*
* ##### Options
*
* - **global** (_boolean_) Read or write configuration options for the current user
* - **system** (_boolean_) Read or write configuration options for all users on the current machine
*
* @param array $options [optional] An array of options {@see ConfigCommand::setDefaultOptions}
*
* @throws GitException
* @return array
*/
public function __invoke(array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('config')
->add('--list')
->add('--null');
$this->addFlags($builder, $options, array('global', 'system'));
$config = array();
$output = $this->git->run($builder->getProcess());
$lines = $this->split($output, true);
foreach ($lines as $line) {
list($name, $value) = explode("\n", $line, 2);
if (isset($config[$name])) {
$config[$name] .= "\n" . $value;
} else {
$config[$name] = $value;
}
}
return $config;
}
/**
* Set an option
*
* ##### Options
*
* - **global** (_boolean_) Read or write configuration options for the current user
* - **system** (_boolean_) Read or write configuration options for all users on the current machine
*
* @param string $name The name of the option
* @param string $value The value to set
* @param array $options [optional] An array of options {@see ConfigCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function set($name, $value, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('config');
$this->addFlags($builder, $options, array('global', 'system'));
$builder->add($name)->add($value);
$process = $builder->getProcess();
$this->git->run($process);
return true;
}
/**
* Adds a new line to the option without altering any existing values
*
* ##### Options
*
* - **global** (_boolean_) Read or write configuration options for the current user
* - **system** (_boolean_) Read or write configuration options for all users on the current machine
*
* @param string $name The name of the option
* @param string $value The value to add
* @param array $options [optional] An array of options {@see ConfigCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function add($name, $value, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('config');
$this->addFlags($builder, $options, array('global', 'system'));
$builder->add('--add')->add($name)->add($value);
$process = $builder->getProcess();
$this->git->run($process);
return true;
}
/**
* {@inheritdoc}
*
* - **global** (_boolean_) Read or write configuration options for the current user
* - **system** (_boolean_) Read or write configuration options for all users on the current machine
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'global' => false,
'system' => false,
));
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Show the most recent tag that is reachable from a commit - `git describe`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class DescribeCommand extends Command
{
/**
* Returns the most recent tag that is reachable from a commit
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->tag->create('v1.0.0');
* $git->commit('Fixes #14');
* echo $git->describe('HEAD', ['tags' => true]);
* ```
*
* ##### Output Example
*
* ```
* v1.0.0-1-g7049efc
* ```
*
* ##### Options
*
* - **all** (_boolean_) Enables matching any known branch, remote-tracking branch, or lightweight tag
* - **tags** (_boolean_) Enables matching a lightweight (non-annotated) tag
* - **always** (_boolean_) Show uniquely abbreviated commit object as fallback
*
* @param string $committish [optional] Committish object names to describe.
* @param array $options [optional] An array of options {@see DescribeCommand::setDefaultOptions}
*
* @return string
*/
public function __invoke($committish = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('describe');
$this->addFlags($builder, $options, array());
if ($committish) {
$builder->add($committish);
}
return trim($this->git->run($builder->getProcess()));
}
/**
* Equivalent to $git->describe($committish, ['tags' => true]);
*
* @param string $committish [optional] Committish object names to describe.
* @param array $options [optional] An array of options {@see DescribeCommand::setDefaultOptions}
*
* @return string
*/
public function tags($committish = null, array $options = array())
{
$options['tags'] = true;
return $this->__invoke($committish, $options);
}
/**
* {@inheritdoc}
*
* - **all** (_boolean_) Enables matching any known branch, remote-tracking branch, or lightweight tag
* - **tags** (_boolean_) Enables matching a lightweight (non-annotated) tag
* - **always** (_boolean_) Show uniquely abbreviated commit object as fallback
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'all' => false,
'tags' => false,
'always' => false,
));
}
}

View File

@@ -0,0 +1,112 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Download objects and refs from another repository - `git fetch`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class FetchCommand extends Command
{
/**
* Fetches named heads or tags from one or more other repositories, along with the objects necessary to complete them
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'git://your/repo.git');
* $git->fetch('origin');
* ```
*
* ##### Options
*
* - **append** (_boolean_) Append ref names and object names of fetched refs to the existing contents of .git/FETCH_HEAD
* - **keep** (_boolean_) Keep downloaded pack
* - **prune** (_boolean_) After fetching, remove any remote-tracking branches which no longer exist on the remote
*
* @param string $repository The "remote" repository that is the source of a fetch or pull operation
* @param string $refspec The format of a <refspec> parameter is an optional plus +, followed by the source ref <src>,
* followed by a colon :, followed by the destination ref <dst>
* @param array $options [optional] An array of options {@see FetchCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function __invoke($repository, $refspec = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('fetch');
$this->addFlags($builder, $options);
$builder->add($repository);
if ($refspec) {
$builder->add($refspec);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Fetch all remotes
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'git://your/repo.git');
* $git->remote->add('release', 'git://your/another_repo.git');
* $git->fetch->all();
* ```
*
* ##### Options
*
* - **append** (_boolean_) Append ref names and object names of fetched refs to the existing contents of .git/FETCH_HEAD
* - **keep** (_boolean_) Keep downloaded pack
* - **prune** (_boolean_) After fetching, remove any remote-tracking branches which no longer exist on the remote
*
* @param array $options [optional] An array of options {@see FetchCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function all(array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('fetch')
->add('--all');
$this->addFlags($builder, $options);
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **append** (_boolean_) Append ref names and object names of fetched refs to the existing contents of .git/FETCH_HEAD
* - **keep** (_boolean_) Keep downloaded pack
* - **prune** (_boolean_) After fetching, remove any remote-tracking branches which no longer exist on the remote
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'append' => false,
//'force' => false,
'keep' => false,
'prune' => false,
));
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Create an empty git repository or reinitialize an existing one - `git init`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class InitCommand extends Command
{
/**
* Create an empty git repository or reinitialize an existing one
*
* ``` php
* $git = new PHPGit\Git();
* $git->init('/path/to/repo1');
* $git->init('/path/to/repo2', array('shared' => true, 'bare' => true));
* ```
*
* ##### Options
*
* - **shared** (_boolean_) Specify that the git repository is to be shared amongst several users
* - **bare** (_boolean_) Create a bare repository
*
* @param string $path The directory to create an empty repository
* @param array $options [optional] An array of options {@see InitCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function __invoke($path, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('init');
$this->addFlags($builder, $options, array('shared', 'bare'));
$process = $builder->add($path)->getProcess();
$this->git->run($process);
return true;
}
/**
* {@inheritdoc}
*
* - **shared** (_boolean_) Specify that the git repository is to be shared amongst several users
* - **bare** (_boolean_) Create a bare repository
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'shared' => false,
'bare' => false
));
}
}

View File

@@ -0,0 +1,105 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Show commit logs - `git log`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class LogCommand extends Command
{
/**
* Returns the commit logs
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $logs = $git->log(array('limit' => 10));
* ```
*
* ##### Output Example
*
* ``` php
* [
* 0 => [
* 'hash' => '1a821f3f8483747fd045eb1f5a31c3cc3063b02b',
* 'name' => 'John Doe',
* 'email' => 'john@example.com',
* 'date' => 'Fri Jan 17 16:32:49 2014 +0900',
* 'title' => 'Initial Commit'
* ],
* 1 => [
* //...
* ]
* ]
* ```
*
* ##### Options
*
* - **limit** (_integer_) Limits the number of commits to show
* - **skip** (_integer_) Skip number commits before starting to show the commit output
*
* @param string $revRange [optional] Show only commits in the specified revision range
* @param string $path [optional] Show only commits that are enough to explain how the files that match the specified paths came to be
* @param array $options [optional] An array of options {@see LogCommand::setDefaultOptions}
*
* @throws GitException
* @return array
*/
public function __invoke($revRange = '', $path = null, array $options = array())
{
$commits = array();
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('log')
->add('-n')->add($options['limit'])
->add('--skip=' . $options['skip'])
->add('--format=%H||%aN||%aE||%aD||%s');
if ($revRange) {
$builder->add($revRange);
}
if ($path) {
$builder->add('--')->add($path);
}
$output = $this->git->run($builder->getProcess());
$lines = $this->split($output);
foreach ($lines as $line) {
list($hash, $name, $email, $date, $title) = preg_split('/\|\|/', $line, -1, PREG_SPLIT_NO_EMPTY);
$commits[] = array(
'hash' => $hash,
'name' => $name,
'email' => $email,
'date' => $date,
'title' => $title
);
}
return $commits;
}
/**
* {@inheritdoc}
*
* - **limit** (_integer_) Limits the number of commits to show
* - **skip** (_integer_) Skip number commits before starting to show the commit output
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'limit' => 10,
'skip' => 0
));
}
}

View File

@@ -0,0 +1,110 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Join two or more development histories together - `git merge`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class MergeCommand extends Command
{
/**
* Incorporates changes from the named commits into the current branch
*
* ```php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->merge('1.0');
* $git->merge('1.1', 'Merge message', ['strategy' => 'ours']);
* ```
*
* ##### Options
*
* - **no-ff** (_boolean_) Do not generate a merge commit if the merge resolved as a fast-forward, only update the branch pointer
* - **rerere-autoupdate** (_boolean_) Allow the rerere mechanism to update the index with the result of auto-conflict resolution if possible
* - **squash** (_boolean_) Allows you to create a single commit on top of the current branch whose effect is the same as merging another branch
* - **strategy** (_string_) Use the given merge strategy
* - **strategy-option** (_string_) Pass merge strategy specific option through to the merge strategy
*
* @param string|array|\Traversable $commit Commits to merge into our branch
* @param string $message [optional] Commit message to be used for the merge commit
* @param array $options [optional] An array of options {@see MergeCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function __invoke($commit, $message = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('merge');
$this->addFlags($builder, $options, array('no-ff', 'rerere-autoupdate', 'squash'));
if (!is_array($commit) && !($commit instanceof \Traversable)) {
$commit = array($commit);
}
foreach ($commit as $value) {
$builder->add($value);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Abort the merge process and try to reconstruct the pre-merge state
*
* ```php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* try {
* $git->merge('dev');
* } catch (PHPGit\Exception\GitException $e) {
* $git->merge->abort();
* }
* ```
*
* @throws GitException
* @return bool
*/
public function abort()
{
$builder = $this->git->getProcessBuilder()
->add('merge')
->add('--abort');
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **no-ff** (_boolean_) Do not generate a merge commit if the merge resolved as a fast-forward, only update the branch pointer
* - **rerere-autoupdate** (_boolean_) Allow the rerere mechanism to update the index with the result of auto-conflict resolution if possible
* - **squash** (_boolean_) Allows you to create a single commit on top of the current branch whose effect is the same as merging another branch
* - **strategy** (_string_) Use the given merge strategy
* - **strategy-option** (_string_) Pass merge strategy specific option through to the merge strategy
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'no-ff' => false,
'rerere-autoupdate' => false,
'squash' => false,
'strategy' => null,
'strategy-option' => null
));
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Move or rename a file, a directory, or a symlink - `git mv`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class MvCommand extends Command
{
/**
* Move or rename a file, a directory, or a symlink
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->mv('UPGRADE-1.0.md', 'UPGRADE-1.1.md');
* ```
*
* ##### Options
*
* - **force** (_boolean_) Force renaming or moving of a file even if the target exists
*
* @param string|array|\Iterator $source The files to move
* @param string $destination The destination
* @param array $options [optional] An array of options {@see MvCommand::setDefaultOptions}
*
* @return bool
*/
public function __invoke($source, $destination, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('mv');
$this->addFlags($builder, $options, array('force'));
if (!is_array($source) && !($source instanceof \Traversable)) {
$source = array($source);
}
foreach ($source as $value) {
$builder->add($value);
}
$builder->add($destination);
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **force** (_boolean_) Force renaming or moving of a file even if the target exists
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'force' => false
));
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Fetch from and merge with another repository or a local branch - `git pull`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class PullCommand extends Command
{
/**
* Fetch from and merge with another repository or a local branch
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->pull('origin', 'master');
* ```
*
* @param string $repository The "remote" repository that is the source of a fetch or pull operation
* @param string $refspec The format of a <refspec> parameter is an optional plus +,
* followed by the source ref <src>, followed by a colon :, followed by the destination ref <dst>
* @param array $options [optional] An array of options {@see PullCommand::setDefaultOptions}
*
* @return bool
*/
public function __invoke($repository = null, $refspec = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('pull');
if ($repository) {
$builder->add($repository);
if ($refspec) {
$builder->add($refspec);
}
}
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Update remote refs along with associated objects - `git push`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class PushCommand extends Command
{
/**
* Update remote refs along with associated objects
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->push('origin', 'master');
* ```
*
* @param string $repository The "remote" repository that is destination of a push operation
* @param string $refspec Specify what destination ref to update with what source object
* @param array $options [optional] An array of options {@see PushCommand::setDefaultOptions}
*
* @return bool
*/
public function __invoke($repository = null, $refspec = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('push');
$this->addFlags($builder, $options);
if ($repository) {
$builder->add($repository);
if ($refspec) {
$builder->add($refspec);
}
}
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'all' => false,
'mirror' => false,
'tags' => false,
'force' => false
));
}
}

View File

@@ -0,0 +1,129 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Forward-port local commits to the updated upstream head - `git rebase`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class RebaseCommand extends Command
{
/**
* Forward-port local commits to the updated upstream head
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->fetch('origin');
* $git->rebase('origin/master');
* ```
*
* ##### Options
*
* - **onto** (_string_) Starting point at which to create the new commits
* - **no-verify** (_boolean_) Bypasses the pre-rebase hook
* - **force-rebase** (_boolean_) Force the rebase even if the current branch is a descendant of the commit you are rebasing onto
*
* @param string $upstream [optional] Upstream branch to compare against
* @param string $branch [optional] Working branch; defaults to HEAD
* @param array $options [optional] An array of options {@see RebaseCommand::setDefaultOptions}
*
* @return bool
*/
public function __invoke($upstream = null, $branch = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('rebase');
if ($options['onto']) {
$builder->add('--onto')->add($options['onto']);
}
if ($upstream) {
$builder->add($upstream);
}
if ($branch) {
$builder->add($branch);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Restart the rebasing process after having resolved a merge conflict
*
* @return bool
*/
public function continues()
{
$builder = $this->git->getProcessBuilder()
->add('rebase')
->add('--continue');
$this->git->run($builder->getProcess());
return true;
}
/**
* Abort the rebase operation and reset HEAD to the original branch
*
* @return bool
*/
public function abort()
{
$builder = $this->git->getProcessBuilder()
->add('rebase')
->add('--abort');
$this->git->run($builder->getProcess());
return true;
}
/**
* Restart the rebasing process by skipping the current patch
*
* @return bool
*/
public function skip()
{
$builder = $this->git->getProcessBuilder()
->add('rebase')
->add('--skip');
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **onto** (_string_) Starting point at which to create the new commits
* - **no-verify** (_boolean_) Bypasses the pre-rebase hook
* - **force-rebase** (_boolean_) Force the rebase even if the current branch is a descendant of the commit you are rebasing onto
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'onto' => null,
'no-verify' => false,
'force-rebase' => false
));
$resolver->setAllowedTypes(array(
'onto' => array('null', 'string')
));
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace PHPGit\Command\Remote;
use PHPGit\Command;
/**
* Changes the list of branches tracked by the named remote
*
* @author Kazuyuki Hayashi
*/
class SetBranchesCommand extends Command
{
/**
* Alias of set()
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->branches('origin', array('master', 'develop'));
* ```
*
* @param string $name The remote name
* @param array $branches The names of the tracked branch
*
* @return bool
*/
public function __invoke($name, array $branches)
{
return $this->set($name, $branches);
}
/**
* Changes the list of branches tracked by the named remote
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->branches->set('origin', array('master', 'develop'));
* ```
*
* @param string $name The remote name
* @param array $branches The names of the tracked branch
*
* @return bool
*/
public function set($name, array $branches)
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('set-branches')
->add($name);
foreach ($branches as $branch) {
$builder->add($branch);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Adds to the list of branches tracked by the named remote
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->branches->add('origin', array('master', 'develop'));
* ```
*
* @param string $name The remote name
* @param array $branches The names of the tracked branch
*
* @return bool
*/
public function add($name, array $branches)
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('set-branches')
->add($name)
->add('--add');
foreach ($branches as $branch) {
$builder->add($branch);
}
$this->git->run($builder->getProcess());
return true;
}
}

View File

@@ -0,0 +1,120 @@
<?php
namespace PHPGit\Command\Remote;
use PHPGit\Command;
/**
* Sets or deletes the default branch (i.e. the target of the symbolic-ref refs/remotes/<name>/HEAD) for the named remote
*
* @author Kazuyuki Hayashi
*/
class SetHeadCommand extends Command
{
/**
* Alias of set()
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->head('origin');
* ```
*
* @param string $name The remote name
* @param string $branch [optional] The symbolic-ref to set
*
* @return bool
*/
public function __invoke($name, $branch = null)
{
return $this->set($name, $branch);
}
/**
* Sets the default branch for the named remote
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->head->set('origin');
* ```
*
* @param string $name The remote name
* @param string $branch [optional] The symbolic-ref to set
*
* @return bool
*/
public function set($name, $branch)
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('set-head')
->add($name);
if ($branch) {
$builder->add($branch);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Deletes the default branch for the named remote
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->head->delete('origin');
* ```
*
* @param string $name The remote name
*
* @return bool
*/
public function delete($name)
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('set-head')
->add($name)
->add('-d');
$this->git->run($builder->getProcess());
return true;
}
/**
* Determine the default branch by querying remote
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->head->remote('origin');
* ```
*
* @param string $name The remote name
*
* @return bool
*/
public function remote($name)
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('set-head')
->add($name)
->add('-a');
$this->git->run($builder->getProcess());
return true;
}
}

View File

@@ -0,0 +1,175 @@
<?php
namespace PHPGit\Command\Remote;
use PHPGit\Command;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Changes URL remote points to
*
* @author Kazuyuki Hayashi
*/
class SetUrlCommand extends Command
{
/**
* Alias of set()
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->url('origin', 'https://github.com/text/Text.git');
* ```
*
* ##### Options
*
* - **push** (_boolean_) Push URLs are manipulated instead of fetch URLs
*
* @param string $name The name of remote
* @param string $newUrl The new URL
* @param string $oldUrl [optional] The old URL
* @param array $options [optional] An array of options {@see SetUrlCommand::setDefaultOptions}
*
* @return bool
*/
public function __invoke($name, $newUrl, $oldUrl = null, array $options = array())
{
return $this->set($name, $newUrl, $oldUrl, $options);
}
/**
* Sets the URL remote to $newUrl
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->url->set('origin', 'https://github.com/text/Text.git');
* ```
*
* ##### Options
*
* - **push** (_boolean_) Push URLs are manipulated instead of fetch URLs
*
* @param string $name The name of remote
* @param string $newUrl The new URL
* @param string $oldUrl [optional] The old URL
* @param array $options [optional] An array of options {@see SetUrlCommand::setDefaultOptions}
*
* @return bool
*/
public function set($name, $newUrl, $oldUrl = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('set-url');
$this->addFlags($builder, $options);
$builder
->add($name)
->add($newUrl);
if ($oldUrl) {
$builder->add($oldUrl);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Adds new URL to remote
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->url->add('origin', 'https://github.com/text/Text.git');
* ```
*
* ##### Options
*
* - **push** (_boolean_) Push URLs are manipulated instead of fetch URLs
*
* @param string $name The name of remote
* @param string $newUrl The new URL
* @param array $options [optional] An array of options {@see SetUrlCommand::setDefaultOptions}
*
* @return bool
*/
public function add($name, $newUrl, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('set-url')
->add('--add');
$this->addFlags($builder, $options);
$builder
->add($name)
->add($newUrl);
$this->git->run($builder->getProcess());
return true;
}
/**
* Deletes all URLs matching regex $url
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->url->delete('origin', 'https://github.com');
* ```
*
* ##### Options
*
* - **push** (_boolean_) Push URLs are manipulated instead of fetch URLs
*
* @param string $name The remote name
* @param string $url The URL to delete
* @param array $options [optional] An array of options {@see SetUrlCommand::setDefaultOptions}
*
* @return bool
*/
public function delete($name, $url, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('set-url')
->add('--delete');
$this->addFlags($builder, $options);
$builder
->add($name)
->add($url);
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **push** (_boolean_) Push URLs are manipulated instead of fetch URLs
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'push' => false
));
}
}

View File

@@ -0,0 +1,278 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Git;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Manage set of tracked repositories - `git remote`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*
* @method head($name, $branch) Sets the default branch for the named remote
* @method branches($name, $branches) Changes the list of branches tracked by the named remote
* @method url($name, $newUrl, $oldUrl = null, $options = array()) Sets the URL remote to $newUrl
*/
class RemoteCommand extends Command
{
/** @var Remote\SetHeadCommand */
public $head;
/** @var Remote\SetBranchesCommand */
public $branches;
/** @var Remote\SetUrlCommand */
public $url;
/**
* @param Git $git
*/
public function __construct(Git $git)
{
parent::__construct($git);
$this->head = new Remote\SetHeadCommand($git);
$this->branches = new Remote\SetBranchesCommand($git);
$this->url = new Remote\SetUrlCommand($git);
}
/**
* Calls sub-commands
*
* @param string $name The name of a property
* @param array $arguments An array of arguments
*
* @throws \BadMethodCallException
* @return mixed
*/
public function __call($name, $arguments)
{
if (isset($this->{$name}) && is_callable($this->{$name})) {
return call_user_func_array($this->{$name}, $arguments);
}
throw new \BadMethodCallException(sprintf('Call to undefined method %s::%s()', __CLASS__, $name));
}
/**
* Returns an array of existing remotes
*
* ``` php
* $git = new PHPGit\Git();
* $git->clone('https://github.com/kzykhys/Text.git', '/path/to/repo');
* $git->setRepository('/path/to/repo');
* $remotes = $git->remote();
* ```
*
* ##### Output Example
*
* ``` php
* [
* 'origin' => [
* 'fetch' => 'https://github.com/kzykhys/Text.git',
* 'push' => 'https://github.com/kzykhys/Text.git'
* ]
* ]
* ```
*
* @return array
*/
public function __invoke()
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('-v');
$remotes = array();
$output = $this->git->run($builder->getProcess());
$lines = $this->split($output);
foreach ($lines as $line) {
if (preg_match('/^(.*)\t(.*)\s\((.*)\)$/', $line, $matches)) {
if (!isset($remotes[$matches[1]])) {
$remotes[$matches[1]] = array();
}
$remotes[$matches[1]][$matches[3]] = $matches[2];
}
}
return $remotes;
}
/**
* Adds a remote named **$name** for the repository at **$url**
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->fetch('origin');
* ```
*
* ##### Options
*
* - **tags** (_boolean_) With this option, `git fetch <name>` imports every tag from the remote repository
* - **no-tags** (_boolean_) With this option, `git fetch <name>` does not import tags from the remote repository
*
* @param string $name The name of the remote
* @param string $url The url of the remote
* @param array $options [optional] An array of options {@see RemoteCommand::setDefaultOptions}
*
* @return bool
*/
public function add($name, $url, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('add');
$this->addFlags($builder, $options, array('tags', 'no-tags'));
$builder->add($name)->add($url);
$this->git->run($builder->getProcess());
return true;
}
/**
* Rename the remote named **$name** to **$newName**
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->rename('origin', 'upstream');
* ```
*
* @param string $name The remote name to rename
* @param string $newName The new remote name
*
* @return bool
*/
public function rename($name, $newName)
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('rename')
->add($name)
->add($newName);
$this->git->run($builder->getProcess());
return true;
}
/**
* Remove the remote named **$name**
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
* $git->remote->rm('origin');
* ```
*
* @param string $name The remote name to remove
*
* @return bool
*/
public function rm($name)
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('rm')
->add($name);
$this->git->run($builder->getProcess());
return true;
}
/**
* Gives some information about the remote **$name**
*
* ``` php
* $git = new PHPGit\Git();
* $git->clone('https://github.com/kzykhys/Text.git', '/path/to/repo');
* $git->setRepository('/path/to/repo');
* echo $git->remote->show('origin');
* ```
*
* ##### Output Example
*
* ```
* \* remote origin
* Fetch URL: https://github.com/kzykhys/Text.git
* Push URL: https://github.com/kzykhys/Text.git
* HEAD branch: master
* Remote branch:
* master tracked
* Local branch configured for 'git pull':
* master merges with remote master
* Local ref configured for 'git push':
* master pushes to master (up to date)
* ```
*
* @param string $name The remote name to show
*
* @return string
*/
public function show($name)
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('show')
->add($name);
return $this->git->run($builder->getProcess());
}
/**
* Deletes all stale remote-tracking branches under **$name**
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->remote->prune('origin');
* ```
*
* @param string $name The remote name
*
* @return bool
*/
public function prune($name = null)
{
$builder = $this->git->getProcessBuilder()
->add('remote')
->add('prune');
if ($name) {
$builder->add($name);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **tags** (_boolean_) With this option, `git fetch <name>` imports every tag from the remote repository
* - **no-tags** (_boolean_) With this option, `git fetch <name>` does not import tags from the remote repository
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'tags' => false,
'no-tags' => false
));
}
}

View File

@@ -0,0 +1,199 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
/**
* Reset current HEAD to the specified state - `git reset`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class ResetCommand extends Command
{
/**
* Resets the index entries for all **$paths** to their state at **$commit**
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->reset();
* ```
*
* @param string|array|\Traversable $paths The paths to reset
* @param string $commit The commit
*
* @return bool
*/
public function __invoke($paths, $commit = null)
{
$builder = $this->git->getProcessBuilder()
->add('reset');
if ($commit) {
$builder->add($commit)->add('--');
}
if (!is_array($paths) && !($paths instanceof \Traversable)) {
$paths = array($paths);
}
foreach ($paths as $path) {
$builder->add($path);
}
try {
$this->git->run($builder->getProcess());
} catch (GitException $e) {
// Confirm exit code
}
return true;
}
/**
* Resets the current branch head to **$commit**
*
* Does not touch the index file nor the working tree at all (but resets the head to **$commit**,
* just like all modes do).
* This leaves all your changed files "Changes to be committed", as git status would put it.
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->reset->soft();
* ```
*
* @param string $commit The commit
*
* @return bool
*/
public function soft($commit = null)
{
return $this->mode('soft', $commit);
}
/**
* Resets the current branch head to **$commit**
*
* Resets the index but not the working tree (i.e., the changed files are preserved but not marked for commit)
* and reports what has not been updated. This is the default action.
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->reset->mixed();
* ```
*
* @param string $commit The commit
*
* @return bool
*/
public function mixed($commit = null)
{
return $this->mode('mixed', $commit);
}
/**
* Resets the current branch head to **$commit**
*
* Resets the index and working tree. Any changes to tracked files in the working tree since **$commit** are discarded
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->reset->hard();
* ```
*
* @param string $commit The commit
*
* @return bool
*/
public function hard($commit = null)
{
return $this->mode('hard', $commit);
}
/**
* Resets the current branch head to **$commit**
*
* Resets the index and updates the files in the working tree that are different between **$commit** and HEAD,
* but keeps those which are different between the index and working tree
* (i.e. which have changes which have not been added). If a file that is different between **$commit** and
* the index has unstaged changes, reset is aborted
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->reset->merge();
* ```
*
* @param string $commit The commit
*
* @return bool
*/
public function merge($commit = null)
{
return $this->mode('merge', $commit);
}
/**
* Resets the current branch head to **$commit**
*
* Resets index entries and updates files in the working tree that are different between **$commit** and HEAD.
* If a file that is different between **$commit** and HEAD has local changes, reset is aborted.
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->reset->keep();
* ```
*
* @param string $commit The commit
*
* @return bool
*/
public function keep($commit = null)
{
return $this->mode('keep', $commit);
}
/**
* Resets the current branch head to **$commit**
*
* Possibly updates the index (resetting it to the tree of **$commit**) and the working tree depending on **$mode**
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->reset->mode('hard');
* ```
*
* @param string $mode --<mode>
* @param string $commit The commit
*
* @throws \InvalidArgumentException
* @return bool
*/
public function mode($mode, $commit = null)
{
if (!in_array($mode, array('soft', 'mixed', 'hard', 'merge', 'keep'))) {
throw new \InvalidArgumentException('$mode must be one of the following: soft, mixed, hard, merge, keep');
}
$builder = $this->git->getProcessBuilder()
->add('reset')
->add('--' . $mode);
if ($commit) {
$builder->add($commit);
}
$this->git->run($builder->getProcess());
return true;
}
}

View File

@@ -0,0 +1,97 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Remove files from the working tree and from the index - `git rm`
*
* @author Kazuyuki Hayashi
*/
class RmCommand extends Command
{
/**
* Remove files from the working tree and from the index
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->rm('CHANGELOG-1.0-1.1.txt', ['force' => true]);
* ```
*
* ##### Options
*
* - **force** (_boolean_) Override the up-to-date check
* - **cached** (_boolean_) Unstage and remove paths only from the index
* - **recursive** (_boolean_) Allow recursive removal when a leading directory name is given
*
* @param string|array|\Traversable $file Files to remove. Fileglobs (e.g. *.c) can be given to remove all matching files.
* @param array $options [optional] An array of options {@see RmCommand::setDefaultOptions}
*
* @return bool
*/
public function __invoke($file, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('rm');
$this->addFlags($builder, $options, array('force', 'cached'));
if ($options['recursive']) {
$builder->add('-r');
}
if (!is_array($file) && !($file instanceof \Traversable)) {
$file = array($file);
}
foreach ($file as $value) {
$builder->add($value);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Equivalent to $git->rm($file, ['cached' => true]);
*
* ##### Options
*
* - **force** (_boolean_) Override the up-to-date check
* - **recursive** (_boolean_) Allow recursive removal when a leading directory name is given
*
* @param string|array|\Traversable $file Files to remove. Fileglobs (e.g. *.c) can be given to remove all matching files.
* @param array $options [optional] An array of options {@see RmCommand::setDefaultOptions}
*
* @return bool
*/
public function cached($file, array $options = array())
{
$options['cached'] = true;
return $this->__invoke($file, $options);
}
/**
* {@inheritdoc}
*
* - **force** (_boolean_) Override the up-to-date check
* - **cached** (_boolean_) Unstage and remove paths only from the index
* - **recursive** (_boolean_) Allow recursive removal when a leading directory name is given
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'force' => false,
'cached' => false,
'recursive' => false
));
}
}

View File

@@ -0,0 +1,134 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
/**
* Summarize 'git log' output - `git shortlog`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class ShortlogCommand extends Command
{
/**
* Summarize 'git log' output
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $shortlog = $git->shortlog();
* ```
*
* ##### Output Example
*
* ``` php
* [
* 'John Doe <john@example.com>' => [
* 0 => ['commit' => '589de67', 'date' => new \DateTime('2014-02-10 12:56:15 +0300'), 'subject' => 'Update README'],
* 1 => ['commit' => '589de67', 'date' => new \DateTime('2014-02-15 12:56:15 +0300'), 'subject' => 'Update README'],
* ],
* //...
* ]
* ```
* @param string|array|\Traversable $commits [optional] Defaults to HEAD
*
* @return array
*/
public function __invoke($commits = 'HEAD')
{
$builder = $this->git->getProcessBuilder()
->add('shortlog')
->add('--numbered')
->add('--format=')
->add('-w256,2,2')
->add('-e');
if (!is_array($commits) && !($commits instanceof \Traversable)) {
$commits = array($commits);
}
foreach ($commits as $commit) {
$builder->add($commit);
}
$process = $builder->getProcess();
$process->setCommandLine(str_replace('--format=', '--format=%h|%ci|%s', $process->getCommandLine()));
$output = $this->git->run($process);
$lines = $this->split($output);
$result = array();
$author = null;
foreach ($lines as $line) {
if (substr($line, 0, 1) != ' ') {
if (preg_match('/([^<>]*? <[^<>]+>)/', $line, $matches)) {
$author = $matches[1];
$result[$author] = array();
}
continue;
}
list ($commit, $date, $subject) = explode('|', trim($line), 3);
$result[$author][] = array(
'commit' => $commit,
'date' => new \DateTime($date),
'subject' => $subject
);
}
return $result;
}
/**
* Suppress commit description and provide a commit count summary only
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $shortlog = $git->shortlog->summary();
* ```
*
* ##### Output Example
*
* ``` php
* [
* 'John Doe <john@example.com>' => 153,
* //...
* ]
* ```
*
* @param string $commits [optional] Defaults to HEAD
*
* @return array
*/
public function summary($commits = 'HEAD')
{
$builder = $this->git->getProcessBuilder()
->add('shortlog')
->add('--numbered')
->add('--summary')
->add('-e');
if (!is_array($commits) && !($commits instanceof \Traversable)) {
$commits = array($commits);
}
foreach ($commits as $commit) {
$builder->add($commit);
}
$output = $this->git->run($builder->getProcess());
$lines = $this->split($output);
$result = array();
foreach ($lines as $line) {
list ($commits, $author) = explode("\t", trim($line), 2);
$result[$author] = (int) $commits;
}
return $result;
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Show various types of objects - `git show`
*
* @author Kazuyuki Hayashi
*/
class ShowCommand extends Command
{
/**
* Shows one or more objects (blobs, trees, tags and commits)
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* echo $git->show('3ddee587e209661c8265d5bfd0df999836f6dfa2');
* ```
*
* ##### Options
*
* - **format** (_string_) Pretty-print the contents of the commit logs in a given format, where <format> can be one of oneline, short, medium, full, fuller, email, raw and format:<string>
* - **abbrev-commit** (_boolean_) Instead of showing the full 40-byte hexadecimal commit object name, show only a partial prefix
*
* @param string $object The names of objects to show
* @param array $options [optional] An array of options {@see ShowCommand::setDefaultOptions}
*
* @return string
*/
public function __invoke($object, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('show');
$this->addFlags($builder, $options, array('abbrev-commit'));
if ($options['format']) {
$builder->add('--format=' . $options['format']);
}
$builder->add($object);
return $this->git->run($builder->getProcess());
}
/**
* {@inheritdoc}
*
* - **format** (_string_) Pretty-print the contents of the commit logs in a given format, where <format> can be one of oneline, short, medium, full, fuller, email, raw and format:<string>
* - **abbrev-commit** (_boolean_) Instead of showing the full 40-byte hexadecimal commit object name, show only a partial prefix
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'format' => null,
'abbrev-commit' => false
));
$resolver->setAllowedTypes(array(
'format' => array('null', 'string'),
));
}
}

View File

@@ -0,0 +1,309 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
/**
* Stash the changes in a dirty working directory away - `git stash`
*
* @author Kazuyuki Hayashi
*/
class StashCommand extends Command
{
/**
* Save your local modifications to a new stash, and run git reset --hard to revert them
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->stash();
* ```
*
* @return bool
*/
public function __invoke()
{
$builder = $this->git->getProcessBuilder()
->add('stash');
$this->git->run($builder->getProcess());
return true;
}
/**
* Save your local modifications to a new stash, and run git reset --hard to revert them.
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->stash->save('My stash');
* ```
*
* @param string $message [optional] The description along with the stashed state
* @param array $options [optional] An array of options {@see StashCommand::setDefaultOptions}
*
* @return bool
*/
public function save($message = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('stash')
->add('save');
$builder->add($message);
$this->git->run($builder->getProcess());
return true;
}
/**
* Returns the stashes that you currently have
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $stashes = $git->stash->lists();
* ```
*
* ##### Output Example
*
* ``` php
* [
* 0 => ['branch' => 'master', 'message' => '0e2f473 Fixes README.md'],
* 1 => ['branch' => 'master', 'message' => 'ce1ddde Initial commit'],
* ]
* ```
*
* @param array $options [optional] An array of options {@see StashCommand::setDefaultOptions}
*
* @return array
*/
public function lists(array $options = array())
{
$builder = $this->git->getProcessBuilder()
->add('stash')
->add('list');
$output = $this->git->run($builder->getProcess());
$lines = $this->split($output);
$list = array();
foreach ($lines as $line) {
if (preg_match('/stash@{(\d+)}:.* [Oo]n (.*): (.*)/', $line, $matches)) {
$list[$matches[1]] = array(
'branch' => $matches[2],
'message' => $matches[3]
);
}
}
return $list;
}
/**
* Show the changes recorded in the stash as a diff between the stashed state and its original parent
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* echo $git->stash->show('stash@{0}');
* ```
*
* ##### Output Example
*
* ```
* REAMDE.md | 2 +-
* 1 files changed, 1 insertions(+), 1 deletions(-)
* ```
*
* @param string $stash The stash to show
*
* @return string
*/
public function show($stash = null)
{
$builder = $this->git->getProcessBuilder()
->add('stash')
->add('show');
if ($stash) {
$builder->add($stash);
}
return $this->git->run($builder->getProcess());
}
/**
* Remove a single stashed state from the stash list
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->stash->drop('stash@{0}');
* ```
*
* @param string $stash The stash to drop
*
* @return mixed
*/
public function drop($stash = null)
{
$builder = $this->git->getProcessBuilder()
->add('stash')
->add('drop');
if ($stash) {
$builder->add($stash);
}
return $this->git->run($builder->getProcess());
}
/**
* Remove a single stashed state from the stash list and apply it on top of the current working tree state
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->stash->pop('stash@{0}');
* ```
*
* @param string $stash The stash to pop
* @param array $options [optional] An array of options {@see StashCommand::setDefaultOptions}
*
* @return bool
*/
public function pop($stash = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('stash')
->add('pop');
$this->addFlags($builder, $options, array('index'));
if ($stash) {
$builder->add($stash);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Like pop, but do not remove the state from the stash list
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->stash->apply('stash@{0}');
* ```
*
* @param string $stash The stash to apply
* @param array $options [optional] An array of options {@see StashCommand::setDefaultOptions}
*
* @return bool
*/
public function apply($stash = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('stash')
->add('apply');
$this->addFlags($builder, $options, array('index'));
if ($stash) {
$builder->add($stash);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Creates and checks out a new branch named <branchname> starting from the commit at which the <stash> was originally created, applies the changes recorded in <stash> to the new working tree and index
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->stash->branch('hotfix', 'stash@{0}');
* ```
*
* @param string $name The name of the branch
* @param string $stash The stash
*
* @return bool
*/
public function branch($name, $stash = null)
{
$builder = $this->git->getProcessBuilder()
->add('stash')
->add('branch')
->add($name);
if ($stash) {
$builder->add($stash);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Remove all the stashed states
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->stash->clear();
* ```
*
* @return bool
*/
public function clear()
{
$builder = $this->git->getProcessBuilder()
->add('stash')
->add('clear');
$this->git->run($builder->getProcess());
return true;
}
/**
* Create a stash (which is a regular commit object) and return its object name, without storing it anywhere in the ref namespace
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $commit = $git->stash->create();
* ```
*
* ##### Output Example
*
* ```
* 877316ea6f95c43b7ccc2c2a362eeedfa78b597d
* ```
*
* @return string
*/
public function create()
{
$builder = $this->git->getProcessBuilder()
->add('stash')
->add('create');
return $this->git->run($builder->getProcess());
}
}

View File

@@ -0,0 +1,147 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Show the working tree status - `git status`
*
* = unmodified
* M = modified
* A = added
* D = deleted
* R = renamed
* C = copied
* U = updated but unmerged
*
* X Y Meaning
* -------------------------------------------------
* [MD] not updated
* M [ MD] updated in index
* A [ MD] added to index
* D [ M] deleted from index
* R [ MD] renamed in index
* C [ MD] copied in index
* [MARC] index and work tree matches
* [ MARC] M work tree changed since index
* [ MARC] D deleted in work tree
* -------------------------------------------------
* D D unmerged, both deleted
* A U unmerged, added by us
* U D unmerged, deleted by them
* U A unmerged, added by them
* D U unmerged, deleted by us
* A A unmerged, both added
* U U unmerged, both modified
* -------------------------------------------------
* ? ? untracked
* ! ! ignored
* -------------------------------------------------
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class StatusCommand extends Command
{
const UNMODIFIED = ' ';
const MODIFIED = 'M';
const ADDED = 'A';
const DELETED = 'D';
const RENAMED = 'R';
const COPIED = 'C';
const UPDATED_BUT_UNMERGED = 'U';
const UNTRACKED = '?';
const IGNORED = '!';
/**
* Returns the working tree status
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $status = $git->status();
* ```
*
* ##### Constants
*
* - StatusCommand::UNMODIFIED [=' '] unmodified
* - StatusCommand::MODIFIED [='M'] modified
* - StatusCommand::ADDED [='A'] added
* - StatusCommand::DELETED [='D'] deleted
* - StatusCommand::RENAMED [='R'] renamed
* - StatusCommand::COPIED [='C'] copied
* - StatusCommand::UPDATED_BUT_UNMERGED [='U'] updated but unmerged
* - StatusCommand::UNTRACKED [='?'] untracked
* - StatusCommand::IGNORED [='!'] ignored
*
* ##### Output Example
*
* ``` php
* [
* 'branch' => 'master',
* 'changes' => [
* ['file' => 'item1.txt', 'index' => 'A', 'work_tree' => 'M'],
* ['file' => 'item2.txt', 'index' => 'A', 'work_tree' => ' '],
* ['file' => 'item3.txt', 'index' => '?', 'work_tree' => '?'],
* ]
* ]
* ```
*
* ##### Options
*
* - **ignored** (_boolean_) Show ignored files as well
*
* @param array $options [optional] An array of options {@see StatusCommand::setDefaultOptions}
*
* @return mixed
*/
public function __invoke(array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('status')
->add('--porcelain')->add('-s')->add('-b')->add('--null');
$this->addFlags($builder, $options);
$process = $builder->getProcess();
$result = array('branch' => null, 'changes' => array());
$output = $this->git->run($process);
list($branch, $changes) = preg_split('/(\0|\n)/', $output, 2);
$lines = $this->split($changes, true);
if (substr($branch, -11) == '(no branch)') {
$result['branch'] = null;
} elseif (preg_match('/([^ ]*)\.\.\..*?\[.*?\]$/', $branch, $matches)) {
$result['branch'] = $matches[1];
} elseif (preg_match('/ ([^ ]*)$/', $branch, $matches)) {
$result['branch'] = $matches[1];
}
foreach ($lines as $line) {
$result['changes'][] = array(
'file' => substr($line, 3),
'index' => substr($line, 0, 1),
'work_tree' => substr($line, 1, 1)
);
}
return $result;
}
/**
* {@inheritdoc}
*
* - **ignored** (_boolean_) Show ignored files as well
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'ignored' => false
));
}
}

View File

@@ -0,0 +1,156 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Create, list, delete or verify a tag object signed with GPG - `git tag`
*
* @author Kazuyuki Hayashi
*/
class TagCommand extends Command
{
/**
* Returns an array of tags
*
* ``` php
* $git = new PHPGit\Git();
* $git->clone('https://github.com/kzykhys/PHPGit.git', '/path/to/repo');
* $git->setRepository('/path/to/repo');
* $tags = $git->tag();
* ```
*
* ##### Output Example
*
* ```
* ['v1.0.0', 'v1.0.1', 'v1.0.2']
* ```
*
* @throws GitException
* @return array
*/
public function __invoke()
{
$builder = $this->git->getProcessBuilder()
->add('tag');
$output = $this->git->run($builder->getProcess());
return $this->split($output);
}
/**
* Creates a tag object
*
* ``` php
* $git = new PHPGit\Git();
* $git->setRepository('/path/to/repo');
* $git->tag->create('v1.0.0');
* ```
*
* ##### Options
*
* - **annotate** (_boolean_) Make an unsigned, annotated tag object
* - **sign** (_boolean_) Make a GPG-signed tag, using the default e-mail addresss key
* - **force** (_boolean_) Replace an existing tag with the given name (instead of failing)
*
* @param string $tag The name of the tag to create
* @param string $commit The SHA1 object name of the commit object
* @param array $options [optional] An array of options {@see TagCommand::setDefaultOptions}
*
* @throws GitException
* @return bool
*/
public function create($tag, $commit = null, array $options = array())
{
$options = $this->resolve($options);
$builder = $this->git->getProcessBuilder()
->add('tag')
->add($tag);
$this->addFlags($builder, $options, array('annotate', 'sign', 'force'));
if ($commit) {
$builder->add($commit);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Delete existing tags with the given names
*
* @param string|array|\Traversable $tag The name of the tag to create
*
* @throws GitException
* @return bool
*/
public function delete($tag)
{
$builder = $this->git->getProcessBuilder()
->add('tag')
->add('-d');
if (!is_array($tag) && !($tag instanceof \Traversable)) {
$tag = array($tag);
}
foreach ($tag as $value) {
$builder->add($value);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* Verify the gpg signature of the given tag names
*
* @param string|array|\Traversable $tag The name of the tag to create
*
* @throws GitException
* @return bool
*/
public function verify($tag)
{
$builder = $this->git->getProcessBuilder()
->add('tag')
->add('-v');
if (!is_array($tag) && !($tag instanceof \Traversable)) {
$tag = array($tag);
}
foreach ($tag as $value) {
$builder->add($value);
}
$this->git->run($builder->getProcess());
return true;
}
/**
* {@inheritdoc}
*
* - **annotate** (_boolean_) Make an unsigned, annotated tag object
* - **sign** (_boolean_) Make a GPG-signed tag, using the default e-mail addresss key
* - **force** (_boolean_) Replace an existing tag with the given name (instead of failing)
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'annotate' => false,
'sign' => false,
'force' => false,
));
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace PHPGit\Command;
use PHPGit\Command;
/**
* List the contents of a tree object - `git ls-tree`
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class TreeCommand extends Command
{
/**
* Returns the contents of a tree object
*
* ``` php
* $git = new PHPGit\Git();
* $git->clone('https://github.com/kzykhys/PHPGit.git', '/path/to/repo');
* $git->setRepository('/path/to/repo');
* $tree = $git->tree('master');
* ```
*
* ##### Output Example
*
* ``` php
* [
* ['mode' => '100644', 'type' => 'blob', 'hash' => '1f100ce9855b66111d34b9807e47a73a9e7359f3', 'file' => '.gitignore', 'sort' => '2:.gitignore'],
* ['mode' => '100644', 'type' => 'blob', 'hash' => 'e0bfe494537037451b09c32636c8c2c9795c05c0', 'file' => '.travis.yml', 'sort' => '2:.travis.yml'],
* ['mode' => '040000', 'type' => 'tree', 'hash' => '8d5438e79f77cd72de80c49a413f4edde1f3e291', 'file' => 'bin', 'sort' => '1:.bin'],
* ]
* ```
*
* @param string $branch The commit
* @param string $path The path
*
* @return array
*/
public function __invoke($branch = 'master', $path = '')
{
$objects = array();
$builder = $this->git->getProcessBuilder();
$process = $builder->add('ls-tree')->add($branch . ':' . $path)->getProcess();
$output = $this->git->run($process);
$lines = $this->split($output);
$types = array(
'submodule' => 0,
'tree' => 1,
'blob' => 2
);
foreach ($lines as $line) {
list($meta, $file) = explode("\t", $line);
list($mode, $type, $hash) = explode(" ", $meta);
$objects[] = array(
'sort' => sprintf('%d:%s', $types[$type], $file),
'mode' => $mode,
'type' => $type,
'hash' => $hash,
'file' => $file
);
}
return $objects;
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace PHPGit\Exception;
/**
* @author Kazuyuki Hayashi <hayashi@valnur.net>
*/
class GitException extends \Exception
{
/**
* @var string
*/
protected $commandLine;
/**
* Construct the exception. Note: The message is NOT binary safe.
*
* @param string $message [optional] The Exception message to throw.
* @param int $code [optional] The Exception code.
* @param string $commandLine [optional] Command-line
* @param \Exception $previous [optional] The previous exception used for the exception chaining. Since 5.3.0
*/
public function __construct($message = "", $code = 0, $commandLine = null, \Exception $previous = null)
{
parent::__construct($message, $code, $previous);
$this->commandLine = $commandLine;
}
/**
* @return null|string
*/
public function getCommandLine()
{
return $this->commandLine;
}
}

View File

@@ -0,0 +1,308 @@
<?php
namespace PHPGit;
use PHPGit\Command;
use PHPGit\Exception\GitException;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\ProcessBuilder;
/**
* PHPGit - A Git wrapper for PHP5.3+
* ==================================
*
* [![Latest Unstable Version](https://poser.pugx.org/kzykhys/git/v/unstable.png)](https://packagist.org/packages/kzykhys/git)
* [![Build Status](https://travis-ci.org/kzykhys/PHPGit.png?branch=master)](https://travis-ci.org/kzykhys/PHPGit)
* [![Coverage Status](https://coveralls.io/repos/kzykhys/PHPGit/badge.png)](https://coveralls.io/r/kzykhys/PHPGit)
* [![SensioLabsInsight](https://insight.sensiolabs.com/projects/04f10b57-a113-47ad-8dda-9a6dacbb079f/mini.png)](https://insight.sensiolabs.com/projects/04f10b57-a113-47ad-8dda-9a6dacbb079f)
*
* Requirements
* ------------
*
* * PHP5.3
* * Git
*
* Installation
* ------------
*
* Update your composer.json and run `composer update`
*
* ``` json
* {
* "require": {
* "kzykhys/git": "dev-master"
* }
* }
* ```
*
* Basic Usage
* -----------
*
* ``` php
* <?php
*
* require __DIR__ . '/vendor/autoload.php';
*
* $git = new PHPGit\Git();
* $git->clone('https://github.com/kzykhys/PHPGit.git', '/path/to/repo');
* $git->setRepository('/path/to/repo');
* $git->remote->add('production', 'git://example.com/your/repo.git');
* $git->add('README.md');
* $git->commit('Adds README.md');
* $git->checkout('release');
* $git->merge('master');
* $git->push();
* $git->push('production', 'release');
* $git->tag->create('v1.0.1', 'release');
*
* foreach ($git->tree('release') as $object) {
* if ($object['type'] == 'blob') {
* echo $git->show($object['file']);
* }
* }
* ```
*
* @author Kazuyuki Hayashi <hayashi@valnur.net>
* @license MIT
*
* @method add($file, $options = array()) Add file contents to the index
* @method archive($file, $tree = null, $path = null, $options = array()) Create an archive of files from a named tree
* @method branch($options = array()) List both remote-tracking branches and local branches
* @method checkout($branch, $options = array()) Checkout a branch or paths to the working tree
* @method clone($repository, $path = null, $options = array()) Clone a repository into a new directory
* @method commit($message = '', $options = array()) Record changes to the repository
* @method config($options = array()) List all variables set in config file
* @method describe($committish = null, $options = array()) Returns the most recent tag that is reachable from a commit
* @method fetch($repository, $refspec = null, $options = array()) Fetches named heads or tags from one or more other repositories
* @method init($path, $options = array()) Create an empty git repository or reinitialize an existing one
* @method log($path = null, $options = array()) Returns the commit logs
* @method merge($commit, $message = null, $options = array()) Incorporates changes from the named commits into the current branch
* @method mv($source, $destination, $options = array()) Move or rename a file, a directory, or a symlink
* @method pull($repository = null, $refspec = null, $options = array()) Fetch from and merge with another repository or a local branch
* @method push($repository = null, $refspec = null, $options = array()) Update remote refs along with associated objects
* @method rebase($upstream = null, $branch = null, $options = array()) Forward-port local commits to the updated upstream head
* @method remote() Returns an array of existing remotes
* @method reset($commit = null, $paths = array()) Resets the index entries for all <paths> to their state at <commit>
* @method rm($file, $options = array()) Remove files from the working tree and from the index
* @method shortlog($commits = array()) Summarize 'git log' output
* @method show($object, $options = array()) Shows one or more objects (blobs, trees, tags and commits)
* @method stash() Save your local modifications to a new stash, and run git reset --hard to revert them
* @method status($options = array()) Show the working tree status
* @method tag() Returns an array of tags
* @method tree($branch = 'master', $path = '') List the contents of a tree object
*/
class Git
{
/** @var Command\AddCommand */
public $add;
/** @var Command\ArchiveCommand */
public $archive;
/** @var Command\BranchCommand */
public $branch;
/** @var Command\CatCommand */
public $cat;
/** @var Command\CheckoutCommand */
public $checkout;
/** @var Command\CloneCommand */
public $clone;
/** @var Command\CommitCommand */
public $commit;
/** @var Command\ConfigCommand */
public $config;
/** @var Command\DescribeCommand */
public $describe;
// Not implemented yet
public $diff;
/** @var Command\FetchCommand */
public $fetch;
/** @var Command\InitCommand */
public $init;
/** @var Command\LogCommand */
public $log;
/** @var Command\MergeCommand */
public $merge;
/** @var Command\MvCommand */
public $mv;
/** @var Command\PullCommand */
public $pull;
/** @var Command\PushCommand */
public $push;
/** @var Command\RebaseCommand */
public $rebase;
/** @var Command\RemoteCommand */
public $remote;
/** @var Command\ResetCommand */
public $reset;
/** @var Command\RmCommand */
public $rm;
/** @var Command\ShortlogCommand */
public $shortlog;
/** @var Command\ShowCommand */
public $show;
/** @var Command\StashCommand */
public $stash;
/** @var Command\StatusCommand */
public $status;
/** @var Command\TagCommand */
public $tag;
/** @var Command\TreeCommand */
public $tree;
/** @var string */
private $bin = 'git';
/** @var string */
private $directory = '.';
/**
* Initializes sub-commands
*/
public function __construct()
{
$this->add = new Command\AddCommand($this);
$this->archive = new Command\ArchiveCommand($this);
$this->branch = new Command\BranchCommand($this);
$this->cat = new Command\CatCommand($this);
$this->checkout = new Command\CheckoutCommand($this);
$this->clone = new Command\CloneCommand($this);
$this->commit = new Command\CommitCommand($this);
$this->config = new Command\ConfigCommand($this);
$this->describe = new Command\DescribeCommand($this);
$this->fetch = new Command\FetchCommand($this);
$this->init = new Command\InitCommand($this);
$this->log = new Command\LogCommand($this);
$this->merge = new Command\MergeCommand($this);
$this->mv = new Command\MvCommand($this);
$this->pull = new Command\PullCommand($this);
$this->push = new Command\PushCommand($this);
$this->rebase = new Command\RebaseCommand($this);
$this->remote = new Command\RemoteCommand($this);
$this->reset = new Command\ResetCommand($this);
$this->rm = new Command\RmCommand($this);
$this->shortlog = new Command\ShortlogCommand($this);
$this->show = new Command\ShowCommand($this);
$this->stash = new Command\StashCommand($this);
$this->status = new Command\StatusCommand($this);
$this->tag = new Command\TagCommand($this);
$this->tree = new Command\TreeCommand($this);
}
/**
* Calls sub-commands
*
* @param string $name The name of a property
* @param array $arguments An array of arguments
*
* @throws \BadMethodCallException
* @return mixed
*/
public function __call($name, $arguments)
{
if (isset($this->{$name}) && is_callable($this->{$name})) {
return call_user_func_array($this->{$name}, $arguments);
}
throw new \BadMethodCallException(sprintf('Call to undefined method PHPGit\Git::%s()', $name));
}
/**
* Sets the Git binary path
*
* @param string $bin
*
* @return Git
*/
public function setBin($bin)
{
$this->bin = $bin;
return $this;
}
/**
* Sets the Git repository path
*
* @var string $directory
*
* @return Git
*/
public function setRepository($directory)
{
$this->directory = $directory;
return $this;
}
/**
* Returns version number
*
* @return mixed
*/
public function getVersion()
{
$process = $this->getProcessBuilder()
->add('--version')
->getProcess();
return $this->run($process);
}
/**
* Returns an instance of ProcessBuilder
*
* @return ProcessBuilder
*/
public function getProcessBuilder()
{
return ProcessBuilder::create()
->setPrefix($this->bin)
->setWorkingDirectory($this->directory);
}
/**
* Executes a process
*
* @param Process $process The process to run
*
* @throws Exception\GitException
* @return mixed
*/
public function run(Process $process)
{
$process->run();
if (!$process->isSuccessful()) {
throw new GitException($process->getErrorOutput(), $process->getExitCode(), $process->getCommandLine());
}
return $process->getOutput();
}
}