Skip to content

Cache broken with GitVersionTask 3.6.0 #942

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
tpluscode opened this issue Jul 14, 2016 · 31 comments
Closed

Cache broken with GitVersionTask 3.6.0 #942

tpluscode opened this issue Jul 14, 2016 · 31 comments

Comments

@tpluscode
Copy link

I've just updated the MSBuild task package and apparently the cache is broken. Each target (WriteVersionInfoToBuildLog, WriteVersionInfoToBuildLog and GitVersion) in each built project recalculates the version number from scratch. Every time I see

Cache file C:\TeamCity\buildAgent\work\aaa244402599d927\.git\gitversion_cache\XYZ123.yml not found

And every time (during one build) the XYZ123 part is different. Worked good in version 3.5.4

@Martin-Andersen
Copy link

Martin-Andersen commented Jul 14, 2016

@tpluscode glad I am not alone and not crazy. I am getting the exact same error
[WriteVersionInfoToBuildLog] INFO [07/14/16 13:55:32:72] Cache file C:\TeamCity\buildAgent\work\e21746ec702e5b5d\.git\gitversion_cache\3012A3203E0BBA59F13642534D34033F6E301B69.yml not found.

It takes forever when the cache is not working.

@DanielRose
Copy link
Contributor

I have the same problem here. My build time went from MSBuild (3m:08s) to MSBuild (2h:04m:52s)!

Even while building an assembly, the name of the cache file keeps on changing for every step:
[WriteVersionInfoToBuildLog] INFO [07/13/16 10:42:19:40] Cache file C:\TeamCity_BuildAgent_2\work\60f2387b524bacc3\.git\gitversion_cache\B979AEEFD2C8836D2DD487256DFC4D03DFE097D1.yml not found.
[UpdateAssemblyInfo] INFO [07/13/16 10:42:51:60] Cache file C:\TeamCity_BuildAgent_2\work\60f2387b524bacc3\.git\gitversion_cache\4641D2244564FA2AAB7EE360234E2E4AD9544A41.yml not found.
[GetVersion] INFO [07/13/16 10:43:17:57] Cache file C:\TeamCity_BuildAgent_2\work\60f2387b524bacc3\.git\gitversion_cache\4F5D3967671A88C07685ED01CF8CCF4AC536CD7C.yml not found.

@JakeGinnivan
Copy link
Contributor

Hey, thanks for posting the issue.

Not sure why this is happening, the code to calculate the hash is:

var ticks = fileSystem.GetLastDirectoryWrite(Path.Combine(gitDir, "refs"));
var configPath = Path.Combine(repo.GetRepositoryDirectory(), "GitVersionConfig.yaml");
var configText = fileSystem.Exists(configPath) ? fileSystem.ReadAllText(configPath) : null;
var configHash = configText != null ? GetHash(configText) : null;
return string.Join(":", gitDir, repo.Head.CanonicalName, repo.Head.Tip.Sha, ticks, configHash);

The result is then hashed. As far as I know the only change is including the config file contents in the hash. Thoughts @asbjornu ?

@JakeGinnivan
Copy link
Contributor

Can you try 3.6.1, we received a PR which cleaned up and fixed some issues around cache.

@DanielRose
Copy link
Contributor

Still broken:

[WriteVersionInfoToBuildLog]   INFO [07/18/16 15:04:21:19] Cache file C:\TeamCity_BuildAgent_2\work\60f2387b524bacc3\.git\gitversion_cache\C29370840A305E08427BF70EDB4606F5D5CEE775.yml not found.
[UpdateAssemblyInfo]   INFO [07/18/16 15:04:53:64] Cache file C:\TeamCity_BuildAgent_2\work\60f2387b524bacc3\.git\gitversion_cache\5D1DF19B99D8DA89FB856708DB2FC6C5037E0962.yml not found.
[GetVersion]   INFO [07/18/16 15:05:19:65] Cache file C:\TeamCity_BuildAgent_2\work\60f2387b524bacc3\.git\gitversion_cache\863886002D4EC50937D970DD5EE8B93E9ECF5376.yml not found.

@DanielRose
Copy link
Contributor

@JakeGinnivan I made a local build of GitVersion, where I added a bunch of debug info:

