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; } } }