using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using LibGit2Sharp.Core;
namespace LibGit2Sharp
{
///
/// The Collection of references in a
///
public class ReferenceCollection : IEnumerable
{
private readonly Repository repo;
///
/// Initializes a new instance of the class.
///
/// The repo.
internal ReferenceCollection(Repository repo)
{
this.repo = repo;
}
///
/// Gets the with the specified name.
///
/// The canonical name of the reference to resolve.
/// The resolved if it has been found, null otherwise.
public Reference this[string name]
{
get { return Resolve(name); }
}
#region IEnumerable Members
///
/// Returns an enumerator that iterates through the collection.
///
/// An object that can be used to iterate through the collection.
public IEnumerator GetEnumerator()
{
return Libgit2UnsafeHelper
.ListAllReferenceNames(repo.Handle, GitReferenceType.ListAll)
.Select(n => this[n])
.GetEnumerator();
}
///
/// Returns an enumerator that iterates through the collection.
///
/// An object that can be used to iterate through the collection.
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
///
/// Creates a direct or symbolic reference with the specified name and target
///
/// The name of the reference to create.
/// The target which can be either a sha or the canonical name of another reference.
/// True to allow silent overwriting a potentially existing reference, false otherwise.
/// A new .
public Reference Create(string name, string target, bool allowOverwrite = false)
{
Ensure.ArgumentNotNullOrEmptyString(name, "name");
Ensure.ArgumentNotNullOrEmptyString(target, "target");
ObjectId id;
IntPtr reference;
int res;
if (ObjectId.TryParse(target, out id))
{
res = CreateDirectReference(name, id, allowOverwrite, out reference);
}
else
{
res = CreateSymbolicReference(name, target, allowOverwrite, out reference);
}
Ensure.Success(res);
return Reference.BuildFromPtr(reference, repo);
}
private int CreateSymbolicReference(string name, string target, bool allowOverwrite, out IntPtr reference)
{
if (allowOverwrite)
{
return NativeMethods.git_reference_create_symbolic_f(out reference, repo.Handle, name, target);
}
return NativeMethods.git_reference_create_symbolic(out reference, repo.Handle, name, target);
}
private int CreateDirectReference(string name, ObjectId targetOid, bool allowOverwrite, out IntPtr reference)
{
if (targetOid is AbbreviatedObjectId) //TODO: This is hacky... :-/
{
var obj = repo.Lookup(targetOid);
if (obj == null)
{
Ensure.Success((int) GitErrorCode.GIT_ENOTFOUND);
}
targetOid = obj.Id;
}
GitOid oid = targetOid.Oid;
if (allowOverwrite)
{
return NativeMethods.git_reference_create_oid_f(out reference, repo.Handle, name, ref oid);
}
return NativeMethods.git_reference_create_oid(out reference, repo.Handle, name, ref oid);
}
///
/// Delete a reference with the specified name
///
/// The name of the reference to delete.
public void Delete(string name)
{
Ensure.ArgumentNotNullOrEmptyString(name, "name");
IntPtr reference = RetrieveReferencePtr(name);
int res = NativeMethods.git_reference_delete(reference);
Ensure.Success(res);
}
///
/// Rename an existing reference with a new name
///
/// The canonical name of the reference to rename.
/// The new canonical name.
/// True to allow silent overwriting a potentially existing reference, false otherwise.
///
public Reference Move(string currentName, string newName, bool allowOverwrite = false)
{
Ensure.ArgumentNotNullOrEmptyString(currentName, "currentName");
Ensure.ArgumentNotNullOrEmptyString(newName, "newName");
IntPtr referencePtr = RetrieveReferencePtr(currentName);
int res;
if (allowOverwrite)
{
res = NativeMethods.git_reference_rename_f(referencePtr, newName);
}
else
{
res = NativeMethods.git_reference_rename(referencePtr, newName);
}
Ensure.Success(res);
return Reference.BuildFromPtr(referencePtr, repo);
}
internal T Resolve(string name) where T : class
{
Ensure.ArgumentNotNullOrEmptyString(name, "name");
IntPtr reference = RetrieveReferencePtr(name, false);
return Reference.BuildFromPtr(reference, repo);
}
///
/// Updates the target on a reference.
///
/// The name of the reference.
/// The target which can be either a sha or the name of another reference.
public Reference UpdateTarget(string name, string target)
{
Ensure.ArgumentNotNullOrEmptyString(name, "name");
Ensure.ArgumentNotNullOrEmptyString(target, "target");
IntPtr reference = RetrieveReferencePtr(name);
int res;
ObjectId id;
bool isObjectIdentifier = ObjectId.TryParse(target, out id);
var type = NativeMethods.git_reference_type(reference);
switch (type)
{
case GitReferenceType.Oid:
if (!isObjectIdentifier) throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "The reference specified by {0} is an Oid reference, you must provide a sha as the target.", name), "target");
var oid = id.Oid;
res = NativeMethods.git_reference_set_oid(reference, ref oid);
break;
case GitReferenceType.Symbolic:
if (isObjectIdentifier) throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "The reference specified by {0} is an Symbolic reference, you must provide a symbol as the target.", name), "target");
res = NativeMethods.git_reference_set_target(reference, target);
break;
default:
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Reference '{0}' has an un unexpected type ('{1}').", name, Enum.GetName(typeof(GitReferenceType), type)));
}
Ensure.Success(res);
return Reference.BuildFromPtr(reference, repo);
}
private IntPtr RetrieveReferencePtr(string referenceName, bool shouldThrow = true)
{
IntPtr reference;
var res = NativeMethods.git_reference_lookup(out reference, repo.Handle, referenceName);
if (!shouldThrow && res == (int)GitErrorCode.GIT_ENOTFOUND)
{
return IntPtr.Zero;
}
Ensure.Success(res);
return reference;
}
}
}