[WriteVersionInfoToBuildLog] INFO [07/20/16 12:30:33:24] Applicable build agent found: 'TeamCity'.
[WriteVersionInfoToBuildLog] INFO [07/20/16 12:30:33:24] Branch from build environment: refs/heads/master
[WriteVersionInfoToBuildLog] INFO [07/20/16 12:30:33:40] IsDynamicGitRepository: False
[WriteVersionInfoToBuildLog] INFO [07/20/16 12:30:33:40] Returning Project Root from DotGitDirectory: C:\TeamCity_BuildAgent_1\work\a458f20b6997ff65\.git - C:\TeamCity_BuildAgent_1\work\a458f20b6997ff65
[WriteVersionInfoToBuildLog] INFO [07/20/16 12:30:33:40] Project root is: C:\TeamCity_BuildAgent_1\work\a458f20b6997ff65
[WriteVersionInfoToBuildLog] INFO [07/20/16 12:30:33:40] DotGit directory is: C:\TeamCity_BuildAgent_1\work\a458f20b6997ff65\.git
[WriteVersionInfoToBuildLog] INFO [07/20/16 12:30:33:40] Begin: Creating cache key
[WriteVersionInfoToBuildLog]   INFO [07/20/16 12:30:33:40] dotGitDirectory: C:\TeamCity_BuildAgent_1\work\a458f20b6997ff65\.git
[WriteVersionInfoToBuildLog]   INFO [07/20/16 12:30:33:40] fileSystem.Type: GitVersion.Helpers.FileSystem
[WriteVersionInfoToBuildLog]   INFO [07/20/16 12:30:33:41] lastWriteDirectory: C:\TeamCity_BuildAgent_1\work\a458f20b6997ff65\.git\refs\heads at 2016-07-20T10:30:33.3694796Z
[WriteVersionInfoToBuildLog]   INFO [07/20/16 12:30:33:42] lastGitRefsChangedTicks: 636046074333694796
[WriteVersionInfoToBuildLog]   INFO [07/20/16 12:30:33:42] gitSystemHash: F5EE0BFC8A9ED77A73138C9758BBDB94582F334E
[WriteVersionInfoToBuildLog]   INFO [07/20/16 12:30:33:42] IsDynamicGitRepository: False
[WriteVersionInfoToBuildLog]   INFO [07/20/16 12:30:33:42] Returning Project Root from DotGitDirectory: C:\TeamCity_BuildAgent_1\work\a458f20b6997ff65\.git - C:\TeamCity_BuildAgent_1\work\a458f20b6997ff65
[WriteVersionInfoToBuildLog]   INFO [07/20/16 12:30:33:42] configFileHash: 
[WriteVersionInfoToBuildLog]   INFO [07/20/16 12:30:33:42] repositorySnapshotHash: 9DC5F0788F5E28502467DE855A9B70A9E678F540
[WriteVersionInfoToBuildLog]   INFO [07/20/16 12:30:33:43] overrideConfigHash: 
[WriteVersionInfoToBuildLog]   INFO [07/20/16 12:30:33:43] compositeHash: 745008D89B2C3CF1ABDA43F572E94F48830177F8
[WriteVersionInfoToBuildLog] INFO [07/20/16 12:30:33:43] End: Creating cache key (Took: 26.00ms)


