Skip to content

Commit d5ae5db

Browse files
committed
can create and delete branches
1 parent b8b946b commit d5ae5db

File tree

3 files changed

+148
-18
lines changed

3 files changed

+148
-18
lines changed

LibGit2Sharp.Tests/BranchFixture.cs

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using NUnit.Framework;
45

@@ -9,6 +10,42 @@ public class BranchFixture
910
{
1011
private readonly List<string> expectedBranches = new List<string> {"packed-test", "packed", "br2", "master", "test"};
1112

13+
[Test]
14+
public void CanCreateBranch()
15+
{
16+
using (var path = new TemporaryRepositoryPath())
17+
using (var repo = new Repository(path.RepositoryPath))
18+
{
19+
const string name = "unit_test";
20+
var newBranch = repo.Branches.Create(name, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
21+
newBranch.ShouldNotBeNull();
22+
newBranch.Name.ShouldEqual(name);
23+
newBranch.Reference.Target.ShouldNotBeNull();
24+
newBranch.Reference.Target.Sha.ShouldEqual("be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
25+
repo.Branches.SingleOrDefault(p => p.Name == name).ShouldNotBeNull();
26+
27+
newBranch.Delete();
28+
}
29+
}
30+
31+
[Test]
32+
public void CanCreateBranchFromAnotherBranch()
33+
{
34+
using (var path = new TemporaryRepositoryPath())
35+
using (var repo = new Repository(path.RepositoryPath))
36+
{
37+
const string name = "unit_test";
38+
var newBranch = repo.Branches.Create(name, "master");
39+
newBranch.ShouldNotBeNull();
40+
newBranch.Name.ShouldEqual(name);
41+
newBranch.Reference.Target.ShouldNotBeNull();
42+
newBranch.Reference.Target.Sha.ShouldEqual("be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
43+
repo.Branches.SingleOrDefault(p => p.Name == name).ShouldNotBeNull();
44+
45+
newBranch.Delete();
46+
}
47+
}
48+
1249
[Test]
1350
public void CanListAllBranches()
1451
{
@@ -56,5 +93,42 @@ public void CanWalkCommitsFromBranch()
5693
master.Commits.Count().ShouldEqual(6);
5794
}
5895
}
96+
97+
[Test]
98+
public void CreatingBranchWithEmptyNameThrows()
99+
{
100+
using (var repo = new Repository(Constants.TestRepoPath))
101+
{
102+
Assert.Throws<ArgumentException>(() => repo.Branches.Create(string.Empty, "not_important_sha"));
103+
}
104+
}
105+
106+
[Test]
107+
public void CreatingBranchWithEmptyTargetThrows()
108+
{
109+
using (var repo = new Repository(Constants.TestRepoPath))
110+
{
111+
Assert.Throws<ArgumentException>(() => repo.Branches.Create("bad_branch", string.Empty));
112+
}
113+
}
114+
115+
[Test]
116+
public void CreatingBranchWithNullNameThrows()
117+
{
118+
using (var repo = new Repository(Constants.TestRepoPath))
119+
{
120+
Assert.Throws<ArgumentNullException>(() => repo.Branches.Create(null, "not_important_sha"));
121+
}
122+
}
123+
124+
[Test]
125+
public void CreatingBranchWithNullTargetThrows()
126+
{
127+
using (var repo = new Repository(Constants.TestRepoPath))
128+
{
129+
Assert.Throws<ArgumentNullException>(() => repo.Branches.Create("bad_branch", (string) null));
130+
Assert.Throws<ArgumentNullException>(() => repo.Branches.Create("bad_branch", (ObjectId) null));
131+
}
132+
}
59133
}
60134
}

LibGit2Sharp/Branch.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,25 @@ internal static Branch CreateBranchFromReference(Reference reference, Repository
5959
return new Branch(repo)
6060
{
6161
Name = tokens[tokens.Length - 1],
62-
Reference = (DirectReference) reference,
62+
Reference = reference.ResolveToDirectReference(),
6363
Type = BranchType.Local
6464
};
6565
}
6666
return new Branch(repo)
6767
{
6868
Name = string.Join("/", tokens, tokens.Length - 2, 2),
6969
RemoteName = tokens[tokens.Length - 2],
70-
Reference = (DirectReference) reference,
70+
Reference = reference.ResolveToDirectReference(),
7171
Type = BranchType.Remote
7272
};
7373
}
74+
75+
/// <summary>
76+
/// Deletes this branch.
77+
/// </summary>
78+
public void Delete()
79+
{
80+
Reference.Delete();
81+
}
7482
}
7583
}

