<?php
namespace Gitonomy\Git;
use Gitonomy\Git\Diff\Diff;
use Gitonomy\Git\Exception\InvalidArgumentException;
use Gitonomy\Git\Exception\ProcessException;
use Gitonomy\Git\Exception\ReferenceNotFoundException;
use Gitonomy\Git\Reference\Branch;
use Gitonomy\Git\Util\StringHelper;
class Commit extends Revision
{
private $data = [];
public function __construct(Repository $repository, $hash, array $data = [])
{
if (!preg_match('/^[a-f0-9]{40}$/', $hash)) {
throw new ReferenceNotFoundException($hash);
}
parent::__construct($repository, $hash);
$this->setData($data);
}
public function setData(array $data)
{
foreach ($data as $name => $value) {
$this->data[$name] = $value;
}
}
public function getDiff()
{
$args = ['-r', '-p', '-m', '-M', '--no-commit-id', '--full-index', $this->revision];
$diff = Diff::parse($this->repository->run('diff-tree', $args));
$diff->setRepository($this->repository);
return $diff;
}
public function getHash()
{
return $this->revision;
}
public function getShortHash()
{
return $this->getData('shortHash');
}
public function getFixedShortHash($length = 6)
{
return StringHelper::substr($this->revision, 0, $length);
}
public function getParentHashes()
{
return $this->getData('parentHashes');
}
public function getParents()
{
$result = [];
foreach ($this->getData('parentHashes') as $parentHash) {
$result[] = $this->repository->getCommit($parentHash);
}
return $result;
}
public function getTreeHash()
{
return $this->getData('treeHash');
}
public function getTree()
{
return $this->getData('tree');
}
public function getLastModification($path = null)
{
if (0 === strpos($path, '/')) {
$path = StringHelper::substr($path, 1);
}
if ($getWorkingDir = $this->repository->getWorkingDir()) {
$path = $getWorkingDir.'/'.$path;
}
$result = $this->repository->run('log', ['--format=%H', '-n', 1, $this->revision, '--', $path]);
return $this->repository->getCommit(trim($result));
}
public function getShortMessage($length = 50, $preserve = false, $separator = '...')
{
$message = $this->getData('subjectMessage');
if (StringHelper::strlen($message) > $length) {
if ($preserve && false !== ($breakpoint = StringHelper::strpos($message, ' ', $length))) {
$length = $breakpoint;
}
return rtrim(StringHelper::substr($message, 0, $length)).$separator;
}
return $message;
}
public function resolveReferences()
{
return $this->repository->getReferences()->resolve($this);
}
public function getIncludingBranches($local = true, $remote = true)
{
$arguments = ['--contains', $this->revision];
if ($local && $remote) {
$arguments[] = '-a';
} elseif (!$local && $remote) {
$arguments[] = '-r';
} elseif (!$local && !$remote) {
throw new InvalidArgumentException('You should a least set one argument to true');
}
try {
$result = $this->repository->run('branch', $arguments);
} catch (ProcessException $e) {
return [];
}
if (!$result) {
return [];
}
$branchesName = explode("\n", trim(str_replace('*', '', $result)));
$branchesName = array_filter($branchesName, function ($v) {
return false === StringHelper::strpos($v, '->');
});
$branchesName = array_map('trim', $branchesName);
$references = $this->repository->getReferences();
$branches = [];
foreach ($branchesName as $branchName) {
if (false === $local) {
$branches[] = $references->getRemoteBranch($branchName);
} elseif (0 === StringHelper::strrpos($branchName, 'remotes/')) {
$branches[] = $references->getRemoteBranch(str_replace('remotes/', '', $branchName));
} else {
$branches[] = $references->getBranch($branchName);
}
}
return $branches;
}
public function getAuthorName()
{
return $this->getData('authorName');
}
public function getAuthorEmail()
{
return $this->getData('authorEmail');
}
public function getAuthorDate()
{
return $this->getData('authorDate');
}
public function getCommitterName()
{
return $this->getData('committerName');
}
public function getCommitterEmail()
{
return $this->getData('committerEmail');
}
public function getCommitterDate()
{
return $this->getData('committerDate');
}
public function getMessage()
{
return $this->getData('message');
}
public function getSubjectMessage()
{
return $this->getData('subjectMessage');
}
public function getBodyMessage()
{
return $this->getData('bodyMessage');
}
public function getCommit()
{
return $this;
}
private function getData($name)
{
if (isset($this->data[$name])) {
return $this->data[$name];
}
if ($name === 'shortHash') {
$this->data['shortHash'] = trim($this->repository->run('log', ['--abbrev-commit', '--format=%h', '-n', 1, $this->revision]));
return $this->data['shortHash'];
}
if ($name === 'tree') {
$this->data['tree'] = $this->repository->getTree($this->getData('treeHash'));
return $this->data['tree'];
}
if ($name === 'subjectMessage') {
$lines = explode("\n", $this->getData('message'));
$this->data['subjectMessage'] = reset($lines);
return $this->data['subjectMessage'];
}
if ($name === 'bodyMessage') {
$message = $this->getData('message');
$lines = explode("\n", $message);
array_shift($lines);
array_shift($lines);
$this->data['bodyMessage'] = implode("\n", $lines);
return $this->data['bodyMessage'];
}
$parser = new Parser\CommitParser();
try {
$result = $this->repository->run('cat-file', ['commit', $this->revision]);
} catch (ProcessException $e) {
throw new ReferenceNotFoundException(sprintf('Can not find reference "%s"', $this->revision));
}
$parser->parse($result);
$this->data['treeHash'] = $parser->tree;
$this->data['parentHashes'] = $parser->parents;
$this->data['authorName'] = $parser->authorName;
$this->data['authorEmail'] = $parser->authorEmail;
$this->data['authorDate'] = $parser->authorDate;
$this->data['committerName'] = $parser->committerName;
$this->data['committerEmail'] = $parser->committerEmail;
$this->data['committerDate'] = $parser->committerDate;
$this->data['message'] = $parser->message;
if (!isset($this->data[$name])) {
throw new \InvalidArgumentException(sprintf('No data named "%s" in Commit.', $name));
}
return $this->data[$name];
}
}