[UpdateAssemblyInfo] INFO [07/20/16 12:30:33:59] Applicable build agent found: 'TeamCity'.
[UpdateAssemblyInfo] INFO [07/20/16 12:30:33:59] Branch from build environment: refs/heads/master
[UpdateAssemblyInfo] INFO [07/20/16 12:30:33:61] IsDynamicGitRepository: False
[UpdateAssemblyInfo] INFO [07/20/16 12:30:33:62] Returning Project Root from DotGitDirectory: C:\TeamCity_BuildAgent_1\work\a458f20b6997ff65\.git - C:\TeamCity_BuildAgent_1\work\a458f20b6997ff65
[UpdateAssemblyInfo] INFO [07/20/16 12:30:33:62] Project root is: C:\TeamCity_BuildAgent_1\work\a458f20b6997ff65
[UpdateAssemblyInfo] INFO [07/20/16 12:30:33:62] DotGit directory is: C:\TeamCity_BuildAgent_1\work\a458f20b6997ff65\.git
[UpdateAssemblyInfo] INFO [07/20/16 12:30:33:62] Begin: Creating cache key
[UpdateAssemblyInfo]   INFO [07/20/16 12:30:33:62] dotGitDirectory: C:\TeamCity_BuildAgent_1\work\a458f20b6997ff65\.git
[UpdateAssemblyInfo]   INFO [07/20/16 12:30:33:62] fileSystem.Type: GitVersion.Helpers.FileSystem
[UpdateAssemblyInfo]   INFO [07/20/16 12:30:33:62] lastWriteDirectory: C:\TeamCity_BuildAgent_1\work\a458f20b6997ff65\.git\refs\heads at 2016-07-20T10:30:33.6024930Z
[UpdateAssemblyInfo]   INFO [07/20/16 12:30:33:62] lastGitRefsChangedTicks: 636046074336024930
[UpdateAssemblyInfo]   INFO [07/20/16 12:30:33:62] gitSystemHash: 8EDD4CCE752764B9B7FA39DD136356CA4A3A1A36
[UpdateAssemblyInfo]   INFO [07/20/16 12:30:33:62] IsDynamicGitRepository: False
[UpdateAssemblyInfo]   INFO [07/20/16 12:30:33:62] Returning Project Root from DotGitDirectory: C:\TeamCity_BuildAgent_1\work\a458f20b6997ff65\.git - C:\TeamCity_BuildAgent_1\work\a458f20b6997ff65
[UpdateAssemblyInfo]   INFO [07/20/16 12:30:33:62] configFileHash: 
[UpdateAssemblyInfo]   INFO [07/20/16 12:30:33:62] repositorySnapshotHash: 9DC5F0788F5E28502467DE855A9B70A9E678F540
[UpdateAssemblyInfo]   INFO [07/20/16 12:30:33:62] overrideConfigHash: 
[UpdateAssemblyInfo]   INFO [07/20/16 12:30:33:62] compositeHash: 183C0614646EBAE4F8EB31BE8ADC17B4A74F488D
[UpdateAssemblyInfo] INFO [07/20/16 12:30:33:62] End: Creating cache key (Took: 8.00ms)

The difference is when getting the last directory write. If you look at the timestamps, the write to .git\refs\heads happens somewhere between Branch from build environment: refs/heads/master and IsDynamicGitRepository: False

@DanielRose
Copy link
Contributor

DanielRose commented Jul 20, 2016

After some more tests: In GitPreparer.Initialise(), it calls GitRepositoryHelper.NormalizeGitDirectory() This method modifies the repository, which causes the cache to fail.

It is a side effect of commit acce06c: the git directory is always normalized in a build server.

@tofutim
Copy link
Contributor

tofutim commented Jul 20, 2016

I have the problem too. It just happened that I was finally able to get my big solution into TeamCity right as 3.6.0 was available.

@tofutim
Copy link
Contributor

tofutim commented Jul 20, 2016

Seems like the problem persists in 3.6.1 - my builds are taking 20 min instead of a few minutes or less\

@JakeGinnivan
Copy link
Contributor

Awesome investigation @DanielRose

If someone wants to pick this up that would be great.

@tofutim
Copy link
Contributor

tofutim commented Jul 21, 2016

tofutim referenced this issue Jul 21, 2016
Removed redundant call to gitPreparer.Initialise since it's now called earlier
@DanielRose
Copy link
Contributor

@tofutim Yes, that is what is being run. However, I don't understand the reason for that. Perhaps it is necessary to run that (once?) for dynamic repositories?

@tofutim
Copy link
Contributor

tofutim commented Jul 21, 2016

@rubenmamo Hi Ruben, can you shed some light on this? I am running TeamCity and the new changes make each instance of GitVersionTask (in every assembly!) to compute the gitversion instead of using the task. Three questions: (1) what does it mean to NormalizeGitDirectory, (2) why does it change the repository (each time it is run), and (3) why force buildServer to do this?

In my case I have a single Visual Studio solution with about 10-12 projects each calling GitVersionTask.

