<?php
namespace Gitonomy\Git;
use Gitonomy\Git\Exception\ReferenceNotFoundException;
use Gitonomy\Git\Exception\RuntimeException;
use Gitonomy\Git\Reference\Branch;
use Gitonomy\Git\Reference\Stash;
use Gitonomy\Git\Reference\Tag;
class ReferenceBag implements \Countable, \IteratorAggregate
{
protected $repository;
protected $references;
protected $tags;
protected $branches;
protected $initialized = false;
public function __construct($repository)
{
$this->repository = $repository;
$this->references = [];
$this->tags = [];
$this->branches = [];
}
public function get($fullname)
{
$this->initialize();
if (!isset($this->references[$fullname])) {
throw new ReferenceNotFoundException($fullname);
}
return $this->references[$fullname];
}
public function has($fullname)
{
$this->initialize();
return isset($this->references[$fullname]);
}
public function update(Reference $reference)
{
$fullname = $reference->getFullname();
$this->initialize();
$this->repository->run('update-ref', [$fullname, $reference->getCommitHash()]);
$this->references[$fullname] = $reference;
return $reference;
}
public function createBranch($name, $commitHash)
{
$branch = new Branch($this->repository, 'refs/heads/'.$name, $commitHash);
return $this->update($branch);
}
public function createTag($name, $commitHash)
{
$tag = new Tag($this->repository, 'refs/tags/'.$name, $commitHash);
return $this->update($tag);
}
public function delete($fullname)
{
$this->repository->run('update-ref', ['-d', $fullname]);
unset($this->references[$fullname]);
}
public function hasBranches()
{
$this->initialize();
return count($this->branches) > 0;
}
public function hasBranch($name)
{
return $this->has('refs/heads/'.$name);
}
public function hasRemoteBranch($name)
{
return $this->has('refs/remotes/'.$name);
}
public function hasTag($name)
{
return $this->has('refs/tags/'.$name);
}
public function getFirstBranch()
{
$this->initialize();
reset($this->branches);
return current($this->references);
}
public function resolveTags($hash)
{
$this->initialize();
if ($hash instanceof Commit) {
$hash = $hash->getHash();
}
$tags = [];
foreach ($this->references as $reference) {
if ($reference instanceof Reference\Tag && $reference->getCommitHash() === $hash) {
$tags[] = $reference;
}
}
return $tags;
}
public function resolveBranches($hash)
{
$this->initialize();
if ($hash instanceof Commit) {
$hash = $hash->getHash();
}
$branches = [];
foreach ($this->references as $reference) {
if ($reference instanceof Reference\Branch && $reference->getCommitHash() === $hash) {
$branches[] = $reference;
}
}
return $branches;
}
public function resolve($hash)
{
$this->initialize();
if ($hash instanceof Commit) {
$hash = $hash->getHash();
}
$result = [];
foreach ($this->references as $k => $reference) {
if ($reference->getCommitHash() === $hash) {
$result[] = $reference;
}
}
return $result;
}
public function getTags()
{
$this->initialize();
return $this->tags;
}
public function getBranches()
{
$this->initialize();
$result = [];
foreach ($this->references as $reference) {
if ($reference instanceof Reference\Branch) {
$result[] = $reference;
}
}
return $result;
}
public function getLocalBranches()
{
$result = [];
foreach ($this->getBranches() as $branch) {
if ($branch->isLocal()) {
$result[] = $branch;
}
}
return $result;
}
public function getRemoteBranches()
{
$result = [];
foreach ($this->getBranches() as $branch) {
if ($branch->isRemote()) {
$result[] = $branch;
}
}
return $result;
}
public function getAll()
{
$this->initialize();
return $this->references;
}
public function getTag($name)
{
$this->initialize();
return $this->get('refs/tags/'.$name);
}
public function getBranch($name)
{
$this->initialize();
return $this->get('refs/heads/'.$name);
}
public function getRemoteBranch($name)
{
$this->initialize();
return $this->get('refs/remotes/'.$name);
}
protected function initialize()
{
if (true === $this->initialized) {
return;
}
$this->initialized = true;
try {
$parser = new Parser\ReferenceParser();
$output = $this->repository->run('show-ref');
} catch (RuntimeException $e) {
$output = null;
}
$parser->parse($output);
foreach ($parser->references as $row) {
list($commitHash, $fullname) = $row;
if (preg_match('#^refs/(heads|remotes)/(.*)$#', $fullname)) {
if (preg_match('#.*HEAD$#', $fullname)) {
continue;
}
$reference = new Branch($this->repository, $fullname, $commitHash);
$this->references[$fullname] = $reference;
$this->branches[] = $reference;
} elseif (preg_match('#^refs/tags/(.*)$#', $fullname)) {
$reference = new Tag($this->repository, $fullname, $commitHash);
$this->references[$fullname] = $reference;
$this->tags[] = $reference;
} elseif ($fullname === 'refs/stash') {
$reference = new Stash($this->repository, $fullname, $commitHash);
$this->references[$fullname] = $reference;
}
}
}
public function count()
{
$this->initialize();
return count($this->references);
}
public function getIterator()
{
$this->initialize();
return new \ArrayIterator($this->references);
}
}