/** * Gets the data for a blob * * @param GitPHP_Blob $blob blob * @return string blob data */ public function Load($blob) { if (!$blob) { return; } return $this->objectLoader->GetObject($blob->GetHash()); }
/** * Gets the data for a tree * * @param GitPHP_Tree $tree tree * @return array array of tree contents */ public function Load($tree) { if (!$tree) { return; } $contents = array(); $treePath = $tree->GetPath(); $treeData = $this->objectLoader->GetObject($tree->GetHash()); $start = 0; $len = strlen($treeData); while ($start < $len) { $pos = strpos($treeData, "", $start); list($mode, $path) = explode(' ', substr($treeData, $start, $pos - $start), 2); $mode = str_pad($mode, 6, '0', STR_PAD_LEFT); $hash = bin2hex(substr($treeData, $pos + 1, 20)); $start = $pos + 21; $octmode = octdec($mode); if ($octmode == 57344) { // submodules not currently supported continue; } if (!empty($treePath)) { $path = $treePath . '/' . $path; } $data = array(); $data['hash'] = $hash; if ($octmode & 0x4000) { // tree $data['type'] = 'tree'; } else { // blob $data['type'] = 'blob'; } $data['mode'] = $mode; $data['path'] = $path; $contents[] = $data; } return $contents; }
/** * Abbreviate a hash * * @param GitPHP_Project $project project * @param string $hash hash to abbreviate * @return string abbreviated hash */ public function AbbreviateHash($project, $hash) { if (!$project) { return $hash; } if (!preg_match('/[0-9A-Fa-f]{40}/', $hash)) { return $hash; } $abbrevLen = GitPHP_ProjectLoad_Raw::HashAbbreviateLength; $projAbbrevLen = $project->GetAbbreviateLength(); if ($projAbbrevLen > 0) { $abbrevLen = max(4, min($projAbbrevLen, 40)); } $prefix = substr($hash, 0, $abbrevLen); if (!$project->GetUniqueAbbreviation()) { return $prefix; } return $this->objectLoader->EnsureUniqueHash($hash, $prefix); }
/** * Gets the data for a commit * * @param GitPHP_Commit $commit commit * @return array commit data */ public function Load($commit) { if (!$commit) { return; } $abbreviatedHash = null; $tree = null; $parents = array(); $author = null; $authorEpoch = null; $authorTimezone = null; $committer = null; $committerEpoch = null; $committerTimezone = null; $title = null; $comment = array(); $data = $this->objectLoader->GetObject($commit->GetHash()); if (empty($data)) { return; } $lines = explode("\n", $data); $linecount = count($lines); $i = 0; $encoding = null; /* Commit header */ for ($i = 0; $i < $linecount; $i++) { $line = $lines[$i]; if (preg_match('/^tree ([0-9a-fA-F]{40})$/', $line, $regs)) { /* Tree */ $tree = $regs[1]; } else { if (preg_match('/^parent ([0-9a-fA-F]{40})$/', $line, $regs)) { /* Parent */ $parents[] = $regs[1]; } else { if (preg_match('/^author (.*) ([0-9]+) (.*)$/', $line, $regs)) { /* author data */ $author = $regs[1]; $authorEpoch = $regs[2]; $authorTimezone = $regs[3]; } else { if (preg_match('/^committer (.*) ([0-9]+) (.*)$/', $line, $regs)) { /* committer data */ $committer = $regs[1]; $committerEpoch = $regs[2]; $committerTimezone = $regs[3]; } else { if (preg_match('/^encoding (.+)$/', $line, $regs)) { $gitEncoding = trim($regs[1]); if (strlen($gitEncoding) > 0 && function_exists('mb_list_encodings')) { $supportedEncodings = mb_list_encodings(); $encIdx = array_search(strtolower($gitEncoding), array_map('strtolower', $supportedEncodings)); if ($encIdx !== false) { $encoding = $supportedEncodings[$encIdx]; } } $encoding = trim($regs[1]); } else { if (strlen($line) == 0) { break; } } } } } } } /* Commit body */ for ($i += 1; $i < $linecount; $i++) { $trimmed = trim($lines[$i]); if (strlen($trimmed) > 0 && strlen($encoding) > 0 && function_exists('mb_convert_encoding')) { $trimmed = mb_convert_encoding($trimmed, 'UTF-8', $encoding); } if (empty($title) && strlen($trimmed) > 0) { $title = $trimmed; } if (!empty($title)) { if (strlen($trimmed) > 0 || $i < $linecount - 1) { $comment[] = $trimmed; } } } return array($abbreviatedHash, $tree, $parents, $author, $authorEpoch, $authorTimezone, $committer, $committerEpoch, $committerTimezone, $title, $comment); }
/** * Gets the data for a tag * * @param GitPHP_Tag $tag tag * @return array array of tag data */ public function Load($tag) { if (!$tag) { return; } $type = null; $object = null; $commitHash = null; $tagger = null; $taggerEpoch = null; $taggerTimezone = null; $comment = array(); $data = $this->objectLoader->GetObject($tag->GetHash(), $packedType); if ($packedType == GitPHP_Pack::OBJ_COMMIT) { /* light tag */ $object = $tag->GetHash(); $commitHash = $tag->GetHash(); $type = 'commit'; return array($type, $object, $commitHash, $tagger, $taggerEpoch, $taggerTimezone, $comment); } $lines = explode("\n", $data); if (!isset($lines[0])) { return; } $objectHash = null; $readInitialData = false; foreach ($lines as $line) { if (!$readInitialData) { if (preg_match('/^object ([0-9a-fA-F]{40})$/', $line, $regs)) { $objectHash = $regs[1]; continue; } else { if (preg_match('/^type (.+)$/', $line, $regs)) { $type = $regs[1]; continue; } else { if (preg_match('/^tag (.+)$/', $line, $regs)) { continue; } else { if (preg_match('/^tagger (.*) ([0-9]+) (.*)$/', $line, $regs)) { $tagger = $regs[1]; $taggerEpoch = $regs[2]; $taggerTimezone = $regs[3]; continue; } } } } } $trimmed = trim($line); if (strlen($trimmed) > 0 || $readInitialData === true) { $comment[] = $line; } $readInitialData = true; } switch ($type) { case 'commit': $object = $objectHash; $commitHash = $objectHash; break; case 'tag': $object = $tag->GetProject()->GetTagList()->GetTagNameFromHash($objectHash); break; case 'blob': $object = $objectHash; break; } return array($type, $object, $commitHash, $tagger, $taggerEpoch, $taggerTimezone, $comment); }
/** * Unpacks an object at an offset * * @param resource $pack pack file pointer * @param int $offset object offset * @return array object type and data */ private function UnpackObject($pack, $offset) { fseek($pack, $offset); /* * object header: * first byte is the type (high 3 bits) and low byte of size (lower 4 bits) * subsequent bytes each have 7 next higher bits of the size (little endian) * most significant bit is either 1 or 0 to indicate whether the next byte * should be read as part of the size. 1 means continue reading the size, * 0 means the data is starting */ $c = ord(fgetc($pack)); $type = $c >> 4 & 0x7; $size = $c & 0xf; for ($i = 4; $c & 0x80; $i += 7) { $c = ord(fgetc($pack)); $size |= ($c & 0x7f) << $i; } if ($type == GitPHP_Pack::OBJ_COMMIT || $type == GitPHP_Pack::OBJ_TREE || $type == GitPHP_Pack::OBJ_BLOB || $type == GitPHP_Pack::OBJ_TAG) { /* * regular gzipped object data */ return array($type, gzuncompress(fread($pack, $size + 512), $size)); } else { if ($type == GitPHP_Pack::OBJ_OFS_DELTA) { /* * delta of an object at offset */ $buf = fread($pack, $size + 512 + 20); /* * read the base object offset * each subsequent byte's 7 least significant bits * are part of the offset in decreasing significance per byte * (opposite of other places) * most significant bit is a flag indicating whether to read the * next byte as part of the offset */ $pos = 0; $off = -1; do { $off++; $c = ord($buf[$pos++]); $off = ($off << 7) + ($c & 0x7f); } while ($c & 0x80); /* * next read the compressed delta data */ $delta = gzuncompress(mb_orig_substr($buf, $pos), $size); $baseOffset = $offset - $off; if ($baseOffset > 0) { /* * read base object at offset and apply delta to it */ list($type, $base) = $this->UnpackObject($pack, $baseOffset); $data = GitPHP_PackData::ApplyDelta($delta, $base); return array($type, $data); } } else { if ($type == GitPHP_Pack::OBJ_REF_DELTA) { /* * delta of object with hash */ /* * first the base object's hash * load that object */ $hash = fread($pack, 20); $hash = bin2hex($hash); $base = $this->objectLoader->GetObject($hash, $type); /* * then the gzipped delta data */ $delta = gzuncompress(fread($pack, $size + 512), $size); $data = GitPHP_PackData::ApplyDelta($delta, $base); return array($type, $data); } } } return false; }