Update. (1) the notes say

    //     Normalisation of a git directory turns all remote branches into local branches,
    //     turns pull request refs into a real branch and a few other things. This is designed
    //     to be run *only on the build server* which checks out repositories in different
    //     ways. It is not recommended to run normalisation against a local repository

It seems that this may be true for some build servers, but not so for TeamCity, which will checkout branches as needed. So buildServer != null should not be used in generally.

(2) perhaps there is something in normalization that is tied to the datetime of normalization, which then changes the cache key Seems that this is the best thing to fix, if you normalize the repo, shouldn't it always produce the same cache key?

(3) We need @rubenmamo to tell us which build server he was targeting and perhaps filter on that. At least if the buildserver.Contains("TeamCity") we should NOT normalize. Or maybe the condition should be buildServer != null && buildServer != "GitVersion.TeamCity".

2016-07-21 10:27:06 INFO [07/21/16 10:27:06:62] Applicable build agent found: 'TeamCity'.
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:62] buildServer is: GitVersion.TeamCity
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:62] IsDynamicGitRepository is: False
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:62] dynamicRepositoryLocation is:
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:62] !string.IsNullOrWhiteSpace(dynamicRepositoryLocation) is: False
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:64] IsDynamicGitRepository: False
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:64] Returning Project Root from DotGitDirectory: C:\TeamCity\buildAgent\work\1fae5bbba96b8466.git - C:\TeamCity\buildAgent\work\1fae5bbba96b8466
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:71] !cacheKey (live) is: F123DEC80EA4C5D324463E177E1D1FEB465A82D9!
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:71] Branch from build environment: refs/heads/master
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:86] gitPreparer.Initialised
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:86] IsDynamicGitRepository: False
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:86] Returning Project Root from DotGitDirectory: C:\TeamCity\buildAgent\work\1fae5bbba96b8466.git - C:\TeamCity\buildAgent\work\1fae5bbba96b8466
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] !cacheKey (live) is: CCEC9C6D925549AFD3393BF1FC144ED0C01CE22B!
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] DotGit directory is: C:\TeamCity\buildAgent\work\1fae5bbba96b8466.git
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] IsDynamicGitRepository: False
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] Returning Project Root from DotGitDirectory: C:\TeamCity\buildAgent\work\1fae5bbba96b8466.git - C:\TeamCity\buildAgent\work\1fae5bbba96b8466
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] !cacheKey (live) is: CCEC9C6D925549AFD3393BF1FC144ED0C01CE22B!
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] IsDynamicGitRepository: False
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] Returning Project Root from DotGitDirectory: C:\TeamCity\buildAgent\work\1fae5bbba96b8466.git - C:\TeamCity\buildAgent\work\1fae5bbba96b8466
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] Project root is: C:\TeamCity\buildAgent\work\1fae5bbba96b8466
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] IsDynamicGitRepository: False
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] Returning Project Root from DotGitDirectory: C:\TeamCity\buildAgent\work\1fae5bbba96b8466.git - C:\TeamCity\buildAgent\work\1fae5bbba96b8466
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:62] Applicable build agent found: 'TeamCity'.
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:62] buildServer is: GitVersion.TeamCity
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:62] IsDynamicGitRepository is: False
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:62] dynamicRepositoryLocation is:
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:62] !string.IsNullOrWhiteSpace(dynamicRepositoryLocation) is: False
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:64] IsDynamicGitRepository: False
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:64] Returning Project Root from DotGitDirectory: C:\TeamCity\buildAgent\work\1fae5bbba96b8466.git - C:\TeamCity\buildAgent\work\1fae5bbba96b8466
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:71] !cacheKey (live) is: F123DEC80EA4C5D324463E177E1D1FEB465A82D9!
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:71] Branch from build environment: refs/heads/master
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:86] gitPreparer.Initialised
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:86] IsDynamicGitRepository: False
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:86] Returning Project Root from DotGitDirectory: C:\TeamCity\buildAgent\work\1fae5bbba96b8466.git - C:\TeamCity\buildAgent\work\1fae5bbba96b8466
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] !cacheKey (live) is: CCEC9C6D925549AFD3393BF1FC144ED0C01CE22B!
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] DotGit directory is: C:\TeamCity\buildAgent\work\1fae5bbba96b8466.git
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] IsDynamicGitRepository: False
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] Returning Project Root from DotGitDirectory: C:\TeamCity\buildAgent\work\1fae5bbba96b8466.git - C:\TeamCity\buildAgent\work\1fae5bbba96b8466
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] !cacheKey (live) is: CCEC9C6D925549AFD3393BF1FC144ED0C01CE22B!
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] IsDynamicGitRepository: False
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] Returning Project Root from DotGitDirectory: C:\TeamCity\buildAgent\work\1fae5bbba96b8466.git - C:\TeamCity\buildAgent\work\1fae5bbba96b8466
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] Project root is: C:\TeamCity\buildAgent\work\1fae5bbba96b8466
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] IsDynamicGitRepository: False
2016-07-21 10:27:06 INFO [07/21/16 10:27:06:87] Returning Project Root from DotGitDirectory: C:\TeamCity\buildAgent\work\1fae5bbba96b8466.git - C:\TeamCity\buildAgent\work\1fae5bbba96b8466

