move granite also to apps-playground because it´s not stable yet and is no longer used
This commit is contained in:
parent
a3d0a74d03
commit
9b134b0636
|
@ -1,162 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Blob - provides a Git blob object
|
|
||||||
*
|
|
||||||
* PHP version 5.3
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT Expat License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Granite\Git;
|
|
||||||
use \Granite\Git\Object\Raw as Raw;
|
|
||||||
use \InvalidArgumentException as InvalidArgumentException;
|
|
||||||
use \finfo as finfo;
|
|
||||||
/**
|
|
||||||
* **Granite\Git\Blob** represents the raw content of an object in a Git repository,
|
|
||||||
* typically a **file**. This class provides methods related to the handling of
|
|
||||||
* blob content, mimetypes, sizes and write support.
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT Expat License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
class Blob
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores the SHA-1 id of the object requested; accessed through the `sha()`
|
|
||||||
* method where it is recalculated based on the blob content.
|
|
||||||
*/
|
|
||||||
private $sha = null;
|
|
||||||
/**
|
|
||||||
* The raw binary string of the file contents.
|
|
||||||
*/
|
|
||||||
private $content = "";
|
|
||||||
/**
|
|
||||||
* The path to the repository location.
|
|
||||||
*/
|
|
||||||
private $path;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches a raw Git object and parses the result. Throws an
|
|
||||||
* InvalidArgumentException if the object is not of the correct type,
|
|
||||||
* or cannot be found.
|
|
||||||
*
|
|
||||||
* @param string $path The path to the repository root.
|
|
||||||
* @param string $sha The SHA-1 id of the requested object, or `null` if
|
|
||||||
* creating a new blob object.
|
|
||||||
*
|
|
||||||
* @throws InvalidArgumentException If the SHA-1 id provided is not a blob.
|
|
||||||
*/
|
|
||||||
public function __construct($path, $sha = NULL)
|
|
||||||
{
|
|
||||||
$this->path = $path;
|
|
||||||
if ($sha !== NULL) {
|
|
||||||
$this->sha = $sha;
|
|
||||||
$object = Raw::factory($path, $sha);
|
|
||||||
|
|
||||||
if ($object->type() !== Raw::OBJ_BLOB) {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
"The object $sha is not a blob, type is {$object->type()}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->content = $object->content();
|
|
||||||
unset($object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets or returns the raw file content, depending whether the parameter is
|
|
||||||
* provided.
|
|
||||||
*
|
|
||||||
* @param string $content The object content to set, or `null` if requesting the
|
|
||||||
* current content.
|
|
||||||
*
|
|
||||||
* @return string The raw binary string of the file contents.
|
|
||||||
*/
|
|
||||||
public function content($content = NULL)
|
|
||||||
{
|
|
||||||
if ($content == NULL) {
|
|
||||||
return $this->content;
|
|
||||||
}
|
|
||||||
$this->content = $content;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the size of the file content in bytes, equivalent to
|
|
||||||
* `strlen($blob->content())`.
|
|
||||||
*
|
|
||||||
* @return int The size of the object in bytes.
|
|
||||||
*/
|
|
||||||
public function size()
|
|
||||||
{
|
|
||||||
return strlen($this->content);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates and returns the SHA-1 id of the object, based on it's contents.
|
|
||||||
*
|
|
||||||
* @return int The SHA-1 id of the object.
|
|
||||||
*/
|
|
||||||
public function sha()
|
|
||||||
{
|
|
||||||
$sha = hash_init('sha1');
|
|
||||||
$header = 'blob ' . strlen($this->content) . "\0";
|
|
||||||
hash_update($sha, $header);
|
|
||||||
hash_update($sha, $this->content);
|
|
||||||
$this->sha = hash_final($sha);
|
|
||||||
return $this->sha;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the mimetype of the object, using `finfo()` to determine the mimetype
|
|
||||||
* of the string.
|
|
||||||
*
|
|
||||||
* @return string The object mimetype.
|
|
||||||
* @see http://php.net/manual/en/function.finfo-open.php
|
|
||||||
*/
|
|
||||||
public function mimetype()
|
|
||||||
{
|
|
||||||
$finfo = new finfo(FILEINFO_MIME);
|
|
||||||
return $finfo->buffer($this->content);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode and compress the object content, saving it to a 'loose' file.
|
|
||||||
*
|
|
||||||
* @return boolean True on success, false on failure.
|
|
||||||
*/
|
|
||||||
public function write()
|
|
||||||
{
|
|
||||||
$sha = $this->sha(TRUE);
|
|
||||||
$path = $this->path
|
|
||||||
. 'objects'
|
|
||||||
. DIRECTORY_SEPARATOR
|
|
||||||
. substr($sha, 0, 2)
|
|
||||||
. DIRECTORY_SEPARATOR
|
|
||||||
. substr($sha, 2);
|
|
||||||
// FIXME: currently writes loose objects only
|
|
||||||
if (file_exists($path)) {
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_dir(dirname($path))) {
|
|
||||||
mkdir(dirname($path), 0777, TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
$loose = fopen($path, 'wb');
|
|
||||||
$data = 'blob ' . strlen($this->content) . "\0" . $this->content;
|
|
||||||
$write = fwrite($loose, gzcompress($data));
|
|
||||||
fclose($loose);
|
|
||||||
|
|
||||||
return ($write !== FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,232 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Commit - provides a 'commit' object
|
|
||||||
*
|
|
||||||
* PHP version 5.3
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT Expat License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Granite\Git;
|
|
||||||
use \Granite\Git\Object\Raw as Raw;
|
|
||||||
use \InvalidArgumentException as InvalidArgumentException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Commit represents a full commit object
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT Expat License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
class Commit
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The path to the repository root
|
|
||||||
*/
|
|
||||||
private $path;
|
|
||||||
/**
|
|
||||||
* The SHA-1 id of the requested commit
|
|
||||||
*/
|
|
||||||
private $sha;
|
|
||||||
/**
|
|
||||||
* The size of the commit in bytes
|
|
||||||
*/
|
|
||||||
private $size;
|
|
||||||
/**
|
|
||||||
* The commit message
|
|
||||||
*/
|
|
||||||
private $message;
|
|
||||||
/**
|
|
||||||
* The full committer string
|
|
||||||
*/
|
|
||||||
private $committer;
|
|
||||||
/**
|
|
||||||
* The full author string
|
|
||||||
*/
|
|
||||||
private $author;
|
|
||||||
/**
|
|
||||||
* The SHA-1 ids of the parent commits
|
|
||||||
*/
|
|
||||||
private $parents = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches a raw Git object and parses the result. Throws an
|
|
||||||
* InvalidArgumentException if the object is not of the correct type,
|
|
||||||
* or cannot be found.
|
|
||||||
*
|
|
||||||
* @param string $path The path to the repository root
|
|
||||||
* @param string $sha The SHA-1 id of the requested object
|
|
||||||
*
|
|
||||||
* @throws InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public function __construct($path, $sha = NULL)
|
|
||||||
{
|
|
||||||
$this->path = $path;
|
|
||||||
if ($sha !== NULL) {
|
|
||||||
$this->sha = $sha;
|
|
||||||
$object = Raw::factory($path, $sha);
|
|
||||||
$this->size = $object->size();
|
|
||||||
|
|
||||||
if ($object->type() !== Raw::OBJ_COMMIT) {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
"The object $sha is not a commit, type is " . $object->type()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse headers and commit message (delimited with "\n\n")
|
|
||||||
list($headers, $this->message) = explode("\n\n", $object->content(), 2);
|
|
||||||
$headers = explode("\n", $headers);
|
|
||||||
|
|
||||||
foreach ($headers as $header) {
|
|
||||||
list($header, $value) = explode(' ', $header, 2);
|
|
||||||
if ($header == 'parent') {
|
|
||||||
$this->parents[] = $value;
|
|
||||||
} else {
|
|
||||||
$this->$header = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->tree = new Tree($this->path, $this->tree);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the message stored in the commit
|
|
||||||
*
|
|
||||||
* @return string The commit message
|
|
||||||
*/
|
|
||||||
public function message($message = NULL)
|
|
||||||
{
|
|
||||||
if ($message !== NULL) {
|
|
||||||
$this->message = $message;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
return $this->message;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the commiter string
|
|
||||||
*
|
|
||||||
* @return string The committer string
|
|
||||||
*/
|
|
||||||
public function committer($committer = NULL)
|
|
||||||
{
|
|
||||||
if ($committer !== NULL) {
|
|
||||||
$this->committer = $committer;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
return $this->committer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the author string
|
|
||||||
*
|
|
||||||
* @return string The author string
|
|
||||||
*/
|
|
||||||
public function author($author = NULL)
|
|
||||||
{
|
|
||||||
if ($author !== NULL) {
|
|
||||||
$this->author = $author;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
return $this->author;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the parents of the commit, or an empty array if none
|
|
||||||
*
|
|
||||||
* @return array The parents of the commit
|
|
||||||
*/
|
|
||||||
public function parents($parents = NULL)
|
|
||||||
{
|
|
||||||
if ($parents !== NULL) {
|
|
||||||
$this->parents = $parents;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
return $this->parents;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a tree object associated with the commit
|
|
||||||
*
|
|
||||||
* @return Tree
|
|
||||||
*/
|
|
||||||
public function tree(Tree $tree = NULL)
|
|
||||||
{
|
|
||||||
if ($tree !== NULL) {
|
|
||||||
$this->tree = $tree;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
return $this->tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the size of the commit in bytes (Git header + data)
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function size()
|
|
||||||
{
|
|
||||||
return $this->size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the size of the commit in bytes (Git header + data)
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function sha()
|
|
||||||
{
|
|
||||||
$this->sha = hash('sha1', $this->_raw());
|
|
||||||
return $this->sha;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function write()
|
|
||||||
{
|
|
||||||
$sha = $this->sha();
|
|
||||||
$path = $this->path
|
|
||||||
. 'objects'
|
|
||||||
. DIRECTORY_SEPARATOR
|
|
||||||
. substr($sha, 0, 2)
|
|
||||||
. DIRECTORY_SEPARATOR
|
|
||||||
. substr($sha, 2);
|
|
||||||
// FIXME: currently writes loose objects only
|
|
||||||
if (file_exists($path)) {
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_dir(dirname($path))) {
|
|
||||||
mkdir(dirname($path), 0777, TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
$loose = fopen($path, 'wb');
|
|
||||||
$data = $this->_raw();
|
|
||||||
$write = fwrite($loose, gzcompress($data));
|
|
||||||
fclose($loose);
|
|
||||||
|
|
||||||
return ($write !== FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function _raw()
|
|
||||||
{
|
|
||||||
$data = 'tree ' . $this->tree->sha() . "\n";
|
|
||||||
foreach ($this->parents as $parent)
|
|
||||||
{
|
|
||||||
$data .= "parent $parent\n";
|
|
||||||
}
|
|
||||||
$data .= 'author ' . $this->author . "\n";
|
|
||||||
$data .= 'committer ' . $this->committer . "\n\n";
|
|
||||||
$data .= $this->message;
|
|
||||||
|
|
||||||
$data = 'commit ' . strlen($data) . "\0" . $data;
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,210 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Index - provides an 'index' object for packfile indexes
|
|
||||||
*
|
|
||||||
* PHP version 5.3
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Granite\Git\Object;
|
|
||||||
use \UnexpectedValueException as UnexpectedValueException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Index represents a packfile index
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
class Index
|
|
||||||
{
|
|
||||||
const INDEX_MAGIC = "\377tOc";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The full path to the packfile index
|
|
||||||
*/
|
|
||||||
private $path;
|
|
||||||
/**
|
|
||||||
* The offset at which the fanout begins, version 2+ indexes have a 2-byte header
|
|
||||||
*/
|
|
||||||
private $offset = 8;
|
|
||||||
/**
|
|
||||||
* The size of the SHA-1 entries, version 1 stores 4-byte offsets alongside to
|
|
||||||
* total 24 bytes, version 2+ stores offsets separately
|
|
||||||
*/
|
|
||||||
private $size = 20;
|
|
||||||
/**
|
|
||||||
* The version of the index file format, versions 1 and 2 are in use and
|
|
||||||
* currently supported
|
|
||||||
*/
|
|
||||||
private $version;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches a raw Git object and parses the result
|
|
||||||
*
|
|
||||||
* @param string $path The path to the repository root
|
|
||||||
* @param string $packname The name of the packfile index to read
|
|
||||||
*/
|
|
||||||
public function __construct($path, $packname)
|
|
||||||
{
|
|
||||||
$this->path = $path
|
|
||||||
. 'objects'
|
|
||||||
. DIRECTORY_SEPARATOR
|
|
||||||
. 'pack'
|
|
||||||
. DIRECTORY_SEPARATOR
|
|
||||||
. 'pack-' . $packname . '.idx';
|
|
||||||
|
|
||||||
$this->version = $this->_readVersion();
|
|
||||||
if ($this->version !== 1 && $this->version !== 2) {
|
|
||||||
throw new UnexpectedValueException(
|
|
||||||
"Unsupported index version (version $version)"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->version == 1) {
|
|
||||||
$this->offset = 0; // Version 1 index has no header/version
|
|
||||||
$this->size = 24; // Offsets + SHA-1 ids are stored together
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the offset of the object stored in the index
|
|
||||||
*
|
|
||||||
* @param string $sha The SHA-1 id of the object being requested
|
|
||||||
*
|
|
||||||
* @return int The offset of the object in the packfile
|
|
||||||
*/
|
|
||||||
public function find($sha)
|
|
||||||
{
|
|
||||||
$index = fopen($this->path, 'rb');
|
|
||||||
$offset = false; // Offset for object in packfile not found by default
|
|
||||||
|
|
||||||
// Read the fanout to skip to the start char in the sorted SHA-1 list
|
|
||||||
list($start, $after) = $this->_readFanout($index, $sha);
|
|
||||||
|
|
||||||
if ($start == $after) {
|
|
||||||
fclose($index);
|
|
||||||
return false; // Object is apparently located in a 0-length section
|
|
||||||
}
|
|
||||||
|
|
||||||
// Seek $offset + 255 4-byte fanout entries and read 256th entry
|
|
||||||
fseek($index, $this->offset + 4 * 255);
|
|
||||||
$totalObjects = $this->_uint32($index);
|
|
||||||
|
|
||||||
// Look up the SHA-1 id of the object
|
|
||||||
// TODO: Binary search
|
|
||||||
fseek($index, $this->offset + 1024 + $this->size * $start);
|
|
||||||
for ($i = $start; $i < $after; $i++) {
|
|
||||||
if ($this->version == 1) {
|
|
||||||
$offset = $this->_uint32($index);
|
|
||||||
}
|
|
||||||
|
|
||||||
$name = fread($index, 20);
|
|
||||||
if ($name == pack('H40', $sha)) {
|
|
||||||
break; // Found it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($i == $after) {
|
|
||||||
fclose($index);
|
|
||||||
return false; // Scanned entire section, couldn't find it
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->version == 2) {
|
|
||||||
// Jump to the offset location and read it
|
|
||||||
fseek($index, 1032 + 24 * $totalObjects + 4 * $i);
|
|
||||||
$offset = $this->_uint32($index);
|
|
||||||
if ($offset & 0x80000000) {
|
|
||||||
// Offset is a 64-bit integer; packfile is larger than 2GB
|
|
||||||
fclose($index);
|
|
||||||
throw new UnexpectedValueException(
|
|
||||||
"Packfile larger than 2GB, currently unsupported"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose($index);
|
|
||||||
return $offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a binary string into a 32-bit unsigned integer
|
|
||||||
*
|
|
||||||
* @param handle $file Binary string to convert
|
|
||||||
*
|
|
||||||
* @return int Integer value
|
|
||||||
*/
|
|
||||||
private function _uint32($file)
|
|
||||||
{
|
|
||||||
$val = unpack('Nx', fread($file, 4));
|
|
||||||
return $val['x'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the fanout for a particular SHA-1 id
|
|
||||||
*
|
|
||||||
* Largely modified from Glip, with some reference to Grit - largely because I
|
|
||||||
* can't see how to re-implement this in PHP
|
|
||||||
*
|
|
||||||
* @param handle $file File handle to the index file
|
|
||||||
* @param string $sha The SHA-1 id to search for
|
|
||||||
* @param int $offset The offset at which the fanout begins
|
|
||||||
*
|
|
||||||
* @return array Array containing integer 'start' and
|
|
||||||
* 'past-the-end' locations
|
|
||||||
*/
|
|
||||||
private function _readFanout($file, $sha)
|
|
||||||
{
|
|
||||||
$sha = pack('H40', $sha);
|
|
||||||
fseek($file, $this->offset);
|
|
||||||
if ($sha{0} == "\00") {
|
|
||||||
/**
|
|
||||||
* First character is 0, read first fanout entry to provide
|
|
||||||
* 'past-the-end' location (since first fanout entry provides start
|
|
||||||
* point for '1'-prefixed SHA-1 ids)
|
|
||||||
*/
|
|
||||||
$start = 0;
|
|
||||||
fseek($file, $this->offset); // Jump to start of fanout, $offset bytes in
|
|
||||||
$after = $this->_uint32($file);
|
|
||||||
} else {
|
|
||||||
/**
|
|
||||||
* Take ASCII value of first character, minus one to get the fanout
|
|
||||||
* position of the offset (minus one because the fanout does not
|
|
||||||
* contain an entry for "\00"), multiplied by four bytes per entry
|
|
||||||
*/
|
|
||||||
fseek($file, $this->offset + (ord($sha{0}) - 1) * 4);
|
|
||||||
$start = $this->_uint32($file);
|
|
||||||
$after = $this->_uint32($file);
|
|
||||||
}
|
|
||||||
|
|
||||||
return array($start, $after);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the version number of the index file, or 1 if there is no version
|
|
||||||
* information
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
private function _readVersion()
|
|
||||||
{
|
|
||||||
$file = fopen($this->path, 'rb');
|
|
||||||
$magic = fread($file, 4);
|
|
||||||
$version = $this->_uint32($file);
|
|
||||||
|
|
||||||
if ($magic !== self::INDEX_MAGIC) {
|
|
||||||
$version = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose($file);
|
|
||||||
return $version;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Loose - provides a 'loose object' object
|
|
||||||
*
|
|
||||||
* PHP version 5.3
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT Expat License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Granite\Git\Object;
|
|
||||||
use \UnexpectedValueException as UnexpectedValueException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loose represents a loose object in the Git repository
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT Expat License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
class Loose extends Raw
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads an object from a loose object file based on the SHA-1 id
|
|
||||||
*
|
|
||||||
* @param string $path The path to the repository root
|
|
||||||
* @param string $sha The SHA-1 id of the requested object
|
|
||||||
*
|
|
||||||
* @throws UnexpectedValueException If the type is not 'commit', 'tree',
|
|
||||||
* 'tag' or 'blob'
|
|
||||||
*/
|
|
||||||
public function __construct($path, $sha)
|
|
||||||
{
|
|
||||||
$this->sha = $sha;
|
|
||||||
|
|
||||||
$loose_path = $path
|
|
||||||
. 'objects/'
|
|
||||||
. substr($sha, 0, 2)
|
|
||||||
. '/'
|
|
||||||
. substr($sha, 2);
|
|
||||||
|
|
||||||
if (!file_exists($loose_path)) {
|
|
||||||
throw new InvalidArgumentException("Cannot open loose object file for $sha");
|
|
||||||
}
|
|
||||||
|
|
||||||
$raw = gzuncompress(file_get_contents($loose_path));
|
|
||||||
$data = explode("\0", $raw, 2);
|
|
||||||
|
|
||||||
$header = $data[0];
|
|
||||||
$this->content = $data[1];
|
|
||||||
|
|
||||||
list($this->type, $this->size) = explode(' ', $header);
|
|
||||||
|
|
||||||
switch ($this->type) {
|
|
||||||
case 'commit':
|
|
||||||
$this->type = Raw::OBJ_COMMIT;
|
|
||||||
break;
|
|
||||||
case 'tree':
|
|
||||||
$this->type = Raw::OBJ_TREE;
|
|
||||||
break;
|
|
||||||
case 'blob':
|
|
||||||
$this->type = Raw::OBJ_BLOB;
|
|
||||||
break;
|
|
||||||
case 'tag':
|
|
||||||
$this->type = Raw::OBJ_TAG;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new UnexpectedValueException(
|
|
||||||
"Unexpected type '{$this->type}'"
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,304 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Packed - provides a 'packed object' object
|
|
||||||
*
|
|
||||||
* PHP version 5.3
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT Expat License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Granite\Git\Object;
|
|
||||||
use \UnexpectedValueException as UnexpectedValueException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Packed represents a packed object in the Git repository
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT Expat License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
class Packed extends Raw
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the packfile being read
|
|
||||||
*/
|
|
||||||
private $_packfile;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Added to the object size to make a 'best-guess' effort at how much compressed
|
|
||||||
* data to read - should be reimplemented, ideally with streams.
|
|
||||||
*/
|
|
||||||
const OBJ_PADDING = 512;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the object data from the compressed data at $offset in $packfile
|
|
||||||
*
|
|
||||||
* @param string $packfile The path to the packfile
|
|
||||||
* @param int $offset The offset of the object data
|
|
||||||
*/
|
|
||||||
public function __construct($packfile, $offset)
|
|
||||||
{
|
|
||||||
$this->_packfile = $packfile;
|
|
||||||
|
|
||||||
list($this->type, $this->size, $this->content)
|
|
||||||
= $this->_readPackedObject($offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the object data at $this->_offset
|
|
||||||
*
|
|
||||||
* @param int $offset Offset of the object header
|
|
||||||
*
|
|
||||||
* @return array Containing the type, size and object data
|
|
||||||
*/
|
|
||||||
private function _readPackedObject($offset)
|
|
||||||
{
|
|
||||||
$file = fopen($this->_packfile, 'rb');
|
|
||||||
fseek($file, $offset);
|
|
||||||
// Read the type and uncompressed size from the object header
|
|
||||||
list($type, $size) = $this->_readHeader($file, $offset);
|
|
||||||
$object_offset = ftell($file);
|
|
||||||
|
|
||||||
if ($type == self::OBJ_OFS_DELTA || $type == self::OBJ_REF_DELTA) {
|
|
||||||
return $this->_unpackDeltified(
|
|
||||||
$file, $offset, $object_offset, $type, $size
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$content = gzuncompress(fread($file, $size + self::OBJ_PADDING), $size);
|
|
||||||
|
|
||||||
return array($type, $size, $content);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a packed object header, returning the type and the size. For more
|
|
||||||
* detailed information, refer to the @see tag.
|
|
||||||
*
|
|
||||||
* From the @see tag: "Each byte is really 7 bits of data, with the first bit
|
|
||||||
* being used to say if that hunk is the last one or not before the data starts.
|
|
||||||
* If the first bit is a 1, you will read another byte, otherwise the data starts
|
|
||||||
* next. The first 3 bits in the first byte specifies the type of data..."
|
|
||||||
*
|
|
||||||
* @param handle $file File handle to read
|
|
||||||
* @param int $offset Offset of the object header
|
|
||||||
*
|
|
||||||
* @return array Containing the type and the size
|
|
||||||
* @see http://book.git-scm.com/7_the_packfile.html
|
|
||||||
*/
|
|
||||||
private function _readHeader($file, $offset)
|
|
||||||
{
|
|
||||||
// Read the object header byte-by-byte
|
|
||||||
fseek($file, $offset);
|
|
||||||
$byte = ord(fgetc($file));
|
|
||||||
/**
|
|
||||||
* Bit-shift right by four, then ignore the first bit with a bitwise AND
|
|
||||||
* This gives us the object type in binary:
|
|
||||||
* 001 commit self::OBJ_COMMIT
|
|
||||||
* 010 tree self::OBJ_TREE
|
|
||||||
* 011 blob self::OBJ_BLOB
|
|
||||||
* 100 tag self::OBJ_TAG
|
|
||||||
* 110 offset delta self::OBJ_OFS_DELTA
|
|
||||||
* 111 ref delta self::OBJ_REF_DELTA
|
|
||||||
*
|
|
||||||
* (000 is undefined, 101 is not currently in use)
|
|
||||||
* See http://book.git-scm.com/7_the_packfile.html for details
|
|
||||||
*/
|
|
||||||
$type = ($byte >> 4) & 0x07;
|
|
||||||
|
|
||||||
// Read the last four bits of the first byte, used to find the size
|
|
||||||
$size = $byte & 0x0F;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* $shift initially set to four, since we use the last four bits of the first
|
|
||||||
* byte
|
|
||||||
*
|
|
||||||
* $byte & 0x80 checks the initial bit is set to 1 (i.e. keep reading data)
|
|
||||||
*
|
|
||||||
* Finally, $shift is incremented by seven for each consecutive byte (because
|
|
||||||
* we ignore the initial bit)
|
|
||||||
*/
|
|
||||||
for ($shift = 4; $byte & 0x80; $shift += 7) {
|
|
||||||
$byte = ord(fgetc($file));
|
|
||||||
/**
|
|
||||||
* The size is ANDed against 0x7F to strip the initial bit, then
|
|
||||||
* bitshifted by left $shift (4 or 7, depending on whether it's the
|
|
||||||
* initial byte) and ORed against the existing binary $size. This
|
|
||||||
* continuously increments the $size variable.
|
|
||||||
*/
|
|
||||||
$size |= (($byte & 0x7F) << $shift);
|
|
||||||
}
|
|
||||||
|
|
||||||
return array($type, $size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unpacks a deltified object located at $offset in $file
|
|
||||||
*
|
|
||||||
* @param handle $file File handle to read
|
|
||||||
* @param int $offset Offset of the object data
|
|
||||||
* @param int $object_offset Offset of the object data, past the header
|
|
||||||
* @param int $type The object type, either OBJ_REF_DELTA
|
|
||||||
or OBJ_OFS_DELTA
|
|
||||||
* @param int $size The expected size of the uncompressed data
|
|
||||||
*
|
|
||||||
* @return array Containing the type, size and object data
|
|
||||||
*/
|
|
||||||
private function _unpackDeltified($file, $offset, $object_offset, $type, $size)
|
|
||||||
{
|
|
||||||
fseek($file, $object_offset);
|
|
||||||
|
|
||||||
if ($type == self::OBJ_REF_DELTA) {
|
|
||||||
|
|
||||||
$base_sha = bin2hex(fread($file, 20));
|
|
||||||
|
|
||||||
$path = substr($this->_packfile, 0, strpos($this->_packfile, '.git')+5);
|
|
||||||
$base = Raw::factory($path, $base_sha);
|
|
||||||
$type = $base->type();
|
|
||||||
$base = $base->content();
|
|
||||||
|
|
||||||
$delta = gzuncompress(
|
|
||||||
fread($file, $size + self::OBJ_PADDING), $size
|
|
||||||
);
|
|
||||||
|
|
||||||
$content = $this->_applyDelta($base, $delta);
|
|
||||||
|
|
||||||
} elseif ($type == self::OBJ_OFS_DELTA) {
|
|
||||||
|
|
||||||
// 20 = maximum varint size according to Glip
|
|
||||||
$data = fread($file, $size + self::OBJ_PADDING + 20);
|
|
||||||
|
|
||||||
list($base_offset, $length) = $this->_bigEndianNumber($data);
|
|
||||||
|
|
||||||
$delta = gzuncompress(substr($data, $length), $size);
|
|
||||||
unset($data);
|
|
||||||
|
|
||||||
$base_offset = $offset - $base_offset;
|
|
||||||
list($type, $size, $base) = $this->_readPackedObject($base_offset);
|
|
||||||
|
|
||||||
$content = $this->_applyDelta($base, $delta);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
throw new UnexpectedValueException(
|
|
||||||
"Unknown type $type for deltified object"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return array($type, strlen($content), $content);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies the $delta byte-sequence to $base and returns the
|
|
||||||
* resultant binary string.
|
|
||||||
*
|
|
||||||
* This code is modified from Grit (see below), the Ruby
|
|
||||||
* implementation used for GitHub under an MIT license.
|
|
||||||
*
|
|
||||||
* @param string $base The base string for the delta to be applied to
|
|
||||||
* @param string $delta The delta string to apply
|
|
||||||
*
|
|
||||||
* @return string The patched binary string
|
|
||||||
* @see
|
|
||||||
* https://github.com/mojombo/grit/blob/master/lib/grit/git-ruby/internal/pack.rb
|
|
||||||
*/
|
|
||||||
private function _applyDelta($base, $delta)
|
|
||||||
{
|
|
||||||
$pos = 0;
|
|
||||||
$src_size = $this->_varint($delta, $pos);
|
|
||||||
$dst_size = $this->_varint($delta, $pos);
|
|
||||||
|
|
||||||
if ($src_size !== strlen($base)) {
|
|
||||||
throw new UnexpectedValueException(
|
|
||||||
'Expected base delta size ' . strlen($base) . ' does not match the expected '
|
|
||||||
. "value $src_size"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$dest = "";
|
|
||||||
while ($pos < strlen($delta)) {
|
|
||||||
$byte = ord($delta{$pos++});
|
|
||||||
|
|
||||||
if ($byte & 0x80) {
|
|
||||||
/* copy a part of $base */
|
|
||||||
$offset = 0;
|
|
||||||
if ($byte & 0x01) $offset = ord($delta{$pos++});
|
|
||||||
if ($byte & 0x02) $offset |= ord($delta{$pos++}) << 8;
|
|
||||||
if ($byte & 0x04) $offset |= ord($delta{$pos++}) << 16;
|
|
||||||
if ($byte & 0x08) $offset |= ord($delta{$pos++}) << 24;
|
|
||||||
$length = 0;
|
|
||||||
if ($byte & 0x10) $length = ord($delta{$pos++});
|
|
||||||
if ($byte & 0x20) $length |= ord($delta{$pos++}) << 8;
|
|
||||||
if ($byte & 0x40) $length |= ord($delta{$pos++}) << 16;
|
|
||||||
if ($length == 0) $length = 0x10000;
|
|
||||||
$dest .= substr($base, $offset, $length);
|
|
||||||
} else {
|
|
||||||
/* take the next $byte bytes as they are */
|
|
||||||
$dest .= substr($delta, $pos, $byte);
|
|
||||||
$pos += $byte;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strlen($dest) !== $dst_size) {
|
|
||||||
throw new UnexpectedValueException(
|
|
||||||
"Deltified string expected to be $dst_size bytes, but actually "
|
|
||||||
. strlen($dest) . ' bytes'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a Git varint (variable-length integer). Used in the `_applyDelta()`
|
|
||||||
* method to read the delta header.
|
|
||||||
*
|
|
||||||
* @param string $string The string to parse
|
|
||||||
* @param int &$pos The position in the string to read from
|
|
||||||
*
|
|
||||||
* @return int The integer value
|
|
||||||
*/
|
|
||||||
private function _varint($string, &$pos = 0)
|
|
||||||
{
|
|
||||||
$varint = 0;
|
|
||||||
$bitmask = 0x80;
|
|
||||||
for ($i = 0; $bitmask & 0x80; $i += 7) {
|
|
||||||
$bitmask = ord($string{$pos++});
|
|
||||||
$varint |= (($bitmask & 0x7F) << $i);
|
|
||||||
}
|
|
||||||
return $varint;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decodes a big endian modified base 128 number (refer to @see tag); this only
|
|
||||||
* appears to be used in one place, the offset delta in packfiles. The offset
|
|
||||||
* is the number of bytes to seek back from the start of the delta object to find
|
|
||||||
* the base object.
|
|
||||||
*
|
|
||||||
* This code has been implemented using the C code given in the @see tag below.
|
|
||||||
*
|
|
||||||
* @param string &$data The data to read from and decode the number
|
|
||||||
*
|
|
||||||
* @return Array Containing the base offset (number of bytes to seek back) and
|
|
||||||
* the length to use when reading the delta
|
|
||||||
* @see http://git.rsbx.net/Documents/Git_Data_Formats.txt
|
|
||||||
*/
|
|
||||||
private function _bigEndianNumber(&$data)
|
|
||||||
{
|
|
||||||
$i = 0;
|
|
||||||
$byte = ord($data{$i++});
|
|
||||||
$number = $byte & 0x7F;
|
|
||||||
while ($byte & 0x80) {
|
|
||||||
$byte = ord($data{$i++});
|
|
||||||
$number = (($number + 1) << 7) | ($byte & 0x7F);
|
|
||||||
}
|
|
||||||
|
|
||||||
return array($number, $i);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,153 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Raw - provides a raw Git object
|
|
||||||
*
|
|
||||||
* PHP version 5.3
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT Expat License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Granite\Git\Object;
|
|
||||||
use \InvalidArgumentException as InvalidArgumentException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Raw represents a raw Git object, using Index to locate
|
|
||||||
* packed objects.
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT Expat License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
class Raw
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Integer values for Git objects
|
|
||||||
* @see http://book.git-scm.com/7_the_packfile.html
|
|
||||||
*/
|
|
||||||
const OBJ_COMMIT = 1;
|
|
||||||
const OBJ_TREE = 2;
|
|
||||||
const OBJ_BLOB = 3;
|
|
||||||
const OBJ_TAG = 4;
|
|
||||||
const OBJ_OFS_DELTA = 6;
|
|
||||||
const OBJ_REF_DELTA = 7;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The SHA-1 id of the requested object
|
|
||||||
*/
|
|
||||||
protected $sha;
|
|
||||||
/**
|
|
||||||
* The type of the requested object (see class constants)
|
|
||||||
*/
|
|
||||||
protected $type;
|
|
||||||
/**
|
|
||||||
* The binary string content of the requested object
|
|
||||||
*/
|
|
||||||
protected $content;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an instance of a raw Git object
|
|
||||||
*
|
|
||||||
* @param string $path The path to the repository root
|
|
||||||
* @param string $sha The SHA-1 id of the requested object
|
|
||||||
*
|
|
||||||
* @return Packed|Loose
|
|
||||||
*/
|
|
||||||
public static function factory($path, $sha)
|
|
||||||
{
|
|
||||||
$loose_path = $path
|
|
||||||
. 'objects/'
|
|
||||||
. substr($sha, 0, 2)
|
|
||||||
. '/'
|
|
||||||
. substr($sha, 2);
|
|
||||||
if (file_exists($loose_path)) {
|
|
||||||
return new Loose($path, $sha);
|
|
||||||
} else {
|
|
||||||
return self::_findPackedObject($path, $sha);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the raw content of the Git object requested
|
|
||||||
*
|
|
||||||
* @return string Raw object content
|
|
||||||
*/
|
|
||||||
public function content()
|
|
||||||
{
|
|
||||||
return $this->content;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the size of the Git object
|
|
||||||
*
|
|
||||||
* @return int The size of the object in bytes
|
|
||||||
*/
|
|
||||||
public function size()
|
|
||||||
{
|
|
||||||
return strlen($this->content);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the type of the object as either commit, tag, blob or tree
|
|
||||||
*
|
|
||||||
* @return string The object type
|
|
||||||
*/
|
|
||||||
public function type()
|
|
||||||
{
|
|
||||||
return $this->type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Searches a packfile for the SHA id and reads the object from the packfile
|
|
||||||
*
|
|
||||||
* @param string $path The path to the repository
|
|
||||||
* @param string $sha The SHA-1 id of the object being requested
|
|
||||||
*
|
|
||||||
* @throws \InvalidArgumentException
|
|
||||||
* @return array An array containing the type, size and object data
|
|
||||||
*/
|
|
||||||
private static function _findPackedObject($path, $sha)
|
|
||||||
{
|
|
||||||
$packfiles = glob(
|
|
||||||
$path
|
|
||||||
. 'objects'
|
|
||||||
. DIRECTORY_SEPARATOR
|
|
||||||
. 'pack'
|
|
||||||
. DIRECTORY_SEPARATOR
|
|
||||||
. 'pack-*.pack'
|
|
||||||
);
|
|
||||||
|
|
||||||
$offset = false;
|
|
||||||
foreach ($packfiles as $packfile) {
|
|
||||||
$packname = substr(basename($packfile, '.pack'), 5);
|
|
||||||
$idx = new Index($path, $packname);
|
|
||||||
$offset = $idx->find($sha);
|
|
||||||
|
|
||||||
if ($offset !== false) {
|
|
||||||
break; // Found it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($offset == false) {
|
|
||||||
throw new InvalidArgumentException("Could not find packed object $sha");
|
|
||||||
}
|
|
||||||
|
|
||||||
$packname = $path
|
|
||||||
. 'objects'
|
|
||||||
. DIRECTORY_SEPARATOR
|
|
||||||
. 'pack'
|
|
||||||
. DIRECTORY_SEPARATOR
|
|
||||||
. 'pack-' . $packname . '.pack';
|
|
||||||
$object = new Packed($packname, $offset);
|
|
||||||
|
|
||||||
return $object;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,293 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Repository - provides a 'repository' object with a set of helper methods
|
|
||||||
*
|
|
||||||
* PHP version 5.3
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT Expat License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Granite\Git;
|
|
||||||
use \InvalidArgumentException as InvalidArgumentException;
|
|
||||||
use \UnexpectedValueException as UnexpectedValueException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Repository represents a Git repository, providing a variety of methods for
|
|
||||||
* fetching objects from SHA-1 ids or the tip of a branch with `head()`
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT Expat License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
class Repository
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The path to the repository root
|
|
||||||
*/
|
|
||||||
private $_path;
|
|
||||||
/**
|
|
||||||
* The indexed version of a commit, ready to write with `commit()`
|
|
||||||
*/
|
|
||||||
private $idx_commit;
|
|
||||||
/**
|
|
||||||
* The indexed version of a tree, modified to with `add()` and `remove()`
|
|
||||||
*/
|
|
||||||
private $idx_tree;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the repository path
|
|
||||||
*
|
|
||||||
* @param string $path The path to the repository root (i.e. /repo/.git/)
|
|
||||||
*/
|
|
||||||
public function __construct($path)
|
|
||||||
{
|
|
||||||
if (!is_dir($path)) {
|
|
||||||
throw new InvalidArgumentException("Unable to find directory $path");
|
|
||||||
} elseif (!is_readable($path)) {
|
|
||||||
throw new InvalidArgumentException("Unable to read directory $path");
|
|
||||||
} elseif (!is_dir($path . DIRECTORY_SEPARATOR . 'objects')
|
|
||||||
|| !is_dir($path . DIRECTORY_SEPARATOR . 'refs')
|
|
||||||
) {
|
|
||||||
throw new UnexpectedValueException(
|
|
||||||
"Invalid directory, could not find 'objects' or 'refs' in $path"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_path = $path;
|
|
||||||
$this->idx_commit = $this->factory('commit');
|
|
||||||
$this->idx_tree = $this->factory('tree');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an object from the Repository of the given type, with the given
|
|
||||||
* SHA-1 id, or false if it cannot be found
|
|
||||||
*
|
|
||||||
* @param string $type The type (blob, commit, tag or tree) of object being
|
|
||||||
* requested
|
|
||||||
* @param string $sha The SHA-1 id of the object (or the name of a tag)
|
|
||||||
*
|
|
||||||
* @return Blob|Commit|Tag|Tree
|
|
||||||
*/
|
|
||||||
public function factory($type, $sha = null)
|
|
||||||
{
|
|
||||||
if (!in_array($type, array('blob', 'commit', 'tag', 'tree'))) {
|
|
||||||
throw new InvalidArgumentException("Invalid type: $type");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($type == 'tag') {
|
|
||||||
$sha = $this->_ref('tags' . DIRECTORY_SEPARATOR . $sha);
|
|
||||||
}
|
|
||||||
$type = 'Granite\\Git\\' . ucwords($type);
|
|
||||||
|
|
||||||
return new $type($this->_path, $sha);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a Commit object representing the HEAD commit
|
|
||||||
*
|
|
||||||
* @param string $branch The branch name to lookup, defaults to 'master'
|
|
||||||
*
|
|
||||||
* @return Commit An object representing the HEAD commit
|
|
||||||
*/
|
|
||||||
public function head($branch = 'master', $value = NULL)
|
|
||||||
{
|
|
||||||
if ($value == NULL)
|
|
||||||
return $this->factory(
|
|
||||||
'commit', $this->_ref('heads' . DIRECTORY_SEPARATOR . $branch)
|
|
||||||
);
|
|
||||||
|
|
||||||
file_put_contents(
|
|
||||||
$this->_path . DIRECTORY_SEPARATOR
|
|
||||||
. 'refs' . DIRECTORY_SEPARATOR
|
|
||||||
. 'heads' . DIRECTORY_SEPARATOR . 'master',
|
|
||||||
$value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a string representing the repository's location, which may or may
|
|
||||||
* not be initialised
|
|
||||||
*
|
|
||||||
* @return string A string representing the repository's location
|
|
||||||
*/
|
|
||||||
public function path()
|
|
||||||
{
|
|
||||||
return $this->_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of the local branches under `refs/heads`
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function tags()
|
|
||||||
{
|
|
||||||
return $this->_refs('tags');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of the local tags under `refs/tags`
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function branches()
|
|
||||||
{
|
|
||||||
return $this->_refs('heads');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function _refs($type)
|
|
||||||
{
|
|
||||||
$dir = $this->_path . 'refs' . DIRECTORY_SEPARATOR . $type;
|
|
||||||
$refs = glob($dir . DIRECTORY_SEPARATOR . '*');
|
|
||||||
foreach ($refs as &$ref) {
|
|
||||||
$ref = basename($ref);
|
|
||||||
}
|
|
||||||
return $refs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialises a Git repository
|
|
||||||
*
|
|
||||||
* @return boolean Returns true on success, false on error
|
|
||||||
*/
|
|
||||||
public static function init($path)
|
|
||||||
{
|
|
||||||
$path .= '/';
|
|
||||||
if (!is_dir($path)) {
|
|
||||||
mkdir($path);
|
|
||||||
} elseif (is_dir($path . 'objects')) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mkdir($path . 'objects');
|
|
||||||
mkdir($path . 'objects/info');
|
|
||||||
mkdir($path . 'objects/pack');
|
|
||||||
mkdir($path . 'refs');
|
|
||||||
mkdir($path . 'refs/heads');
|
|
||||||
mkdir($path . 'refs/tags');
|
|
||||||
|
|
||||||
file_put_contents($path . 'HEAD', 'ref: refs/heads/master');
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the indexed commit to disk, with blobs added/removed via `add()` and
|
|
||||||
* `rm()`
|
|
||||||
*
|
|
||||||
* @param string $message The commit message
|
|
||||||
* @param string $author The author name
|
|
||||||
*
|
|
||||||
* @return boolean True on success, or false on failure
|
|
||||||
*/
|
|
||||||
public function commit($message, $author)
|
|
||||||
{
|
|
||||||
$user_string = $username . ' ' . time() . ' +0000';
|
|
||||||
|
|
||||||
try {
|
|
||||||
$parents = array($this->repo->head()->sha());
|
|
||||||
} catch (InvalidArgumentException $e) {
|
|
||||||
$parents = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->idx_commit->message($message);
|
|
||||||
$this->idx_commit->author($user_string);
|
|
||||||
$this->idx_commit->committer($user_string);
|
|
||||||
$this->idx_commit->tree($this->idx_tree);
|
|
||||||
$commit->parents($parents);
|
|
||||||
|
|
||||||
$this->idx_tree->write();
|
|
||||||
$this->idx_commit->write();
|
|
||||||
|
|
||||||
$this->repo->head('master', $this->idx_commit->sha());
|
|
||||||
|
|
||||||
$this->idx_commit = $this->factory('commit');
|
|
||||||
$this->idx_tree = $this->factory('tree');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a file to the indexed commit, to be written to disk with `commit()`
|
|
||||||
*
|
|
||||||
* @param string $filename The filename to save it under
|
|
||||||
* @param Granite\Git\Blob $blob The raw blob object to add to the tree
|
|
||||||
*/
|
|
||||||
public function add($filename, Granite\Git\Blob $blob)
|
|
||||||
{
|
|
||||||
$blob->write();
|
|
||||||
$nodes = $this->idx_tree->nodes();
|
|
||||||
$nodes[$filename] = new Granite\Git\Tree\Node($filename, '100644', $blob->sha());
|
|
||||||
$this->idx_tree->nodes($nodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a file from the indexed commit
|
|
||||||
*/
|
|
||||||
public function rm($filename)
|
|
||||||
{
|
|
||||||
$nodes = $this->idx_tree->nodes();
|
|
||||||
unset($nodes[$filename]);
|
|
||||||
$this->idx_tree->nodes($nodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an SHA-1 id of the ref resource
|
|
||||||
*
|
|
||||||
* @param string $ref The ref name to lookup
|
|
||||||
*
|
|
||||||
* @return string An SHA-1 id of the ref resource
|
|
||||||
*/
|
|
||||||
private function _ref($ref)
|
|
||||||
{
|
|
||||||
// All refs are stored in `.git/refs`
|
|
||||||
$file = $this->_path . 'refs' . DIRECTORY_SEPARATOR . $ref;
|
|
||||||
|
|
||||||
if (file_exists($file)) {
|
|
||||||
return trim(file_get_contents($file));
|
|
||||||
}
|
|
||||||
|
|
||||||
$sha = $this->_packedRef($ref);
|
|
||||||
|
|
||||||
if ($sha == false) {
|
|
||||||
throw new InvalidArgumentException("The ref $ref could not be found");
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sha;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an SHA-1 id of the ref resource, or false if it cannot be found
|
|
||||||
*
|
|
||||||
* @param string $ref The ref name to lookup
|
|
||||||
*
|
|
||||||
* @return string An SHA-1 id of the ref resource
|
|
||||||
*/
|
|
||||||
private function _packedRef($ref)
|
|
||||||
{
|
|
||||||
$sha = false;
|
|
||||||
if (file_exists($this->_path . 'packed-refs')) {
|
|
||||||
$file = fopen($this->_path . 'packed-refs', 'r');
|
|
||||||
|
|
||||||
while (($line = fgets($file)) !== false) {
|
|
||||||
$info = explode(' ', $line);
|
|
||||||
if (count($info) == 2
|
|
||||||
&& trim($info[1]) == 'refs' . DIRECTORY_SEPARATOR . $ref
|
|
||||||
) {
|
|
||||||
$sha = trim($info[0]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose($file);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sha;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Tag - provides a 'tag' object
|
|
||||||
*
|
|
||||||
* PHP version 5.3
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT Expat License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Granite\Git;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tag represents a full tag object
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT Expat License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
class Tag
|
|
||||||
{
|
|
||||||
|
|
||||||
public function __construct($path, $sha)
|
|
||||||
{
|
|
||||||
$this->sha = $sha;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function sha()
|
|
||||||
{
|
|
||||||
return $this->sha;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,198 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Tree - provides a 'tree' object
|
|
||||||
*
|
|
||||||
* PHP version 5.3
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT Expat License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Granite\Git;
|
|
||||||
use \Granite\Git\Tree\Node as Node;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tree represents a full tree object, with nodes pointing to other tree objects
|
|
||||||
* and file blobs
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT Expat License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
class Tree
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The SHA-1 id of the requested tree
|
|
||||||
*/
|
|
||||||
private $sha;
|
|
||||||
/**
|
|
||||||
* The nodes/entries for the requested tree
|
|
||||||
*/
|
|
||||||
private $nodes = array();
|
|
||||||
/**
|
|
||||||
* The path to the repository
|
|
||||||
*/
|
|
||||||
private $path;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a tree object by fetching the raw object
|
|
||||||
*
|
|
||||||
* @param string $path The path to the repository root
|
|
||||||
* @param string $sha The SHA-1 id of the requested object
|
|
||||||
*/
|
|
||||||
public function __construct($path, $sha = NULL, $dbg = FALSE)
|
|
||||||
{
|
|
||||||
$this->path = $path;
|
|
||||||
if ($sha !== NULL) {
|
|
||||||
$object = Object\Raw::factory($path, $sha);
|
|
||||||
$this->sha = $sha;
|
|
||||||
|
|
||||||
if ($object->type() !== Object\Raw::OBJ_TREE) {
|
|
||||||
throw new \InvalidArgumentException(
|
|
||||||
"The object $sha is not a tree, type is " . $object->type()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$content = $object->content();
|
|
||||||
file_put_contents('/tmp/tree_from_real_repo'.time(), $content);
|
|
||||||
$nodes = array();
|
|
||||||
|
|
||||||
for ($i = 0; $i < strlen($content); $i = $data_start + 21) {
|
|
||||||
$data_start = strpos($content, "\0", $i);
|
|
||||||
$info = substr($content, $i, $data_start-$i);
|
|
||||||
list($mode, $name) = explode(' ', $info, 2);
|
|
||||||
// Read the object SHA-1 id
|
|
||||||
$sha = bin2hex(substr($content, $data_start + 1, 20));
|
|
||||||
|
|
||||||
$this->nodes[$name] = new Node($name, $mode, $sha);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of Tree and Granite\Git\Blob objects,
|
|
||||||
* representing subdirectories and files
|
|
||||||
*
|
|
||||||
* @return array Array of Tree and Granite\Git\Blob objects
|
|
||||||
*/
|
|
||||||
public function nodes($nodes = null)
|
|
||||||
{
|
|
||||||
if ($nodes == null) {
|
|
||||||
return $this->nodes;
|
|
||||||
}
|
|
||||||
$this->nodes = $nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a blob or a tree to the list of nodes
|
|
||||||
*
|
|
||||||
* @param string $name The basename (filename) of the blob or tree
|
|
||||||
* @param string $mode The mode of the blob or tree (see above)
|
|
||||||
* @param string $sha The SHA-1 id of the blob or tree to add
|
|
||||||
*/
|
|
||||||
public function add($name, $mode, $sha)
|
|
||||||
{
|
|
||||||
$this->nodes[$name] = new Node($name, $mode, $sha);
|
|
||||||
uasort($this->nodes, array($this, '_sort'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function write()
|
|
||||||
{
|
|
||||||
$sha = $this->sha();
|
|
||||||
$path = $this->path
|
|
||||||
. 'objects'
|
|
||||||
. DIRECTORY_SEPARATOR
|
|
||||||
. substr($sha, 0, 2)
|
|
||||||
. DIRECTORY_SEPARATOR
|
|
||||||
. substr($sha, 2);
|
|
||||||
// FIXME: currently writes loose objects only
|
|
||||||
if (file_exists($path)) {
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_dir(dirname($path))) {
|
|
||||||
mkdir(dirname($path), 0777, TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
$loose = fopen($path, 'wb');
|
|
||||||
$data = $this->_raw();
|
|
||||||
$data = 'tree ' . strlen($data) . "\0" . $data;
|
|
||||||
$write = fwrite($loose, gzcompress($data));
|
|
||||||
fclose($loose);
|
|
||||||
|
|
||||||
return ($write !== FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the SHA-1 id of the Tree
|
|
||||||
*
|
|
||||||
* @return string SHA-1 id of the Tree
|
|
||||||
*/
|
|
||||||
public function sha()
|
|
||||||
{
|
|
||||||
$data = $this->_raw();
|
|
||||||
$raw = 'tree ' . strlen($data) . "\0" . $data;
|
|
||||||
$this->sha = hash('sha1', $raw);
|
|
||||||
return $this->sha;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the raw object content to be saved to disk
|
|
||||||
*/
|
|
||||||
public function _raw()
|
|
||||||
{
|
|
||||||
uasort($this->nodes, array($this, '_sort'));
|
|
||||||
$data = '';
|
|
||||||
foreach ($this->nodes as $node)
|
|
||||||
{
|
|
||||||
$data .= base_convert($node->mode(), 10, 8) . ' ' . $node->name() . "\0";
|
|
||||||
$data .= pack('H40', $node->sha());
|
|
||||||
}
|
|
||||||
file_put_contents('/tmp/tree_made'.time(), $data);
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sorts the node entries in a tree, general sort method adapted from original
|
|
||||||
* Git C code (see @see tag below).
|
|
||||||
*
|
|
||||||
* @return 1, 0 or -1 if the first entry is greater than, the same as, or less
|
|
||||||
* than the second, respectively.
|
|
||||||
* @see https://github.com/gitster/git/blob/master/read-cache.c Around line 352,
|
|
||||||
* the `base_name_compare` function
|
|
||||||
*/
|
|
||||||
public function _sort(&$a, &$b)
|
|
||||||
{
|
|
||||||
$length = strlen($a->name()) < strlen($b->name()) ? strlen($a->name()) : strlen($b->name());
|
|
||||||
|
|
||||||
$cmp = strncmp($a->name(), $b->name(), $length);
|
|
||||||
if ($cmp) {
|
|
||||||
return $cmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
$suffix1 = $a->name();
|
|
||||||
$suffix1 = (strlen($suffix1) > $length) ? $suffix1{$length} : FALSE;
|
|
||||||
$suffix2 = $b->name();
|
|
||||||
$suffix2 = (strlen($suffix2) > $length) ? $suffix2{$length} : FALSE;
|
|
||||||
if (!$suffix1 && $a->isDirectory()) {
|
|
||||||
$suffix1 = '/';
|
|
||||||
}
|
|
||||||
if (!$suffix2 && $b->isDirectory()) {
|
|
||||||
$suffix2 = '/';
|
|
||||||
}
|
|
||||||
if ($suffix1 < $suffix2) {
|
|
||||||
return -1;
|
|
||||||
} elseif ($suffix1 > $suffix2) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,126 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Node - provides a tree node object for tree entries
|
|
||||||
*
|
|
||||||
* PHP version 5.3
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT Expat License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Granite\Git\Tree;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Node represents an entry in a Tree
|
|
||||||
*
|
|
||||||
* @category Git
|
|
||||||
* @package Granite
|
|
||||||
* @author Craig Roberts <craig0990@googlemail.com>
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php MIT Expat License
|
|
||||||
* @link http://craig0990.github.com/Granite/
|
|
||||||
*/
|
|
||||||
class Node
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Name of the file, directory or submodule
|
|
||||||
*/
|
|
||||||
private $_name;
|
|
||||||
/**
|
|
||||||
* Mode of the object, in octal
|
|
||||||
*/
|
|
||||||
private $_mode;
|
|
||||||
/**
|
|
||||||
* SHA-1 id of the tree
|
|
||||||
*/
|
|
||||||
private $_sha;
|
|
||||||
/**
|
|
||||||
* Boolean value for whether the entry represents a directory
|
|
||||||
*/
|
|
||||||
private $_is_dir;
|
|
||||||
/**
|
|
||||||
* Boolean value for whether the entry represents a submodule
|
|
||||||
*/
|
|
||||||
private $_is_submodule;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up a Node class with properties corresponding to the $mode parameter
|
|
||||||
*
|
|
||||||
* @param string $name The name of the object (file, directory or submodule name)
|
|
||||||
* @param int $mode The mode of the object, retrieved from the repository
|
|
||||||
* @param string $sha The SHA-1 id of the object
|
|
||||||
*/
|
|
||||||
public function __construct($name, $mode, $sha)
|
|
||||||
{
|
|
||||||
$this->_name = $name;
|
|
||||||
$this->_mode = intval($mode, 8);
|
|
||||||
$this->_sha = $sha;
|
|
||||||
|
|
||||||
$this->_is_dir = (bool) ($this->_mode & 0x4000);
|
|
||||||
$this->_is_submodule = ($this->_mode == 0xE000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a boolean value indicating whether the node is a directory
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function isDirectory()
|
|
||||||
{
|
|
||||||
return $this->_is_dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a boolean value indicating whether the node is a submodule
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function isSubmodule()
|
|
||||||
{
|
|
||||||
return $this->_is_submodule;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the object name
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function name()
|
|
||||||
{
|
|
||||||
return $this->_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the object's SHA-1 id
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function sha()
|
|
||||||
{
|
|
||||||
return $this->_sha;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the octal value of the file mode
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function mode()
|
|
||||||
{
|
|
||||||
return $this->_mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function type()
|
|
||||||
{
|
|
||||||
if ($this->isDirectory()) {
|
|
||||||
return 'tree';
|
|
||||||
} elseif ($this->isSubmodule()) {
|
|
||||||
return 'commit';
|
|
||||||
} else {
|
|
||||||
return 'blob';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue