Skip to content

Commit 37592c4

Browse files
committed
can update reference target
This works for both direct and symbolic references.
1 parent d9c333e commit 37592c4

File tree

3 files changed

+116
-0
lines changed

3 files changed

+116
-0
lines changed

LibGit2Sharp.Tests/ReferenceFixture.cs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,43 @@ public void CanResolveRefsByName()
125125
}
126126
}
127127

128+
[Test]
129+
public void CanUpdateTargetOnReference()
130+
{
131+
const string masterRef = "refs/heads/master";
132+
using (var path = new TemporaryCloneOfTestRepo())
133+
using (var repo = new Repository(path.RepositoryPath))
134+
{
135+
var sha = repo.Refs["refs/heads/test"].ResolveToDirectReference().Target.Sha;
136+
var master = repo.Refs[masterRef];
137+
master.ResolveToDirectReference().Target.Sha.ShouldNotEqual(sha);
138+
139+
repo.Refs.UpdateTarget(masterRef, sha);
140+
141+
master = repo.Refs[masterRef];
142+
master.ResolveToDirectReference().Target.Sha.ShouldEqual(sha);
143+
}
144+
}
145+
146+
[Test]
147+
public void CanUpdateTargetOnSymolicReference()
148+
{
149+
const string name = "refs/heads/unit_test";
150+
using (var path = new TemporaryCloneOfTestRepo())
151+
using (var repo = new Repository(path.RepositoryPath))
152+
{
153+
var newRef = (SymbolicReference) repo.Refs.Create(name, "refs/heads/master");
154+
newRef.ShouldNotBeNull();
155+
156+
repo.Refs.UpdateTarget(newRef.CanonicalName, "refs/heads/test");
157+
158+
newRef = (SymbolicReference) repo.Refs[newRef.CanonicalName];
159+
newRef.ResolveToDirectReference().Target.ShouldEqual(repo.Refs["refs/heads/test"].ResolveToDirectReference().Target);
160+
161+
repo.Refs.Delete(newRef.CanonicalName);
162+
}
163+
}
164+
128165
[Test]
129166
public void CreateWithEmptyStringForTargetThrows()
130167
{
@@ -200,5 +237,46 @@ public void ResolvingWithNullThrows()
200237
Assert.Throws<ArgumentNullException>(() => { var head = repo.Refs[null]; });
201238
}
202239
}
240+
241+
[Test]
242+
public void TryingToUpdateADirectRefWithSymbolFails()
243+
{
244+
const string name = "refs/heads/unit_test";
245+
using (var path = new TemporaryCloneOfTestRepo())
246+
using (var repo = new Repository(path.RepositoryPath))
247+
{
248+
var newRef = (SymbolicReference) repo.Refs.Create(name, "refs/heads/master");
249+
newRef.ShouldNotBeNull();
250+
251+
Assert.Throws<ArgumentException>(
252+
() => repo.Refs.UpdateTarget(newRef.CanonicalName, repo.Refs["refs/heads/test"].ResolveToDirectReference().Target.Sha));
253+
254+
repo.Refs.Delete(newRef.CanonicalName);
255+
}
256+
}
257+
258+
[Test]
259+
public void TryingToUpdateASymbolicRefWithOidFails()
260+
{
261+
const string masterRef = "refs/heads/master";
262+
using (var path = new TemporaryCloneOfTestRepo())
263+
using (var repo = new Repository(path.RepositoryPath))
264+
{
265+
Assert.Throws<ArgumentException>(() => repo.Refs.UpdateTarget(masterRef, "refs/heads/test"));
266+
}
267+
}
268+
269+
[Test]
270+
public void UpdateReferenceTargetWithBadParametersFails()
271+
{
272+
using (var path = new TemporaryCloneOfTestRepo())
273+
using (var repo = new Repository(path.RepositoryPath))
274+
{
275+
Assert.Throws<ArgumentException>(() => repo.Refs.UpdateTarget(string.Empty, "refs/heads/packed"));
276+
Assert.Throws<ArgumentException>(() => repo.Refs.UpdateTarget("master", string.Empty));
277+
Assert.Throws<ArgumentNullException>(() => repo.Refs.UpdateTarget(null, "refs/heads/packed"));
278+
Assert.Throws<ArgumentNullException>(() => repo.Refs.UpdateTarget("master", null));
279+
}
280+
}
203281
}
204282
}

LibGit2Sharp/Core/NativeMethods.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ internal class NativeMethods
9191
[DllImport(libgit2)]
9292
public static extern int git_reference_resolve(out IntPtr resolvedReference, IntPtr reference);
9393

94+
[DllImport(libgit2, SetLastError = true)]
95+
public static extern int git_reference_set_oid(IntPtr reference, ref GitOid id);
96+
97+
[DllImport(libgit2, SetLastError = true)]
98+
public static extern int git_reference_set_target(IntPtr reference, string target);
99+
94100
[DllImport(libgit2)]
95101
[return: MarshalAs(UnmanagedType.AnsiBStr)]
96102
public static extern string git_reference_target(IntPtr reference);

LibGit2Sharp/ReferenceCollection.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,5 +131,37 @@ internal T Resolve<T>(string name) where T : class
131131

132132
return Reference.BuildFromPtr<T>(reference, repo);
133133
}
134+
135+
/// <summary>
136+
/// Updates the target on a reference.
137+
/// </summary>
138+
/// <param name = "name">The name of the reference.</param>
139+
/// <param name = "target">The target which can be either a sha or the name of another reference.</param>
140+
public void UpdateTarget(string name, string target)
141+
{
142+
Ensure.ArgumentNotNullOrEmptyString(name, "name");
143+
Ensure.ArgumentNotNullOrEmptyString(target, "target");
144+
145+
IntPtr reference;
146+
var res = NativeMethods.git_reference_lookup(out reference, repo.Handle, name);
147+
Ensure.Success(res);
148+
149+
var id = ObjectId.CreateFromMaybeSha(target);
150+
var type = NativeMethods.git_reference_type(reference);
151+
switch (type)
152+
{
153+
case GitReferenceType.Oid:
154+
if (id == null) throw new ArgumentException(String.Format("The reference specified by {0} is an Oid reference, you must provide a sha as the target.", name), "target");
155+
var oid = id.Oid;
156+
res = NativeMethods.git_reference_set_oid(reference, ref oid);
157+
break;
158+
case GitReferenceType.Symbolic:
159+
if (id != null) throw new ArgumentException(String.Format("The reference specified by {0} is an Symbolic reference, you must provide a symbol as the target.", name), "target");
160+
res = NativeMethods.git_reference_set_target(reference, target);
161+
break;
162+
}
163+
164+
Ensure.Success(res);
165+
}
134166
}
135167
}

0 commit comments

Comments
 (0)