I'm not sure I can make any further progress. Hopefully this is helpful to someone.

**Update 2. I forgot to add that it is not a problem for the cachekey to change, but it is a problem for the cachekey to change if you normalize a second time. In my first run,

cacheKey starts: B1CDC2E9CA33CDDDF9D0476308147083C9C0812D!!
after Initialize: F123DEC80EA4C5D324463E177E1D1FEB465A82D9!!

In the second run (same repo, just run GitVersion again),

cacheKey starts: F123DEC80EA4C5D324463E177E1D1FEB465A82D9!
after Initialize: CCEC9C6D925549AFD3393BF1FC144ED0C01CE22B!

This gives me the idea that maybe you should put a marker that says this repo has been normalized. If it has been normalized already, do not re-normalize.

@rubenmamo
Copy link
Contributor

rubenmamo commented Jul 21, 2016

Hi All,

The change in question was done to get gitversion to work again when using dynamic repositories on team city as this was broken in an earlier commit which was not calling the initialize method before using stuff which is initialized in that particular method.

In my environment I cannot use agent side checkout in TeamCity and call the gitversion command line as the first build step (I'm not using the ms build task) and hence I need to use dynamic repositories which meant I couldn't use the latest version of gitversion.

The change I made Was to get the initialize method to be called earlier (previously it was called in executeinternal) and hence I did not change its structure apart from modifying how it determines that it's a dynamic repository.

Hope this helps.

Ruben

@tofutim
Copy link
Contributor

tofutim commented Jul 22, 2016

@rubenmamo I would like to get your feedback on a possible solution. Given,

gitPreparer.Initialise(buildServer != null, ResolveCurrentBranch(buildServer, targetBranch, !string.IsNullOrWhiteSpace(dynamicRepositoryLocation)));

instead of forcing all build servers to normalize, the normalization test should instead be for gitPreparer.IsDynamicGitRepository so that we have

gitPreparer.Initialise(gitPreparer.IsDynamicGitRepository,ResolveCurrentBranch(buildServer, targetBranch, !string.IsNullOrWhiteSpace(dynamicRepositoryLocation)));

(actually that IsNullOrWhitesSpace could also use gitPreparer.IsDynamicGitRepository). BTW, for your TeamCity setup, how are you doing it? Are you doing the .git checkout server-side? I am doing the .git checkout agent-side.

Update. Scratch that, undoing that breaks some of the tests. Maybe the real problem is that repeated normalization changes the hash. Why does it change the hash? Because:

    private static string GetGitSystemHash(GitPreparer gitPreparer, IFileSystem fileSystem)
    {
        var dotGitDirectory = gitPreparer.GetDotGitDirectory();

        // Maybe using timestamp in .git/refs directory is enough?
        var lastGitRefsChangedTicks = fileSystem.GetLastDirectoryWrite(Path.Combine(dotGitDirectory, "refs"));

        return GetHash(dotGitDirectory, lastGitRefsChangedTicks.ToString());
    }

Maybe using timestamp in .git/refs directory is not enough.

Question. How would one test for the cache in the tests?

@DanielRose
Copy link
Contributor

As I stated above, the problem is that every run of GitRepositoryHelper.NormalizeGitDirectory() changes the git repository. Maybe a next step would be to find out what is actually being done there, and why rerunning it modifies the repository again?

@tofutim
Copy link
Contributor

tofutim commented Jul 22, 2016

The problem here is that normalizeGitDirectory touches the LastDirectoryWrite on .git/refs. So even though the content is the same, the Hash ends up different because of GetGitSystemHash. I just wrote a test for this. Now I want to traverse .git/refs directory, make a list of names and directories, and use that in the hash instead of the timestamp.

The test looks like this:

[Test]
public void CacheKeySameAfterReNormalizing()
{
    var versionAndBranchFinder = new ExecuteCore(fileSystem);

    RepositoryScope(versionAndBranchFinder, (fixture, vv) =>
    {
        var targetUrl = "https://github.com/GitTools/GitVersion.git";
        var targetBranch = "refs/head/master";
        var gitPreparer = new GitPreparer(targetUrl, null, new Authentication(), false, fixture.RepositoryPath);
        var cacheKey1 = GitVersionCacheKeyFactory.Create(fileSystem, gitPreparer, null);
        gitPreparer.Initialise(true, targetBranch);
        var cacheKey2 = GitVersionCacheKeyFactory.Create(fileSystem, gitPreparer, null);
        gitPreparer.Initialise(true, targetBranch);
        var cacheKey3 = GitVersionCacheKeyFactory.Create(fileSystem, gitPreparer, null);

        cacheKey3.Value.ShouldBe(cacheKey2.Value);
    });        
}

Update. Thinking something like this

 private static string GetGitSystemHash(GitPreparer gitPreparer, IFileSystem fileSystem)
    {
        var dotGitDirectory = gitPreparer.GetDotGitDirectory();

        // traverse the directory and get a list of files, use that for GetHash
        var traverse = TraverseTree(dotGitDirectory);

        return GetHash(traverse.ToArray());

        /* No!
        // Maybe using timestamp in .git/refs directory is enough?
        var lastGitRefsChangedTicks = fileSystem.GetLastDirectoryWrite(Path.Combine(dotGitDirectory, "refs"));

        return GetHash(dotGitDirectory, lastGitRefsChangedTicks.ToString());
        */
    }

    // lifted from https://msdn.microsoft.com/en-us/library/bb513869.aspx
    public static List<string> TraverseTree(string root)
    {
        var result = new List<string>();
        result.Add(root);
        // Data structure to hold names of subfolders to be
        // examined for files.
        Stack<string> dirs = new Stack<string>(20);

        if (!System.IO.Directory.Exists(root))
        {
            throw new ArgumentException();
        }

        dirs.Push(root);

        while (dirs.Count > 0)
        {
            string currentDir = dirs.Pop();

            var di = new DirectoryInfo(currentDir);
            result.Add(di.Name);

            string[] subDirs;
            try
            {
                subDirs = System.IO.Directory.GetDirectories(currentDir);
            }
            // An UnauthorizedAccessException exception will be thrown if we do not have
            // discovery permission on a folder or file. It may or may not be acceptable 
            // to ignore the exception and continue enumerating the remaining files and 
            // folders. It is also possible (but unlikely) that a DirectoryNotFound exception 
            // will be raised. This will happen if currentDir has been deleted by
            // another application or thread after our call to Directory.Exists. The 
            // choice of which exceptions to catch depends entirely on the specific task 
            // you are intending to perform and also on how much you know with certainty 
            // about the systems on which this code will run.
            catch (UnauthorizedAccessException e)
            {
                Logger.WriteError(e.Message);
                continue;
            }
            catch (System.IO.DirectoryNotFoundException e)
            {
                Logger.WriteError(e.Message);
                continue;
            }

            string[] files = null;
            try
            {
                files = System.IO.Directory.GetFiles(currentDir);
            }

            catch (UnauthorizedAccessException e)
            {
                Logger.WriteError(e.Message);
                continue;
            }

            catch (System.IO.DirectoryNotFoundException e)
            {
                Logger.WriteError(e.Message);
                continue;
            }
            // Perform the required action on each file here.
            // Modify this block to perform your required task.
            foreach (string file in files)
            {
                try
                {
                    // Perform whatever action is required in your scenario.
                    System.IO.FileInfo fi = new System.IO.FileInfo(file);
                    result.Add(fi.Name);
                    Logger.WriteInfo(string.Format("{0}: {1}, {2}", fi.Name, fi.Length, fi.CreationTime));
                }
                catch (System.IO.FileNotFoundException e)
                {
                    // If file was deleted by a separate application
                    //  or thread since the call to TraverseTree()
                    // then just continue.
                    Logger.WriteError(e.Message);
                    continue;
                }
            }

            // Push the subdirectories onto the stack for traversal.
            // This could also be done before handing the files.
            foreach (string str in subDirs)
                dirs.Push(str);
        }

        return result;
    }

@rubenmamo
Copy link
Contributor

@tofutim, for my TeamCity setup I'm using Server Side Checkout (not as I incorrectly stated in my previous comment which I've now amended). I could not get Agent Side Checkout to work, if I remember correctly I was having issues with using TFS as my Git Server since it requires authentication (it might have been something else, not 100% sure). Since I'm using Server Side Checkout I need to use alternate repositories as document here: (http://gitversion.readthedocs.io/en/latest/build-server-support/build-server/teamcity/).

