Skip to content

Commit 22ee2bc

Browse files
committed
Add IGitRepositoryCommands
Add IGitRepositoryCommands interface and default implementation as a mockable and testable wrapper around the static Commands class.
1 parent a809171 commit 22ee2bc

File tree

7 files changed

+216
-15
lines changed

7 files changed

+216
-15
lines changed

src/GitVersionCore.Tests/Core/RepositoryExtensionsTests.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using NSubstitute;
99
using System;
1010
using System.Collections.Generic;
11+
using GitVersion;
1112

1213
namespace GitVersionCore.Tests
1314
{
@@ -17,14 +18,22 @@ public class RepositoryExtensionsTests : TestBase
1718
[Test]
1819
public void EnsureLocalBranchExistsForCurrentBranch_CaseInsensitivelyMatchesBranches()
1920
{
20-
ILog log = Substitute.For<ILog>();
21-
var repository = Substitute.For<IRepository>();
22-
var remote = ConstructRemote(repository);
21+
var log = Substitute.For<ILog>();
22+
var repository = MockRepository();
23+
var remote = MockRemote(repository);
2324

2425
repository.EnsureLocalBranchExistsForCurrentBranch(log, remote, "refs/heads/featurE/feat-test");
2526
}
2627

27-
private Remote ConstructRemote(IRepository repository)
28+
private IGitRepository MockRepository()
29+
{
30+
var repository = Substitute.For<IGitRepository>();
31+
var commands = Substitute.For<IGitRepositoryCommands>();
32+
repository.Commands.Returns(commands);
33+
return repository;
34+
}
35+
36+
private Remote MockRemote(IGitRepository repository)
2837
{
2938
var branches = new TestableBranchCollection(repository);
3039
var tipId = new ObjectId("c6d8764d20ff16c0df14c73680e52b255b608926");

src/GitVersionCore/Core/GitPreparer.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ private void CloneRepository(string repositoryUrl, string gitDirectory, Authenti
202202
private void NormalizeGitDirectory(string gitDirectory, bool noFetch, string currentBranch, bool isDynamicRepository)
203203
{
204204
var authentication = options.Value.Authentication;
205-
using var repository = new Repository(gitDirectory);
205+
using var repository = new GitRepository(gitDirectory);
206206
// Need to ensure the HEAD does not move, this is essentially a BugCheck
207207
var expectedSha = repository.Head.Tip.Sha;
208208
var expectedBranchName = repository.Head.CanonicalName;
@@ -221,7 +221,7 @@ private void NormalizeGitDirectory(string gitDirectory, bool noFetch, string cur
221221
else
222222
{
223223
log.Info($"Fetching from remote '{remote.Name}' using the following refspecs: {string.Join(", ", remote.FetchRefSpecs.Select(r => r.Specification))}.");
224-
Commands.Fetch(repository, remote.Name, new string[0], authentication.ToFetchOptions(), null);
224+
repository.Commands.Fetch(remote.Name, new string[0], authentication.ToFetchOptions(), null);
225225
}
226226

227227
repository.EnsureLocalBranchExistsForCurrentBranch(log, remote, currentBranch);
@@ -262,7 +262,7 @@ private void NormalizeGitDirectory(string gitDirectory, bool noFetch, string cur
262262
if (matchingCurrentBranch != null)
263263
{
264264
log.Info($"Checking out local branch '{currentBranch}'.");
265-
Commands.Checkout(repository, matchingCurrentBranch);
265+
repository.Commands.Checkout( matchingCurrentBranch);
266266
}
267267
else if (localBranchesWhereCommitShaIsHead.Count > 1)
268268
{
@@ -275,7 +275,7 @@ private void NormalizeGitDirectory(string gitDirectory, bool noFetch, string cur
275275
if (master != null)
276276
{
277277
log.Warning("Because one of the branches is 'master', will build master." + moveBranchMsg);
278-
Commands.Checkout(repository, master);
278+
repository.Commands.Checkout(master);
279279
}
280280
else
281281
{
@@ -284,7 +284,7 @@ private void NormalizeGitDirectory(string gitDirectory, bool noFetch, string cur
284284
{
285285
var branchWithoutSeparator = branchesWithoutSeparators[0];
286286
log.Warning($"Choosing {branchWithoutSeparator.CanonicalName} as it is the only branch without / or - in it. " + moveBranchMsg);
287-
Commands.Checkout(repository, branchWithoutSeparator);
287+
repository.Commands.Checkout(branchWithoutSeparator);
288288
}
289289
else
290290
{
@@ -300,7 +300,7 @@ private void NormalizeGitDirectory(string gitDirectory, bool noFetch, string cur
300300
else
301301
{
302302
log.Info($"Checking out local branch 'refs/heads/{localBranchesWhereCommitShaIsHead[0].FriendlyName}'.");
303-
Commands.Checkout(repository, repository.Branches[localBranchesWhereCommitShaIsHead[0].FriendlyName]);
303+
repository.Commands.Checkout(repository.Branches[localBranchesWhereCommitShaIsHead[0].FriendlyName]);
304304
}
305305
}
306306
finally

src/GitVersionCore/Core/GitRepository.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,20 @@
55

66
namespace GitVersion
77
{
8-
public class GitRepository : IRepository
8+
public class GitRepository : IGitRepository
99
{
1010
private Lazy<IRepository> repositoryLazy;
1111
private IRepository repositoryInstance => repositoryLazy.Value;
1212

1313
public GitRepository(IOptions<GitVersionOptions> options)
14+
: this(options.Value.DotGitDirectory)
1415
{
15-
repositoryLazy = new Lazy<IRepository>(() => new Repository(options.Value.DotGitDirectory));
16+
}
17+
18+
public GitRepository(string dotGitDirectory)
19+
{
20+
Commands = new GitRepositoryCommands(this);
21+
repositoryLazy = new Lazy<IRepository>(() => new Repository(dotGitDirectory));
1622
}
1723

1824
public void Dispose()
@@ -161,5 +167,7 @@ public void RevParse(string revision, out Reference reference, out GitObject obj
161167
public Network Network => repositoryInstance.Network;
162168

163169
public StashCollection Stashes => repositoryInstance.Stashes;
170+
171+
public IGitRepositoryCommands Commands { get; }
164172
}
165173
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
using System.Collections.Generic;
2+
using LibGit2Sharp;
3+
4+
namespace GitVersion
5+
{
6+
/// <summary>
7+
/// Default implementation of <see cref="IGitRepositoryCommands"/> using
8+
/// the <c>static</c> <see cref="Commands"/> <c>class</c>.
9+
/// </summary>
10+
public class GitRepositoryCommands : IGitRepositoryCommands
11+
{
12+
private readonly IRepository repository;
13+
public GitRepositoryCommands(IRepository repository)
14+
{
15+
this.repository = repository ?? throw new System.ArgumentNullException(nameof(repository));
16+
}
17+
18+
public Branch Checkout(string committishOrBranchSpec)
19+
{
20+
return Commands.Checkout(this.repository, committishOrBranchSpec);
21+
}
22+
23+
public Branch Checkout(string committishOrBranchSpec, CheckoutOptions options)
24+
{
25+
return Commands.Checkout(this.repository, committishOrBranchSpec, options);
26+
}
27+
28+
public Branch Checkout(Branch branch)
29+
{
30+
return Commands.Checkout(this.repository, branch);
31+
}
32+
33+
public Branch Checkout(Branch branch, CheckoutOptions options)
34+
{
35+
return Commands.Checkout(this.repository, branch, options);
36+
}
37+
38+
public Branch Checkout(Commit commit)
39+
{
40+
return Commands.Checkout(this.repository, commit);
41+
}
42+
43+
public Branch Checkout(Commit commit, CheckoutOptions options)
44+
{
45+
return Commands.Checkout(this.repository, commit, options);
46+
}
47+
48+
public void Checkout(Tree tree, CheckoutOptions checkoutOptions, string refLogHeadSpec)
49+
{
50+
Commands.Checkout(this.repository, tree, checkoutOptions, refLogHeadSpec);
51+
}
52+
53+
public void Fetch(string remote, IEnumerable<string> refspecs, FetchOptions options, string logMessage)
54+
{
55+
Commands.Fetch((Repository)this.repository, remote, refspecs, options, logMessage);
56+
}
57+
58+
public void Move(string sourcePath, string destinationPath)
59+
{
60+
Commands.Move(this.repository, sourcePath, destinationPath);
61+
}
62+
63+
public void Move(IEnumerable<string> sourcePaths, IEnumerable<string> destinationPaths)
64+
{
65+
Commands.Move(this.repository, sourcePaths, destinationPaths);
66+
}
67+
68+
public MergeResult Pull(Signature merger, PullOptions options)
69+
{
70+
return Commands.Pull((Repository)this.repository, merger, options);
71+
}
72+
73+
public void Remove(string path, bool removeFromWorkingDirectory)
74+
{
75+
Commands.Remove(this.repository, path, removeFromWorkingDirectory);
76+
}
77+
78+
public void Remove(IEnumerable<string> paths)
79+
{
80+
Commands.Remove(this.repository, paths);
81+
}
82+
83+
public void Remove(IEnumerable<string> paths, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions)
84+
{
85+
Commands.Remove(this.repository, paths, removeFromWorkingDirectory, explicitPathsOptions);
86+
}
87+
88+
public void Remove(string path)
89+
{
90+
Commands.Remove(this.repository, path);
91+
}
92+
93+
public void Remove(string path, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions)
94+
{
95+
Commands.Remove(this.repository, path, removeFromWorkingDirectory, explicitPathsOptions);
96+
}
97+
98+
public void Stage(string path)
99+
{
100+
Commands.Stage(this.repository, path);
101+
}
102+
103+
public void Stage(string path, StageOptions stageOptions)
104+
{
105+
Commands.Stage(this.repository, path, stageOptions);
106+
}
107+
108+
public void Stage(IEnumerable<string> paths)
109+
{
110+
Commands.Stage(this.repository, paths);
111+
}
112+
113+
public void Stage(IEnumerable<string> paths, StageOptions stageOptions)
114+
{
115+
Commands.Stage(this.repository, paths, stageOptions);
116+
}
117+
118+
public void Unstage(string path)
119+
{
120+
Commands.Unstage(this.repository, path);
121+
}
122+
123+
public void Unstage(string path, ExplicitPathsOptions explicitPathsOptions)
124+
{
125+
Commands.Unstage(this.repository, path, explicitPathsOptions);
126+
}
127+
128+
public void Unstage(IEnumerable<string> paths)
129+
{
130+
Commands.Unstage(this.repository, paths);
131+
}
132+
133+
public void Unstage(IEnumerable<string> paths, ExplicitPathsOptions explicitPathsOptions)
134+
{
135+
Commands.Unstage(this.repository, paths, explicitPathsOptions);
136+
}
137+
}
138+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using LibGit2Sharp;
2+
3+
namespace GitVersion
4+
{
5+
public interface IGitRepository : IRepository
6+
{
7+
IGitRepositoryCommands Commands { get; }
8+
}
9+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System.Collections.Generic;
2+
using LibGit2Sharp;
3+
4+
namespace GitVersion
5+
{
6+
/// <summary>
7+
/// Mockable and testable interface wrapper for the <c>static</c>
8+
/// <see cref="Commands"/> <c>class</c>.
9+
/// </summary>
10+
public interface IGitRepositoryCommands
11+
{
12+
Branch Checkout(string committishOrBranchSpec);
13+
Branch Checkout(string committishOrBranchSpec, CheckoutOptions options);
14+
Branch Checkout(Branch branch);
15+
Branch Checkout(Branch branch, CheckoutOptions options);
16+
Branch Checkout(Commit commit);
17+
Branch Checkout(Commit commit, CheckoutOptions options);
18+
void Checkout(Tree tree, CheckoutOptions checkoutOptions, string refLogHeadSpec);
19+
void Fetch(string remote, IEnumerable<string> refspecs, FetchOptions options, string logMessage);
20+
void Move(string sourcePath, string destinationPath);
21+
void Move(IEnumerable<string> sourcePaths, IEnumerable<string> destinationPaths);
22+
MergeResult Pull(Signature merger, PullOptions options);
23+
void Remove(string path, bool removeFromWorkingDirectory);
24+
void Remove(IEnumerable<string> paths);
25+
void Remove(IEnumerable<string> paths, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions);
26+
void Remove(string path);
27+
void Remove(string path, bool removeFromWorkingDirectory, ExplicitPathsOptions explicitPathsOptions);
28+
void Stage(string path);
29+
void Stage(string path, StageOptions stageOptions);
30+
void Stage(IEnumerable<string> paths);
31+
void Stage(IEnumerable<string> paths, StageOptions stageOptions);
32+
void Unstage(string path);
33+
void Unstage(string path, ExplicitPathsOptions explicitPathsOptions);
34+
void Unstage(IEnumerable<string> paths);
35+
void Unstage(IEnumerable<string> paths, ExplicitPathsOptions explicitPathsOptions); }
36+
}

src/GitVersionCore/Extensions/RepositoryExtensions.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public static void DumpGraph(this IRepository repository, Action<string> writer
3434
LibGitExtensions.DumpGraph(repository.Info.Path, writer, maxCommits);
3535
}
3636

37-
public static void EnsureLocalBranchExistsForCurrentBranch(this IRepository repo, ILog log, Remote remote, string currentBranch)
37+
public static void EnsureLocalBranchExistsForCurrentBranch(this IGitRepository repo, ILog log, Remote remote, string currentBranch)
3838
{
3939
if (log is null)
4040
{
@@ -78,10 +78,11 @@ public static void EnsureLocalBranchExistsForCurrentBranch(this IRepository repo
7878
{
7979
log.Info(isBranch ? $"Updating local branch {localCanonicalName} to point at {repoTip.Sha}"
8080
: $"Updating local branch {localCanonicalName} to match ref {currentBranch}");
81-
repo.Refs.UpdateTarget(repo.Refs[localCanonicalName], repoTipId);
81+
var localRef = repo.Refs[localCanonicalName];
82+
repo.Refs.UpdateTarget(localRef, repoTipId);
8283
}
8384

84-
Commands.Checkout(repo, localCanonicalName);
85+
repo.Commands.Checkout(localCanonicalName);
8586
}
8687

8788
public static void AddMissingRefSpecs(this IRepository repo, ILog log, Remote remote)

0 commit comments

Comments
 (0)