aboutsummaryrefslogtreecommitdiffstats
path: root/library/kzykhys/git
diff options
context:
space:
mode:
authorAndrew Manning <tamanning@zoho.com>2016-05-01 22:29:51 -0400
committerAndrew Manning <tamanning@zoho.com>2016-05-01 22:29:51 -0400
commitc2d15e6c3bd8a29bae89d184a999ddac15fcb807 (patch)
tree96cbbfa98f131d830fbfd154f82e37383bb37bd8 /library/kzykhys/git
parentb1ae4d776c7c093c7f3ff6905e1a5302fbd5e3f6 (diff)
downloadvolse-hubzilla-c2d15e6c3bd8a29bae89d184a999ddac15fcb807.tar.gz
volse-hubzilla-c2d15e6c3bd8a29bae89d184a999ddac15fcb807.tar.bz2
volse-hubzilla-c2d15e6c3bd8a29bae89d184a999ddac15fcb807.zip
New plugin repo is cloned to /store/pluginrepos/REPONAME for analysis
Diffstat (limited to 'library/kzykhys/git')
-rw-r--r--library/kzykhys/git/.travis.yml20
-rw-r--r--library/kzykhys/git/README.md1347
-rw-r--r--library/kzykhys/git/phpunit.xml.dist28
-rw-r--r--library/kzykhys/git/src/PHPGit/Command.php123
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/AddCommand.php75
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/ArchiveCommand.php96
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/BranchCommand.php229
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/CatCommand.php91
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/CheckoutCommand.php145
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/CloneCommand.php71
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/CommitCommand.php87
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/ConfigCommand.php132
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/DescribeCommand.php90
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/FetchCommand.php112
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/InitCommand.php65
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/LogCommand.php105
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/MergeCommand.php110
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/MvCommand.php70
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/PullCommand.php59
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/PushCommand.php65
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/RebaseCommand.php129
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/Remote/SetBranchesCommand.php98
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/Remote/SetHeadCommand.php120
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/Remote/SetUrlCommand.php175
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/RemoteCommand.php278
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/ResetCommand.php199
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/RmCommand.php97
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/ShortlogCommand.php134
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/ShowCommand.php70
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/StashCommand.php309
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/StatusCommand.php147
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/TagCommand.php156
-rw-r--r--library/kzykhys/git/src/PHPGit/Command/TreeCommand.php70
-rw-r--r--library/kzykhys/git/src/PHPGit/Exception/GitException.php39
-rw-r--r--library/kzykhys/git/src/PHPGit/Git.php308
-rw-r--r--library/kzykhys/git/test/PHPGit/BaseTestCase.php33
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/AddCommandTest.php42
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/ArchiveCommandTest.php32
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/BranchCommandTest.php106
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/CatCommandTest.php65
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/CheckoutCommandTest.php65
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/CloneCommandTest.php28
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/CommitCommandTest.php26
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/ConfigCommandTest.php55
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/DescribeCommandTest.php36
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/FetchCommandTest.php36
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/MergeCommandTest.php101
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/MvCommandTest.php26
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/PullCommandTest.php22
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/PushCommandTest.php34
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/RebaseCommandTest.php154
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/Remote/SetBranchesCommandTest.php28
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/Remote/SetHeadCommandTest.php56
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/Remote/SetUrlCommandTest.php46
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/RemoteCommandTest.php100
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/ResetCommandTest.php128
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/RmCommandTest.php51
-rwxr-xr-xlibrary/kzykhys/git/test/PHPGit/Command/ShortlogCommandTest.php71
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/ShowCommandTest.php26
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/StashCommandTest.php204
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/StatusCommandTest.php76
-rw-r--r--library/kzykhys/git/test/PHPGit/Command/TagCommandTest.php58
-rw-r--r--library/kzykhys/git/test/PHPGit/Exception/GitExceptionTest.php23
-rw-r--r--library/kzykhys/git/test/PHPGit/GitTest.php33
64 files changed, 7210 insertions, 0 deletions
diff --git a/library/kzykhys/git/.travis.yml b/library/kzykhys/git/.travis.yml
new file mode 100644
index 000000000..ff4164104
--- /dev/null
+++ b/library/kzykhys/git/.travis.yml
@@ -0,0 +1,20 @@
+language: php
+
+php:
+ - 5.5
+ - 5.4
+ - 5.3
+
+before_script:
+ - composer require --no-update satooshi/php-coveralls:dev-master@dev sensiolabs/security-checker:dev-master
+ - composer update --dev --no-interaction
+ - git config --global user.name "John Doe"
+ - git config --global user.email "example@example.com"
+
+script:
+ - mkdir -p build/logs
+ - phpunit -c phpunit.xml.dist --coverage-clover build/logs/clover.xml
+ - php vendor/bin/security-checker security:check composer.lock
+
+after_script:
+ - php vendor/bin/coveralls \ No newline at end of file
diff --git a/library/kzykhys/git/README.md b/library/kzykhys/git/README.md
new file mode 100644
index 000000000..b2f6635af
--- /dev/null
+++ b/library/kzykhys/git/README.md
@@ -0,0 +1,1347 @@
+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']);
+ }
+}
+```
+
+API
+---
+
+### Git commands
+
+* [git add](#git-add)
+ * $git->[add](#git-addstringarraytraversable-file-array-options--)(_string|array|\Traversable_ $file, _array_ $options = [])
+* [git archive](#git-archive)
+ * $git->[archive](#git-archivestring-file-string-tree--null-stringarraytraversable-path--null-array-options--)(_string_ $file, _string_ $tree = null, _string|array|\Traversable_ $path = null, _array_ $options = [])
+* [git branch](#git-branch)
+ * $git->[branch](#git-brancharray-options--)(_array_ $options = [])
+ * $git->branch->[create](#git-branch-createstring-branch-string-startpoint--null-array-options--)(_string_ $branch, _string_ $startPoint = null, _array_ $options = [])
+ * $git->branch->[move](#git-branch-movestring-branch-string-newbranch-array-options--)(_string_ $branch, _string_ $newBranch, _array_ $options = [])
+ * $git->branch->[delete](#git-branch-deletestring-branch-array-options--)(_string_ $branch, _array_ $options = [])
+* [git cat-file](#git-cat-file)
+ * $git->cat->[blob](#git-cat-blobstring-object)(_string_ $object)
+ * $git->cat->[type](#git-cat-typestring-object)(_string_ $object)
+ * $git->cat->[size](#git-cat-sizestring-object)(_string_ $object)
+* [git checkout](#git-checkout)
+ * $git->[checkout](#git-checkoutstring-branch-array-options--)(_string_ $branch, _array_ $options = [])
+ * $git->checkout->[create](#git-checkout-createstring-branch-string-startpoint--null-array-options--)(_string_ $branch, _string_ $startPoint = null, _array_ $options = [])
+ * $git->checkout->[orphan](#git-checkout-orphanstring-branch-string-startpoint--null-array-options--)(_string_ $branch, _string_ $startPoint = null, _array_ $options = [])
+* [git clone](#git-clone)
+ * $git->[clone](#git-clonestring-repository-string-path--null-array-options--)(_string_ $repository, _string_ $path = null, _array_ $options = [])
+* [git commit](#git-commit)
+ * $git->[commit](#git-commitstring-message-array-options--)(_string_ $message, _array_ $options = [])
+* [git config](#git-config)
+ * $git->[config](#git-configarray-options--)(_array_ $options = [])
+ * $git->config->[set](#git-config-setstring-name-string-value-array-options--)(_string_ $name, _string_ $value, _array_ $options = [])
+ * $git->config->[add](#git-config-addstring-name-string-value-array-options--)(_string_ $name, _string_ $value, _array_ $options = [])
+* [git describe](#git-describe)
+ * $git->[describe](#git-describestring-committish--null-array-options--)(_string_ $committish = null, _array_ $options = [])
+ * $git->describe->[tags](#git-describe-tagsstring-committish--null-array-options--)(_string_ $committish = null, _array_ $options = [])
+* [git fetch](#git-fetch)
+ * $git->[fetch](#git-fetchstring-repository-string-refspec--null-array-options--)(_string_ $repository, _string_ $refspec = null, _array_ $options = [])
+ * $git->fetch->[all](#git-fetch-allarray-options--)(_array_ $options = [])
+* [git init](#git-init)
+ * $git->[init](#git-initstring-path-array-options--)(_string_ $path, _array_ $options = [])
+* [git log](#git-log)
+ * $git->[log](#git-logstring-revrange---string-path--null-array-options--)(_string_ $revRange = '', _string_ $path = null, _array_ $options = [])
+* [git merge](#git-merge)
+ * $git->[merge](#git-mergestringarraytraversable-commit-string-message--null-array-options--)(_string|array|\Traversable_ $commit, _string_ $message = null, _array_ $options = [])
+ * $git->merge->[abort](#git-merge-abort)()
+* [git mv](#git-mv)
+ * $git->[mv](#git-mvstringarrayiterator-source-string-destination-array-options--)(_string|array|\Iterator_ $source, _string_ $destination, _array_ $options = [])
+* [git pull](#git-pull)
+ * $git->[pull](#git-pullstring-repository--null-string-refspec--null-array-options--)(_string_ $repository = null, _string_ $refspec = null, _array_ $options = [])
+* [git push](#git-push)
+ * $git->[push](#git-pushstring-repository--null-string-refspec--null-array-options--)(_string_ $repository = null, _string_ $refspec = null, _array_ $options = [])
+* [git rebase](#git-rebase)
+ * $git->[rebase](#git-rebasestring-upstream--null-string-branch--null-array-options--)(_string_ $upstream = null, _string_ $branch = null, _array_ $options = [])
+ * $git->rebase->[continues](#git-rebase-continues)()
+ * $git->rebase->[abort](#git-rebase-abort)()
+ * $git->rebase->[skip](#git-rebase-skip)()
+* [git remote](#git-remote)
+ * $git->[remote](#git-remote)()
+ * $git->remote->[add](#git-remote-addstring-name-string-url-array-options--)(_string_ $name, _string_ $url, _array_ $options = [])
+ * $git->remote->[rename](#git-remote-renamestring-name-string-newname)(_string_ $name, _string_ $newName)
+ * $git->remote->[rm](#git-remote-rmstring-name)(_string_ $name)
+ * $git->remote->[show](#git-remote-showstring-name)(_string_ $name)
+ * $git->remote->[prune](#git-remote-prunestring-name--null)(_string_ $name = null)
+ * $git->remote->[head](#git-remote-headstring-name-string-branch--null)(_string_ $name, _string_ $branch = null)
+ * $git->remote->head->[set](#git-remote-head-setstring-name-string-branch)(_string_ $name, _string_ $branch)
+ * $git->remote->head->[delete](#git-remote-head-deletestring-name)(_string_ $name)
+ * $git->remote->head->[remote](#git-remote-head-remotestring-name)(_string_ $name)
+ * $git->remote->[branches](#git-remote-branchesstring-name-array-branches)(_string_ $name, _array_ $branches)
+ * $git->remote->branches->[set](#git-remote-branches-setstring-name-array-branches)(_string_ $name, _array_ $branches)
+ * $git->remote->branches->[add](#git-remote-branches-addstring-name-array-branches)(_string_ $name, _array_ $branches)
+ * $git->remote->[url](#git-remote-urlstring-name-string-newurl-string-oldurl--null-array-options--)(_string_ $name, _string_ $newUrl, _string_ $oldUrl = null, _array_ $options = [])
+ * $git->remote->url->[set](#git-remote-url-setstring-name-string-newurl-string-oldurl--null-array-options--)(_string_ $name, _string_ $newUrl, _string_ $oldUrl = null, _array_ $options = [])
+ * $git->remote->url->[add](#git-remote-url-addstring-name-string-newurl-array-options--)(_string_ $name, _string_ $newUrl, _array_ $options = [])
+ * $git->remote->url->[delete](#git-remote-url-deletestring-name-string-url-array-options--)(_string_ $name, _string_ $url, _array_ $options = [])
+* [git reset](#git-reset)
+ * $git->[reset](#git-resetstringarraytraversable-paths-string-commit--null)(_string|array|\Traversable_ $paths, _string_ $commit = null)
+ * $git->reset->[soft](#git-reset-softstring-commit--null)(_string_ $commit = null)
+ * $git->reset->[mixed](#git-reset-mixedstring-commit--null)(_string_ $commit = null)
+ * $git->reset->[hard](#git-reset-hardstring-commit--null)(_string_ $commit = null)
+ * $git->reset->[merge](#git-reset-mergestring-commit--null)(_string_ $commit = null)
+ * $git->reset->[keep](#git-reset-keepstring-commit--null)(_string_ $commit = null)
+ * $git->reset->[mode](#git-reset-modestring-mode-string-commit--null)(_string_ $mode, _string_ $commit = null)
+* [git rm](#git-rm)
+ * $git->[rm](#git-rmstringarraytraversable-file-array-options--)(_string|array|\Traversable_ $file, _array_ $options = [])
+ * $git->rm->[cached](#git-rm-cachedstringarraytraversable-file-array-options--)(_string|array|\Traversable_ $file, _array_ $options = [])
+* [git shortlog](#git-shortlog)
+ * $git->[shortlog](#git-shortlogstringarraytraversable-commits--head)(_string|array|\Traversable_ $commits = HEAD)
+ * $git->shortlog->[summary](#git-shortlog-summarystring-commits--head)(_string_ $commits = HEAD)
+* [git show](#git-show)
+ * $git->[show](#git-showstring-object-array-options--)(_string_ $object, _array_ $options = [])
+* [git stash](#git-stash)
+ * $git->[stash](#git-stash)()
+ * $git->stash->[save](#git-stash-savestring-message--null-array-options--)(_string_ $message = null, _array_ $options = [])
+ * $git->stash->[lists](#git-stash-listsarray-options--)(_array_ $options = [])
+ * $git->stash->[show](#git-stash-showstring-stash--null)(_string_ $stash = null)
+ * $git->stash->[drop](#git-stash-dropstring-stash--null)(_string_ $stash = null)
+ * $git->stash->[pop](#git-stash-popstring-stash--null-array-options--)(_string_ $stash = null, _array_ $options = [])
+ * $git->stash->[apply](#git-stash-applystring-stash--null-array-options--)(_string_ $stash = null, _array_ $options = [])
+ * $git->stash->[branch](#git-stash-branchstring-name-string-stash--null)(_string_ $name, _string_ $stash = null)
+ * $git->stash->[clear](#git-stash-clear)()
+ * $git->stash->[create](#git-stash-create)()
+* [git status](#git-status)
+ * $git->[status](#git-statusarray-options--)(_array_ $options = [])
+* [git tag](#git-tag)
+ * $git->[tag](#git-tag)()
+ * $git->tag->[create](#git-tag-createstring-tag-string-commit--null-array-options--)(_string_ $tag, _string_ $commit = null, _array_ $options = [])
+ * $git->tag->[delete](#git-tag-deletestringarraytraversable-tag)(_string|array|\Traversable_ $tag)
+ * $git->tag->[verify](#git-tag-verifystringarraytraversable-tag)(_string|array|\Traversable_ $tag)
+* [git ls-tree](#git-ls-tree)
+ * $git->[tree](#git-treestring-branch--master-string-path--)(_string_ $branch = master, _string_ $path = '')
+
+* * * * *
+
+### git add
+
+#### $git->add(_string|array|\Traversable_ $file, _array_ $options = [])
+
+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
+
+* * * * *
+
+### git archive
+
+#### $git->archive(_string_ $file, _string_ $tree = null, _string|array|\Traversable_ $path = null, _array_ $options = [])
+
+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
+
+* * * * *
+
+### git branch
+
+#### $git->branch(_array_ $options = [])
+
+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
+
+#### $git->branch->create(_string_ $branch, _string_ $startPoint = null, _array_ $options = [])
+
+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
+
+#### $git->branch->move(_string_ $branch, _string_ $newBranch, _array_ $options = [])
+
+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
+
+#### $git->branch->delete(_string_ $branch, _array_ $options = [])
+
+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
+
+* * * * *
+
+### git cat-file
+
+#### $git->cat->blob(_string_ $object)
+
+Returns the contents of blob object
+
+``` php
+$git = new PHPGit\Git();
+$git->setRepository('/path/to/repo');
+$contents = $git->cat->blob('e69de29bb2d1d6434b8b29ae775ad8');
+```
+
+#### $git->cat->type(_string_ $object)
+
+Returns the object type identified by **$object**
+
+``` php
+$git = new PHPGit\Git();
+$git->setRepository('/path/to/repo');
+$type = $git->cat->type('e69de29bb2d1d6434b8b29ae775ad8');
+```
+
+#### $git->cat->size(_string_ $object)
+
+Returns the object size identified by **$object**
+
+``` php
+$git = new PHPGit\Git();
+$git->setRepository('/path/to/repo');
+$type = $git->cat->size('e69de29bb2d1d6434b8b29ae775ad8');
+```
+
+* * * * *
+
+### git checkout
+
+#### $git->checkout(_string_ $branch, _array_ $options = [])
+
+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
+
+#### $git->checkout->create(_string_ $branch, _string_ $startPoint = null, _array_ $options = [])
+
+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
+
+#### $git->checkout->orphan(_string_ $branch, _string_ $startPoint = null, _array_ $options = [])
+
+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
+
+* * * * *
+
+### git clone
+
+#### $git->clone(_string_ $repository, _string_ $path = null, _array_ $options = [])
+
+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
+
+* * * * *
+
+### git commit
+
+#### $git->commit(_string_ $message, _array_ $options = [])
+
+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
+
+* * * * *
+
+### git config
+
+#### $git->config(_array_ $options = [])
+
+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
+
+#### $git->config->set(_string_ $name, _string_ $value, _array_ $options = [])
+
+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
+
+#### $git->config->add(_string_ $name, _string_ $value, _array_ $options = [])
+
+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
+
+* * * * *
+
+### git describe
+
+#### $git->describe(_string_ $committish = null, _array_ $options = [])
+
+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
+
+#### $git->describe->tags(_string_ $committish = null, _array_ $options = [])
+
+Equivalent to $git->describe($committish, ['tags' => true]);
+
+* * * * *
+
+### git fetch
+
+#### $git->fetch(_string_ $repository, _string_ $refspec = null, _array_ $options = [])
+
+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
+
+#### $git->fetch->all(_array_ $options = [])
+
+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
+
+* * * * *
+
+### git init
+
+#### $git->init(_string_ $path, _array_ $options = [])
+
+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
+
+* * * * *
+
+### git log
+
+#### $git->log(_string_ $revRange = '', _string_ $path = null, _array_ $options = [])
+
+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
+
+* * * * *
+
+### git merge
+
+#### $git->merge(_string|array|\Traversable_ $commit, _string_ $message = null, _array_ $options = [])
+
+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
+
+#### $git->merge->abort()
+
+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();
+}
+```
+
+* * * * *
+
+### git mv
+
+#### $git->mv(_string|array|\Iterator_ $source, _string_ $destination, _array_ $options = [])
+
+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
+
+* * * * *
+
+### git pull
+
+#### $git->pull(_string_ $repository = null, _string_ $refspec = null, _array_ $options = [])
+
+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');
+```
+
+* * * * *
+
+### git push
+
+#### $git->push(_string_ $repository = null, _string_ $refspec = null, _array_ $options = [])
+
+Update remote refs along with associated objects
+
+``` php
+$git = new PHPGit\Git();
+$git->setRepository('/path/to/repo');
+$git->push('origin', 'master');
+```
+
+* * * * *
+
+### git rebase
+
+#### $git->rebase(_string_ $upstream = null, _string_ $branch = null, _array_ $options = [])
+
+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
+
+#### $git->rebase->continues()
+
+Restart the rebasing process after having resolved a merge conflict
+
+#### $git->rebase->abort()
+
+Abort the rebase operation and reset HEAD to the original branch
+
+#### $git->rebase->skip()
+
+Restart the rebasing process by skipping the current patch
+
+* * * * *
+
+### git remote
+
+#### $git->remote()
+
+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'
+ ]
+]
+```
+
+#### $git->remote->add(_string_ $name, _string_ $url, _array_ $options = [])
+
+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
+
+#### $git->remote->rename(_string_ $name, _string_ $newName)
+
+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');
+```
+
+#### $git->remote->rm(_string_ $name)
+
+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');
+```
+
+#### $git->remote->show(_string_ $name)
+
+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)
+```
+
+#### $git->remote->prune(_string_ $name = null)
+
+Deletes all stale remote-tracking branches under **$name**
+
+``` php
+$git = new PHPGit\Git();
+$git->setRepository('/path/to/repo');
+$git->remote->prune('origin');
+```
+
+#### $git->remote->head(_string_ $name, _string_ $branch = null)
+
+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');
+```
+
+#### $git->remote->head->set(_string_ $name, _string_ $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');
+```
+
+#### $git->remote->head->delete(_string_ $name)
+
+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');
+```
+
+#### $git->remote->head->remote(_string_ $name)
+
+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');
+```
+
+#### $git->remote->branches(_string_ $name, _array_ $branches)
+
+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'));
+```
+
+#### $git->remote->branches->set(_string_ $name, _array_ $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'));
+```
+
+#### $git->remote->branches->add(_string_ $name, _array_ $branches)
+
+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'));
+```
+
+#### $git->remote->url(_string_ $name, _string_ $newUrl, _string_ $oldUrl = null, _array_ $options = [])
+
+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
+
+#### $git->remote->url->set(_string_ $name, _string_ $newUrl, _string_ $oldUrl = null, _array_ $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
+
+#### $git->remote->url->add(_string_ $name, _string_ $newUrl, _array_ $options = [])
+
+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
+
+#### $git->remote->url->delete(_string_ $name, _string_ $url, _array_ $options = [])
+
+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
+
+* * * * *
+
+### git reset
+
+#### $git->reset(_string|array|\Traversable_ $paths, _string_ $commit = null)
+
+Resets the index entries for all **$paths** to their state at **$commit**
+
+``` php
+$git = new PHPGit\Git();
+$git->setRepository('/path/to/repo');
+$git->reset();
+```
+
+#### $git->reset->soft(_string_ $commit = null)
+
+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();
+```
+
+#### $git->reset->mixed(_string_ $commit = null)
+
+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();
+```
+
+#### $git->reset->hard(_string_ $commit = null)
+
+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();
+```
+
+#### $git->reset->merge(_string_ $commit = null)
+
+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();
+```
+
+#### $git->reset->keep(_string_ $commit = null)
+
+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();
+```
+
+#### $git->reset->mode(_string_ $mode, _string_ $commit = null)
+
+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');
+```
+
+* * * * *
+
+### git rm
+
+#### $git->rm(_string|array|\Traversable_ $file, _array_ $options = [])
+
+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
+
+#### $git->rm->cached(_string|array|\Traversable_ $file, _array_ $options = [])
+
+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
+
+* * * * *
+
+### git shortlog
+
+#### $git->shortlog(_string|array|\Traversable_ $commits = HEAD)
+
+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'],
+ ],
+ //...
+]
+```
+
+#### $git->shortlog->summary(_string_ $commits = HEAD)
+
+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,
+ //...
+]
+```
+
+* * * * *
+
+### git show
+
+#### $git->show(_string_ $object, _array_ $options = [])
+
+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
+
+* * * * *
+
+### git stash
+
+#### $git->stash()
+
+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();
+```
+
+#### $git->stash->save(_string_ $message = null, _array_ $options = [])
+
+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');
+```
+
+#### $git->stash->lists(_array_ $options = [])
+
+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'],
+]
+```
+
+#### $git->stash->show(_string_ $stash = null)
+
+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(-)
+```
+
+#### $git->stash->drop(_string_ $stash = null)
+
+Remove a single stashed state from the stash list
+
+``` php
+$git = new PHPGit\Git();
+$git->setRepository('/path/to/repo');
+$git->stash->drop('stash@{0}');
+```
+
+#### $git->stash->pop(_string_ $stash = null, _array_ $options = [])
+
+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}');
+```
+
+#### $git->stash->apply(_string_ $stash = null, _array_ $options = [])
+
+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}');
+```
+
+#### $git->stash->branch(_string_ $name, _string_ $stash = null)
+
+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}');
+```
+
+#### $git->stash->clear()
+
+Remove all the stashed states
+
+``` php
+$git = new PHPGit\Git();
+$git->setRepository('/path/to/repo');
+$git->stash->clear();
+```
+
+#### $git->stash->create()
+
+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
+```
+
+* * * * *
+
+### git status
+
+#### $git->status(_array_ $options = [])
+
+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
+
+* * * * *
+
+### git tag
+
+#### $git->tag()
+
+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']
+```
+
+#### $git->tag->create(_string_ $tag, _string_ $commit = null, _array_ $options = [])
+
+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 address’s key
+- **force** (_boolean_) Replace an existing tag with the given name (instead of failing)
+
+#### $git->tag->delete(_string|array|\Traversable_ $tag)
+
+Delete existing tags with the given names
+
+#### $git->tag->verify(_string|array|\Traversable_ $tag)
+
+Verify the gpg signature of the given tag names
+
+* * * * *
+
+### git ls-tree
+
+#### $git->tree(_string_ $branch = master, _string_ $path = '')
+
+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'],
+]
+```
+
+License
+-------
+
+The MIT License
+
+Author
+------
+
+Kazuyuki Hayashi (@kzykhys)
diff --git a/library/kzykhys/git/phpunit.xml.dist b/library/kzykhys/git/phpunit.xml.dist
new file mode 100644
index 000000000..ad8dd7b00
--- /dev/null
+++ b/library/kzykhys/git/phpunit.xml.dist
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- http://www.phpunit.de/manual/current/en/appendixes.configuration.html -->
+<phpunit
+ backupGlobals = "false"
+ backupStaticAttributes = "false"
+ colors = "true"
+ convertErrorsToExceptions = "true"
+ convertNoticesToExceptions = "true"
+ convertWarningsToExceptions = "true"
+ processIsolation = "false"
+ stopOnFailure = "false"
+ syntaxCheck = "false"
+ bootstrap = "vendor/autoload.php" >
+
+ <testsuites>
+ <testsuite name="Project Test Suite">
+ <directory>test</directory>
+ </testsuite>
+ </testsuites>
+
+ <filter>
+ <whitelist>
+ <directory>src</directory>
+ </whitelist>
+ </filter>
+
+</phpunit> \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command.php b/library/kzykhys/git/src/PHPGit/Command.php
new file mode 100644
index 000000000..9238a5454
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command.php
@@ -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);
+ }
+ }
+ }
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/AddCommand.php b/library/kzykhys/git/src/PHPGit/Command/AddCommand.php
new file mode 100644
index 000000000..c035b2412
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/AddCommand.php
@@ -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,
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/ArchiveCommand.php b/library/kzykhys/git/src/PHPGit/Command/ArchiveCommand.php
new file mode 100644
index 000000000..a6c9bd0d9
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/ArchiveCommand.php
@@ -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')
+ ));
+ }
+
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/BranchCommand.php b/library/kzykhys/git/src/PHPGit/Command/BranchCommand.php
new file mode 100644
index 000000000..4b42f5048
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/BranchCommand.php
@@ -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');
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/CatCommand.php b/library/kzykhys/git/src/PHPGit/Command/CatCommand.php
new file mode 100644
index 000000000..0c4fc1f41
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/CatCommand.php
@@ -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));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/CheckoutCommand.php b/library/kzykhys/git/src/PHPGit/Command/CheckoutCommand.php
new file mode 100644
index 000000000..caddf07bd
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/CheckoutCommand.php
@@ -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
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/CloneCommand.php b/library/kzykhys/git/src/PHPGit/Command/CloneCommand.php
new file mode 100644
index 000000000..cc6b4ab37
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/CloneCommand.php
@@ -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
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/CommitCommand.php b/library/kzykhys/git/src/PHPGit/Command/CommitCommand.php
new file mode 100644
index 000000000..a4f2bdd95
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/CommitCommand.php
@@ -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')
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/ConfigCommand.php b/library/kzykhys/git/src/PHPGit/Command/ConfigCommand.php
new file mode 100644
index 000000000..cb8bb625f
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/ConfigCommand.php
@@ -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,
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/DescribeCommand.php b/library/kzykhys/git/src/PHPGit/Command/DescribeCommand.php
new file mode 100644
index 000000000..affdd009b
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/DescribeCommand.php
@@ -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,
+ ));
+ }
+
+}
diff --git a/library/kzykhys/git/src/PHPGit/Command/FetchCommand.php b/library/kzykhys/git/src/PHPGit/Command/FetchCommand.php
new file mode 100644
index 000000000..1302038e8
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/FetchCommand.php
@@ -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,
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/InitCommand.php b/library/kzykhys/git/src/PHPGit/Command/InitCommand.php
new file mode 100644
index 000000000..1ff56fa5b
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/InitCommand.php
@@ -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
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/LogCommand.php b/library/kzykhys/git/src/PHPGit/Command/LogCommand.php
new file mode 100644
index 000000000..c116550f7
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/LogCommand.php
@@ -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
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/MergeCommand.php b/library/kzykhys/git/src/PHPGit/Command/MergeCommand.php
new file mode 100644
index 000000000..e1987151f
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/MergeCommand.php
@@ -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
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/MvCommand.php b/library/kzykhys/git/src/PHPGit/Command/MvCommand.php
new file mode 100644
index 000000000..fe7ce6af6
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/MvCommand.php
@@ -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
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/PullCommand.php b/library/kzykhys/git/src/PHPGit/Command/PullCommand.php
new file mode 100644
index 000000000..a7cea0025
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/PullCommand.php
@@ -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)
+ {
+
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/PushCommand.php b/library/kzykhys/git/src/PHPGit/Command/PushCommand.php
new file mode 100644
index 000000000..d0665d735
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/PushCommand.php
@@ -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
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/RebaseCommand.php b/library/kzykhys/git/src/PHPGit/Command/RebaseCommand.php
new file mode 100644
index 000000000..7516b360c
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/RebaseCommand.php
@@ -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')
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/Remote/SetBranchesCommand.php b/library/kzykhys/git/src/PHPGit/Command/Remote/SetBranchesCommand.php
new file mode 100644
index 000000000..4e17a4d48
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/Remote/SetBranchesCommand.php
@@ -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;
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/Remote/SetHeadCommand.php b/library/kzykhys/git/src/PHPGit/Command/Remote/SetHeadCommand.php
new file mode 100644
index 000000000..9241ef5b7
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/Remote/SetHeadCommand.php
@@ -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;
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/Remote/SetUrlCommand.php b/library/kzykhys/git/src/PHPGit/Command/Remote/SetUrlCommand.php
new file mode 100644
index 000000000..7b7d84ff5
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/Remote/SetUrlCommand.php
@@ -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
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/RemoteCommand.php b/library/kzykhys/git/src/PHPGit/Command/RemoteCommand.php
new file mode 100644
index 000000000..08220a551
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/RemoteCommand.php
@@ -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
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/ResetCommand.php b/library/kzykhys/git/src/PHPGit/Command/ResetCommand.php
new file mode 100644
index 000000000..f70f53e2e
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/ResetCommand.php
@@ -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;
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/RmCommand.php b/library/kzykhys/git/src/PHPGit/Command/RmCommand.php
new file mode 100644
index 000000000..d6da31230
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/RmCommand.php
@@ -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
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/ShortlogCommand.php b/library/kzykhys/git/src/PHPGit/Command/ShortlogCommand.php
new file mode 100644
index 000000000..23c66e464
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/ShortlogCommand.php
@@ -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;
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/ShowCommand.php b/library/kzykhys/git/src/PHPGit/Command/ShowCommand.php
new file mode 100644
index 000000000..866388357
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/ShowCommand.php
@@ -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'),
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/StashCommand.php b/library/kzykhys/git/src/PHPGit/Command/StashCommand.php
new file mode 100644
index 000000000..52dceaa6b
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/StashCommand.php
@@ -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());
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/StatusCommand.php b/library/kzykhys/git/src/PHPGit/Command/StatusCommand.php
new file mode 100644
index 000000000..c2bc983fe
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/StatusCommand.php
@@ -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
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/TagCommand.php b/library/kzykhys/git/src/PHPGit/Command/TagCommand.php
new file mode 100644
index 000000000..197d3e887
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/TagCommand.php
@@ -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 address’s 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 address’s 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,
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Command/TreeCommand.php b/library/kzykhys/git/src/PHPGit/Command/TreeCommand.php
new file mode 100644
index 000000000..ea1040a6a
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Command/TreeCommand.php
@@ -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;
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Exception/GitException.php b/library/kzykhys/git/src/PHPGit/Exception/GitException.php
new file mode 100644
index 000000000..f5901010c
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Exception/GitException.php
@@ -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;
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/src/PHPGit/Git.php b/library/kzykhys/git/src/PHPGit/Git.php
new file mode 100644
index 000000000..f952d6f1f
--- /dev/null
+++ b/library/kzykhys/git/src/PHPGit/Git.php
@@ -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();
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/BaseTestCase.php b/library/kzykhys/git/test/PHPGit/BaseTestCase.php
new file mode 100644
index 000000000..df69b216e
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/BaseTestCase.php
@@ -0,0 +1,33 @@
+<?php
+
+use Symfony\Component\Filesystem\Filesystem;
+
+/**
+ * @author Kazuyuki Hayashi <hayashi@siance.co.jp>
+ */
+abstract class BaseTestCase extends PHPUnit_Framework_TestCase
+{
+
+ /**
+ * @var string
+ */
+ protected $directory;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setUp()
+ {
+ $this->directory = __DIR__.'/../../build/' . strtolower(get_class($this));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function tearDown()
+ {
+ $filesystem = new Filesystem();
+ $filesystem->remove($this->directory);
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/AddCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/AddCommandTest.php
new file mode 100644
index 000000000..1fad08417
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/AddCommandTest.php
@@ -0,0 +1,42 @@
+<?php
+
+use PHPGit\Git;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+/**
+ * @author Kazuyuki Hayashi <hayashi@valnur.net>
+ */
+class AddCommandTest extends BaseTestCase
+{
+
+ public function testAdd()
+ {
+ $filesystem = new Filesystem();
+ $filesystem->mkdir($this->directory);
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem->dumpFile($this->directory . '/test.txt', 'foo');
+ $filesystem->dumpFile($this->directory . '/test.md', '**foo**');
+
+ $this->assertTrue($git->add('test.txt'));
+ $this->assertTrue($git->add(array('test.md'), array('force' => true)));
+ }
+
+ /**
+ * @expectedException \PHPGit\Exception\GitException
+ * @expectedExceptionCode 128
+ */
+ public function testException()
+ {
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $git->add('foo');
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/ArchiveCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/ArchiveCommandTest.php
new file mode 100644
index 000000000..982c8f7c7
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/ArchiveCommandTest.php
@@ -0,0 +1,32 @@
+<?php
+
+use PHPGit\Git;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+/**
+ * @author Kazuyuki Hayashi <hayashi@valnur.net>
+ */
+class ArchiveCommandTest extends BaseTestCase
+{
+
+ public function testArchive()
+ {
+ $filesystem = new Filesystem();
+ $filesystem->mkdir($this->directory);
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem->dumpFile($this->directory . '/test.txt', 'hello');
+ $git->add('test.txt');
+ $git->commit('Initial commit');
+
+ $git->archive($this->directory . '/test.zip', 'master', null, array('format' => 'zip', 'prefix' => 'test/'));
+
+ $this->assertFileExists($this->directory . '/test.zip');
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/BranchCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/BranchCommandTest.php
new file mode 100644
index 000000000..4deeea367
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/BranchCommandTest.php
@@ -0,0 +1,106 @@
+<?php
+
+use PHPGit\Git;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class BranchCommandTest extends BaseTestCase
+{
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem->dumpFile($this->directory . '/test.txt', '');
+ $git->add('test.txt');
+ $git->commit('Initial commit');
+ }
+
+ public function testBranch()
+ {
+ $git = new Git();
+ $git->setRepository($this->directory);
+
+ $branches = $git->branch();
+
+ $this->assertCount(1, $branches);
+ $this->assertEquals('master', $branches['master']['name']);
+ $this->assertTrue($branches['master']['current']);
+ $this->assertEquals('Initial commit', $branches['master']['title']);
+ }
+
+ public function testAllBranch()
+ {
+ $git = new Git();
+ $git->clone('file://' . realpath($this->directory), $this->directory.'2');
+ $git->setRepository($this->directory.'2');
+
+ $branches = $git->branch(array('remotes' => true));
+ $this->assertArrayHasKey('origin/master', $branches);
+
+ $branches = $git->branch(array('all' => true));
+ $this->assertArrayHasKey('master', $branches);
+ $this->assertArrayHasKey('remotes/origin/master', $branches);
+
+ $filesystem = new Filesystem();
+ $filesystem->remove($this->directory.'2');
+ }
+
+ public function testBranchCreate()
+ {
+ $git = new Git();
+ $git->setRepository($this->directory);
+
+ $git->branch->create('1.0');
+ $branches = $git->branch();
+ $this->assertCount(2, $branches);
+
+ $git->branch->create('1.0-fix', '1.0', array('force' => true));
+ $branches = $git->branch();
+ $this->assertCount(3, $branches);
+ $this->assertArrayHasKey('1.0', $branches);
+ $this->assertArrayHasKey('1.0-fix', $branches);
+ }
+
+ public function testBranchMove()
+ {
+ $git = new Git();
+ $git->setRepository($this->directory);
+ $git->branch->create('1.0');
+ $git->branch->move('1.0', '1.0.x');
+ $branches = $git->branch();
+ $this->assertCount(2, $branches);
+ $this->assertArrayHasKey('1.0.x', $branches);
+
+ $git->branch->move('1.0.x', '2.x', array('force' => true));
+ $branches = $git->branch();
+ $this->assertCount(2, $branches);
+ $this->assertArrayHasKey('2.x', $branches);
+ }
+
+ public function testBranchDelete()
+ {
+ $git = new Git();
+ $git->setRepository($this->directory);
+ $git->branch->create('1.0');
+ $git->branch->create('2.0');
+ $branches = $git->branch();
+ $this->assertCount(3, $branches);
+
+ $git->branch->delete('1.0');
+ $branches = $git->branch();
+ $this->assertCount(2, $branches);
+
+ $git->branch->delete('2.0', array('force' => true));
+ $branches = $git->branch();
+ $this->assertCount(1, $branches);
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/CatCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/CatCommandTest.php
new file mode 100644
index 000000000..945924ccb
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/CatCommandTest.php
@@ -0,0 +1,65 @@
+<?php
+
+use PHPGit\Git;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class CatCommandTest extends BaseTestCase
+{
+
+ public function testCatBlob()
+ {
+ $filesystem = new Filesystem();
+ $filesystem->mkdir($this->directory);
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem->dumpFile($this->directory . '/test.txt', 'foo');
+ $git->add('test.txt');
+ $git->commit('Initial commit');
+
+ $tree = $git->tree();
+
+ $this->assertEquals('foo', $git->cat->blob($tree[0]['hash']));
+ }
+
+ public function testCatType()
+ {
+ $filesystem = new Filesystem();
+ $filesystem->mkdir($this->directory);
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem->dumpFile($this->directory . '/test.txt', 'foo');
+ $git->add('test.txt');
+ $git->commit('Initial commit');
+
+ $tree = $git->tree();
+
+ $this->assertEquals('blob', $git->cat->type($tree[0]['hash']));
+ }
+
+ public function testCatSize()
+ {
+ $filesystem = new Filesystem();
+ $filesystem->mkdir($this->directory);
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem->dumpFile($this->directory . '/test.txt', 'foo');
+ $git->add('test.txt');
+ $git->commit('Initial commit');
+
+ $tree = $git->tree();
+
+ $this->assertEquals(3, $git->cat->size($tree[0]['hash']));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/CheckoutCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/CheckoutCommandTest.php
new file mode 100644
index 000000000..c306ea407
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/CheckoutCommandTest.php
@@ -0,0 +1,65 @@
+<?php
+
+use PHPGit\Git;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class CheckoutCommandTest extends BaseTestCase
+{
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem->dumpFile($this->directory . '/test.txt', '');
+ $git->add('test.txt');
+ $git->commit('Initial commit');
+ }
+
+ public function testCheckout()
+ {
+ $git = new Git();
+ $git->setRepository($this->directory);
+ $git->branch->create('next');
+ $git->checkout('next');
+
+ $branches = $git->branch();
+ $this->assertArrayHasKey('next', $branches);
+ $this->assertTrue($branches['next']['current']);
+ }
+
+ public function testCheckoutCreate()
+ {
+ $git = new Git();
+ $git->setRepository($this->directory);
+ $git->checkout->create('next');
+
+ $branches = $git->branch();
+ $this->assertArrayHasKey('next', $branches);
+ $this->assertTrue($branches['next']['current']);
+
+ $git->checkout->create('develop', 'next');
+
+ $branches = $git->branch();
+ $this->assertArrayHasKey('develop', $branches);
+ $this->assertTrue($branches['develop']['current']);
+ }
+
+ public function testCheckoutOrphan()
+ {
+ $git = new Git();
+ $git->setRepository($this->directory);
+ $git->checkout->orphan('gh-pages', 'master', array('force' => true));
+
+ $status = $git->status();
+ $this->assertEquals('gh-pages', $status['branch']);
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/CloneCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/CloneCommandTest.php
new file mode 100644
index 000000000..d6a4d26ff
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/CloneCommandTest.php
@@ -0,0 +1,28 @@
+<?php
+
+use PHPGit\Git;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class CloneCommandTest extends BaseTestCase
+{
+
+ public function testClone()
+ {
+ $git = new Git();
+ $git->clone('https://github.com/kzykhys/Text.git', $this->directory);
+ $git->setRepository($this->directory);
+
+ $this->assertFileExists($this->directory . '/.git');
+
+ $filesystem = new Filesystem();
+ $filesystem->remove($this->directory);
+
+ $git->setRepository('.');
+ $git->clone('https://github.com/kzykhys/Text.git', $this->directory, array('shared' => true));
+
+ $this->assertFileExists($this->directory . '/.git');
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/CommitCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/CommitCommandTest.php
new file mode 100644
index 000000000..01b50ad8d
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/CommitCommandTest.php
@@ -0,0 +1,26 @@
+<?php
+
+use PHPGit\Git;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class CommitCommandTest extends BaseTestCase
+{
+
+ public function testCommit()
+ {
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem = new Filesystem();
+ $filesystem->dumpFile($this->directory . '/test.txt', '');
+ $git->add('test.txt');
+ $git->commit('Initial commit');
+ $logs = $git->log('test.txt');
+
+ $this->assertCount(1, $logs);
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/ConfigCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/ConfigCommandTest.php
new file mode 100644
index 000000000..fba2fbf76
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/ConfigCommandTest.php
@@ -0,0 +1,55 @@
+<?php
+
+use PHPGit\Git;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class ConfigCommandTest extends BaseTestCase
+{
+
+ public function testConfigSetAndList()
+ {
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $before = $git->config();
+
+ $git->config->set('user.name', 'John Doe');
+
+ $config = $git->config();
+ $this->assertArrayHasKey('user.name', $config);
+
+ $expected = 'John Doe';
+
+ if (isset($before['user.name'])) {
+ $expected = $before['user.name'] . "\n" . $expected;
+ }
+
+ $this->assertEquals($expected, $config['user.name']);
+ }
+
+ public function testConfigAdd()
+ {
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $before = $git->config();
+
+ $git->config->set('user.name', 'John Doe');
+ $git->config->add('user.name', 'Foo');
+
+ $config = $git->config();
+ $this->assertArrayHasKey('user.name', $config);
+
+ $expected = "John Doe\nFoo";
+
+ if (isset($before['user.name'])) {
+ $expected = $before['user.name'] . "\n" . $expected;
+ }
+
+ $this->assertEquals($expected, $config['user.name']);
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/DescribeCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/DescribeCommandTest.php
new file mode 100644
index 000000000..04d3bd3b0
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/DescribeCommandTest.php
@@ -0,0 +1,36 @@
+<?php
+
+use PHPGit\Git;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class DescribeCommandTest extends BaseTestCase
+{
+
+ public function testDescribeTags()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+ $git->add('README.md');
+ $git->commit('Initial commit');
+ $git->tag->create('v1.0.0');
+ $version = $git->describe->tags('HEAD');
+
+ $this->assertEquals('v1.0.0', $version);
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello2');
+ $git->add('README.md');
+ $git->commit('Fixes README');
+ $version = $git->describe->tags('HEAD');
+
+ $this->assertStringStartsWith('v1.0.0', $version);
+ $this->assertStringEndsNotWith('v1.0.0', $version);
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/FetchCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/FetchCommandTest.php
new file mode 100644
index 000000000..f52943099
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/FetchCommandTest.php
@@ -0,0 +1,36 @@
+<?php
+
+use PHPGit\Git;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class FetchCommandTest extends BaseTestCase
+{
+
+ public function testFetch()
+ {
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
+ $git->fetch('origin', '+refs/heads/*:refs/remotes/origin/*');
+
+ $tags = $git->tag();
+ $this->assertContains('v1.0.0', $tags);
+ }
+
+ public function testFetchAll()
+ {
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
+ $git->fetch->all();
+
+ $tags = $git->tag();
+ $this->assertContains('v1.0.0', $tags);
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/MergeCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/MergeCommandTest.php
new file mode 100644
index 000000000..208461523
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/MergeCommandTest.php
@@ -0,0 +1,101 @@
+<?php
+
+use PHPGit\Git;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class MergeCommandTest extends BaseTestCase
+{
+
+ public function testMerge()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem->dumpFile($this->directory . '/test.txt', 'foo');
+ $git->add('test.txt');
+ $git->commit('master');
+
+ $git->checkout->create('develop');
+ $filesystem->dumpFile($this->directory . '/test.txt', 'bar');
+ $git->add('test.txt');
+ $git->commit('develop');
+
+ $git->checkout('master');
+
+ $this->assertEquals('foo', file_get_contents($this->directory . '/test.txt'));
+
+ $git->merge('develop');
+
+ $this->assertEquals('bar', file_get_contents($this->directory . '/test.txt'));
+ }
+
+ /**
+ * @expectedException \PHPGit\Exception\GitException
+ */
+ public function testMergeFail()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ // branch:master
+ $filesystem->dumpFile($this->directory . '/test.txt', 'foo');
+ $git->add('test.txt');
+ $git->commit('master');
+
+ // branch:develop
+ $git->checkout->create('develop');
+ $filesystem->dumpFile($this->directory . '/test.txt', 'bar');
+ $git->add('test.txt');
+ $git->commit('develop');
+
+ // branch:master
+ $git->checkout('master');
+ $filesystem->dumpFile($this->directory . '/test.txt', 'baz');
+ $git->merge('develop');
+ }
+
+ public function testMergeAbort()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ // branch:master
+ $filesystem->dumpFile($this->directory . '/test.txt', 'foo');
+ $git->add('test.txt');
+ $git->commit('master');
+
+ // branch:develop
+ $git->checkout->create('develop');
+ $filesystem->dumpFile($this->directory . '/test.txt', 'bar');
+ $git->add('test.txt');
+ $git->commit('develop');
+
+ // branch:master
+ $git->checkout('master');
+ $filesystem->dumpFile($this->directory . '/test.txt', 'baz');
+ $git->add('test.txt');
+ $git->commit('master');
+
+ try {
+ $git->merge('develop');
+ $this->fail('$git->merge("develop") should fail');
+ } catch (Exception $e) {
+ }
+
+ $git->merge->abort();
+
+ $this->assertEquals('baz', file_get_contents($this->directory . '/test.txt'));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/MvCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/MvCommandTest.php
new file mode 100644
index 000000000..dd5f46c55
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/MvCommandTest.php
@@ -0,0 +1,26 @@
+<?php
+
+use PHPGit\Git;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class MvCommandTest extends BaseTestCase
+{
+
+ public function testMv()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/test.txt', 'foo');
+ $git->add('test.txt');
+ $git->commit('Initial commit');
+ $git->mv('test.txt', 'test2.txt');
+
+ $this->assertFileExists($this->directory . '/test2.txt');
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/PullCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/PullCommandTest.php
new file mode 100644
index 000000000..89dec75a9
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/PullCommandTest.php
@@ -0,0 +1,22 @@
+<?php
+
+use PHPGit\Git;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class PullCommandTest extends BaseTestCase
+{
+
+ public function testPull()
+ {
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
+ $git->pull('origin', 'master');
+
+ $this->assertFileExists($this->directory . '/README.md');
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/PushCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/PushCommandTest.php
new file mode 100644
index 000000000..11424cebc
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/PushCommandTest.php
@@ -0,0 +1,34 @@
+<?php
+
+use PHPGit\Git;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class PushCommandTest extends BaseTestCase
+{
+
+ public function testPush()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory, array('shared' => true, 'bare' => true));
+
+ $git->clone('file://' . realpath($this->directory), $this->directory.'2');
+ $git->setRepository($this->directory.'2');
+
+ $filesystem->dumpFile($this->directory.'2/test.txt', 'foobar');
+ $git->add('test.txt');
+ $git->commit('test');
+ $git->push('origin', 'master');
+
+ $git->clone('file://' . realpath($this->directory), $this->directory.'3');
+
+ $this->assertFileExists($this->directory.'3/test.txt');
+
+ $filesystem->remove($this->directory.'2');
+ $filesystem->remove($this->directory.'3');
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/RebaseCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/RebaseCommandTest.php
new file mode 100644
index 000000000..af7e87a19
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/RebaseCommandTest.php
@@ -0,0 +1,154 @@
+<?php
+
+use PHPGit\Git;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class RebaseCommandTest extends BaseTestCase
+{
+
+ public function testRebase()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem->dumpFile($this->directory . '/test.txt', '123');
+ $git->add('test.txt');
+ $git->commit('initial commit');
+
+ $git->checkout->create('next');
+ $filesystem->dumpFile($this->directory . '/test2.txt', '123');
+ $git->add('test2.txt');
+ $git->commit('test');
+
+ $git->checkout('master');
+ $git->rebase('next', 'master');
+
+ $this->assertFileExists($this->directory. '/test2.txt');
+ }
+
+ public function testRebaseOnto()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/test.txt', '123');
+ $git->add('test.txt');
+ $git->commit('initial commit');
+
+ $git->checkout->create('next');
+ $filesystem->dumpFile($this->directory . '/test2.txt', '123');
+ $git->add('test2.txt');
+ $git->commit('test');
+
+ $git->checkout->create('topic', 'next');
+ $filesystem->dumpFile($this->directory . '/test3.txt', '123');
+ $git->add('test3.txt');
+ $git->commit('test');
+
+ $git->rebase('next', null, array('onto' => 'master'));
+ $this->assertFileNotExists($this->directory . '/test2.txt');
+ }
+
+ public function testRebaseContinue()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem->dumpFile($this->directory . '/test.txt', 'foo');
+ $git->add('test.txt');
+ $git->commit('initial commit');
+
+ $git->checkout->create('next');
+ $filesystem->dumpFile($this->directory . '/test.txt', 'bar');
+ $git->add('test.txt');
+ $git->commit('next commit');
+
+ $git->checkout('master');
+ $filesystem->dumpFile($this->directory . '/test.txt', 'baz');
+ $git->add('test.txt');
+ $git->commit('master commit');
+
+ try {
+ $git->rebase('next');
+ $this->fail('GitException should be thrown');
+ } catch (\PHPGit\Exception\GitException $e) {
+ }
+
+ $filesystem->dumpFile($this->directory . '/test.txt', 'foobar');
+ $git->add('test.txt');
+ $git->rebase->continues();
+ }
+
+ public function testRebaseAbort()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem->dumpFile($this->directory . '/test.txt', 'foo');
+ $git->add('test.txt');
+ $git->commit('initial commit');
+
+ $git->checkout->create('next');
+ $filesystem->dumpFile($this->directory . '/test.txt', 'bar');
+ $git->add('test.txt');
+ $git->commit('next commit');
+
+ $git->checkout('master');
+ $filesystem->dumpFile($this->directory . '/test.txt', 'baz');
+ $git->add('test.txt');
+ $git->commit('master commit');
+
+ try {
+ $git->rebase('next');
+ $this->fail('GitException should be thrown');
+ } catch (\PHPGit\Exception\GitException $e) {
+ }
+
+ $git->rebase->abort();
+ }
+
+ public function testRebaseSkip()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem->dumpFile($this->directory . '/test.txt', 'foo');
+ $git->add('test.txt');
+ $git->commit('initial commit');
+
+ $git->checkout->create('next');
+ $filesystem->dumpFile($this->directory . '/test.txt', 'bar');
+ $git->add('test.txt');
+ $git->commit('next commit');
+
+ $git->checkout('master');
+ $filesystem->dumpFile($this->directory . '/test.txt', 'baz');
+ $git->add('test.txt');
+ $git->commit('master commit');
+
+ try {
+ $git->rebase('next');
+ $this->fail('GitException should be thrown');
+ } catch (\PHPGit\Exception\GitException $e) {
+ }
+
+ $git->rebase->skip();
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/Remote/SetBranchesCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/Remote/SetBranchesCommandTest.php
new file mode 100644
index 000000000..4f428f832
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/Remote/SetBranchesCommandTest.php
@@ -0,0 +1,28 @@
+<?php
+
+use PHPGit\Git;
+
+require_once __DIR__ . '/../../BaseTestCase.php';
+
+class SetBranchesCommandTest extends BaseTestCase
+{
+
+ public function testSetBranches()
+ {
+ $git = new Git();
+ $git->clone('https://github.com/kzykhys/Text.git', $this->directory);
+ $git->setRepository($this->directory);
+
+ $git->remote->branches('origin', array('master'));
+ }
+
+ public function testSetBranchesAdd()
+ {
+ $git = new Git();
+ $git->clone('https://github.com/kzykhys/Text.git', $this->directory);
+ $git->setRepository($this->directory);
+
+ $git->remote->branches->add('origin', array('gh-pages'));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/Remote/SetHeadCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/Remote/SetHeadCommandTest.php
new file mode 100644
index 000000000..679c2976f
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/Remote/SetHeadCommandTest.php
@@ -0,0 +1,56 @@
+<?php
+
+use PHPGit\Git;
+
+require_once __DIR__ . '/../../BaseTestCase.php';
+
+class SetHeadCommandTest extends BaseTestCase
+{
+
+ public function testSetHead()
+ {
+ $git = new Git();
+ $git->clone('https://github.com/kzykhys/Text.git', $this->directory);
+ $git->setRepository($this->directory);
+
+ $before = $git->branch(array('all' => true));
+
+ $git->remote->head('origin', 'master');
+
+ $after = $git->branch(array('all' => true));
+
+ $this->assertEquals($before, $after);
+ }
+
+ public function testSetHeadDelete()
+ {
+ $git = new Git();
+ $git->clone('https://github.com/kzykhys/Text.git', $this->directory);
+ $git->setRepository($this->directory);
+
+ $before = $git->branch(array('all' => true));
+
+ $git->remote->head->delete('origin');
+
+ $after = $git->branch(array('all' => true));
+
+ $this->assertNotEquals($before, $after);
+ }
+
+ public function testSetHeadRemote()
+ {
+ $git = new Git();
+ $git->clone('https://github.com/kzykhys/Text.git', $this->directory);
+ $git->setRepository($this->directory);
+
+ $before = $git->branch(array('all' => true));
+
+ $git->remote->head->delete('origin');
+ $git->remote->head->remote('origin');
+
+ $after = $git->branch(array('all' => true));
+
+ $this->assertEquals($before, $after);
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/Remote/SetUrlCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/Remote/SetUrlCommandTest.php
new file mode 100644
index 000000000..b70b67d40
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/Remote/SetUrlCommandTest.php
@@ -0,0 +1,46 @@
+<?php
+
+use PHPGit\Git;
+
+require_once __DIR__ . '/../../BaseTestCase.php';
+
+class SetUrlCommandTest extends BaseTestCase
+{
+
+ public function testSetUrl()
+ {
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $git->remote->add('origin', 'http://example.com/test.git');
+ $git->remote->url('origin', 'https://github.com/kzykhys/Text.git', 'http://example.com/test.git');
+
+ $remotes = $git->remote();
+
+ $this->assertEquals('https://github.com/kzykhys/Text.git', $remotes['origin']['fetch']);
+ }
+
+ public function testSetUrlAdd()
+ {
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $git->remote->add('origin', 'http://example.com/test.git');
+ $git->remote->url->add('origin', 'https://github.com/kzykhys/Text.git');
+ }
+
+ public function testSetUrlDelete()
+ {
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $git->remote->add('origin', 'http://example.com/test.git');
+ $git->remote->url->add('origin', 'https://github.com/kzykhys/Text.git');
+ $git->remote->url->delete('origin', 'https://github.com');
+
+ $remotes = $git->remote();
+
+ $this->assertEquals('http://example.com/test.git', $remotes['origin']['fetch']);
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/RemoteCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/RemoteCommandTest.php
new file mode 100644
index 000000000..78aa81309
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/RemoteCommandTest.php
@@ -0,0 +1,100 @@
+<?php
+
+use PHPGit\Git;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class RemoteCommandTest extends BaseTestCase
+{
+
+ public function testRemote()
+ {
+ $git = new Git();
+ $git->clone('https://github.com/kzykhys/Text.git', $this->directory);
+ $git->setRepository($this->directory);
+
+ $remotes = $git->remote();
+
+ $this->assertEquals(array(
+ 'origin' => array(
+ 'fetch' => 'https://github.com/kzykhys/Text.git',
+ 'push' => 'https://github.com/kzykhys/Text.git'
+ )
+ ), $remotes);
+ }
+
+ public function testRemoteAdd()
+ {
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
+
+ $remotes = $git->remote();
+
+ $this->assertEquals(array(
+ 'origin' => array(
+ 'fetch' => 'https://github.com/kzykhys/Text.git',
+ 'push' => 'https://github.com/kzykhys/Text.git'
+ )
+ ), $remotes);
+ }
+
+ public function testRemoteRename()
+ {
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
+ $git->remote->rename('origin', 'upstream');
+
+ $remotes = $git->remote();
+ $this->assertEquals(array(
+ 'upstream' => array(
+ 'fetch' => 'https://github.com/kzykhys/Text.git',
+ 'push' => 'https://github.com/kzykhys/Text.git'
+ )
+ ), $remotes);
+ }
+
+ public function testRemoteRm()
+ {
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
+ $git->remote->rm('origin');
+
+ $remotes = $git->remote();
+ $this->assertEquals(array(), $remotes);
+ }
+
+ public function testRemoteShow()
+ {
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
+
+ $this->assertNotEmpty($git->remote->show('origin'));
+ }
+
+ public function testRemotePrune()
+ {
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $git->remote->add('origin', 'https://github.com/kzykhys/Text.git');
+ $git->remote->prune('origin');
+ }
+
+ /**
+ * @expectedException \BadMethodCallException
+ */
+ public function testBadMethodCall()
+ {
+ $git = new Git();
+ $git->remote->foo();
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/ResetCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/ResetCommandTest.php
new file mode 100644
index 000000000..777e48512
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/ResetCommandTest.php
@@ -0,0 +1,128 @@
+<?php
+
+use PHPGit\Git;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+
+class ResetCommandTest extends BaseTestCase
+{
+
+ public function testReset()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'foo');
+ $git->add('README.md');
+ $git->commit('Initial commit');
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+ $git->add('README.md');
+
+ $git->reset('README.md', 'HEAD');
+ }
+
+ public function testResetSoft()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'foo');
+ $git->add('README.md');
+ $git->commit('Initial commit');
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+
+ $git->reset->soft();
+ }
+
+ public function testResetMixed()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'foo');
+ $git->add('README.md');
+ $git->commit('Initial commit');
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+
+ $git->reset->mixed();
+ }
+
+ public function testResetHard()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'foo');
+ $git->add('README.md');
+ $git->commit('Initial commit');
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+
+ $git->reset->hard('HEAD');
+ }
+
+ public function testResetMerge()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'foo');
+ $git->add('README.md');
+ $git->commit('Initial commit');
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+
+ $git->reset->merge();
+ }
+
+ public function testResetKeep()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'foo');
+ $git->add('README.md');
+ $git->commit('Initial commit');
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+
+ $git->reset->keep();
+ }
+
+ /**
+ * @expectedException InvalidArgumentException
+ */
+ public function testResetInvalidMode()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'foo');
+ $git->add('README.md');
+ $git->commit('Initial commit');
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+
+ $git->reset->mode('foo');
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/RmCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/RmCommandTest.php
new file mode 100644
index 000000000..34996cc12
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/RmCommandTest.php
@@ -0,0 +1,51 @@
+<?php
+
+use PHPGit\Git;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class RmCommandTest extends BaseTestCase
+{
+
+ public function testRm()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'foo');
+ $filesystem->dumpFile($this->directory . '/bin/test.php', 'foo');
+ $git->add(array('README.md', 'bin/test.php'));
+ $git->commit('Initial commit');
+
+ $git->rm('README.md');
+ $git->rm('bin', array('recursive' => true));
+
+ $this->assertFileNotExists($this->directory . '/README.md');
+ }
+
+ public function testRmCached()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'foo');
+ $git->add('README.md');
+ $git->commit('Initial commit');
+
+ $git->rm->cached('README.md');
+ $git->commit('Delete README.md');
+
+ $this->assertFileExists($this->directory . '/README.md');
+
+ $tree = $git->tree();
+ $this->assertEquals(array(), $tree);
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/ShortlogCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/ShortlogCommandTest.php
new file mode 100755
index 000000000..48967cab1
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/ShortlogCommandTest.php
@@ -0,0 +1,71 @@
+<?php
+
+use PHPGit\Git;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class ShortlogCommandTest extends BaseTestCase
+{
+
+ public function testShortlog()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $git->config->set('user.name', 'Name One');
+ $git->config->set('user.email', 'one@example.com');
+ $filesystem->dumpFile($this->directory . '/test.txt', '');
+ $git->add('test.txt');
+ $git->commit('1');
+ $filesystem->dumpFile($this->directory . '/test2.txt', '');
+ $git->add('test2.txt');
+ $git->commit('2');
+
+ $git->config->set('user.name', 'Name Two');
+ $git->config->set('user.email', 'two@example.com');
+ $filesystem->dumpFile($this->directory . '/test3.txt', '');
+ $git->add('test3.txt');
+ $git->commit('3');
+
+ $shortlog = $git->shortlog();
+
+ $this->assertCount(2, $shortlog);
+ $this->assertCount(2, $shortlog['Name One <one@example.com>']);
+ $this->assertCount(1, $shortlog['Name Two <two@example.com>']);
+ $this->assertEquals('1', $shortlog['Name One <one@example.com>'][0]['subject']);
+ }
+
+ public function testShortlogSummary()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $git->config->set('user.name', 'Name One');
+ $git->config->set('user.email', 'one@example.com');
+ $filesystem->dumpFile($this->directory . '/test.txt', '');
+ $git->add('test.txt');
+ $git->commit('1');
+ $filesystem->dumpFile($this->directory . '/test2.txt', '');
+ $git->add('test2.txt');
+ $git->commit('2');
+
+ $git->config->set('user.name', 'Name Two');
+ $git->config->set('user.email', 'two@example.com');
+ $filesystem->dumpFile($this->directory . '/test3.txt', '');
+ $git->add('test3.txt');
+ $git->commit('3');
+
+ $summary = $git->shortlog->summary();
+
+ $this->assertEquals(array(
+ 'Name One <one@example.com>' => 2,
+ 'Name Two <two@example.com>' => 1
+ ), $summary);
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/ShowCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/ShowCommandTest.php
new file mode 100644
index 000000000..25b22fe4f
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/ShowCommandTest.php
@@ -0,0 +1,26 @@
+<?php
+
+use PHPGit\Git;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class ShowCommandTest extends BaseTestCase
+{
+
+ public function testShow()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'foobar');
+ $git->add('README.md');
+ $git->commit('Initial commit');
+
+ $git->show('master', array('format' => 'oneline'));
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/StashCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/StashCommandTest.php
new file mode 100644
index 000000000..bb87e48ea
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/StashCommandTest.php
@@ -0,0 +1,204 @@
+<?php
+
+use PHPGit\Git;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class StashCommandTest extends BaseTestCase
+{
+
+ public function testStash()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+ $git->add('.');
+ $git->commit('Initial commit');
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hi!');
+ $git->stash();
+
+ $this->assertEquals('hello', file_get_contents($this->directory.'/README.md'));
+ }
+
+ public function testStashSave()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+ $git->add('.');
+ $git->commit('Initial commit');
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hi!');
+ $git->stash->save('stash test');
+
+ $this->assertEquals('hello', file_get_contents($this->directory.'/README.md'));
+ }
+
+ public function testStashList()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+ $git->add('.');
+ $git->commit('Initial commit');
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hi!');
+ $git->stash();
+
+ $stashes = $git->stash->lists();
+
+ $this->assertCount(1, $stashes);
+ $this->assertEquals('master', $stashes[0]['branch']);
+ $this->assertStringEndsWith('Initial commit', $stashes[0]['message']);
+ }
+
+ public function testStashShow()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+ $git->add('.');
+ $git->commit('Initial commit');
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hi!');
+ $git->stash();
+ $git->stash->show('stash@{0}');
+ }
+
+ public function testStashDrop()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+ $git->add('.');
+ $git->commit('Initial commit');
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hi!');
+ $git->stash();
+ $git->stash->drop();
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hi!');
+ $git->stash();
+ $git->stash->drop('stash@{0}');
+
+ $this->assertCount(0, $git->stash->lists());
+ }
+
+ public function testStashPop()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+ $git->add('.');
+ $git->commit('Initial commit');
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hi!');
+ $git->stash->save('stash#1');
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'bar');
+ $git->stash->save('stash#2');
+ $git->stash->pop('stash@{1}');
+
+ $this->assertEquals('hi!', file_get_contents($this->directory.'/README.md'));
+ $this->assertCount(1, $git->stash->lists());
+ }
+
+ public function testStashApply()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+ $git->add('.');
+ $git->commit('Initial commit');
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hi!');
+ $git->stash->save('stash#1');
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'bar');
+ $git->stash->save('stash#2');
+ $git->stash->apply('stash@{1}');
+
+ $this->assertEquals('hi!', file_get_contents($this->directory.'/README.md'));
+ $this->assertCount(2, $git->stash->lists());
+ }
+
+ public function testStashBranch()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+ $git->add('.');
+ $git->commit('Initial commit');
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hi!');
+ $git->stash();
+
+ $git->stash->branch('dev', 'stash@{0}');
+ $status = $git->status();
+
+ $this->assertEquals('dev', $status['branch']);
+ $this->assertEquals('hi!', file_get_contents($this->directory.'/README.md'));
+ }
+
+ public function testStashClear()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+ $git->add('.');
+ $git->commit('Initial commit');
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hi!');
+ $git->stash();
+ $git->stash->clear();
+
+ $this->assertCount(0, $git->stash->lists());
+ }
+
+ public function testStashCreate()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+ $git->add('.');
+ $git->commit('Initial commit');
+
+ $filesystem->dumpFile($this->directory . '/README.md', 'hi!');
+ $object = $git->stash->create();
+
+ $this->assertNotEmpty($object);
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/StatusCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/StatusCommandTest.php
new file mode 100644
index 000000000..ad04a74c0
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/StatusCommandTest.php
@@ -0,0 +1,76 @@
+<?php
+
+use PHPGit\Command\StatusCommand;
+use PHPGit\Git;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class StatusCommandTest extends BaseTestCase
+{
+
+ public function testStatus()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem->dumpFile($this->directory . '/item1.txt', '1');
+ $filesystem->dumpFile($this->directory . '/item2.txt', '2');
+ $filesystem->dumpFile($this->directory . '/item3.txt', '3');
+
+ $git->add('item1.txt');
+ $git->add('item2.txt');
+
+ $filesystem->dumpFile($this->directory . '/item1.txt', '1-1');
+
+ $status = $git->status();
+
+ $this->assertEquals(array(
+ 'branch' => 'master',
+ 'changes' => array(
+ array('file' => 'item1.txt', 'index' => StatusCommand::ADDED, 'work_tree' => StatusCommand::MODIFIED),
+ array('file' => 'item2.txt', 'index' => StatusCommand::ADDED, 'work_tree' => StatusCommand::UNMODIFIED),
+ array('file' => 'item3.txt', 'index' => StatusCommand::UNTRACKED, 'work_tree' => StatusCommand::UNTRACKED),
+ )
+ ), $status);
+ }
+
+ public function testDetachedHeadStatus()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem->dumpFile($this->directory . '/item1.txt', '1');
+ $git->add('item1.txt');
+ $git->commit('initial commit');
+ $logs = $git->log();
+ $hash = $logs[0]['hash'];
+
+ $git->checkout($hash);
+ $status = $git->status();
+ $this->assertEquals(null, $status['branch']);
+ }
+
+ public function testTrackingBranchStatus()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->clone('https://github.com/kzykhys/Text.git', $this->directory);
+ $git->setRepository($this->directory);
+
+ $filesystem->dumpFile($this->directory . '/test.txt', '1');
+ $git->add('test.txt');
+ $git->commit('test');
+
+ $status = $git->status();
+ $this->assertEquals('master', $status['branch']);
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Command/TagCommandTest.php b/library/kzykhys/git/test/PHPGit/Command/TagCommandTest.php
new file mode 100644
index 000000000..5715ba92e
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Command/TagCommandTest.php
@@ -0,0 +1,58 @@
+<?php
+
+use PHPGit\Git;
+use Symfony\Component\Filesystem\Filesystem;
+
+require_once __DIR__ . '/../BaseTestCase.php';
+
+class TagCommandTest extends BaseTestCase
+{
+
+ public function testTagDelete()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+ $git->add('.');
+ $git->commit('Initial commit');
+ $git->tag->create('v1.0.0');
+ $git->tag->delete('v1.0.0');
+ $this->assertCount(0, $git->tag());
+ }
+
+ /**
+ * @expectedException \PHPGit\Exception\GitException
+ */
+ public function testTagVerify()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+ $git->add('.');
+ $git->commit('Initial commit');
+ $git->tag->create('v1.0.0');
+ $git->tag->verify('v1.0.0');
+ }
+
+ public function testCreateTagFromCommit()
+ {
+ $filesystem = new Filesystem();
+
+ $git = new Git();
+ $git->init($this->directory);
+ $git->setRepository($this->directory);
+ $filesystem->dumpFile($this->directory . '/README.md', 'hello');
+ $git->add('.');
+ $git->commit('Initial commit');
+ $log = $git->log(null, null, array('limit' => 1));
+ $git->tag->create('v1.0.0', $log[0]['hash']);
+ $this->assertCount(1, $git->tag());
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/Exception/GitExceptionTest.php b/library/kzykhys/git/test/PHPGit/Exception/GitExceptionTest.php
new file mode 100644
index 000000000..154d6b906
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/Exception/GitExceptionTest.php
@@ -0,0 +1,23 @@
+<?php
+
+use PHPGit\Exception\GitException;
+use PHPGit\Git;
+
+class GitExceptionTest extends PHPUnit_Framework_TestCase
+{
+
+ public function testException()
+ {
+ $git = new Git();
+ $git->setRepository(sys_get_temp_dir());
+ try {
+ $git->status();
+ $this->fail('Previous operation should fail');
+ } catch (GitException $e) {
+ $command = $e->getCommandLine();
+ $command = str_replace(array('"', "'"), '', $command);
+ $this->assertStringEndsWith('status --porcelain -s -b --null', $command);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/library/kzykhys/git/test/PHPGit/GitTest.php b/library/kzykhys/git/test/PHPGit/GitTest.php
new file mode 100644
index 000000000..c9aceeb13
--- /dev/null
+++ b/library/kzykhys/git/test/PHPGit/GitTest.php
@@ -0,0 +1,33 @@
+<?php
+
+use PHPGit\Git;
+
+class GitTest extends PHPUnit_Framework_TestCase
+{
+
+ public function testGetVersion()
+ {
+ $git = new Git();
+ $this->assertNotEmpty($git->getVersion());
+ }
+
+ /**
+ * @expectedException \PHPGit\Exception\GitException
+ */
+ public function testInvalidGitBinary()
+ {
+ $git = new Git();
+ $git->setBin('/foo/bar');
+ $git->getVersion();
+ }
+
+ /**
+ * @expectedException \BadMethodCallException
+ */
+ public function testBadMethodCall()
+ {
+ $git = new Git();
+ $git->foo();
+ }
+
+} \ No newline at end of file