I did this particular change because the application was failing with an unhandled Exception when using Dynamic Repositories. I traced it down to the way GitPreparer was being initialised and the calls made to GitPreparer. The ExecuteCore was calling GitPreparer.IsDynamicGitRepository before calling GItPreparer.Initialise and GitPreparer.IsDynamicGitRepository requires Initialise to have been called beforehand. I was wary of changing the way GitPreparer is initialised as this would probably have affected other areas and opted to call the initialise earlier. This has obviously caused the issue with the cache. I tried to keep the code similar to what was present before as I did not understand the full ramifications of doing other more structural changes.

@tofutim
Copy link
Contributor

tofutim commented Jul 22, 2016

@rubenmamo Your change is fine. There might be repeated normalization over multiple assemblies (in GitVersionTask) but I think is ok. The biggest problem is that the cache hash (haha) is computed on the timestamp of refs, which the author admits maybe not enough. The real deal is to traverse the refs, which I'm working on. PR soon.

Incidentally, I was able to get agent checkout to work by using uploaded key.

@tofutim
Copy link
Contributor

tofutim commented Jul 22, 2016

Pls someone do a sanity check on my PR. Thanks!

@JakeGinnivan
Copy link
Contributor

Releasing 3.6.2 now. Thanks everyone for your help!

