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