using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using LibGit2Sharp.Core;
using LibGit2Sharp.Core.Handles;
namespace LibGit2Sharp
{
///
/// The Collection of references in a
///
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class ReferenceCollection : IEnumerable
{
internal readonly Repository repo;
///
/// Needed for mocking purposes.
///
protected ReferenceCollection()
{ }
///
/// 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 virtual 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 virtual IEnumerator GetEnumerator()
{
return Proxy.git_reference_list(repo.Handle)
.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 reference with the specified name and target
///
/// The canonical name of the reference to create.
/// Id of the target object.
/// True to allow silent overwriting a potentially existing reference, false otherwise.
/// The optional message to log in the when adding the
/// A new .
public virtual DirectReference Add(string name, ObjectId targetId, bool allowOverwrite = false, string logMessage = null)
{
Ensure.ArgumentNotNullOrEmptyString(name, "name");
Ensure.ArgumentNotNull(targetId, "targetId");
using (ReferenceSafeHandle handle = Proxy.git_reference_create(repo.Handle, name, targetId, allowOverwrite))
{
var newTarget = (DirectReference)Reference.BuildFromPtr(handle, repo);
LogReference(newTarget, targetId, logMessage);
return newTarget;
}
}
///
/// Creates a symbolic reference with the specified name and target
///
/// The canonical name of the reference to create.
/// The target reference.
/// True to allow silent overwriting a potentially existing reference, false otherwise.
/// The optional message to log in the when adding the
/// A new .
public virtual SymbolicReference Add(string name, Reference targetRef, bool allowOverwrite = false, string logMessage = null)
{
Ensure.ArgumentNotNullOrEmptyString(name, "name");
Ensure.ArgumentNotNull(targetRef, "targetRef");
using (ReferenceSafeHandle handle = Proxy.git_reference_symbolic_create(repo.Handle, name, targetRef.CanonicalName, allowOverwrite))
{
var newTarget = (SymbolicReference)Reference.BuildFromPtr(handle, repo);
LogReference(newTarget, targetRef, logMessage);
return newTarget;
}
}
///
/// Remove a reference from the repository
///
/// The reference to delete.
public virtual void Remove(Reference reference)
{
Ensure.ArgumentNotNull(reference, "reference");
using (ReferenceSafeHandle handle = RetrieveReferencePtr(reference.CanonicalName))
{
Proxy.git_reference_delete(handle);
}
}
///
/// Rename an existing reference with a new name
///
/// The reference to rename.
/// The new canonical name.
/// True to allow silent overwriting a potentially existing reference, false otherwise.
/// A new .
public virtual Reference Move(Reference reference, string newName, bool allowOverwrite = false)
{
Ensure.ArgumentNotNull(reference, "reference");
Ensure.ArgumentNotNullOrEmptyString(newName, "newName");
using (ReferenceSafeHandle handle = RetrieveReferencePtr(reference.CanonicalName))
{
using (ReferenceSafeHandle handle_out = Proxy.git_reference_rename(handle, newName, allowOverwrite))
{
return Reference.BuildFromPtr(handle_out, repo);
}
}
}
internal T Resolve(string name) where T : Reference
{
Ensure.ArgumentNotNullOrEmptyString(name, "name");
using (ReferenceSafeHandle referencePtr = RetrieveReferencePtr(name, false))
{
return referencePtr == null ? null : Reference.BuildFromPtr(referencePtr, repo);
}
}
///
/// Updates the target of a direct reference.
///
/// The direct reference which target should be updated.
/// The new target.
/// The optional message to log in the of the reference
/// A new .
public virtual Reference UpdateTarget(Reference directRef, ObjectId targetId, string logMessage = null)
{
Ensure.ArgumentNotNull(directRef, "directRef");
Ensure.ArgumentNotNull(targetId, "targetId");
Reference newTarget = UpdateTarget(directRef, targetId,
Proxy.git_reference_set_target);
LogReference(directRef, targetId, logMessage);
return newTarget;
}
///
/// Updates the target of a symbolic reference.
///
/// The symbolic reference which target should be updated.
/// The new target.
/// The optional message to log in the of the reference.
/// A new .
public virtual Reference UpdateTarget(Reference symbolicRef, Reference targetRef, string logMessage = null)
{
Ensure.ArgumentNotNull(symbolicRef, "symbolicRef");
Ensure.ArgumentNotNull(targetRef, "targetRef");
Reference newTarget = UpdateTarget(symbolicRef, targetRef,
(h, r) => Proxy.git_reference_symbolic_set_target(h, r.CanonicalName));
LogReference(symbolicRef, targetRef, logMessage);
return newTarget;
}
private Reference UpdateTarget(Reference reference, T target, Func setter)
{
if (reference.CanonicalName == "HEAD")
{
if (target is ObjectId)
{
return Add("HEAD", target as ObjectId, true);
}
if (target is DirectReference)
{
return Add("HEAD", target as DirectReference, true);
}
if (target is SymbolicReference)
{
return Add("HEAD", target as SymbolicReference, true);
}
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,
"'{0}' is not a valid target type.", typeof(T)));
}
using (ReferenceSafeHandle referencePtr = RetrieveReferencePtr(reference.CanonicalName))
{
using (ReferenceSafeHandle ref_out = setter(referencePtr, target))
{
return Reference.BuildFromPtr(ref_out, repo);
}
}
}
private void LogReference(Reference reference, Reference target, string logMessage)
{
var directReference = target.ResolveToDirectReference();
if (directReference == null)
{
return;
}
LogReference(reference, directReference.Target.Id, logMessage);
}
private void LogReference(Reference reference, ObjectId target, string logMessage)
{
if (string.IsNullOrEmpty(logMessage))
{
return;
}
repo.Refs.Log(reference).Append(target, logMessage);
}
internal ReferenceSafeHandle RetrieveReferencePtr(string referenceName, bool shouldThrowIfNotFound = true)
{
ReferenceSafeHandle reference = Proxy.git_reference_lookup(repo.Handle, referenceName, shouldThrowIfNotFound);
return reference;
}
///
/// Returns the list of references of the repository matching the specified .
///
/// The glob pattern the reference name should match.
/// A list of references, ready to be enumerated.
public virtual IEnumerable FromGlob(string pattern)
{
Ensure.ArgumentNotNullOrEmptyString(pattern, "pattern");
return Proxy.git_reference_foreach_glob(repo.Handle, pattern, Utf8Marshaler.FromNative)
.Select(n => this[n]);
}
///
/// Determines if the proposed reference name is well-formed.
///
///
/// - Top-level names must contain only capital letters and underscores,
/// and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
///
/// - Names prefixed with "refs/" can be almost anything. You must avoid
/// the characters '~', '^', ':', '\\', '?', '[', and '*', and the
/// sequences ".." and "@{" which have special meaning to revparse.
///
/// The name to be checked.
/// true is the name is valid; false otherwise.
public virtual bool IsValidName(string canonicalName)
{
return Proxy.git_reference_is_valid_name(canonicalName);
}
///
/// Shortcut to return the HEAD reference.
///
///
/// A if the HEAD is detached;
/// otherwise a .
///
public virtual Reference Head
{
get { return this["HEAD"]; }
}
private string DebuggerDisplay
{
get
{
return string.Format(CultureInfo.InvariantCulture,
"Count = {0}", this.Count());
}
}
///
/// Returns as a the reflog of the named
///
/// The canonical name of the reference
/// a , enumerable of
public virtual ReflogCollection Log(string canonicalName)
{
Ensure.ArgumentNotNullOrEmptyString(canonicalName, "canonicalName");
return new ReflogCollection(repo, canonicalName);
}
///
/// Returns as a the reflog of the
///
/// The reference
/// a , enumerable of
public virtual ReflogCollection Log(Reference reference)
{
Ensure.ArgumentNotNull(reference, "reference");
return new ReflogCollection(repo, reference.CanonicalName);
}
}
}