Merge remote-tracking branch 'upstream/dev' into dev

This commit is contained in:
zottel 2016-05-12 12:25:10 +02:00
commit 710d6ebd49
895 changed files with 83794 additions and 42765 deletions

View File

@ -1,5 +1,8 @@
<?php <?php
namespace Zotlabs\Module; namespace Zotlabs\Module;
use \Zotlabs\Storage\GitRepo as GitRepo;
/** /**
* @file mod/admin.php * @file mod/admin.php
* @brief Hubzilla's admin controller. * @brief Hubzilla's admin controller.
@ -36,6 +39,22 @@ class Admin extends \Zotlabs\Web\Controller {
$this->admin_page_channels_post($a); $this->admin_page_channels_post($a);
break; break;
case 'plugins': case 'plugins':
if (argc() > 2 && argv(2) === 'addrepo') {
$this->admin_page_plugins_post('addrepo');
break;
}
if (argc() > 2 && argv(2) === 'installrepo') {
$this->admin_page_plugins_post('installrepo');
break;
}
if (argc() > 2 && argv(2) === 'removerepo') {
$this->admin_page_plugins_post('removerepo');
break;
}
if (argc() > 2 && argv(2) === 'updaterepo') {
$this->admin_page_plugins_post('updaterepo');
break;
}
if (argc() > 2 && if (argc() > 2 &&
is_file("addon/" . argv(2) . "/" . argv(2) . ".php")){ is_file("addon/" . argv(2) . "/" . argv(2) . ".php")){
@include_once("addon/" . argv(2) . "/" . argv(2) . ".php"); @include_once("addon/" . argv(2) . "/" . argv(2) . ".php");
@ -88,7 +107,7 @@ class Admin extends \Zotlabs\Web\Controller {
* @param App &$a * @param App &$a
* @return string * @return string
*/ */
function get() { function get() {
logger('admin_content', LOGGER_DEBUG); logger('admin_content', LOGGER_DEBUG);
@ -211,17 +230,26 @@ class Admin extends \Zotlabs\Web\Controller {
. ' A list about current PHP versions can be found <a href="http://php.net/supported-versions.php" class="alert-link">here</a>.'; . ' A list about current PHP versions can be found <a href="http://php.net/supported-versions.php" class="alert-link">here</a>.';
} }
$vmaster = get_repository_version('master');
$vdev = get_repository_version('dev');
$upgrade = ((version_compare(STD_VERSION,$vmaster) < 0) ? t('Your software should be updated') : '');
$t = get_markup_template('admin_summary.tpl'); $t = get_markup_template('admin_summary.tpl');
return replace_macros($t, array( return replace_macros($t, array(
'$title' => t('Administration'), '$title' => t('Administration'),
'$page' => t('Summary'), '$page' => t('Summary'),
'$adminalertmsg' => $alertmsg, '$adminalertmsg' => $alertmsg,
'$queues' => $queues, '$queues' => $queues,
'$accounts' => array( t('Registered accounts'), $accounts), '$accounts' => array( t('Registered accounts'), $accounts),
'$pending' => array( t('Pending registrations'), $pending), '$pending' => array( t('Pending registrations'), $pending),
'$channels' => array( t('Registered channels'), $channels), '$channels' => array( t('Registered channels'), $channels),
'$plugins' => array( t('Active plugins'), $plugins ), '$plugins' => array( t('Active plugins'), $plugins ),
'$version' => array( t('Version'), STD_VERSION), '$version' => array( t('Version'), STD_VERSION),
'$vmaster' => array( t('Repository version (master)'), $vmaster),
'$vdev' => array( t('Repository version (dev)'), $vdev),
'$upgrade' => $upgrade,
'$build' => get_config('system', 'db_version') '$build' => get_config('system', 'db_version')
)); ));
} }
@ -1343,6 +1371,33 @@ class Admin extends \Zotlabs\Web\Controller {
usort($plugins,'self::plugin_sort'); usort($plugins,'self::plugin_sort');
$admin_plugins_add_repo_form= replace_macros(
get_markup_template('admin_plugins_addrepo.tpl'), array(
'$post' => 'admin/plugins/addrepo',
'$desc' => t('Enter the public git repository URL of the plugin repo.'),
'$repoURL' => array('repoURL', t('Plugin repo git URL'), '', ''),
'$repoName' => array('repoName', t('Custom repo name'), '', '', t('(optional)')),
'$submit' => t('Download Plugin Repo')
)
);
$newRepoModalID = random_string(3);
$newRepoModal = replace_macros(
get_markup_template('generic_modal.tpl'), array(
'$id' => $newRepoModalID,
'$title' => t('Install new repo'),
'$ok' => t('Install'),
'$cancel' => t('Cancel')
)
);
$reponames = $this->listAddonRepos();
$addonrepos = [];
foreach($reponames as $repo) {
$addonrepos[] = array('name' => $repo, 'description' => '');
// TODO: Parse repo info to provide more information about repos
}
$t = get_markup_template('admin_plugins.tpl'); $t = get_markup_template('admin_plugins.tpl');
return replace_macros($t, array( return replace_macros($t, array(
'$title' => t('Administration'), '$title' => t('Administration'),
@ -1353,9 +1408,32 @@ class Admin extends \Zotlabs\Web\Controller {
'$plugins' => $plugins, '$plugins' => $plugins,
'$disabled' => t('Disabled - version incompatibility'), '$disabled' => t('Disabled - version incompatibility'),
'$form_security_token' => get_form_security_token('admin_plugins'), '$form_security_token' => get_form_security_token('admin_plugins'),
'$addrepo' => t('Add Plugin Repo'),
'$expandform' => false,
'$form' => $admin_plugins_add_repo_form,
'$newRepoModal' => $newRepoModal,
'$newRepoModalID' => $newRepoModalID,
'$addonrepos' => $addonrepos,
'$repoUpdateButton' => t('Update'),
'$repoBranchButton' => t('Switch branch'),
'$repoRemoveButton' => t('Remove')
)); ));
} }
function listAddonRepos() {
$addonrepos = [];
$addonDir = __DIR__ . '/../../extend/addon/';
if ($handle = opendir($addonDir)) {
while (false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != "..") {
$addonrepos[] = $entry;
}
}
closedir($handle);
}
return $addonrepos;
}
static public function plugin_sort($a,$b) { static public function plugin_sort($a,$b) {
return(strcmp(strtolower($a[2]['name']),strtolower($b[2]['name']))); return(strcmp(strtolower($a[2]['name']),strtolower($b[2]['name'])));
} }
@ -1647,6 +1725,181 @@ class Admin extends \Zotlabs\Web\Controller {
)); ));
} }
function admin_page_plugins_post($action) {
switch ($action) {
case 'updaterepo':
if (array_key_exists('repoName', $_REQUEST)) {
$repoName = $_REQUEST['repoName'];
} else {
json_return_and_die(array('message' => 'No repo name provided.', 'success' => false));
}
$repoDir = __DIR__ . '/../../store/git/sys/extend/addon/' . $repoName;
if (!is_dir($repoDir)) {
logger('Repo directory does not exist: ' . $repoDir);
json_return_and_die(array('message' => 'Invalid addon repo.', 'success' => false));
}
if (!is_writable($repoDir)) {
logger('Repo directory not writable to web server: ' . $repoDir);
json_return_and_die(array('message' => 'Repo directory not writable to web server.', 'success' => false));
}
$git = new GitRepo('sys', null, false, $repoName, $repoDir);
try {
if ($git->pull()) {
json_return_and_die(array('message' => 'Repo updated.', 'success' => true));
} else {
json_return_and_die(array('message' => 'Error updating addon repo.', 'success' => false));
}
} catch (\PHPGit\Exception\GitException $e) {
json_return_and_die(array('message' => 'Error updating addon repo.', 'success' => false));
}
case 'removerepo':
if (array_key_exists('repoName', $_REQUEST)) {
$repoName = $_REQUEST['repoName'];
} else {
json_return_and_die(array('message' => 'No repo name provided.', 'success' => false));
}
$repoDir = __DIR__ . '/../../store/git/sys/extend/addon/' . $repoName;
if (!is_dir($repoDir)) {
logger('Repo directory does not exist: ' . $repoDir);
json_return_and_die(array('message' => 'Invalid addon repo.', 'success' => false));
}
if (!is_writable($repoDir)) {
logger('Repo directory not writable to web server: ' . $repoDir);
json_return_and_die(array('message' => 'Repo directory not writable to web server.', 'success' => false));
}
// TODO: remove directory and unlink /addon/files
if (rrmdir($repoDir)) {
json_return_and_die(array('message' => 'Repo deleted.', 'success' => true));
} else {
json_return_and_die(array('message' => 'Error deleting addon repo.', 'success' => false));
}
case 'installrepo':
require_once('library/markdown.php');
if (array_key_exists('repoURL', $_REQUEST)) {
require __DIR__ . '/../../library/PHPGit.autoload.php'; // Load PHPGit dependencies
$repoURL = $_REQUEST['repoURL'];
$extendDir = __DIR__ . '/../../store/git/sys/extend';
$addonDir = $extendDir . '/addon';
if (!file_exists($extendDir)) {
if (!mkdir($extendDir, 0770, true)) {
logger('Error creating extend folder: ' . $extendDir);
json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false));
} else {
if (!symlink(__DIR__ . '/../../extend/addon', $addonDir)) {
logger('Error creating symlink to addon folder: ' . $addonDir);
json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false));
}
}
}
if (!is_writable($extendDir)) {
logger('Directory not writable to web server: ' . $extendDir);
json_return_and_die(array('message' => 'Directory not writable to web server.', 'success' => false));
}
$repoName = null;
if (array_key_exists('repoName', $_REQUEST) && $_REQUEST['repoName'] !== '') {
$repoName = $_REQUEST['repoName'];
} else {
$repoName = GitRepo::getRepoNameFromURL($repoURL);
}
if (!$repoName) {
logger('Invalid git repo');
json_return_and_die(array('message' => 'Invalid git repo', 'success' => false));
}
$repoDir = $addonDir . '/' . $repoName;
$tempRepoBaseDir = __DIR__ . '/../../store/git/sys/temp/';
$tempAddonDir = $tempRepoBaseDir . $repoName;
if (!is_writable($addonDir) || !is_writable($tempAddonDir)) {
logger('Temp repo directory or /extend/addon not writable to web server: ' . $tempAddonDir);
json_return_and_die(array('message' => 'Temp repo directory not writable to web server.', 'success' => false));
}
rename($tempAddonDir, $repoDir);
if (!is_writable(realpath(__DIR__ . '/../../addon/'))) {
logger('/addon directory not writable to web server: ' . $tempAddonDir);
json_return_and_die(array('message' => '/addon directory not writable to web server.', 'success' => false));
}
$files = array_diff(scandir($repoDir), array('.', '..'));
foreach ($files as $file) {
if (is_dir($repoDir . '/' . $file) && $file !== '.git') {
$source = '../extend/addon/' . $repoName . '/' . $file;
$target = realpath(__DIR__ . '/../../addon/') . '/' . $file;
unlink($target);
if (!symlink($source, $target)) {
logger('Error linking addons to /addon');
json_return_and_die(array('message' => 'Error linking addons to /addon', 'success' => false));
}
}
}
$git = new GitRepo('sys', $repoURL, false, $repoName, $repoDir);
$repo = $git->probeRepo();
json_return_and_die(array('repo' => $repo, 'message' => '', 'success' => true));
}
case 'addrepo':
require_once('library/markdown.php');
if (array_key_exists('repoURL', $_REQUEST)) {
require __DIR__ . '/../../library/PHPGit.autoload.php'; // Load PHPGit dependencies
$repoURL = $_REQUEST['repoURL'];
$extendDir = __DIR__ . '/../../store/git/sys/extend';
$addonDir = $extendDir . '/addon';
$tempAddonDir = __DIR__ . '/../../store/git/sys/temp';
if (!file_exists($extendDir)) {
if (!mkdir($extendDir, 0770, true)) {
logger('Error creating extend folder: ' . $extendDir);
json_return_and_die(array('message' => 'Error creating extend folder: ' . $extendDir, 'success' => false));
} else {
if (!symlink(__DIR__ . '/../../extend/addon', $addonDir)) {
logger('Error creating symlink to addon folder: ' . $addonDir);
json_return_and_die(array('message' => 'Error creating symlink to addon folder: ' . $addonDir, 'success' => false));
}
}
}
$repoName = null;
if (array_key_exists('repoName', $_REQUEST) && $_REQUEST['repoName'] !== '') {
$repoName = $_REQUEST['repoName'];
} else {
$repoName = GitRepo::getRepoNameFromURL($repoURL);
}
if (!$repoName) {
logger('Invalid git repo');
json_return_and_die(array('message' => 'Invalid git repo: ' . $repoName, 'success' => false));
}
$repoDir = $tempAddonDir . '/' . $repoName;
if (!is_writable($tempAddonDir)) {
logger('Temporary directory for new addon repo is not writable to web server: ' . $tempAddonDir);
json_return_and_die(array('message' => 'Temporary directory for new addon repo is not writable to web server.', 'success' => false));
}
// clone the repo if new automatically
$git = new GitRepo('sys', $repoURL, true, $repoName, $repoDir);
$remotes = $git->git->remote();
$fetchURL = $remotes['origin']['fetch'];
if ($fetchURL !== $git->url) {
if (rrmdir($repoDir)) {
$git = new GitRepo('sys', $repoURL, true, $repoName, $repoDir);
} else {
json_return_and_die(array('message' => 'Error deleting existing addon repo.', 'success' => false));
}
}
$repo = $git->probeRepo();
$repo['readme'] = $repo['manifest'] = null;
foreach ($git->git->tree('master') as $object) {
if ($object['type'] == 'blob' && (strtolower($object['file']) === 'readme.md' || strtolower($object['file']) === 'readme')) {
$repo['readme'] = Markdown($git->git->cat->blob($object['hash']));
} else if ($object['type'] == 'blob' && strtolower($object['file']) === 'manifest.json') {
$repo['manifest'] = $git->git->cat->blob($object['hash']);
}
}
json_return_and_die(array('repo' => $repo, 'message' => '', 'success' => true));
} else {
json_return_and_die(array('message' => 'No repo URL provided', 'success' => false));
}
break;
default:
break;
}
}
function admin_page_profs_post(&$a) { function admin_page_profs_post(&$a) {
if(array_key_exists('basic',$_REQUEST)) { if(array_key_exists('basic',$_REQUEST)) {

View File

@ -83,10 +83,6 @@ class Blocks extends \Zotlabs\Web\Controller {
$mimetype = (($_REQUEST['mimetype']) ? $_REQUEST['mimetype'] : get_pconfig($owner,'system','page_mimetype')); $mimetype = (($_REQUEST['mimetype']) ? $_REQUEST['mimetype'] : get_pconfig($owner,'system','page_mimetype'));
if(! $mimetype) {
$mimetype = 'choose';
}
$x = array( $x = array(
'webpage' => ITEM_TYPE_BLOCK, 'webpage' => ITEM_TYPE_BLOCK,
'is_owner' => true, 'is_owner' => true,
@ -96,6 +92,8 @@ class Blocks extends \Zotlabs\Web\Controller {
'showacl' => false, 'showacl' => false,
'visitor' => true, 'visitor' => true,
'mimetype' => $mimetype, 'mimetype' => $mimetype,
'mimeselect' => true,
'hide_location' => true,
'ptlabel' => t('Block Name'), 'ptlabel' => t('Block Name'),
'profile_uid' => intval($owner), 'profile_uid' => intval($owner),
'expanded' => true, 'expanded' => true,

View File

@ -90,7 +90,6 @@ class Cloud extends \Zotlabs\Web\Controller {
} }
} }
// require_once('\Zotlabs/Storage/Browser.php');
// provide a directory view for the cloud in Hubzilla // provide a directory view for the cloud in Hubzilla
$browser = new \Zotlabs\Storage\Browser($auth); $browser = new \Zotlabs\Storage\Browser($auth);
$auth->setBrowserPlugin($browser); $auth->setBrowserPlugin($browser);

View File

@ -60,38 +60,53 @@ class Dav extends \Zotlabs\Web\Controller {
if ($which) if ($which)
profile_load($a, $which, $profile); profile_load($a, $which, $profile);
$auth = new \Zotlabs\Storage\BasicAuth(); $auth = new \Zotlabs\Storage\BasicAuth();
$ob_hash = get_observer_hash(); // $authBackend = new \Sabre\DAV\Auth\Backend\BasicCallBack(function($userName,$password) {
// if(account_verify_password($userName,$password))
// return true;
// return false;
// });
if ($ob_hash) { // $ob_hash = get_observer_hash();
if (local_channel()) {
$channel = \App::get_channel();
$auth->setCurrentUser($channel['channel_address']);
$auth->channel_id = $channel['channel_id'];
$auth->channel_hash = $channel['channel_hash'];
$auth->channel_account_id = $channel['channel_account_id'];
if($channel['channel_timezone'])
$auth->setTimezone($channel['channel_timezone']);
}
$auth->observer = $ob_hash;
}
if ($_GET['davguest']) // if ($ob_hash) {
$_SESSION['davguest'] = true; // if (local_channel()) {
// $channel = \App::get_channel();
// $auth->setCurrentUser($channel['channel_address']);
// $auth->channel_id = $channel['channel_id'];
// $auth->channel_hash = $channel['channel_hash'];
// $auth->channel_account_id = $channel['channel_account_id'];
// if($channel['channel_timezone'])
// $auth->setTimezone($channel['channel_timezone']);
// }
// $auth->observer = $ob_hash;
// }
$_SERVER['QUERY_STRING'] = str_replace(array('?f=', '&f='), array('', ''), $_SERVER['QUERY_STRING']); // if ($_GET['davguest'])
$_SERVER['QUERY_STRING'] = strip_zids($_SERVER['QUERY_STRING']); // $_SESSION['davguest'] = true;
$_SERVER['QUERY_STRING'] = preg_replace('/[\?&]davguest=(.*?)([\?&]|$)/ism', '', $_SERVER['QUERY_STRING']);
$_SERVER['REQUEST_URI'] = str_replace(array('?f=', '&f='), array('', ''), $_SERVER['REQUEST_URI']); // $_SERVER['QUERY_STRING'] = str_replace(array('?f=', '&f='), array('', ''), $_SERVER['QUERY_STRING']);
$_SERVER['REQUEST_URI'] = strip_zids($_SERVER['REQUEST_URI']); // $_SERVER['QUERY_STRING'] = strip_zids($_SERVER['QUERY_STRING']);
$_SERVER['REQUEST_URI'] = preg_replace('/[\?&]davguest=(.*?)([\?&]|$)/ism', '', $_SERVER['REQUEST_URI']); // $_SERVER['QUERY_STRING'] = preg_replace('/[\?&]davguest=(.*?)([\?&]|$)/ism', '', $_SERVER['QUERY_STRING']);
//
// $_SERVER['REQUEST_URI'] = str_replace(array('?f=', '&f='), array('', ''), $_SERVER['REQUEST_URI']);
// $_SERVER['REQUEST_URI'] = strip_zids($_SERVER['REQUEST_URI']);
// $_SERVER['REQUEST_URI'] = preg_replace('/[\?&]davguest=(.*?)([\?&]|$)/ism', '', $_SERVER['REQUEST_URI']);
$rootDirectory = new \Zotlabs\Storage\Directory('/', $auth); $rootDirectory = new \Zotlabs\Storage\Directory('/', $auth);
// A SabreDAV server-object // A SabreDAV server-object
$server = new SDAV\Server($rootDirectory); $server = new SDAV\Server($rootDirectory);
$authPlugin = new \Sabre\DAV\Auth\Plugin($auth);
$server->addPlugin($authPlugin);
// prevent overwriting changes each other with a lock backend // prevent overwriting changes each other with a lock backend
$lockBackend = new SDAV\Locks\Backend\File('store/[data]/locks'); $lockBackend = new SDAV\Locks\Backend\File('store/[data]/locks');
$lockPlugin = new SDAV\Locks\Plugin($lockBackend); $lockPlugin = new SDAV\Locks\Plugin($lockBackend);
@ -108,29 +123,29 @@ class Dav extends \Zotlabs\Web\Controller {
// In order to avoid prompting for passwords for viewing a DIRECTORY, add // In order to avoid prompting for passwords for viewing a DIRECTORY, add
// the URL query parameter 'davguest=1'. // the URL query parameter 'davguest=1'.
$isapublic_file = false; // $isapublic_file = false;
$davguest = ((x($_SESSION, 'davguest')) ? true : false); // $davguest = ((x($_SESSION, 'davguest')) ? true : false);
if ((! $auth->observer) && ($_SERVER['REQUEST_METHOD'] === 'GET')) { // if ((! $auth->observer) && ($_SERVER['REQUEST_METHOD'] === 'GET')) {
try { // try {
$x = RedFileData('/' . \App::$cmd, $auth); // $x = RedFileData('/' . \App::$cmd, $auth);
if($x instanceof \Zotlabs\Storage\File) // if($x instanceof \Zotlabs\Storage\File)
$isapublic_file = true; // $isapublic_file = true;
} // }
catch (Exception $e) { // catch (Exception $e) {
$isapublic_file = false; // $isapublic_file = false;
} // }
} // }
if ((! $auth->observer) && (! $isapublic_file) && (! $davguest)) { // if ((! $auth->observer) && (! $isapublic_file) && (! $davguest)) {
try { // try {
$auth->Authenticate($server, t('$Projectname channel')); // $auth->Authenticate($server, t('$Projectname channel'));
} // }
catch (Exception $e) { // catch (Exception $e) {
logger('mod_cloud: auth exception' . $e->getMessage()); // logger('mod_cloud: auth exception' . $e->getMessage());
http_status_exit($e->getHTTPCode(), $e->getMessage()); // http_status_exit($e->getHTTPCode(), $e->getMessage());
} // }
} // }
// require_once('Zotlabs/Storage/Browser.php'); // require_once('Zotlabs/Storage/Browser.php');
// provide a directory view for the cloud in Hubzilla // provide a directory view for the cloud in Hubzilla

View File

@ -3,6 +3,7 @@ namespace Zotlabs\Module;
require_once('include/identity.php'); require_once('include/identity.php');
require_once('include/acl_selectors.php'); require_once('include/acl_selectors.php');
require_once('include/conversation.php');
class Editblock extends \Zotlabs\Web\Controller { class Editblock extends \Zotlabs\Web\Controller {
@ -95,81 +96,38 @@ class Editblock extends \Zotlabs\Web\Controller {
return; return;
} }
$plaintext = true;
$mimeselect = '';
$mimetype = $itm[0]['mimetype']; $mimetype = $itm[0]['mimetype'];
if($mimetype != 'text/bbcode')
$plaintext = true;
if(get_config('system','page_mimetype'))
$mimeselect = '<input type="hidden" name="mimetype" value="' . $mimetype . '" />';
else
$mimeselect = mimetype_select($itm[0]['uid'],$mimetype);
\App::$page['htmlhead'] .= replace_macros(get_markup_template('jot-header.tpl'), array(
'$baseurl' => z_root(),
'$editselect' => (($plaintext) ? 'none' : '/(profile-jot-text|prvmail-text)/'),
'$pretext' => '',
'$ispublic' => '&nbsp;', // t('Visible to <strong>everybody</strong>'),
'$geotag' => '',
'$nickname' => $channel['channel_address'],
'$confirmdelete' => t('Delete block?'),
'$bbco_autocomplete'=> (($mimetype == 'text/bbcode') ? 'bbcode' : 'comanche-block')
));
$tpl = get_markup_template("jot.tpl");
$jotplugins = '';
$jotnets = '';
call_hooks('jot_tool', $jotplugins);
call_hooks('jot_networks', $jotnets);
$rp = 'blocks/' . $channel['channel_address']; $rp = 'blocks/' . $channel['channel_address'];
$editor = replace_macros($tpl,array( $x = array(
'$return_path' => $rp, 'nickname' => $channel['channel_address'],
'$action' => 'item', 'bbco_autocomplete'=> (($mimetype == 'text/bbcode') ? 'bbcode' : 'comanche-block'),
'$webpage' => ITEM_TYPE_BLOCK, 'return_path' => $rp,
'$share' => t('Edit'), 'webpage' => ITEM_TYPE_BLOCK,
'$bold' => t('Bold'), 'ptlabel' => t('Block Name'),
'$italic' => t('Italic'), 'button' => t('Edit'),
'$underline' => t('Underline'), 'writefiles' => (($mimetype == 'text/bbcode') ? perm_is_allowed($owner, get_observer_hash(), 'write_storage') : false),
'$quote' => t('Quote'), 'weblink' => (($mimetype == 'text/bbcode') ? t('Insert web link') : false),
'$code' => t('Code'), 'hide_voting' => true,
'$writefiles' => (($mimetype == 'text/bbcode') ? perm_is_allowed($owner, get_observer_hash(), 'write_storage') : false), 'hide_future' => true,
'$attach' => t('Attach file'), 'hide_location' => true,
'$weblink' => (($mimetype == 'text/bbcode') ? t('Insert web link') : false), 'hide_expire' => true,
'$setloc' => false, 'showacl' => false,
'$noloc' => false, 'ptyp' => $itm[0]['type'],
'$permset' => t('Permission settings'), 'mimeselect' => true,
'$ptyp' => $itm[0]['type'], 'mimetype' => $itm[0]['mimetype'],
'$mimeselect' => $mimeselect, 'body' => undo_post_tagging($itm[0]['body']),
'$content' => undo_post_tagging($itm[0]['body']), 'post_id' => $post_id,
'$post_id' => $post_id, 'visitor' => true,
'$baseurl' => z_root(), 'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'),
'$defloc' => $channel['channel_location'], 'placeholdertitle' => t('Title (optional)'),
'$visitor' => true, 'pagetitle' => $block_title,
'$public' => t('Public post'), 'profile_uid' => (intval($channel['channel_id'])),
'$jotnets' => $jotnets, 'bbcode' => (($mimetype == 'text/bbcode') ? true : false)
'$title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'), );
'$placeholdertitle' => t('Title (optional)'),
'$pagetitle' => $block_title, $editor = status_editor($a, $x);
'$category' => '',
'$placeholdercategory' => t('Categories (optional, comma-separated list)'),
'$emtitle' => t('Example: bob@example.com, mary@example.com'),
'$lockstate' => $lockstate,
'$acl' => '',
'$bang' => '',
'$profile_uid' => (intval($channel['channel_id'])),
'$preview' => t('Preview'),
'$jotplugins' => $jotplugins,
'$sourceapp' => $itm[0]['app'],
'$defexpire' => '',
'$bbcode' => (($mimetype == 'text/bbcode') ? true : false)
));
$o .= replace_macros(get_markup_template('edpost_head.tpl'), array( $o .= replace_macros(get_markup_template('edpost_head.tpl'), array(
'$title' => t('Edit Block'), '$title' => t('Edit Block'),

View File

@ -85,6 +85,7 @@ class Editpost extends \Zotlabs\Web\Controller {
'hide_voting' => true, 'hide_voting' => true,
'hide_future' => true, 'hide_future' => true,
'hide_location' => true, 'hide_location' => true,
'mimetype' => $itm[0]['mimetype'],
'ptyp' => $itm[0]['obj_type'], 'ptyp' => $itm[0]['obj_type'],
'body' => undo_post_tagging($itm[0]['body']), 'body' => undo_post_tagging($itm[0]['body']),
'post_id' => $post_id, 'post_id' => $post_id,

View File

@ -3,6 +3,7 @@ namespace Zotlabs\Module;
require_once('include/identity.php'); require_once('include/identity.php');
require_once('include/acl_selectors.php'); require_once('include/acl_selectors.php');
require_once('include/conversation.php');
require_once('include/PermissionDescription.php'); require_once('include/PermissionDescription.php');
@ -26,8 +27,7 @@ class Editwebpage extends \Zotlabs\Web\Controller {
} }
function get() {
function get() {
if(! \App::$profile) { if(! \App::$profile) {
notice( t('Requested profile is not available.') . EOL ); notice( t('Requested profile is not available.') . EOL );
@ -77,7 +77,6 @@ class Editwebpage extends \Zotlabs\Web\Controller {
// Figure out which post we're editing // Figure out which post we're editing
$post_id = ((argc() > 2) ? intval(argv(2)) : 0); $post_id = ((argc() > 2) ? intval(argv(2)) : 0);
if(! $post_id) { if(! $post_id) {
notice( t('Item not found') . EOL); notice( t('Item not found') . EOL);
return; return;
@ -121,8 +120,6 @@ class Editwebpage extends \Zotlabs\Web\Controller {
if($item_id) if($item_id)
$page_title = $item_id[0]['sid']; $page_title = $item_id[0]['sid'];
$plaintext = true;
$mimetype = $itm[0]['mimetype']; $mimetype = $itm[0]['mimetype'];
if($mimetype === 'application/x-php') { if($mimetype === 'application/x-php') {
@ -132,90 +129,41 @@ class Editwebpage extends \Zotlabs\Web\Controller {
} }
} }
$mimeselect = ''; $layout = $itm[0]['layout_mid'];
if($mimetype != 'text/bbcode')
$plaintext = true;
if(get_config('system','page_mimetype'))
$mimeselect = '<input type="hidden" name="mimetype" value="' . $mimetype . '" />';
else
$mimeselect = mimetype_select($itm[0]['uid'],$mimetype);
$layout = get_config('system','page_layout');
if($layout)
$layoutselect = '<input type="hidden" name="layout_mid" value="' . $layout . '" />';
else
$layoutselect = layout_select($itm[0]['uid'],$itm[0]['layout_mid']);
\App::$page['htmlhead'] .= replace_macros(get_markup_template('jot-header.tpl'), array(
'$baseurl' => z_root(),
'$editselect' => (($plaintext) ? 'none' : '/(profile-jot-text|prvmail-text)/'),
'$pretext' => '',
'$ispublic' => '&nbsp;', // t('Visible to <strong>everybody</strong>'),
'$geotag' => $geotag,
'$nickname' => $channel['channel_address'],
'$confirmdelete' => t('Delete webpage?'),
'$bbco_autocomplete'=> (($mimetype == 'text/bbcode') ? 'bbcode' : '')
));
$tpl = get_markup_template("jot.tpl"); $tpl = get_markup_template("jot.tpl");
$jotplugins = '';
$jotnets = '';
call_hooks('jot_tool', $jotplugins);
call_hooks('jot_networks', $jotnets);
// FIXME A return path with $_SESSION doesn't always work for observer - it may WSoD
// instead of loading a sensible page. So, send folk to the webpage list.
$rp = 'webpages/' . $which; $rp = 'webpages/' . $which;
$editor = replace_macros($tpl,array( $x = array(
'$return_path' => $rp, 'nickname' => $channel['channel_address'],
'$webpage' => ITEM_TYPE_WEBPAGE, 'bbco_autocomplete'=> (($mimetype == 'text/bbcode') ? 'bbcode' : ''),
'$placeholdpagetitle' => t('Page link title'), 'return_path' => $rp,
'$pagetitle' => $page_title, 'webpage' => ITEM_TYPE_WEBPAGE,
'$writefiles' => (($mimetype == 'text/bbcode') ? perm_is_allowed($owner, get_observer_hash(), 'write_storage') : false), 'ptlabel' => t('Page link'),
'$action' => 'item', 'pagetitle' => $page_title,
'$share' => t('Edit'), 'writefiles' => (($mimetype == 'text/bbcode') ? perm_is_allowed($owner, get_observer_hash(), 'write_storage') : false),
'$bold' => t('Bold'), 'button' => t('Edit'),
'$italic' => t('Italic'), 'weblink' => (($mimetype == 'text/bbcode') ? t('Insert web link') : false),
'$underline' => t('Underline'), 'hide_location' => true,
'$quote' => t('Quote'), 'hide_voting' => true,
'$code' => t('Code'), 'ptyp' => $itm[0]['type'],
'$attach' => t('Attach file'), 'body' => undo_post_tagging($itm[0]['body']),
'$weblink' => (($mimetype == 'text/bbcode') ? t('Insert web link') : false), 'post_id' => $post_id,
'$setloc' => false, //t('Set your location'), 'visitor' => ($is_owner) ? true : false,
'$noloc' => false, //((get_pconfig($uid, 'system', 'use_browser_location')) ? t('Clear browser location') : ''), 'acl' => populate_acl($itm[0],false,\PermissionDescription::fromGlobalPermission('view_pages')),
'$permset' => t('Permission settings'), 'showacl' => ($is_owner) ? true : false,
'$ptyp' => $itm[0]['type'], 'mimetype' => $mimetype,
'$content' => undo_post_tagging($itm[0]['body']), 'mimeselect' => true,
'$post_id' => $post_id, 'layout' => $layout,
'$baseurl' => z_root(), 'layoutselect' => true,
'$defloc' => $itm[0]['location'], 'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'),
'$visitor' => ($is_owner) ? true : false,
'$acl' => populate_acl($itm[0],false,\PermissionDescription::fromGlobalPermission('view_pages')),
'$showacl' => ($is_owner) ? true : false,
'$public' => t('Public post'),
'$jotnets' => $jotnets,
'$mimeselect' => $mimeselect,
'$layoutselect' => $layoutselect,
'$title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'),
'$placeholdertitle' => t('Title (optional)'),
'$category' => '',
'$placeholdercategory' => t('Categories (optional, comma-separated list)'),
'$emtitle' => t('Example: bob@example.com, mary@example.com'),
'lockstate' => (((strlen($itm[0]['allow_cid'])) || (strlen($itm[0]['allow_gid'])) || (strlen($itm[0]['deny_cid'])) || (strlen($itm[0]['deny_gid']))) ? 'lock' : 'unlock'), 'lockstate' => (((strlen($itm[0]['allow_cid'])) || (strlen($itm[0]['allow_gid'])) || (strlen($itm[0]['deny_cid'])) || (strlen($itm[0]['deny_gid']))) ? 'lock' : 'unlock'),
'$bang' => '', 'profile_uid' => (intval($owner)),
'$profile_uid' => (intval($owner)), 'bbcode' => (($mimetype == 'text/bbcode') ? true : false)
'$preview' => t('Preview'), );
'$jotplugins' => $jotplugins,
'$sourceapp' => \App::$sourcename, $editor = status_editor($a, $x);
'$defexpire' => '',
'$bbcode' => (($mimetype == 'text/bbcode') ? true : false)
));
$o .= replace_macros(get_markup_template('edpost_head.tpl'), array( $o .= replace_macros(get_markup_template('edpost_head.tpl'), array(
'$title' => t('Edit Webpage'), '$title' => t('Edit Webpage'),
@ -228,6 +176,4 @@ class Editwebpage extends \Zotlabs\Web\Controller {
} }
} }

View File

@ -29,7 +29,9 @@ class Manage extends \Zotlabs\Web\Controller {
goaway(z_root() . '/manage'); goaway(z_root() . '/manage');
} }
if($change_channel) { if($change_channel) {
$r = change_channel($change_channel); $r = change_channel($change_channel);
if((argc() > 2) && !(argv(2) === 'default')) { if((argc() > 2) && !(argv(2) === 'default')) {

View File

@ -66,7 +66,7 @@ class Page extends \Zotlabs\Web\Controller {
$sql_options = item_permissions_sql($u[0]['channel_id']); $sql_options = item_permissions_sql($u[0]['channel_id']);
$r = q("select item.* from item left join item_id on item.id = item_id.iid $r = q("select item.* from item left join item_id on item.id = item_id.iid
where item.uid = %d and sid = '%s' and (( service = 'WEBPAGE' and item_type = %d ) where item.uid = %d and sid = '%s' and item.item_delayed = 0 and (( service = 'WEBPAGE' and item_type = %d )
OR ( service = 'PDL' AND item_type = %d )) $sql_options $revision limit 1", OR ( service = 'PDL' AND item_type = %d )) $sql_options $revision limit 1",
intval($u[0]['channel_id']), intval($u[0]['channel_id']),
dbesc($page_id), dbesc($page_id),
@ -78,7 +78,7 @@ class Page extends \Zotlabs\Web\Controller {
// Check again with no permissions clause to see if it is a permissions issue // Check again with no permissions clause to see if it is a permissions issue
$x = q("select item.* from item left join item_id on item.id = item_id.iid $x = q("select item.* from item left join item_id on item.id = item_id.iid
where item.uid = %d and sid = '%s' and service = 'WEBPAGE' and where item.uid = %d and sid = '%s' and item.item_delayed = 0 and service = 'WEBPAGE' and
item_type = %d $revision limit 1", item_type = %d $revision limit 1",
intval($u[0]['channel_id']), intval($u[0]['channel_id']),
dbesc($page_id), dbesc($page_id),

View File

@ -28,14 +28,14 @@ class Pubstream extends \Zotlabs\Web\Controller {
if(! $maxheight) if(! $maxheight)
$maxheight = 400; $maxheight = 400;
$o .= '<div id="live-public"></div>' . "\r\n"; $o .= '<div id="live-pubstream"></div>' . "\r\n";
$o .= "<script> var profile_uid = " . ((intval(local_channel())) ? local_channel() : (-1)) $o .= "<script> var profile_uid = " . ((intval(local_channel())) ? local_channel() : (-1))
. "; var profile_page = " . \App::$pager['page'] . "; var profile_page = " . \App::$pager['page']
. "; divmore_height = " . intval($maxheight) . "; </script>\r\n"; . "; divmore_height = " . intval($maxheight) . "; </script>\r\n";
\App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),array( \App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),array(
'$baseurl' => z_root(), '$baseurl' => z_root(),
'$pgtype' => 'public', '$pgtype' => 'pubstream',
'$uid' => ((local_channel()) ? local_channel() : '0'), '$uid' => ((local_channel()) ? local_channel() : '0'),
'$gid' => '0', '$gid' => '0',
'$cid' => '0', '$cid' => '0',

View File

@ -1066,7 +1066,7 @@ class Settings extends \Zotlabs\Web\Controller {
'$maxreq' => array('maxreq', t('Maximum Friend Requests/Day:'), intval($channel['channel_max_friend_req']) , t('May reduce spam activity')), '$maxreq' => array('maxreq', t('Maximum Friend Requests/Day:'), intval($channel['channel_max_friend_req']) , t('May reduce spam activity')),
'$permissions' => t('Default Post Permissions'), '$permissions' => t('Default Post Permissions'),
'$permdesc' => t("\x28click to open/close\x29"), '$permdesc' => t("\x28click to open/close\x29"),
'$aclselect' => populate_acl($perm_defaults, false, \PermissionDescription::fromDescription(t('Use my default audience setting for the type of channel'))), '$aclselect' => populate_acl($perm_defaults, false, \PermissionDescription::fromDescription(t('Use my default audience setting for the type of post'))),
'$suggestme' => $suggestme, '$suggestme' => $suggestme,
'$group_select' => $group_select, '$group_select' => $group_select,
'$role' => array('permissions_role' , t('Channel permissions category:'), $permissions_role, '', get_roles()), '$role' => array('permissions_role' , t('Channel permissions category:'), $permissions_role, '', get_roles()),

View File

@ -13,7 +13,7 @@ class Siteinfo extends \Zotlabs\Web\Controller {
function get() { function get() {
if(! get_config('system','hidden_version_siteinfo')) { if(! get_config('system','hidden_version_siteinfo')) {
$version = sprintf( t('Version %s'), \Zotlabs\Project\System::get_project_version()); $version = sprintf( t('Version %s'), \Zotlabs\Project\System::get_project_version());

View File

@ -77,13 +77,7 @@ class Webpages extends \Zotlabs\Web\Controller {
$mimetype = (($_REQUEST['mimetype']) ? $_REQUEST['mimetype'] : get_pconfig($owner,'system','page_mimetype')); $mimetype = (($_REQUEST['mimetype']) ? $_REQUEST['mimetype'] : get_pconfig($owner,'system','page_mimetype'));
if(! $mimetype) {
$mimetype = 'choose';
}
$layout = (($_REQUEST['layout']) ? $_REQUEST['layout'] : get_pconfig($owner,'system','page_layout')); $layout = (($_REQUEST['layout']) ? $_REQUEST['layout'] : get_pconfig($owner,'system','page_layout'));
if(! $layout)
$layout = 'choose';
// Create a status editor (for now - we'll need a WYSIWYG eventually) to create pages // Create a status editor (for now - we'll need a WYSIWYG eventually) to create pages
// Nickname is set to the observers xchan, and profile_uid to the owner's. // Nickname is set to the observers xchan, and profile_uid to the owner's.
@ -107,18 +101,21 @@ class Webpages extends \Zotlabs\Web\Controller {
$o = profile_tabs($a, $is_owner, \App::$profile['channel_address']); $o = profile_tabs($a, $is_owner, \App::$profile['channel_address']);
$x = array( $x = array(
'webpage' => ITEM_TYPE_WEBPAGE, 'webpage' => ITEM_TYPE_WEBPAGE,
'is_owner' => true, 'is_owner' => true,
'nickname' => \App::$profile['channel_address'], 'nickname' => \App::$profile['channel_address'],
'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), 'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'),
'bang' => '', 'acl' => (($is_owner) ? populate_acl($channel_acl,false, \PermissionDescription::fromGlobalPermission('view_pages')) : ''),
'acl' => (($is_owner) ? populate_acl($channel_acl,false, \PermissionDescription::fromGlobalPermission('view_pages')) : ''), 'showacl' => (($is_owner) ? true : false),
'showacl' => (($is_owner) ? true : false), 'visitor' => true,
'visitor' => true, 'hide_location' => true,
'hide_voting' => true,
'profile_uid' => intval($owner), 'profile_uid' => intval($owner),
'mimetype' => $mimetype, 'mimetype' => $mimetype,
'layout' => $layout, 'mimeselect' => true,
'expanded' => true, 'layout' => $layout,
'layoutselect' => true,
'expanded' => true,
'novoting'=> true, 'novoting'=> true,
'bbco_autocomplete' => 'bbcode', 'bbco_autocomplete' => 'bbcode',
'bbcode' => true 'bbcode' => true

View File

@ -84,10 +84,6 @@ class BasicAuth extends DAV\Auth\Backend\AbstractBasic {
* @return bool * @return bool
*/ */
protected function validateUserPass($username, $password) { protected function validateUserPass($username, $password) {
if (trim($password) === '+++') {
logger('guest: ' . $username);
return true;
}
require_once('include/auth.php'); require_once('include/auth.php');
$record = account_verify_password($username, $password); $record = account_verify_password($username, $password);

View File

@ -101,8 +101,8 @@ class Browser extends DAV\Browser\Plugin {
$parentpath = array(); $parentpath = array();
// only show parent if not leaving /cloud/; TODO how to improve this? // only show parent if not leaving /cloud/; TODO how to improve this?
if ($path && $path != "cloud") { if ($path && $path != "cloud") {
list($parentUri) = DAV\URLUtil::splitPath($path); list($parentUri) = \Sabre\HTTP\URLUtil::splitPath($path);
$fullPath = DAV\URLUtil::encodePath($this->server->getBaseUri() . $parentUri); $fullPath = \Sabre\HTTP\URLUtil::encodePath($this->server->getBaseUri() . $parentUri);
$parentpath['icon'] = $this->enableAssets ? '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl('icons/parent' . $this->iconExtension) . '" width="24" alt="' . t('parent') . '"></a>' : ''; $parentpath['icon'] = $this->enableAssets ? '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl('icons/parent' . $this->iconExtension) . '" width="24" alt="' . t('parent') . '"></a>' : '';
$parentpath['path'] = $fullPath; $parentpath['path'] = $fullPath;
@ -116,7 +116,7 @@ class Browser extends DAV\Browser\Plugin {
// This is the current directory, we can skip it // This is the current directory, we can skip it
if (rtrim($file['href'],'/') == $path) continue; if (rtrim($file['href'],'/') == $path) continue;
list(, $name) = DAV\URLUtil::splitPath($file['href']); list(, $name) = \Sabre\HTTP\URLUtil::splitPath($file['href']);
if (isset($file[200]['{DAV:}resourcetype'])) { if (isset($file[200]['{DAV:}resourcetype'])) {
$type = $file[200]['{DAV:}resourcetype']->getValue(); $type = $file[200]['{DAV:}resourcetype']->getValue();
@ -166,7 +166,7 @@ class Browser extends DAV\Browser\Plugin {
$size = isset($file[200]['{DAV:}getcontentlength']) ? (int)$file[200]['{DAV:}getcontentlength'] : ''; $size = isset($file[200]['{DAV:}getcontentlength']) ? (int)$file[200]['{DAV:}getcontentlength'] : '';
$lastmodified = ((isset($file[200]['{DAV:}getlastmodified'])) ? $file[200]['{DAV:}getlastmodified']->getTime()->format('Y-m-d H:i:s') : ''); $lastmodified = ((isset($file[200]['{DAV:}getlastmodified'])) ? $file[200]['{DAV:}getlastmodified']->getTime()->format('Y-m-d H:i:s') : '');
$fullPath = DAV\URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path ? $path . '/' : '') . $name, '/')); $fullPath = \Sabre\HTTP\URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path ? $path . '/' : '') . $name, '/'));
$displayName = isset($file[200]['{DAV:}displayname']) ? $file[200]['{DAV:}displayname'] : $name; $displayName = isset($file[200]['{DAV:}displayname']) ? $file[200]['{DAV:}displayname'] : $name;
@ -219,7 +219,7 @@ class Browser extends DAV\Browser\Plugin {
$output = ''; $output = '';
if ($this->enablePost) { if ($this->enablePost) {
$this->server->broadcastEvent('onHTMLActionsPanel', array($parent, &$output)); $this->server->emit('onHTMLActionsPanel', array($parent, &$output));
} }
$html .= replace_macros(get_markup_template('cloud.tpl'), array( $html .= replace_macros(get_markup_template('cloud.tpl'), array(

141
Zotlabs/Storage/GitRepo.php Normal file
View File

@ -0,0 +1,141 @@
<?php
namespace Zotlabs\Storage;
use PHPGit\Git as PHPGit;
require __DIR__ . '/../../library/PHPGit.autoload.php'; // Load PHPGit dependencies
/**
* Wrapper class for PHPGit class for git repositories managed by Hubzilla
*
* @author Andrew Manning <andrewmanning@grid.reticu.li>
*/
class GitRepo {
public $url = null;
public $name = null;
private $path = null;
private $channel = null;
public $git = null;
private $repoBasePath = null;
function __construct($channel = 'sys', $url = null, $clone = false, $name = null, $path = null) {
if ($channel === 'sys' && !is_site_admin()) {
logger('Only admin can use channel sys');
return null;
}
$this->repoBasePath = __DIR__ . '/../../store/git';
$this->channel = $channel;
$this->git = new PHPGit();
// Allow custom path for repo in the case of , for example
if ($path) {
$this->path = $path;
} else {
$this->path = $this->repoBasePath . "/" . $this->channel . "/" . $this->name;
}
if ($this->isValidGitRepoURL($url)) {
$this->url = $url;
}
if ($name) {
$this->name = $name;
} else {
$this->name = $this->getRepoNameFromURL($url);
}
if (!$this->name) {
logger('Error creating GitRepo. No repo name found.');
return null;
}
if (is_dir($this->path)) {
// ignore the $url input if it exists
// TODO: Check if the path is either empty or is a valid git repo and error if not
$this->git->setRepository($this->path);
// TODO: get repo metadata
return;
}
if ($this->url) {
// create the folder and clone the repo at url to that folder if $clone is true
if ($clone) {
if (mkdir($this->path, 0770, true)) {
$this->git->setRepository($this->path);
if (!$this->cloneRepo()) {
// TODO: throw error
logger('git clone failed: ' . json_encode($this->git));
}
} else {
logger('git repo path could not be created: ' . json_encode($this->git));
}
}
}
}
public function pull() {
try {
$success = $this->git->pull();
} catch (\PHPGit\Exception\GitException $ex) {
return false;
}
return $success;
}
public function getRepoPath() {
return $this->path;
}
public function setRepoPath($directory) {
if (is_dir($directory)) {
$this->path->$directory;
$this->git->setRepository($directory);
return true;
}
return false;
}
public function setIdentity($user_name, $user_email) {
// setup user for commit messages
$this->git->config->set("user.name", $user_name, ['global' => false, 'system' => false]);
$this->git->config->set("user.email", $user_email, ['global' => false, 'system' => false]);
}
public function cloneRepo() {
if (validate_url($this->url) && $this->isValidGitRepoURL($this->url) && is_dir($this->path)) {
return $this->git->clone($this->url, $this->path);
}
}
public function probeRepo() {
$git = $this->git;
$repo = array();
$repo['remote'] = $git->remote();
$repo['branches'] = $git->branch(['all' => true]);
$repo['logs'] = $git->log(array('limit' => 50));
return $repo;
}
public static function isValidGitRepoURL($url) {
if (validate_url($url) && strrpos(parse_url($url, PHP_URL_PATH), '.')) {
return true;
} else {
return false;
}
}
public static function getRepoNameFromURL($url) {
$urlpath = parse_url($url, PHP_URL_PATH);
$lastslash = strrpos($urlpath, '/') + 1;
$gitext = strrpos($urlpath, '.');
if ($gitext) {
return substr($urlpath, $lastslash, $gitext - $lastslash);
} else {
return null;
}
}
}

View File

@ -31,7 +31,7 @@ class Session {
$handler = new \Zotlabs\Web\SessionHandler(); $handler = new \Zotlabs\Web\SessionHandler();
self::$handler = $handler; self::$handler = $handler;
$x = session_set_save_handler($handler,true); $x = session_set_save_handler($handler,false);
if(! $x) if(! $x)
logger('Session save handler initialisation failed.',LOGGER_NORMAL,LOG_ERR); logger('Session save handler initialisation failed.',LOGGER_NORMAL,LOG_ERR);
@ -46,6 +46,9 @@ class Session {
((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false), ((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false),
((isset($arr['httponly'])) ? $arr['httponly'] : true) ((isset($arr['httponly'])) ? $arr['httponly'] : true)
); );
register_shutdown_function('session_write_close');
} }
function start() { function start() {

View File

@ -46,7 +46,7 @@ require_once('include/account.php');
define ( 'PLATFORM_NAME', 'hubzilla' ); define ( 'PLATFORM_NAME', 'hubzilla' );
define ( 'STD_VERSION', '1.4.4' ); define ( 'STD_VERSION', '1.7' );
define ( 'ZOT_REVISION', 1 ); define ( 'ZOT_REVISION', 1 );
define ( 'DB_UPDATE_VERSION', 1168 ); define ( 'DB_UPDATE_VERSION', 1168 );
@ -904,6 +904,7 @@ class App {
spl_autoload_register('ZotlabsAutoloader::loader'); spl_autoload_register('ZotlabsAutoloader::loader');
self::$meta= new Zotlabs\Web\HttpMeta(); self::$meta= new Zotlabs\Web\HttpMeta();
} }
public static function get_baseurl($ssl = false) { public static function get_baseurl($ssl = false) {
@ -1632,6 +1633,16 @@ function login($register = false, $form_id = 'main-login', $hiddens=false) {
* @brief Used to end the current process, after saving session state. * @brief Used to end the current process, after saving session state.
*/ */
function killme() { function killme() {
// Ensure that closing the database is the last function on the shutdown stack.
// If it is closed prematurely sessions might not get saved correctly.
// Note the second arg to PHP's session_set_save_handler() seems to order that shutdown
// procedure last despite our best efforts, so we don't use that and implictly
// call register_shutdown_function('session_write_close'); within Zotlabs\Web\Session::init()
// and then register the database close function here where nothing else can register
// after it.
register_shutdown_function('shutdown');
exit; exit;
} }
@ -1643,6 +1654,11 @@ function goaway($s) {
killme(); killme();
} }
function shutdown() {
global $db;
$db->close();
}
/** /**
* @brief Returns the entity id of locally logged in account or false. * @brief Returns the entity id of locally logged in account or false.
* *

View File

@ -3,13 +3,11 @@
<h2>Post Permissions</h2> <h2>Post Permissions</h2>
<p>Sometimes called Access Control List, or ACL, the permissions set who is able to see your new post.</p> <p>The permissions dialog lets you select which channels and/or privacy groups can see the post. You can also select who is explicitly denied access. For example, say you are planning a surprise party for a friend. You can send an invitation post to everyone in your <b>Friends</b> group <i>except</i> the friend you are surprising. In this case you "Show" the <b>Friends</b> group but "Don't show" that one person.</p>
<p>Pressing the ACL button (<i class="fa fa-lock"></i> or <i class="fa fa-unlock"></i>) beside the Submit button will display a dialog in which you can select what channels and/or privacy groups can see the post. You can also select who is explicitly denied access. For example, say you are planning a surprise party for a friend. You can send an invitation post to everyone in your <b>Friends</b> group <i>except</i> the friend you are surprising. In this case you "Show" the <b>Friends</b> group but "Don't show" that one person.</p> <dl class="text-info dl-terms-large dl-horizontal">
<dt style="width: 3em;">Tip!</td>
<dl> <dd style="margin-left: 4em;">The border color of each channel indicates whether that channel &mdash; or one of the groups it's a member of &mdash; will have access to the post. The border color will also indicate when a channel, or group it belongs to, has been explicitly set to "Don't show".</dd>
<dt>Tip:</dt>
<dd>The border color of each channel indicates whether that channel &mdash; or one of the groups it belongs to &mdash; has access to the post. The border color will also indicate when a channel [or group it belongs to] has been expliciyly set to "Don't show".</dd>
</dl> </dl>
<h3>Why can't I edit a post's permissions after I saved it?</h3> <h3>Why can't I edit a post's permissions after I saved it?</h3>

View File

@ -34,8 +34,9 @@ class PermissionDescription {
/** /**
* If the interpretation of an empty ACL can't be summarised with a global default permission * If the interpretation of an empty ACL can't be summarised with a global default permission
* or a specific permission setting then use this method and describe what it means instead. * or a specific permission setting then use this method and describe what it means instead.
* Remember to localize the description first.
* *
* @param string $description - the caption for the no-ACL option in the ACL dialog. * @param string $description - the localized caption for the no-ACL option in the ACL dialog.
* @return a new instance of PermissionDescription * @return a new instance of PermissionDescription
*/ */
public static function fromDescription($description) { public static function fromDescription($description) {
@ -45,7 +46,8 @@ class PermissionDescription {
/** /**
* Use this method only if the interpretation of an empty ACL doesn't fall back to a global * Use this method only if the interpretation of an empty ACL doesn't fall back to a global
* default permission. You should pass one of the constants from boot.php - PERMS_PUBLIC, PERMS_NETWORK etc. * default permission. You should pass one of the constants from boot.php - PERMS_PUBLIC,
* PERMS_NETWORK etc.
* *
* @param integer $perm - a single enumerated constant permission - PERMS_PUBLIC, PERMS_NETWORK etc. * @param integer $perm - a single enumerated constant permission - PERMS_PUBLIC, PERMS_NETWORK etc.
* @return a new instance of PermissionDescription * @return a new instance of PermissionDescription
@ -81,6 +83,15 @@ class PermissionDescription {
if (array_key_exists($permname, $global_perms)) { if (array_key_exists($permname, $global_perms)) {
$permDetails = $global_perms[$permname]; $permDetails = $global_perms[$permname];
// It should be OK to always just read the permissions from App::$channel
//
// App::$profile is a union of channel and profile fields.
// The distinction is basically that App::$profile is pointing to the resource
// being observed. App::$channel is referring to the current logged-in channel
// member (if this is a local channel) e.g. the observer. We only show the ACL
// widget to the page owner (observer and observed are the same) so in that case
// I believe either may be safely used here.
$channelPerm = \App::$channel[$permDetails[0]]; $channelPerm = \App::$channel[$permDetails[0]];
$result = new PermissionDescription($permDetails[1], $channelPerm); $result = new PermissionDescription($permDetails[1], $channelPerm);
} else { } else {

View File

@ -1138,33 +1138,29 @@ function status_editor($a, $x, $popup = false) {
if(x($x, 'hide_location')) if(x($x, 'hide_location'))
$geotag = $setloc = $clearloc = ''; $geotag = $setloc = $clearloc = '';
$weblink = t('Insert web link'); $mimetype = ((x($x,'mimetype')) ? $x['mimetype'] : 'text/bbcode');
if(x($x, 'hide_weblink'))
$weblink = '';
$writefiles = perm_is_allowed($x['profile_uid'], get_observer_hash(), 'write_storage'); $mimeselect = ((x($x,'mimeselect')) ? $x['mimeselect'] : false);
if($mimeselect)
$mimeselect = mimetype_select($x['profile_uid'], $mimetype);
else
$mimeselect = '<input type="hidden" name="mimetype" value="' . $mimetype . '" />';
$weblink = (($mimetype === 'text/bbcode') ? t('Insert web link') : false);
if(x($x, 'hide_weblink'))
$weblink = false;
$writefiles = (($mimetype === 'text/bbcode') ? perm_is_allowed($x['profile_uid'], get_observer_hash(), 'write_storage') : false);
if(x($x, 'hide_attach')) if(x($x, 'hide_attach'))
$writefiles = false; $writefiles = false;
$mimeselect = ''; $layout = ((x($x,'layout')) ? $x['layout'] : '');
if(array_key_exists('mimetype', $x) && $x['mimetype']) {
if($x['mimetype'] != 'text/bbcode')
$plaintext = true;
if($x['mimetype'] === 'choose') {
$mimeselect = mimetype_select($x['profile_uid']);
}
else
$mimeselect = '<input type="hidden" name="mimetype" value="' . $x['mimetype'] . '" />';
}
$layoutselect = ''; $layoutselect = ((x($x,'layoutselect')) ? $x['layoutselect'] : false);
if(array_key_exists('layout', $x) && $x['layout']) { if($layoutselect)
if($x['layout'] === 'choose') { $layoutselect = layout_select($x['profile_uid'], $layout);
$layoutselect = layout_select($x['profile_uid']); else
} $layoutselect = '<input type="hidden" name="layout_mid" value="' . $layout . '" />';
else
$layoutselect = '<input type="hidden" name="layout_mid" value="' . $x['layout'] . '" />';
}
if(array_key_exists('channel_select',$x) && $x['channel_select']) { if(array_key_exists('channel_select',$x) && $x['channel_select']) {
require_once('include/identity.php'); require_once('include/identity.php');

View File

@ -59,8 +59,9 @@ abstract class dba_driver {
const NULL_DATE = '0000-00-00 00:00:00'; const NULL_DATE = '0000-00-00 00:00:00';
const UTC_NOW = 'UTC_TIMESTAMP()'; const UTC_NOW = 'UTC_TIMESTAMP()';
protected $debug = 0;
protected $db; protected $db;
public $debug = 0;
public $connected = false; public $connected = false;
public $error = false; public $error = false;
@ -302,10 +303,10 @@ function q($sql) {
$stmt = vsprintf($sql, $args); $stmt = vsprintf($sql, $args);
if($stmt === false) { if($stmt === false) {
if(version_compare(PHP_VERSION, '5.4.0') >= 0) if(version_compare(PHP_VERSION, '5.4.0') >= 0)
logger('dba: vsprintf error: ' . db_logger('dba: vsprintf error: ' .
print_r(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1), true),LOGGER_NORMAL,LOG_CRIT); print_r(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1), true),LOGGER_NORMAL,LOG_CRIT);
else else
logger('dba: vsprintf error: ' . print_r(debug_backtrace(), true),LOGGER_NORMAL,LOG_CRIT); db_logger('dba: vsprintf error: ' . print_r(debug_backtrace(), true),LOGGER_NORMAL,LOG_CRIT);
} }
return $db->q($stmt); return $db->q($stmt);
} }
@ -314,7 +315,7 @@ function q($sql) {
* This will happen occasionally trying to store the * This will happen occasionally trying to store the
* session data after abnormal program termination * session data after abnormal program termination
*/ */
logger('dba: no database: ' . print_r($args,true),LOGGER_NORMAL,LOG_CRIT); db_logger('dba: no database: ' . print_r($args,true),LOGGER_NORMAL,LOG_CRIT);
return false; return false;
} }
@ -385,7 +386,19 @@ function db_getfunc($f) {
if(isset($lookup[$f]) && isset($lookup[$f][ACTIVE_DBTYPE])) if(isset($lookup[$f]) && isset($lookup[$f][ACTIVE_DBTYPE]))
return $lookup[$f][ACTIVE_DBTYPE]; return $lookup[$f][ACTIVE_DBTYPE];
logger('Unable to abstract DB function "'. $f . '" for dbtype ' . ACTIVE_DBTYPE, LOGGER_DEBUG, LOG_ERR); db_logger('Unable to abstract DB function "'. $f . '" for dbtype ' . ACTIVE_DBTYPE, LOGGER_DEBUG, LOG_ERR);
return $f; return $f;
} }
// The logger function may make DB calls internally to query the system logging parameters.
// This can cause a recursion if database debugging is enabled.
// So this function preserves the current database debugging state and then turns it off while
// doing the logger() call
function db_logger($s,$level = LOGGER_NORMAL,$syslog = LOG_INFO) {
global $db;
$saved = $db->debug;
$db->debug = false;
logger($s,$level,$syslog);
$db->debug = $saved;
}

View File

@ -38,7 +38,7 @@ class dba_mysqli extends dba_driver {
if($this->error) { if($this->error) {
logger('dba_mysqli: ERROR: ' . printable($sql) . "\n" . $this->error, LOGGER_NORMAL, LOG_ERR); db_logger('dba_mysqli: ERROR: ' . printable($sql) . "\n" . $this->error, LOGGER_NORMAL, LOG_ERR);
if(file_exists('dbfail.out')) { if(file_exists('dbfail.out')) {
file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . "\n" . $this->error . "\n", FILE_APPEND); file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . "\n" . $this->error . "\n", FILE_APPEND);
} }
@ -46,13 +46,13 @@ class dba_mysqli extends dba_driver {
if(($result === true) || ($result === false)) { if(($result === true) || ($result === false)) {
if($this->debug) { if($this->debug) {
logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returns ' . (($result) ? 'true' : 'false'), LOGGER_NORMAL,(($result) ? LOG_INFO : LOG_ERR)); db_logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returns ' . (($result) ? 'true' : 'false'), LOGGER_NORMAL,(($result) ? LOG_INFO : LOG_ERR));
} }
return $result; return $result;
} }
if($this->debug) { if($this->debug) {
logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returned ' . $result->num_rows . ' results.', LOGGER_NORMAL, LOG_INFO); db_logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returned ' . $result->num_rows . ' results.', LOGGER_NORMAL, LOG_INFO);
} }
$r = array(); $r = array();
@ -61,7 +61,7 @@ class dba_mysqli extends dba_driver {
$r[] = $x; $r[] = $x;
$result->free_result(); $result->free_result();
if($this->debug) { if($this->debug) {
logger('dba_mysqli: ' . printable(print_r($r,true)), LOGGER_NORMAL, LOG_INFO); db_logger('dba_mysqli: ' . printable(print_r($r,true)), LOGGER_NORMAL, LOG_INFO);
} }
} }
return $r; return $r;

View File

@ -50,7 +50,7 @@ class dba_postgres extends dba_driver {
$this->error = pg_last_error($this->db); $this->error = pg_last_error($this->db);
if($result === false || $this->error) { if($result === false || $this->error) {
//logger('dba_postgres: ' . printable($sql) . ' returned false.' . "\n" . $this->error); //db_logger('dba_postgres: ' . printable($sql) . ' returned false.' . "\n" . $this->error);
if(file_exists('dbfail.out')) if(file_exists('dbfail.out'))
file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . ' returned false' . "\n" . $this->error . "\n", FILE_APPEND); file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . ' returned false' . "\n" . $this->error . "\n", FILE_APPEND);
} }
@ -67,7 +67,7 @@ class dba_postgres extends dba_driver {
$r[] = $x; $r[] = $x;
pg_free_result($result); pg_free_result($result);
if($this->debug) if($this->debug)
logger('dba_postgres: ' . printable(print_r($r,true))); db_logger('dba_postgres: ' . printable(print_r($r,true)));
} }
return $r; return $r;
} }

View File

@ -536,20 +536,10 @@ function event_import_ical($ical, $uid) {
} }
$dtstart = $ical->DTSTART->getDateTime(); $dtstart = $ical->DTSTART->getDateTime();
$ev['adjust'] = (($ical->DTSTART->isFloating()) ? 1 : 0);
// logger('dtstart: ' . var_export($dtstart,true)); // logger('dtstart: ' . var_export($dtstart,true));
switch($dtstart->timezone_type) {
case VObject\Property\DateTime::UTC :
$ev['adjust'] = 0;
break;
case VObject\Property\DateTime::LOCALTZ :
default:
$ev['adjust'] = 1;
break;
}
$ev['start'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC', $ev['start'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC',
$dtstart->format(\DateTime::W3C)); $dtstart->format(\DateTime::W3C));

View File

@ -9,15 +9,19 @@ function import_channel($channel, $account_id, $seize) {
$channel['channel_removed'] = (($channel['channel_pageflags'] & 0x8000) ? 1 : 0); $channel['channel_removed'] = (($channel['channel_pageflags'] & 0x8000) ? 1 : 0);
} }
// Ignore the hash provided and re-calculate
$channel['channel_hash'] = make_xchan_hash($channel['channel_guid'],$channel['channel_guid_sig']);
// Check for duplicate channels
$r = q("select * from channel where (channel_guid = '%s' or channel_hash = '%s' or channel_address = '%s' ) limit 1", $r = q("select * from channel where (channel_guid = '%s' or channel_hash = '%s' or channel_address = '%s' ) limit 1",
dbesc($channel['channel_guid']), dbesc($channel['channel_guid']),
dbesc($channel['channel_hash']), dbesc($channel['channel_hash']),
dbesc($channel['channel_address']) dbesc($channel['channel_address'])
); );
// We should probably also verify the hash if(($r) || (check_webbie(array($channel['channel_hash'])) !== $channel['channel_hash'])) {
if($r) {
if($r[0]['channel_guid'] === $channel['channel_guid'] || $r[0]['channel_hash'] === $channel['channel_hash']) { if($r[0]['channel_guid'] === $channel['channel_guid'] || $r[0]['channel_hash'] === $channel['channel_hash']) {
logger('mod_import: duplicate channel. ', print_r($channel,true)); logger('mod_import: duplicate channel. ', print_r($channel,true));
notice( t('Cannot create a duplicate channel identifier on this system. Import failed.') . EOL); notice( t('Cannot create a duplicate channel identifier on this system. Import failed.') . EOL);

View File

@ -2223,7 +2223,7 @@ function item_store($arr, $allow_exec = false, $deliver = true) {
dbesc($arr['layout_mid']), dbesc($arr['layout_mid']),
intval($arr['uid']) intval($arr['uid'])
); );
if((! $l) || (! ($l[0]['item_type'] != ITEM_TYPE_PDL))) if((! $l) || ($l[0]['item_type'] != ITEM_TYPE_PDL))
unset($arr['layout_mid']); unset($arr['layout_mid']);
} }

View File

@ -2128,3 +2128,18 @@ function deliverable_singleton($channel_id,$xchan) {
return false; return false;
} }
function get_repository_version($branch = 'master') {
$path = "https://raw.githubusercontent.com/redmatrix/hubzilla/$branch/boot.php";
$x = z_fetch_url($path);
if($x['success']) {
$y = preg_match('/define(.*?)STD_VERSION(.*?)([0-9.].*)\'/',$x['body'],$matches);
if($y)
return $matches[3];
}
return '?.?';
}

View File

@ -469,6 +469,11 @@ function notifier_run($argv, $argc){
$deliveries[] = $pq; $deliveries[] = $pq;
} }
// notifier_process can alter the recipient list
$recipients = $narr['recipients'];
$env_recips = $narr['env_recips'];
$packet_recips = $narr['packet_recips'];
if(($private) && (! $env_recips)) { if(($private) && (! $env_recips)) {
// shouldn't happen // shouldn't happen

View File

@ -2025,7 +2025,7 @@ function check_webbie($arr) {
if(strlen($reservechan)) if(strlen($reservechan))
$taken = explode(',', $reservechan); $taken = explode(',', $reservechan);
else else
$taken = array(); $taken = array('principals','addressbooks','calendars');
$str = ''; $str = '';
if(count($arr)) { if(count($arr)) {
@ -2057,6 +2057,20 @@ function check_webbie($arr) {
return ''; return '';
} }
function ids_to_array($arr,$idx = 'id') {
$t = array();
if($arr) {
foreach($arr as $x) {
if(! in_array($x[$idx],$t)) {
$t[] = $x[$idx];
}
}
}
return($t);
}
function ids_to_querystr($arr,$idx = 'id') { function ids_to_querystr($arr,$idx = 'id') {
$t = array(); $t = array();

View File

@ -179,4 +179,4 @@ call_hooks('page_end', App::$page['content']);
construct_page($a); construct_page($a);
exit; killme();

View File

@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInit929ad7f4e2f44798d12d2ab06db7b39b::getLoader();

View File

@ -0,0 +1,413 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0 class loader
*
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-0 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
if ('\\' == $class[0]) {
$class = substr($class, 1);
}
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative) {
return false;
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if ($file === null && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if ($file === null) {
// Remember that this class does not exist.
return $this->classMap[$class] = false;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
if (0 === strpos($class, $prefix)) {
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

View File

@ -0,0 +1,9 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@ -0,0 +1,11 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'PHPGit' => array($baseDir . '/src'),
'' => array($vendorDir . '/kzykhys/git/src'),
);

View File

@ -0,0 +1,11 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'),
'Symfony\\Component\\OptionsResolver\\' => array($vendorDir . '/symfony/options-resolver'),
);

View File

@ -0,0 +1,50 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit929ad7f4e2f44798d12d2ab06db7b39b
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit929ad7f4e2f44798d12d2ab06db7b39b', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit929ad7f4e2f44798d12d2ab06db7b39b', 'loadClassLoader'));
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
$loader->register(true);
return $loader;
}
}
function composerRequire929ad7f4e2f44798d12d2ab06db7b39b($file)
{
require $file;
}

View File

@ -0,0 +1,157 @@
[
{
"name": "symfony/process",
"version": "v2.8.0",
"version_normalized": "2.8.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "1b988a88e3551102f3c2d9e1d47a18c3a78d6312"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/1b988a88e3551102f3c2d9e1d47a18c3a78d6312",
"reference": "1b988a88e3551102f3c2d9e1d47a18c3a78d6312",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"time": "2015-11-30 12:35:10",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Component\\Process\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com"
},
{
"name": "symfony/options-resolver",
"version": "v2.8.0",
"version_normalized": "2.8.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
"reference": "e7f62cf7d9e48238299cfa5d0556aee8cff1e075"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/e7f62cf7d9e48238299cfa5d0556aee8cff1e075",
"reference": "e7f62cf7d9e48238299cfa5d0556aee8cff1e075",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"time": "2015-11-18 13:45:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Component\\OptionsResolver\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony OptionsResolver Component",
"homepage": "https://symfony.com",
"keywords": [
"config",
"configuration",
"options"
]
},
{
"name": "kzykhys/git",
"version": "dev-master",
"version_normalized": "9999999-dev",
"source": {
"type": "git",
"url": "https://github.com/kzykhys/PHPGit.git",
"reference": "8b3ee3147d58e0aee62610f0aebcd30eb076ad5d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/kzykhys/PHPGit/zipball/8b3ee3147d58e0aee62610f0aebcd30eb076ad5d",
"reference": "8b3ee3147d58e0aee62610f0aebcd30eb076ad5d",
"shasum": ""
},
"require": {
"php": ">=5.3.2",
"symfony/options-resolver": ">=2.3",
"symfony/process": ">=2.3"
},
"require-dev": {
"symfony/filesystem": ">=2.3"
},
"time": "2014-02-27 13:14:59",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "source",
"autoload": {
"psr-0": {
"": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Kazuyuki Hayashi",
"email": "hayashi@valnur.net"
}
],
"description": "A Git wrapper for PHP5.3+"
}
]

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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']));
}
}

View File

@ -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']);
}
}

View File

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

View File

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

View File

@ -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']);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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']);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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']);
}
}

View File

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

Some files were not shown because too many files have changed in this diff Show More