@DanielRose
Copy link
Contributor

Updated to 3.6.2. Cache is working again on my build server. Thanks!

@nzrytmn
Copy link

nzrytmn commented Jan 31, 2017

Hi everybody, i have still same problem with 3.6.5 version on tfs build server. I got same error with extension task , than i tried to add a commandline task and call gitversion.exe by command prompt and i got same error again.

C:\agent_work\3\s.git\gitversion_cache\93DBD2FCAC2BADF8F53E06EE747571BA784376DF.yml not found

.
Because of this error, gitversion.exe never can't calculate the real version number, it always increase version number added by tag for each release.

Do you have a suggestion? Am i have to back an older version recommended above ?

@asbjornu
Copy link
Member

@nzrytmn: GitVersion should calculate the same version number with or without the cache. The only thing the cache should affect, is performance. If you're experiencing different version numbering depending, I would be inclined to conclude that that's another issue.

Also, the line you've quoted is not an error, but expected behaviour if the version should infact change. If running GitVersion on the same commit several times in a row results in the same cache miss being logged, that's a problem, but as I said, that should not affect the version calculation.

@nzrytmn
Copy link

nzrytmn commented Feb 6, 2017

Thanks for information @asbjornu , yes i know it is not an error, but "not found" message makes me feel something is not correct; by the way the main problem is the version number is not increasing that i expect.
I am using gitflow for branching management and these are the steps i did; pls let me know if i did something wrong:
1- Tag the base version as 1.2.0
2- Create a release from this branch and deployed (version number is still 1.2.0)
3- Create two feature branches and add new features. Merge with develop branch.
4- Create a release branch from develop branch and deploy (version number is 1.2.1)
(until here everything is ok, this is what i expect)
5- Create new feature branches and add new features. Merge with develop branch.
6- Create a release branch from develop branch and deploy (version number is still 1.2.1)
(this is unexpected for me , because i added new features and i expect version will be changed)

