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.
/// The optional message to log in the when adding the
/// True to allow silent overwriting a potentially existing reference, false otherwise.
/// A new .
public virtual DirectReference Add(string name, ObjectId targetId, string logMessage, bool allowOverwrite = false)
{
Ensure.ArgumentNotNullOrEmptyString(name, "name");
Ensure.ArgumentNotNull(targetId, "targetId");
using (ReferenceSafeHandle handle = Proxy.git_reference_create(repo.Handle, name, targetId, allowOverwrite, logMessage))
{
return (DirectReference)Reference.BuildFromPtr(handle, repo);
}
}
///
/// 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.
/// A new .
public virtual DirectReference Add(string name, ObjectId targetId, bool allowOverwrite = false)
{
return Add(name, targetId, null, allowOverwrite);
}
///
/// Creates a symbolic reference with the specified name and target
///
/// The canonical name of the reference to create.
/// The target reference.
/// The optional message to log in the when adding the
/// True to allow silent overwriting a potentially existing reference, false otherwise.
/// A new .
public virtual SymbolicReference Add(string name, Reference targetRef, string logMessage, bool allowOverwrite = false)
{
Ensure.ArgumentNotNullOrEmptyString(name, "name");
Ensure.ArgumentNotNull(targetRef, "targetRef");
using (ReferenceSafeHandle handle = Proxy.git_reference_symbolic_create(repo.Handle, name, targetRef.CanonicalName,
allowOverwrite, logMessage))
{
return (SymbolicReference)Reference.BuildFromPtr(handle, repo);
}
}
///
/// 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.
/// A new .
public virtual SymbolicReference Add(string name, Reference targetRef, bool allowOverwrite = false)
{
return Add(name, targetRef, null, allowOverwrite);
}
///
/// Remove a reference from the repository
///
/// The reference to delete.
public virtual void Remove(Reference reference)
{
Ensure.ArgumentNotNull(reference, "reference");
Proxy.git_reference_remove(repo.Handle, reference.CanonicalName);
}
///
/// Rename an existing reference with a new name, and update the reflog
///
/// The reference to rename.
/// The new canonical name.
/// Message added to the reflog.
/// True to allow silent overwriting a potentially existing reference, false otherwise.
/// A new .
public virtual Reference Rename(Reference reference, string newName, string logMessage = null, bool allowOverwrite = false)
{
Ensure.ArgumentNotNull(reference, "reference");
Ensure.ArgumentNotNullOrEmptyString(newName, "newName");
if (logMessage == null)
{
logMessage = string.Format(CultureInfo.InvariantCulture, "{0}: renamed {1} to {2}",
reference.IsLocalBranch() ? "branch" : "reference", reference.CanonicalName, newName);
}
using (ReferenceSafeHandle referencePtr = RetrieveReferencePtr(reference.CanonicalName))
using (ReferenceSafeHandle handle = Proxy.git_reference_rename(referencePtr, newName, allowOverwrite, logMessage))
{
return Reference.BuildFromPtr(handle, repo);
}
}
///
/// 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 Rename(Reference reference, string newName, bool allowOverwrite = false)
{
return Rename(reference, newName, null, allowOverwrite);
}
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)
{
Ensure.ArgumentNotNull(directRef, "directRef");
Ensure.ArgumentNotNull(targetId, "targetId");
if (directRef.CanonicalName == "HEAD")
{
return UpdateHeadTarget(targetId, logMessage);
}
return UpdateDirectReferenceTarget(directRef, targetId, logMessage);
}
private Reference UpdateDirectReferenceTarget(Reference directRef, ObjectId targetId, string logMessage)
{
using (ReferenceSafeHandle referencePtr = RetrieveReferencePtr(directRef.CanonicalName))
using (ReferenceSafeHandle handle = Proxy.git_reference_set_target(referencePtr, targetId, logMessage))
{
return Reference.BuildFromPtr(handle, repo);
}
}
///
/// Updates the target of a direct reference
///
/// The direct reference which target should be updated.
/// The new target.
/// A new .
public virtual Reference UpdateTarget(Reference directRef, ObjectId targetId)
{
return UpdateTarget(directRef, targetId, null);
}
///
/// 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)
{
Ensure.ArgumentNotNull(symbolicRef, "symbolicRef");
Ensure.ArgumentNotNull(targetRef, "targetRef");
if (symbolicRef.CanonicalName == "HEAD")
{
return UpdateHeadTarget(targetRef, logMessage);
}
return UpdateSymbolicRefenceTarget(symbolicRef, targetRef, logMessage);
}
private Reference UpdateSymbolicRefenceTarget(Reference symbolicRef, Reference targetRef, string logMessage)
{
using (ReferenceSafeHandle referencePtr = RetrieveReferencePtr(symbolicRef.CanonicalName))
using (ReferenceSafeHandle handle = Proxy.git_reference_symbolic_set_target(referencePtr, targetRef.CanonicalName, logMessage))
{
return Reference.BuildFromPtr(handle, repo);
}
}
///
/// Updates the target of a symbolic reference
///
/// The symbolic reference which target should be updated.
/// The new target.
/// A new .
public virtual Reference UpdateTarget(Reference symbolicRef, Reference targetRef)
{
return UpdateTarget(symbolicRef, targetRef, null);
}
internal Reference MoveHeadTarget(T target)
{
if (target is ObjectId)
{
Proxy.git_repository_set_head_detached(repo.Handle, target as ObjectId);
}
else if (target is DirectReference || target is SymbolicReference)
{
Proxy.git_repository_set_head(repo.Handle, (target as Reference).CanonicalName);
}
else if (target is string)
{
var targetIdentifier = target as string;
if (Reference.IsValidName(targetIdentifier) && targetIdentifier.LooksLikeLocalBranch())
{
Proxy.git_repository_set_head(repo.Handle, targetIdentifier);
}
else
{
using (var annotatedCommit = Proxy.git_annotated_commit_from_revspec(repo.Handle, targetIdentifier))
{
Proxy.git_repository_set_head_detached_from_annotated(repo.Handle, annotatedCommit);
}
}
}
else
{
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,
"'{0}' is not a valid target type.", typeof(T)));
}
return repo.Refs.Head;
}
internal Reference UpdateHeadTarget(ObjectId target, string logMessage)
{
Add("HEAD", target, logMessage, true);
return repo.Refs.Head;
}
internal Reference UpdateHeadTarget(Reference target, string logMessage)
{
Ensure.ArgumentConformsTo(target, r => (r is DirectReference || r is SymbolicReference), "target");
Add("HEAD", target, logMessage, true);
return repo.Refs.Head;
}
internal Reference UpdateHeadTarget(string target, string logMessage)
{
this.Add("HEAD", target, logMessage, true);
return repo.Refs.Head;
}
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, LaxUtf8Marshaler.FromNative)
.Select(n => this[n]);
}
///
/// 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);
}
///
/// Rewrite some of the commits in the repository and all the references that can reach them.
///
/// Specifies behavior for this rewrite.
/// The objects to rewrite.
public virtual void RewriteHistory(RewriteHistoryOptions options, params Commit[] commitsToRewrite)
{
Ensure.ArgumentNotNull(commitsToRewrite, "commitsToRewrite");
RewriteHistory(options, commitsToRewrite.AsEnumerable());
}
///
/// Rewrite some of the commits in the repository and all the references that can reach them.
///
/// Specifies behavior for this rewrite.
/// The objects to rewrite.
public virtual void RewriteHistory(RewriteHistoryOptions options, IEnumerable commitsToRewrite)
{
Ensure.ArgumentNotNull(commitsToRewrite, "commitsToRewrite");
Ensure.ArgumentNotNull(options, "options");
Ensure.ArgumentNotNullOrEmptyString(options.BackupRefsNamespace, "options.BackupRefsNamespace");
IList originalRefs = this.ToList();
if (originalRefs.Count == 0)
{
// Nothing to do
return;
}
var historyRewriter = new HistoryRewriter(repo, commitsToRewrite, options);
historyRewriter.Execute();
}
///
/// Ensure that a reflog exists for the given canonical name
///
/// Canonical name of the reference
internal void EnsureHasLog(string canonicalName)
{
Proxy.git_reference_ensure_log(repo.Handle, canonicalName);
}
}
}