using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using LibGit2Sharp.Core; using LibGit2Sharp.Core.Handles; namespace LibGit2Sharp { /// /// Provides helper overloads to a . /// public static class ReferenceCollectionExtensions { private enum RefState { Exists, DoesNotExistButLooksValid, DoesNotLookValid, } private static RefState TryResolveReference(out Reference reference, ReferenceCollection refsColl, string canonicalName) { if (!Reference.IsValidName(canonicalName)) { reference = null; return RefState.DoesNotLookValid; } reference = refsColl[canonicalName]; return reference != null ? RefState.Exists : RefState.DoesNotExistButLooksValid; } /// /// Creates a direct or symbolic reference with the specified name and target /// /// The being worked with. /// The name of the reference to create. /// The target which can be either the canonical name of a reference or a revparse spec. /// The optional message to log in the when adding the /// A new . public static Reference Add(this ReferenceCollection refsColl, string name, string canonicalRefNameOrObjectish, string logMessage) { return refsColl.Add(name, canonicalRefNameOrObjectish, logMessage, false); } /// /// Creates a direct or symbolic reference with the specified name and target /// /// The being worked with. /// The name of the reference to create. /// The target which can be either the canonical name of a reference or a revparse spec. /// The optional message to log in the when adding the /// True to allow silent overwriting a potentially existing reference, false otherwise. /// A new . public static Reference Add(this ReferenceCollection refsColl, string name, string canonicalRefNameOrObjectish, string logMessage, bool allowOverwrite) { Ensure.ArgumentNotNullOrEmptyString(name, "name"); Ensure.ArgumentNotNullOrEmptyString(canonicalRefNameOrObjectish, "canonicalRefNameOrObjectish"); Reference reference; RefState refState = TryResolveReference(out reference, refsColl, canonicalRefNameOrObjectish); var gitObject = refsColl.repo.Lookup(canonicalRefNameOrObjectish, GitObjectType.Any, LookUpOptions.None); if (refState == RefState.Exists) { return refsColl.Add(name, reference, logMessage, allowOverwrite); } if (refState == RefState.DoesNotExistButLooksValid && gitObject == null) { using (ReferenceSafeHandle handle = Proxy.git_reference_symbolic_create(refsColl.repo.Handle, name, canonicalRefNameOrObjectish, allowOverwrite, logMessage)) { return Reference.BuildFromPtr(handle, refsColl.repo); } } Ensure.GitObjectIsNotNull(gitObject, canonicalRefNameOrObjectish); if (logMessage == null) { logMessage = string.Format(CultureInfo.InvariantCulture, "{0}: Created from {1}", name.LooksLikeLocalBranch() ? "branch" : "reference", canonicalRefNameOrObjectish); } refsColl.EnsureHasLog(name); return refsColl.Add(name, gitObject.Id, logMessage, allowOverwrite); } /// /// Creates a direct or symbolic reference with the specified name and target /// /// The being worked with. /// The name of the reference to create. /// The target which can be either the canonical name of a reference or a revparse spec. /// A new . public static Reference Add(this ReferenceCollection refsColl, string name, string canonicalRefNameOrObjectish) { return Add(refsColl, name, canonicalRefNameOrObjectish, null, false); } /// /// Creates a direct or symbolic reference with the specified name and target /// /// The being worked with. /// The name of the reference to create. /// The target which can be either the canonical name of a reference or a revparse spec. /// True to allow silent overwriting a potentially existing reference, false otherwise. /// A new . public static Reference Add(this ReferenceCollection refsColl, string name, string canonicalRefNameOrObjectish, bool allowOverwrite) { return Add(refsColl, name, canonicalRefNameOrObjectish, null, allowOverwrite); } /// /// Updates the target of a direct reference. /// /// The being worked with. /// The direct reference which target should be updated. /// The revparse spec of the target. /// The optional message to log in the /// A new . public static Reference UpdateTarget(this ReferenceCollection refsColl, Reference directRef, string objectish, string logMessage) { Ensure.ArgumentNotNull(directRef, "directRef"); Ensure.ArgumentNotNull(objectish, "objectish"); GitObject target = refsColl.repo.Lookup(objectish); Ensure.GitObjectIsNotNull(target, objectish); return refsColl.UpdateTarget(directRef, target.Id, logMessage); } /// /// Updates the target of a direct reference /// /// The being worked with. /// The direct reference which target should be updated. /// The revparse spec of the target. /// A new . public static Reference UpdateTarget(this ReferenceCollection refsColl, Reference directRef, string objectish) { return UpdateTarget(refsColl, directRef, objectish, null); } /// /// Rename an existing reference with a new name /// /// The canonical name of the reference to rename. /// The new canonical name. /// The being worked with. /// A new . public static Reference Rename(this ReferenceCollection refsColl, string currentName, string newName) { return refsColl.Rename(currentName, newName, null, false); } /// /// 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. /// The being worked with. /// A new . public static Reference Rename(this ReferenceCollection refsColl, string currentName, string newName, bool allowOverwrite) { return refsColl.Rename(currentName, newName, null, allowOverwrite); } /// /// Rename an existing reference with a new name /// /// The canonical name of the reference to rename. /// The new canonical name. /// The optional message to log in the /// The being worked with. /// A new . public static Reference Rename(this ReferenceCollection refsColl, string currentName, string newName, string logMessage) { return refsColl.Rename(currentName, newName, logMessage, false); } /// /// Rename an existing reference with a new name /// /// The canonical name of the reference to rename. /// The new canonical name. /// The optional message to log in the /// True to allow silent overwriting a potentially existing reference, false otherwise. /// The being worked with. /// A new . public static Reference Rename(this ReferenceCollection refsColl, string currentName, string newName, string logMessage, bool allowOverwrite) { Ensure.ArgumentNotNullOrEmptyString(currentName, "currentName"); Reference reference = refsColl[currentName]; if (reference == null) { throw new LibGit2SharpException( string.Format(CultureInfo.InvariantCulture, "Reference '{0}' doesn't exist. One cannot move a non existing reference.", currentName)); } return refsColl.Rename(reference, newName, logMessage, allowOverwrite); } /// /// Updates the target of a reference /// /// The being worked with. /// The canonical name of the reference. /// The target which can be either the canonical name of a reference or a revparse spec. /// The optional message to log in the of the reference. /// A new . public static Reference UpdateTarget(this ReferenceCollection refsColl, string name, string canonicalRefNameOrObjectish, string logMessage) { Ensure.ArgumentNotNullOrEmptyString(name, "name"); Ensure.ArgumentNotNullOrEmptyString(canonicalRefNameOrObjectish, "canonicalRefNameOrObjectish"); if (name == "HEAD") { return refsColl.UpdateHeadTarget(canonicalRefNameOrObjectish, logMessage); } Reference reference = refsColl[name]; var directReference = reference as DirectReference; if (directReference != null) { return refsColl.UpdateTarget(directReference, canonicalRefNameOrObjectish, logMessage); } var symbolicReference = reference as SymbolicReference; if (symbolicReference != null) { Reference targetRef; RefState refState = TryResolveReference(out targetRef, refsColl, canonicalRefNameOrObjectish); if (refState == RefState.DoesNotLookValid) { throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "The reference specified by {0} is a Symbolic reference, you must provide a reference canonical name as the target.", name), "canonicalRefNameOrObjectish"); } return refsColl.UpdateTarget(symbolicReference, targetRef, logMessage); } throw new LibGit2SharpException(string.Format(CultureInfo.InvariantCulture, "Reference '{0}' has an unexpected type ('{1}').", name, reference.GetType())); } /// /// Updates the target of a reference /// /// The being worked with. /// The canonical name of the reference. /// The target which can be either the canonical name of a reference or a revparse spec. /// A new . public static Reference UpdateTarget(this ReferenceCollection refsColl, string name, string canonicalRefNameOrObjectish) { return UpdateTarget(refsColl, name, canonicalRefNameOrObjectish, null); } /// /// Delete a reference with the specified name /// /// The being worked with. /// The canonical name of the reference to delete. public static void Remove(this ReferenceCollection refsColl, string name) { Ensure.ArgumentNotNullOrEmptyString(name, "name"); Reference reference = refsColl[name]; if (reference == null) { return; } refsColl.Remove(reference); } /// /// Find the s among /// that can reach at least one in the specified . /// /// The being worked with. /// The set of s to examine. /// The set of s that are interesting. /// A subset of that can reach at least one within . public static IEnumerable ReachableFrom( this ReferenceCollection refsColl, IEnumerable refSubset, IEnumerable targets) { Ensure.ArgumentNotNull(refSubset, "refSubset"); Ensure.ArgumentNotNull(targets, "targets"); var refs = new List(refSubset); if (refs.Count == 0) { return Enumerable.Empty(); } List targetsSet = targets.Select(c => c.Id).Distinct().ToList(); if (targetsSet.Count == 0) { return Enumerable.Empty(); } var result = new List(); foreach (var reference in refs) { var peeledTargetCommit = reference .ResolveToDirectReference() .Target.DereferenceToCommit(false); if (peeledTargetCommit == null) { continue; } var commitId = peeledTargetCommit.Id; foreach (var potentialAncestorId in targetsSet) { if (potentialAncestorId == commitId) { result.Add(reference); break; } if (Proxy.git_graph_descendant_of(refsColl.repo.Handle, commitId, potentialAncestorId)) { result.Add(reference); break; } } } return result; } /// /// Find the s /// that can reach at least one in the specified . /// /// The being worked with. /// The set of s that are interesting. /// The list of that can reach at least one within . public static IEnumerable ReachableFrom( this ReferenceCollection refsColl, IEnumerable targets) { return ReachableFrom(refsColl, refsColl, targets); } } }