LibGit2Sharp/BranchCollection.cs

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,7 @@ public BranchCollection(Repository repo)
2727
/// </summary>
2828
public Branch this[string name]
2929
{
30-
get
31-
{
32-
var tokens = name.Split('/');
33-
if (tokens.Length == 1)
34-
{
35-
return Branch.CreateBranchFromReference(repo.Refs[string.Format(CultureInfo.InvariantCulture, "refs/heads/{0}", name)], repo);
36-
}
37-
if (tokens.Length == 2)
38-
{
39-
return Branch.CreateBranchFromReference(repo.Refs[string.Format(CultureInfo.InvariantCulture, "refs/{0}", name)], repo);
40-
}
41-
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Unable to parse branch name: {0}. Expecting local branches in the form <branchname> and remotes in the form <remote>/<branchname>.", name));
42-
}
30+
get { return Branch.CreateBranchFromReference(repo.Refs[ParseName(name)], repo); }
4331
}
4432

4533
#region IEnumerable<Branch> Members
@@ -59,10 +47,70 @@ IEnumerator IEnumerable.GetEnumerator()
5947

6048
#endregion
6149

50+
/// <summary>
51+
/// Create a new local branch with the specified name
52+
/// </summary>
53+
/// <param name = "name">The name of the branch.</param>
54+
/// <param name = "target">The target sha, ref or branch name.</param>
55+
/// <returns></returns>
56+
public Branch Create(string name, string target)
57+
{
58+
Ensure.ArgumentNotNullOrEmptyString(target, "target");
59+
60+
GitOid oid;
61+
if(NativeMethods.git_oid_mkstr(out oid, target) == (int)GitErrorCode.GIT_SUCCESS)
62+
{
63+
return Create(name, new ObjectId(oid));
64+
}
65+
66+
var reference = repo.Refs.Create(EnsureValidBranchName(name), ParseName(target));
67+
return Branch.CreateBranchFromReference(reference, repo);
68+
}
69+
70+
/// <summary>
71+
/// Create a new local branch with the specified name.
72+
/// </summary>
73+
/// <param name = "name">The name of the branch.</param>
74+
/// <param name = "target">The target.</param>
75+
/// <returns></returns>
76+
public Branch Create(string name, ObjectId target)
77+
{
78+
Ensure.ArgumentNotNull(target, "target");
79+
80+
var reference = repo.Refs.Create(EnsureValidBranchName(name), target);
81+
return Branch.CreateBranchFromReference(reference, repo);
82+
}
83+
84+
private static string EnsureValidBranchName(string name)
85+
{
86+
Ensure.ArgumentNotNullOrEmptyString(name, "name");
87+
88+
if (name.Contains("/"))
89+
{
90+
throw new ArgumentException("Branch names cannot contain the character '/'.");
91+
}
92+
93+
return string.Format(CultureInfo.InvariantCulture, "refs/heads/{0}", name);
94+
}
95+
6296
private static bool IsABranch(Reference reference)
6397
{
64-
return reference.Type == GitReferenceType.Oid
65-
&& !reference.Name.StartsWith("refs/tags/");
98+
return /*reference.Type == GitReferenceType.Oid
99+
&&*/ !reference.Name.StartsWith("refs/tags/");
100+
}
101+
102+
private static string ParseName(string name)
103+
{
104+
var tokens = name.Split('/');
105+
if (tokens.Length == 1)
106+
{
107+
return string.Format(CultureInfo.InvariantCulture, "refs/heads/{0}", name);
108+
}
109+
if (tokens.Length == 2)
110+
{
111+
return string.Format(CultureInfo.InvariantCulture, "refs/{0}", name);
112+
}
113+
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Unable to parse branch name: {0}. Expecting local branches in the form <branchname> and remotes in the form <remote>/<branchname>.", name));
66114
}
67115
}
68116
}

0 commit comments

Comments
 (0)