For my perspective the problem is version tool always calculates version number from base tag and because of this it is calculating same version for each release, it couldn't keep mind the old releases were deployed with same version it gave,

gitversionresult

@asbjornu
Copy link
Member

asbjornu commented Feb 6, 2017

@nzrytmn: What versioning mode are you using? If it's ContinuousDelivery, what you see is expected. Every release is expected to have an associated tag. If you want the behaviour you see in develop, where the minor version is incremented automatically, to also apply to all other branches, you need to switch to mode: ContinuousDeployment.

@nzrytmn
Copy link

nzrytmn commented Feb 8, 2017

@asbjornu I tried both mode, but it is calculating same version number for each release. But you are right, it may be because of my configuration settings. This is my GitVersion.yml file for one of my samples.

branches: {
  master:
    regex: master
    mode: ContinuousDeployment
    tag: ''
    increment: Patch
    prevent-increment-of-merged-branch-version: true
    track-merge-target: false
    tracks-release-branches: false
    is-release-branch: false
  release:
    regex: release?[/-]
    mode: ContinuousDeployment
    tag: beta
    increment: Patch
    prevent-increment-of-merged-branch-version: true
    track-merge-target: false
    tracks-release-branches: false
    is-release-branch: true
  feature:
    regex: feature?[/-]
    mode: ContinuousDeployment
    tag: useBranchName
    increment: Inherit
    prevent-increment-of-merged-branch-version: false
    track-merge-target: false
    tracks-release-branches: false
    is-release-branch: false
  pull-request:
    regex: (pull|pull\-requests|pr)[/-]
    mode: ContinuousDeployment
    tag: PullRequest
    increment: Inherit
    prevent-increment-of-merged-branch-version: false
    tag-number-pattern: '[/-](?<number>\d+)[-/]'
    track-merge-target: false
    tracks-release-branches: false
    is-release-branch: false
  hotfix:
    regex: hotfix(es)?[/-]
    mode: ContinuousDeployment
    tag: beta
    increment: Patch
    prevent-increment-of-merged-branch-version: false
    track-merge-target: false
    tracks-release-branches: false
    is-release-branch: false
  support:
    regex: support[/-]
    mode: ContinuousDeployment
    tag: ''
    increment: Patch
    prevent-increment-of-merged-branch-version: true
    track-merge-target: false
    tracks-release-branches: false
    is-release-branch: false
  develop:
    regex: dev(elop)?(ment)?$
    mode: ContinuousDeployment
    tag: unstable
    increment: Minor
    prevent-increment-of-merged-branch-version: false
    track-merge-target: true
    tracks-release-branches: true
    is-release-branch: false
}
next-version: 1.2
assembly-versioning-scheme: MajorMinorPatch
assembly-informational-format: '{InformationalVersion}'
mode: ContinuousDeployment
increment: Inherit
continuous-delivery-fallback-tag: ci
tag-prefix: '[vV]'
major-version-bump-message: '\+semver:\s?(breaking|major)'
minor-version-bump-message: '\+semver:\s?(feature|minor)'
patch-version-bump-message: '\+semver:\s?(fix|patch)'
no-bump-message: '\+semver:\s?(none|skip)'
legacy-semver-padding: 4
build-metadata-padding: 4
commits-since-version-source-padding: 4
commit-message-incrementing: Enabled
ignore:
  sha: []
  commits-before: yyyy-MM-ddTHH:mm:ss

@asbjornu
Copy link
Member

asbjornu commented Feb 9, 2017

@nzrytmn: Can you please try deleting the GitVersion.yml file and see how GitVersion behaves then?

@dsadikone
Copy link

@nzrytmn I am experiencing a basically identical issue, did you ever resolve it?

@fenneh
Copy link

fenneh commented Sep 7, 2017

Having the exact same issues running 3.6.4. Nothing in the working directory is changing in-between TeamCity builds, the .yml used for the cache can't be found and GitVersion will go off and re-calculate a repo. Taking a build from 4 minutes to 33 minutes.

@asbjornu
Copy link
Member

asbjornu commented Sep 7, 2017

@fenneh: Can you please upgrade to v4 beta 12 and let us know if that fixes the issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests