Skip to content

Commit 6d01254

Browse files
committed
Restructured cache code
- No more in memory cache - Pulled all caching code out to it's own class - Only open repo once - Moved open repo into execute core
1 parent 64c50bc commit 6d01254

File tree

9 files changed

+148
-186
lines changed

9 files changed

+148
-186
lines changed

src/GitVersionCore.Tests/ExecuteCoreTests.cs

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Collections.Concurrent;
32
using System.Text;
43

54
using GitVersion;
@@ -14,14 +13,12 @@ public class ExecuteCoreTests
1413
{
1514
IFileSystem fileSystem;
1615

17-
1816
[SetUp]
1917
public void SetUp()
2018
{
2119
fileSystem = new FileSystem();
2220
}
2321

24-
2522
[Test]
2623
public void CacheFileExistsOnDisk()
2724
{
@@ -62,33 +59,13 @@ public void CacheFileExistsOnDisk()
6259
info.ShouldContain("Deserializing version variables from cache file", () => info);
6360
}
6461

65-
66-
[Test]
67-
public void CacheFileExistsInMemory()
68-
{
69-
var cache = new ConcurrentDictionary<string, VersionVariables>();
70-
var versionAndBranchFinder = new ExecuteCore(fileSystem, cache.GetOrAdd);
71-
72-
var info = RepositoryScope(versionAndBranchFinder, (fixture, vv) =>
73-
{
74-
vv.AssemblySemVer.ShouldBe("0.1.0.0");
75-
vv = versionAndBranchFinder.ExecuteGitVersion(null, null, null, null, false, fixture.RepositoryPath, null);
76-
vv.AssemblySemVer.ShouldBe("0.1.0.0");
77-
});
78-
79-
info.ShouldContain("yml not found", () => info);
80-
info.ShouldNotContain("Deserializing version variables from cache file", () => info);
81-
}
82-
83-
8462
[Test]
8563
public void CacheFileIsMissing()
8664
{
8765
var info = RepositoryScope();
8866
info.ShouldContain("yml not found", () => info);
8967
}
9068

91-
9269
string RepositoryScope(ExecuteCore executeCore = null, Action<EmptyRepositoryFixture, VersionVariables> fixtureAction = null)
9370
{
9471
// Make sure GitVersion doesn't trigger build server mode when we are running the tests

src/GitVersionCore.Tests/TestFileSystem.cs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55

66
using GitVersion.Helpers;
77

8-
using LibGit2Sharp;
9-
108
public class TestFileSystem : IFileSystem
119
{
1210
Dictionary<string, string> fileSystem = new Dictionary<string, string>();
@@ -77,9 +75,4 @@ public long GetLastDirectoryWrite(string path)
7775
{
7876
return 1;
7977
}
80-
81-
public IRepository GetRepository(string gitDirectory)
82-
{
83-
throw new NotImplementedException();
84-
}
8578
}

src/GitVersionCore/ExecuteCore.cs

Lines changed: 32 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,40 @@
11
namespace GitVersion
22
{
33
using System;
4-
using System.Collections.Generic;
5-
using System.IO;
64
using System.Linq;
7-
using System.Security.Cryptography;
8-
using System.Text;
9-
105
using GitVersion.Helpers;
116

127
using LibGit2Sharp;
138

14-
using YamlDotNet.Serialization;
15-
169
public class ExecuteCore
1710
{
1811
readonly IFileSystem fileSystem;
12+
readonly GitVersionCache gitVersionCache;
1913

2014
public ExecuteCore(IFileSystem fileSystem)
2115
{
2216
if (fileSystem == null) throw new ArgumentNullException("fileSystem");
2317

2418
this.fileSystem = fileSystem;
19+
gitVersionCache = new GitVersionCache(fileSystem);
2520
}
2621

2722
public VersionVariables ExecuteGitVersion(string targetUrl, string dynamicRepositoryLocation, Authentication authentication, string targetBranch, bool noFetch, string workingDirectory, string commitId)
2823
{
2924
var gitDir = Repository.Discover(workingDirectory);
30-
using (var repo = fileSystem.GetRepository(gitDir))
25+
using (var repo = GetRepository(gitDir))
3126
{
32-
// Maybe using timestamp in .git/refs directory is enough?
33-
var ticks = fileSystem.GetLastDirectoryWrite(Path.Combine(gitDir, "refs"));
34-
var key = string.Format("{0}:{1}:{2}:{3}", gitDir, repo.Head.CanonicalName, repo.Head.Tip.Sha, ticks);
35-
36-
var versionVariables = LoadVersionVariablesFromDiskCache(key, gitDir);
27+
var versionVariables = gitVersionCache.LoadVersionVariablesFromDiskCache(repo, gitDir);
3728
if (versionVariables == null)
3829
{
39-
versionVariables = ExecuteInternal(targetUrl, dynamicRepositoryLocation, authentication, targetBranch, noFetch, workingDirectory, commitId);
40-
WriteVariablesToDiskCache(key, gitDir, versionVariables);
30+
versionVariables = ExecuteInternal(targetUrl, dynamicRepositoryLocation, authentication, targetBranch, noFetch, workingDirectory, commitId, repo);
31+
gitVersionCache.WriteVariablesToDiskCache(repo, gitDir, versionVariables);
4132
}
4233

4334
return versionVariables;
4435
}
4536
}
4637

47-
void WriteVariablesToDiskCache(string key, string gitDir, VersionVariables variablesFromCache)
48-
{
49-
var cacheFileName = GetCacheFileName(key, GetCacheDir(gitDir));
50-
variablesFromCache.FileName = cacheFileName;
51-
52-
using (var stream = fileSystem.OpenWrite(cacheFileName))
53-
{
54-
using (var sw = new StreamWriter(stream))
55-
{
56-
Dictionary<string, string> dictionary;
57-
using (Logger.IndentLog("Creating dictionary"))
58-
{
59-
dictionary = variablesFromCache.ToDictionary(x => x.Key, x => x.Value);
60-
}
61-
62-
using (Logger.IndentLog("Storing version variables to cache file " + cacheFileName))
63-
{
64-
var serializer = new Serializer();
65-
serializer.Serialize(sw, dictionary);
66-
}
67-
}
68-
}
69-
}
70-
7138
public bool TryGetVersion(string directory, out VersionVariables versionVariables, bool noFetch, Authentication authentication)
7239
{
7340
try
@@ -96,66 +63,7 @@ static string ResolveCurrentBranch(IBuildServer buildServer, string targetBranch
9663
return currentBranch;
9764
}
9865

99-
VersionVariables LoadVersionVariablesFromDiskCache(string key, string gitDir)
100-
{
101-
using (Logger.IndentLog("Loading version variables from disk cache"))
102-
{
103-
// If the cacheDir already exists, CreateDirectory just won't do anything (it won't fail). @asbjornu
104-
105-
var cacheDir = GetCacheDir(gitDir);
106-
fileSystem.CreateDirectory(cacheDir);
107-
var cacheFileName = GetCacheFileName(key, cacheDir);
108-
VersionVariables vv = null;
109-
if (fileSystem.Exists(cacheFileName))
110-
{
111-
using (Logger.IndentLog("Deserializing version variables from cache file " + cacheFileName))
112-
{
113-
try
114-
{
115-
vv = VersionVariables.FromFile(cacheFileName, fileSystem);
116-
}
117-
catch (Exception ex)
118-
{
119-
Logger.WriteWarning("Unable to read cache file " + cacheFileName + ", deleting it.");
120-
Logger.WriteInfo(ex.ToString());
121-
try
122-
{
123-
fileSystem.Delete(cacheFileName);
124-
}
125-
catch (Exception deleteEx)
126-
{
127-
Logger.WriteWarning(string.Format("Unable to delete corrupted version cache file {0}. Got {1} exception.", cacheFileName, deleteEx.GetType().FullName));
128-
}
129-
}
130-
}
131-
}
132-
else
133-
{
134-
Logger.WriteInfo("Cache file " + cacheFileName + " not found.");
135-
}
136-
137-
return vv;
138-
}
139-
}
140-
141-
static string GetCacheFileName(string key, string cacheDir)
142-
{
143-
string cacheKey;
144-
using (var sha1 = SHA1.Create())
145-
{
146-
// Make a shorter key by hashing, to avoid having to long cache filename.
147-
cacheKey = BitConverter.ToString(sha1.ComputeHash(Encoding.UTF8.GetBytes(key))).Replace("-", "");
148-
}
149-
var cacheFileName = string.Concat(Path.Combine(cacheDir, cacheKey), ".yml");
150-
return cacheFileName;
151-
}
152-
153-
static string GetCacheDir(string gitDir)
154-
{
155-
return Path.Combine(gitDir, "gitversion_cache");
156-
}
157-
158-
VersionVariables ExecuteInternal(string targetUrl, string dynamicRepositoryLocation, Authentication authentication, string targetBranch, bool noFetch, string workingDirectory, string commitId)
66+
VersionVariables ExecuteInternal(string targetUrl, string dynamicRepositoryLocation, Authentication authentication, string targetBranch, bool noFetch, string workingDirectory, string commitId, IRepository repo)
15967
{
16068
// Normalise if we are running on build server
16169
var gitPreparer = new GitPreparer(targetUrl, dynamicRepositoryLocation, authentication, noFetch, workingDirectory);
@@ -172,18 +80,36 @@ VersionVariables ExecuteInternal(string targetUrl, string dynamicRepositoryLocat
17280
// TODO Link to wiki article
17381
throw new Exception(string.Format("Failed to prepare or find the .git directory in path '{0}'.", workingDirectory));
17482
}
175-
VersionVariables variables;
17683
var versionFinder = new GitVersionFinder();
17784
var configuration = ConfigurationProvider.Provide(projectRoot, fileSystem);
17885

179-
using (var repo = fileSystem.GetRepository(dotGitDirectory))
86+
var gitVersionContext = new GitVersionContext(repo, configuration, commitId : commitId);
87+
var semanticVersion = versionFinder.FindVersion(gitVersionContext);
88+
89+
return VariableProvider.GetVariablesFor(semanticVersion, gitVersionContext.Configuration, gitVersionContext.IsCurrentCommitTagged);
90+
}
91+
92+
IRepository GetRepository(string gitDirectory)
93+
{
94+
try
18095
{
181-
var gitVersionContext = new GitVersionContext(repo, configuration, commitId : commitId);
182-
var semanticVersion = versionFinder.FindVersion(gitVersionContext);
183-
variables = VariableProvider.GetVariablesFor(semanticVersion, gitVersionContext.Configuration, gitVersionContext.IsCurrentCommitTagged);
184-
}
96+
var repository = new Repository(gitDirectory);
18597

186-
return variables;
98+
var branch = repository.Head;
99+
if (branch.Tip == null)
100+
{
101+
throw new WarningException("No Tip found. Has repo been initialized?");
102+
}
103+
return repository;
104+
}
105+
catch (Exception exception)
106+
{
107+
if (exception.Message.Contains("LibGit2Sharp.Core.NativeMethods") || exception.Message.Contains("FilePathMarshaler"))
108+
{
109+
throw new WarningException("Restart of the process may be required to load an updated version of LibGit2Sharp.");
110+
}
111+
throw;
112+
}
187113
}
188114
}
189115
}

src/GitVersionCore/GitVersionCache.cs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
namespace GitVersion
2+
{
3+
using System;
4+
using System.Collections.Generic;
5+
using System.IO;
6+
using System.Linq;
7+
using System.Security.Cryptography;
8+
using System.Text;
9+
using GitVersion.Helpers;
10+
using LibGit2Sharp;
11+
using YamlDotNet.Serialization;
12+
13+
public class GitVersionCache
14+
{
15+
readonly IFileSystem fileSystem;
16+
17+
public GitVersionCache(IFileSystem fileSystem)
18+
{
19+
this.fileSystem = fileSystem;
20+
}
21+
22+
public void WriteVariablesToDiskCache(IRepository repo, string gitDir, VersionVariables variablesFromCache)
23+
{
24+
var cacheFileName = GetCacheFileName(GetKey(repo, gitDir), GetCacheDir(gitDir));
25+
variablesFromCache.FileName = cacheFileName;
26+
27+
using (var stream = fileSystem.OpenWrite(cacheFileName))
28+
{
29+
using (var sw = new StreamWriter(stream))
30+
{
31+
Dictionary<string, string> dictionary;
32+
using (Logger.IndentLog("Creating dictionary"))
33+
{
34+
dictionary = variablesFromCache.ToDictionary(x => x.Key, x => x.Value);
35+
}
36+
37+
using (Logger.IndentLog("Storing version variables to cache file " + cacheFileName))
38+
{
39+
var serializer = new Serializer();
40+
serializer.Serialize(sw, dictionary);
41+
}
42+
}
43+
}
44+
}
45+
46+
public VersionVariables LoadVersionVariablesFromDiskCache(IRepository repo, string gitDir)
47+
{
48+
using (Logger.IndentLog("Loading version variables from disk cache"))
49+
{
50+
// If the cacheDir already exists, CreateDirectory just won't do anything (it won't fail). @asbjornu
51+
52+
var cacheDir = GetCacheDir(gitDir);
53+
fileSystem.CreateDirectory(cacheDir);
54+
var cacheFileName = GetCacheFileName(GetKey(repo, gitDir), cacheDir);
55+
VersionVariables vv = null;
56+
if (fileSystem.Exists(cacheFileName))
57+
{
58+
using (Logger.IndentLog("Deserializing version variables from cache file " + cacheFileName))
59+
{
60+
try
61+
{
62+
vv = VersionVariables.FromFile(cacheFileName, fileSystem);
63+
}
64+
catch (Exception ex)
65+
{
66+
Logger.WriteWarning("Unable to read cache file " + cacheFileName + ", deleting it.");
67+
Logger.WriteInfo(ex.ToString());
68+
try
69+
{
70+
fileSystem.Delete(cacheFileName);
71+
}
72+
catch (Exception deleteEx)
73+
{
74+
Logger.WriteWarning(string.Format("Unable to delete corrupted version cache file {0}. Got {1} exception.", cacheFileName, deleteEx.GetType().FullName));
75+
}
76+
}
77+
}
78+
}
79+
else
80+
{
81+
Logger.WriteInfo("Cache file " + cacheFileName + " not found.");
82+
}
83+
84+
return vv;
85+
}
86+
}
87+
88+
string GetKey(IRepository repo, string gitDir)
89+
{
90+
// Maybe using timestamp in .git/refs directory is enough?
91+
var ticks = fileSystem.GetLastDirectoryWrite(Path.Combine(gitDir, "refs"));
92+
return string.Format("{0}:{1}:{2}:{3}", gitDir, repo.Head.CanonicalName, repo.Head.Tip.Sha, ticks);
93+
}
94+
95+
static string GetCacheFileName(string key, string cacheDir)
96+
{
97+
string cacheKey;
98+
using (var sha1 = SHA1.Create())
99+
{
100+
// Make a shorter key by hashing, to avoid having to long cache filename.
101+
cacheKey = BitConverter.ToString(sha1.ComputeHash(Encoding.UTF8.GetBytes(key))).Replace("-", "");
102+
}
103+
var cacheFileName = string.Concat(Path.Combine(cacheDir, cacheKey), ".yml");
104+
return cacheFileName;
105+
}
106+
107+
static string GetCacheDir(string gitDir)
108+
{
109+
return Path.Combine(gitDir, "gitversion_cache");
110+
}
111+
}
112+
}

src/GitVersionCore/GitVersionCore.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
<Compile Include="EffectiveConfiguration.cs" />
111111
<Compile Include="ExecuteCore.cs" />
112112
<Compile Include="GitPreparer.cs" />
113+
<Compile Include="GitVersionCache.cs" />
113114
<Compile Include="Helpers\FileSystem.cs" />
114115
<Compile Include="Helpers\IFileSystem.cs" />
115116
<Compile Include="Helpers\ProcessHelper.cs" />

0 commit comments

Comments
 (0)