@@ -66,6 +66,106 @@ IEnumerator IEnumerable.GetEnumerator()
6666
6767 #endregion
6868
69+ /// <summary>
70+ /// Creates a direct or symbolic reference with the specified name and target
71+ /// </summary>
72+ /// <param name="name">The name of the reference to create.</param>
73+ /// <param name="canonicalRefNameOrObjectish">The target which can be either the canonical name of a reference or a revparse spec.</param>
74+ /// <param name="logMessage">The optional message to log in the <see cref="ReflogCollection"/> when adding the <see cref="Reference"/></param>
75+ /// <returns>A new <see cref="Reference"/>.</returns>
76+ public virtual Reference Add ( string name , string canonicalRefNameOrObjectish ,
77+ string logMessage )
78+ {
79+ return Add ( name , canonicalRefNameOrObjectish , logMessage , false ) ;
80+ }
81+
82+ private enum RefState
83+ {
84+ Exists ,
85+ DoesNotExistButLooksValid ,
86+ DoesNotLookValid ,
87+ }
88+
89+ private static RefState TryResolveReference ( out Reference reference , ReferenceCollection refsColl , string canonicalName )
90+ {
91+ if ( ! Reference . IsValidName ( canonicalName ) )
92+ {
93+ reference = null ;
94+ return RefState . DoesNotLookValid ;
95+ }
96+
97+ reference = refsColl [ canonicalName ] ;
98+
99+ return reference != null ? RefState . Exists : RefState . DoesNotExistButLooksValid ;
100+ }
101+
102+ /// <summary>
103+ /// Creates a direct or symbolic reference with the specified name and target
104+ /// </summary>
105+ /// <param name="name">The name of the reference to create.</param>
106+ /// <param name="canonicalRefNameOrObjectish">The target which can be either the canonical name of a reference or a revparse spec.</param>
107+ /// <param name="logMessage">The optional message to log in the <see cref="ReflogCollection"/> when adding the <see cref="Reference"/></param>
108+ /// <param name="allowOverwrite">True to allow silent overwriting a potentially existing reference, false otherwise.</param>
109+ /// <returns>A new <see cref="Reference"/>.</returns>
110+ public virtual Reference Add ( string name , string canonicalRefNameOrObjectish , string logMessage , bool allowOverwrite )
111+ {
112+ Ensure . ArgumentNotNullOrEmptyString ( name , "name" ) ;
113+ Ensure . ArgumentNotNullOrEmptyString ( canonicalRefNameOrObjectish , "canonicalRefNameOrObjectish" ) ;
114+
115+ Reference reference ;
116+ RefState refState = TryResolveReference ( out reference , this , canonicalRefNameOrObjectish ) ;
117+
118+ var gitObject = repo . Lookup ( canonicalRefNameOrObjectish , GitObjectType . Any , LookUpOptions . None ) ;
119+
120+ if ( refState == RefState . Exists )
121+ {
122+ return Add ( name , reference , logMessage , allowOverwrite ) ;
123+ }
124+
125+ if ( refState == RefState . DoesNotExistButLooksValid && gitObject == null )
126+ {
127+ using ( ReferenceSafeHandle handle = Proxy . git_reference_symbolic_create ( repo . Handle , name , canonicalRefNameOrObjectish , allowOverwrite ,
128+ logMessage ) )
129+ {
130+ return Reference . BuildFromPtr < Reference > ( handle , repo ) ;
131+ }
132+ }
133+
134+ Ensure . GitObjectIsNotNull ( gitObject , canonicalRefNameOrObjectish ) ;
135+
136+ if ( logMessage == null )
137+ {
138+ logMessage = string . Format ( CultureInfo . InvariantCulture , "{0}: Created from {1}" ,
139+ name . LooksLikeLocalBranch ( ) ? "branch" : "reference" , canonicalRefNameOrObjectish ) ;
140+ }
141+
142+ EnsureHasLog ( name ) ;
143+ return Add ( name , gitObject . Id , logMessage , allowOverwrite ) ;
144+ }
145+
146+
147+ /// <summary>
148+ /// Creates a direct or symbolic reference with the specified name and target
149+ /// </summary>
150+ /// <param name="name">The name of the reference to create.</param>
151+ /// <param name="canonicalRefNameOrObjectish">The target which can be either the canonical name of a reference or a revparse spec.</param>
152+ /// <returns>A new <see cref="Reference"/>.</returns>
153+ public virtual Reference Add ( string name , string canonicalRefNameOrObjectish )
154+ {
155+ return Add ( name , canonicalRefNameOrObjectish , null , false ) ;
156+ }
157+
158+ /// <summary>
159+ /// Creates a direct or symbolic reference with the specified name and target
160+ /// </summary>
161+ /// <param name="name">The name of the reference to create.</param>
162+ /// <param name="canonicalRefNameOrObjectish">The target which can be either the canonical name of a reference or a revparse spec.</param>
163+ /// <param name="allowOverwrite">True to allow silent overwriting a potentially existing reference, false otherwise.</param>
164+ /// <returns>A new <see cref="Reference"/>.</returns>
165+ public virtual Reference Add ( string name , string canonicalRefNameOrObjectish , bool allowOverwrite )
166+ {
167+ return Add ( name , canonicalRefNameOrObjectish , null , allowOverwrite ) ;
168+ }
69169 /// <summary>
70170 /// Creates a direct reference with the specified name and target
71171 /// </summary>
@@ -175,6 +275,24 @@ public virtual SymbolicReference Add(string name, Reference targetRef, bool allo
175275 return Add ( name , targetRef , null , allowOverwrite ) ;
176276 }
177277
278+ /// <summary>
279+ /// Remove a reference with the specified name
280+ /// </summary>
281+ /// <param name="name">The canonical name of the reference to delete.</param>
282+ public virtual void Remove ( string name )
283+ {
284+ Ensure . ArgumentNotNullOrEmptyString ( name , "name" ) ;
285+
286+ Reference reference = this [ name ] ;
287+
288+ if ( reference == null )
289+ {
290+ return ;
291+ }
292+
293+ Remove ( reference ) ;
294+ }
295+
178296 /// <summary>
179297 /// Remove a reference from the repository
180298 /// </summary>
@@ -224,6 +342,68 @@ public virtual Reference Rename(Reference reference, string newName, string logM
224342 }
225343 }
226344
345+ /// <summary>
346+ /// Rename an existing reference with a new name
347+ /// </summary>
348+ /// <param name="currentName">The canonical name of the reference to rename.</param>
349+ /// <param name="newName">The new canonical name.</param>
350+ /// <returns>A new <see cref="Reference"/>.</returns>
351+ public virtual Reference Rename ( string currentName , string newName )
352+ {
353+ return Rename ( currentName , newName , null , false ) ;
354+ }
355+
356+ /// <summary>
357+ /// Rename an existing reference with a new name
358+ /// </summary>
359+ /// <param name="currentName">The canonical name of the reference to rename.</param>
360+ /// <param name="newName">The new canonical name.</param>
361+ /// <param name="allowOverwrite">True to allow silent overwriting a potentially existing reference, false otherwise.</param>
362+ /// <returns>A new <see cref="Reference"/>.</returns>
363+ public virtual Reference Rename ( string currentName , string newName ,
364+ bool allowOverwrite )
365+ {
366+ return Rename ( currentName , newName , null , allowOverwrite ) ;
367+ }
368+
369+ /// <summary>
370+ /// Rename an existing reference with a new name
371+ /// </summary>
372+ /// <param name="currentName">The canonical name of the reference to rename.</param>
373+ /// <param name="newName">The new canonical name.</param>
374+ /// <param name="logMessage">The optional message to log in the <see cref="ReflogCollection"/></param>
375+ /// <returns>A new <see cref="Reference"/>.</returns>
376+ public virtual Reference Rename ( string currentName , string newName ,
377+ string logMessage )
378+ {
379+ return Rename ( currentName , newName , logMessage , false ) ;
380+ }
381+
382+ /// <summary>
383+ /// Rename an existing reference with a new name
384+ /// </summary>
385+ /// <param name="currentName">The canonical name of the reference to rename.</param>
386+ /// <param name="newName">The new canonical name.</param>
387+ /// <param name="logMessage">The optional message to log in the <see cref="ReflogCollection"/></param>
388+ /// <param name="allowOverwrite">True to allow silent overwriting a potentially existing reference, false otherwise.</param>
389+ /// <returns>A new <see cref="Reference"/>.</returns>
390+ public virtual Reference Rename ( string currentName , string newName ,
391+ string logMessage , bool allowOverwrite )
392+ {
393+ Ensure . ArgumentNotNullOrEmptyString ( currentName , "currentName" ) ;
394+
395+ Reference reference = this [ currentName ] ;
396+
397+ if ( reference == null )
398+ {
399+ throw new LibGit2SharpException (
400+ string . Format ( CultureInfo . InvariantCulture ,
401+ "Reference '{0}' doesn't exist. One cannot move a non existing reference." , currentName ) ) ;
402+ }
403+
404+ return Rename ( reference , newName , logMessage , allowOverwrite ) ;
405+ }
406+
227407 /// <summary>
228408 /// Rename an existing reference with a new name
229409 /// </summary>
@@ -286,6 +466,93 @@ private Reference UpdateDirectReferenceTarget(Reference directRef, ObjectId targ
286466 }
287467 }
288468
469+ /// <summary>
470+ /// Updates the target of a direct reference.
471+ /// </summary>
472+ /// <param name="directRef">The direct reference which target should be updated.</param>
473+ /// <param name="objectish">The revparse spec of the target.</param>
474+ /// <param name="logMessage">The optional message to log in the <see cref="ReflogCollection"/></param>
475+ /// <returns>A new <see cref="Reference"/>.</returns>
476+ public virtual Reference UpdateTarget ( Reference directRef , string objectish , string logMessage )
477+ {
478+ Ensure . ArgumentNotNull ( directRef , "directRef" ) ;
479+ Ensure . ArgumentNotNull ( objectish , "objectish" ) ;
480+
481+ GitObject target = repo . Lookup ( objectish ) ;
482+
483+ Ensure . GitObjectIsNotNull ( target , objectish ) ;
484+
485+ return UpdateTarget ( directRef , target . Id , logMessage ) ;
486+ }
487+
488+ /// <summary>
489+ /// Updates the target of a direct reference
490+ /// </summary>
491+ /// <param name="directRef">The direct reference which target should be updated.</param>
492+ /// <param name="objectish">The revparse spec of the target.</param>
493+ /// <returns>A new <see cref="Reference"/>.</returns>
494+ public virtual Reference UpdateTarget ( Reference directRef , string objectish )
495+ {
496+ return UpdateTarget ( directRef , objectish , null ) ;
497+ }
498+
499+ /// <summary>
500+ /// Updates the target of a reference
501+ /// </summary>
502+ /// <param name="name">The canonical name of the reference.</param>
503+ /// <param name="canonicalRefNameOrObjectish">The target which can be either the canonical name of a reference or a revparse spec.</param>
504+ /// <param name="logMessage">The optional message to log in the <see cref="ReflogCollection"/> of the <paramref name="name"/> reference.</param>
505+ /// <returns>A new <see cref="Reference"/>.</returns>
506+ public virtual Reference UpdateTarget ( string name , string canonicalRefNameOrObjectish , string logMessage )
507+ {
508+ Ensure . ArgumentNotNullOrEmptyString ( name , "name" ) ;
509+ Ensure . ArgumentNotNullOrEmptyString ( canonicalRefNameOrObjectish , "canonicalRefNameOrObjectish" ) ;
510+
511+ if ( name == "HEAD" )
512+ {
513+ return UpdateHeadTarget ( canonicalRefNameOrObjectish , logMessage ) ;
514+ }
515+
516+ Reference reference = this [ name ] ;
517+
518+ var directReference = reference as DirectReference ;
519+ if ( directReference != null )
520+ {
521+ return UpdateTarget ( directReference , canonicalRefNameOrObjectish , logMessage ) ;
522+ }
523+
524+ var symbolicReference = reference as SymbolicReference ;
525+ if ( symbolicReference != null )
526+ {
527+ Reference targetRef ;
528+
529+ RefState refState = TryResolveReference ( out targetRef , this , canonicalRefNameOrObjectish ) ;
530+
531+ if ( refState == RefState . DoesNotLookValid )
532+ {
533+ 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" ) ;
534+ }
535+
536+ return UpdateTarget ( symbolicReference , targetRef , logMessage ) ;
537+ }
538+
539+ throw new LibGit2SharpException ( CultureInfo . InvariantCulture ,
540+ "Reference '{0}' has an unexpected type ('{1}')." ,
541+ name ,
542+ reference . GetType ( ) ) ;
543+ }
544+
545+ /// <summary>
546+ /// Updates the target of a reference
547+ /// </summary>
548+ /// <param name="name">The canonical name of the reference.</param>
549+ /// <param name="canonicalRefNameOrObjectish">The target which can be either the canonical name of a reference or a revparse spec.</param>
550+ /// <returns>A new <see cref="Reference"/>.</returns>
551+ public virtual Reference UpdateTarget ( string name , string canonicalRefNameOrObjectish )
552+ {
553+ return UpdateTarget ( name , canonicalRefNameOrObjectish , null ) ;
554+ }
555+
289556 /// <summary>
290557 /// Updates the target of a direct reference
291558 /// </summary>
@@ -427,6 +694,79 @@ public virtual Reference Head
427694 get { return this [ "HEAD" ] ; }
428695 }
429696
697+
698+ /// <summary>
699+ /// Find the <see cref="Reference"/>s among <paramref name="refSubset"/>
700+ /// that can reach at least one <see cref="Commit"/> in the specified <paramref name="targets"/>.
701+ /// </summary>
702+ /// <param name="refSubset">The set of <see cref="Reference"/>s to examine.</param>
703+ /// <param name="targets">The set of <see cref="Commit"/>s that are interesting.</param>
704+ /// <returns>A subset of <paramref name="refSubset"/> that can reach at least one <see cref="Commit"/> within <paramref name="targets"/>.</returns>
705+ public virtual IEnumerable < Reference > ReachableFrom (
706+ IEnumerable < Reference > refSubset ,
707+ IEnumerable < Commit > targets )
708+ {
709+ Ensure . ArgumentNotNull ( refSubset , "refSubset" ) ;
710+ Ensure . ArgumentNotNull ( targets , "targets" ) ;
711+
712+ var refs = new List < Reference > ( refSubset ) ;
713+ if ( refs . Count == 0 )
714+ {
715+ return Enumerable . Empty < Reference > ( ) ;
716+ }
717+
718+ List < ObjectId > targetsSet = targets . Select ( c => c . Id ) . Distinct ( ) . ToList ( ) ;
719+ if ( targetsSet . Count == 0 )
720+ {
721+ return Enumerable . Empty < Reference > ( ) ;
722+ }
723+
724+ var result = new List < Reference > ( ) ;
725+
726+ foreach ( var reference in refs )
727+ {
728+ var peeledTargetCommit = reference
729+ . ResolveToDirectReference ( )
730+ . Target . DereferenceToCommit ( false ) ;
731+
732+ if ( peeledTargetCommit == null )
733+ {
734+ continue ;
735+ }
736+
737+ var commitId = peeledTargetCommit . Id ;
738+
739+ foreach ( var potentialAncestorId in targetsSet )
740+ {
741+ if ( potentialAncestorId == commitId )
742+ {
743+ result . Add ( reference ) ;
744+ break ;
745+ }
746+
747+ if ( Proxy . git_graph_descendant_of ( repo . Handle , commitId , potentialAncestorId ) )
748+ {
749+ result . Add ( reference ) ;
750+ break ;
751+ }
752+ }
753+ }
754+
755+ return result ;
756+ }
757+
758+ /// <summary>
759+ /// Find the <see cref="Reference"/>s
760+ /// that can reach at least one <see cref="Commit"/> in the specified <paramref name="targets"/>.
761+ /// </summary>
762+ /// <param name="targets">The set of <see cref="Commit"/>s that are interesting.</param>
763+ /// <returns>The list of <see cref="Reference"/> that can reach at least one <see cref="Commit"/> within <paramref name="targets"/>.</returns>
764+ public virtual IEnumerable < Reference > ReachableFrom (
765+ IEnumerable < Commit > targets )
766+ {
767+ return ReachableFrom ( this , targets ) ;
768+ }
769+
430770 private string DebuggerDisplay
431771 {
432772 get
0 commit comments