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