From e06ffc15ada28f91c5de92f7244bb473bc8b8ca2 Mon Sep 17 00:00:00 2001 From: Markus Horstmann Date: Thu, 4 Feb 2021 12:29:56 -0800 Subject: [PATCH 01/15] Retry some file reads and writes --- src/GitVersion.Core/Model/VersionVariables.cs | 31 +++++++++++++++---- .../OutputGenerator/OutputGenerator.cs | 21 ++++++++++++- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/GitVersion.Core/Model/VersionVariables.cs b/src/GitVersion.Core/Model/VersionVariables.cs index 29e4fda19d..1ee8587f1b 100644 --- a/src/GitVersion.Core/Model/VersionVariables.cs +++ b/src/GitVersion.Core/Model/VersionVariables.cs @@ -166,12 +166,31 @@ public static VersionVariables FromJson(string json) public static VersionVariables FromFile(string filePath, IFileSystem fileSystem) { - using var stream = fileSystem.OpenRead(filePath); - using var reader = new StreamReader(stream); - var dictionary = new Deserializer().Deserialize>(reader); - var versionVariables = FromDictionary(dictionary); - versionVariables.FileName = filePath; - return versionVariables; + int remainingAttempts = 5; + do + { + try + { + using var stream = fileSystem.OpenRead(filePath); + using var reader = new StreamReader(stream); + var dictionary = new Deserializer().Deserialize>(reader); + var versionVariables = FromDictionary(dictionary); + versionVariables.FileName = filePath; + return versionVariables; + } + catch (System.IO.IOException ex) + { + remainingAttempts--; + if (remainingAttempts <= 0) + { + throw; + } + Console.WriteLine($"Error reading file {filePath}. Retrying {remainingAttempts } more times: {ex.Message}"); + Thread.Sleep(1000); + } + } + while (remainingAttempts > 0); + throw new Exception("Read Retry: Should never get here."); } public bool TryGetValue(string variable, out string variableValue) diff --git a/src/GitVersion.Core/VersionConverters/OutputGenerator/OutputGenerator.cs b/src/GitVersion.Core/VersionConverters/OutputGenerator/OutputGenerator.cs index 312424f6a7..a790cdbfed 100644 --- a/src/GitVersion.Core/VersionConverters/OutputGenerator/OutputGenerator.cs +++ b/src/GitVersion.Core/VersionConverters/OutputGenerator/OutputGenerator.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using GitVersion.Logging; using GitVersion.Model; using GitVersion.OutputVariables; @@ -34,7 +35,25 @@ public void Execute(VersionVariables variables, OutputContext context) } if (gitVersionOptions.Output.Contains(OutputType.File)) { - fileSystem.WriteAllText(context.OutputFile, variables.ToString()); + int remainingAttempts = 5; + do + { + try + { + fileSystem.WriteAllText(context.OutputFile, variables.ToString()); + remainingAttempts = 0; + } + catch (System.IO.IOException ex) + { + remainingAttempts--; + if (remainingAttempts <= 0) + { + throw; + } + console.WriteLine($"Error writing file {context.OutputFile}. Retrying {remainingAttempts } more times: {ex.Message}"); + Thread.Sleep(1000); + } + } while (remainingAttempts > 0); } if (gitVersionOptions.Output.Contains(OutputType.Json)) { From bbd195f4df0f706b3319dc3c47b797971c302e96 Mon Sep 17 00:00:00 2001 From: Markus Horstmann Date: Thu, 4 Feb 2021 14:06:50 -0800 Subject: [PATCH 02/15] Add OperationWithExponentialBackoff flavor with return values. --- .../OperationWithExponentialBackoff.cs | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs b/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs index 365e9028b0..373899d078 100644 --- a/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs +++ b/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs @@ -5,14 +5,27 @@ namespace GitVersion.Helpers { - internal class OperationWithExponentialBackoff where T : Exception + internal class OperationWithExponentialBackoff : OperationWithExponentialBackoff where T : Exception + { + public OperationWithExponentialBackoff(IThreadSleep threadSleep, ILog log, Action operation, int maxRetries = 5) + : base(threadSleep, log, () => { operation(); return false; }, maxRetries) + { + } + public new Task ExecuteAsync() + { + return base.ExecuteAsync(); + } + + + } + internal class OperationWithExponentialBackoff where T : Exception { private readonly IThreadSleep threadSleep; private readonly ILog log; - private readonly Action operation; + private readonly Func operation; private readonly int maxRetries; - public OperationWithExponentialBackoff(IThreadSleep threadSleep, ILog log, Action operation, int maxRetries = 5) + public OperationWithExponentialBackoff(IThreadSleep threadSleep, ILog log, Func operation, int maxRetries = 5) { if (maxRetries < 0) throw new ArgumentOutOfRangeException(nameof(maxRetries)); @@ -23,7 +36,7 @@ public OperationWithExponentialBackoff(IThreadSleep threadSleep, ILog log, Actio this.maxRetries = maxRetries; } - public async Task ExecuteAsync() + public async Task ExecuteAsync() { var exceptions = new List(); @@ -36,8 +49,7 @@ public async Task ExecuteAsync() try { - operation(); - break; + return operation(); } catch (T e) { @@ -53,6 +65,7 @@ public async Task ExecuteAsync() sleepMSec *= 2; } + return default; } } } From e7a7ee4c598e6fb0ab44cfd23dd4019c993001ac Mon Sep 17 00:00:00 2001 From: Markus Horstmann Date: Thu, 4 Feb 2021 14:07:47 -0800 Subject: [PATCH 03/15] Use OperationWithExponentialBackoff for retry operations. --- src/GitVersion.Core/Model/VersionVariables.cs | 41 +++++++------------ .../Cache/GitVersionCache.cs | 2 +- .../OutputGenerator/OutputGenerator.cs | 28 ++++--------- .../GitVersionTaskExecutor.cs | 13 +++--- 4 files changed, 31 insertions(+), 53 deletions(-) diff --git a/src/GitVersion.Core/Model/VersionVariables.cs b/src/GitVersion.Core/Model/VersionVariables.cs index 1ee8587f1b..00cae5eb3d 100644 --- a/src/GitVersion.Core/Model/VersionVariables.cs +++ b/src/GitVersion.Core/Model/VersionVariables.cs @@ -164,33 +164,22 @@ public static VersionVariables FromJson(string json) return FromDictionary(variablePairs); } - public static VersionVariables FromFile(string filePath, IFileSystem fileSystem) + public static VersionVariables FromFile(string filePath, IFileSystem fileSystem, ILog log) { - int remainingAttempts = 5; - do - { - try - { - using var stream = fileSystem.OpenRead(filePath); - using var reader = new StreamReader(stream); - var dictionary = new Deserializer().Deserialize>(reader); - var versionVariables = FromDictionary(dictionary); - versionVariables.FileName = filePath; - return versionVariables; - } - catch (System.IO.IOException ex) - { - remainingAttempts--; - if (remainingAttempts <= 0) - { - throw; - } - Console.WriteLine($"Error reading file {filePath}. Retrying {remainingAttempts } more times: {ex.Message}"); - Thread.Sleep(1000); - } - } - while (remainingAttempts > 0); - throw new Exception("Read Retry: Should never get here."); + var retryOperation = new OperationWithExponentialBackoff(new ThreadSleep(), null, () => FromFileInternal(filePath, fileSystem), maxRetries: 6); + + var versionVariables = retryOperation.ExecuteAsync().Result; + return versionVariables; + + } + private static VersionVariables FromFileInternal(string filePath, IFileSystem fileSystem) + { + using var stream = fileSystem.OpenRead(filePath); + using var reader = new StreamReader(stream); + var dictionary = new Deserializer().Deserialize>(reader); + var versionVariables = FromDictionary(dictionary); + versionVariables.FileName = filePath; + return versionVariables; } public bool TryGetValue(string variable, out string variableValue) diff --git a/src/GitVersion.Core/VersionCalculation/Cache/GitVersionCache.cs b/src/GitVersion.Core/VersionCalculation/Cache/GitVersionCache.cs index 2e53231a21..36cd01b68f 100644 --- a/src/GitVersion.Core/VersionCalculation/Cache/GitVersionCache.cs +++ b/src/GitVersion.Core/VersionCalculation/Cache/GitVersionCache.cs @@ -74,7 +74,7 @@ public VersionVariables LoadVersionVariablesFromDiskCache(GitVersionCacheKey key { try { - var loadedVariables = VersionVariables.FromFile(cacheFileName, fileSystem); + var loadedVariables = VersionVariables.FromFile(cacheFileName, fileSystem, log); return loadedVariables; } catch (Exception ex) diff --git a/src/GitVersion.Core/VersionConverters/OutputGenerator/OutputGenerator.cs b/src/GitVersion.Core/VersionConverters/OutputGenerator/OutputGenerator.cs index a790cdbfed..7d17c362de 100644 --- a/src/GitVersion.Core/VersionConverters/OutputGenerator/OutputGenerator.cs +++ b/src/GitVersion.Core/VersionConverters/OutputGenerator/OutputGenerator.cs @@ -1,5 +1,6 @@ using System; -using System.Threading; +using System.IO; +using GitVersion.Helpers; using GitVersion.Logging; using GitVersion.Model; using GitVersion.OutputVariables; @@ -14,13 +15,15 @@ public interface IOutputGenerator : IVersionConverter public class OutputGenerator : IOutputGenerator { private readonly IConsole console; + private readonly ILog log; private readonly IFileSystem fileSystem; private readonly IOptions options; private readonly ICurrentBuildAgent buildAgent; - public OutputGenerator(ICurrentBuildAgent buildAgent, IConsole console, IFileSystem fileSystem, IOptions options) + public OutputGenerator(ICurrentBuildAgent buildAgent, IConsole console, ILog log, IFileSystem fileSystem, IOptions options) { this.console = console ?? throw new ArgumentNullException(nameof(console)); + this.log = log; this.fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); this.options = options ?? throw new ArgumentNullException(nameof(options)); this.buildAgent = buildAgent; @@ -35,25 +38,8 @@ public void Execute(VersionVariables variables, OutputContext context) } if (gitVersionOptions.Output.Contains(OutputType.File)) { - int remainingAttempts = 5; - do - { - try - { - fileSystem.WriteAllText(context.OutputFile, variables.ToString()); - remainingAttempts = 0; - } - catch (System.IO.IOException ex) - { - remainingAttempts--; - if (remainingAttempts <= 0) - { - throw; - } - console.WriteLine($"Error writing file {context.OutputFile}. Retrying {remainingAttempts } more times: {ex.Message}"); - Thread.Sleep(1000); - } - } while (remainingAttempts > 0); + var retryOperation = new OperationWithExponentialBackoff(new ThreadSleep(), log, () => fileSystem.WriteAllText(context.OutputFile, variables.ToString()), maxRetries: 6); + retryOperation.ExecuteAsync().Wait(); } if (gitVersionOptions.Output.Contains(OutputType.Json)) { diff --git a/src/GitVersion.MsBuild/GitVersionTaskExecutor.cs b/src/GitVersion.MsBuild/GitVersionTaskExecutor.cs index f49b275b58..5bf5eee688 100644 --- a/src/GitVersion.MsBuild/GitVersionTaskExecutor.cs +++ b/src/GitVersion.MsBuild/GitVersionTaskExecutor.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using GitVersion.Logging; using GitVersion.MsBuild.Tasks; using GitVersion.OutputVariables; using Microsoft.Extensions.Options; @@ -9,20 +10,22 @@ namespace GitVersion.MsBuild public class GitVersionTaskExecutor : IGitVersionTaskExecutor { private readonly IFileSystem fileSystem; + private readonly ILog log; private readonly IGitVersionOutputTool gitVersionOutputTool; private readonly IOptions options; private VersionVariables versionVariables; - public GitVersionTaskExecutor(IFileSystem fileSystem, IGitVersionOutputTool gitVersionOutputTool, IOptions options) + public GitVersionTaskExecutor(IFileSystem fileSystem, IGitVersionOutputTool gitVersionOutputTool, IOptions options, ILog log) { this.fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); + this.log = log; this.gitVersionOutputTool = gitVersionOutputTool ?? throw new ArgumentNullException(nameof(gitVersionOutputTool)); this.options = options ?? throw new ArgumentNullException(nameof(options)); } public void GetVersion(GetVersion task) { - versionVariables = VersionVariables.FromFile(task.VersionFile, fileSystem); + versionVariables = VersionVariables.FromFile(task.VersionFile, fileSystem, log); var outputType = typeof(GetVersion); foreach (var variable in versionVariables) { @@ -32,7 +35,7 @@ public void GetVersion(GetVersion task) public void UpdateAssemblyInfo(UpdateAssemblyInfo task) { - versionVariables = VersionVariables.FromFile(task.VersionFile, fileSystem); + versionVariables = VersionVariables.FromFile(task.VersionFile, fileSystem, log); FileHelper.DeleteTempFiles(); if (task.CompileFiles != null) FileHelper.CheckForInvalidFiles(task.CompileFiles, task.ProjectFile); @@ -50,7 +53,7 @@ public void UpdateAssemblyInfo(UpdateAssemblyInfo task) public void GenerateGitVersionInformation(GenerateGitVersionInformation task) { - versionVariables = VersionVariables.FromFile(task.VersionFile, fileSystem); + versionVariables = VersionVariables.FromFile(task.VersionFile, fileSystem, log); var fileWriteInfo = task.IntermediateOutputPath.GetFileWriteInfo(task.Language, task.ProjectFile, "GitVersionInformation"); task.GitVersionInformationFilePath = Path.Combine(fileWriteInfo.WorkingDirectory, fileWriteInfo.FileName); @@ -62,7 +65,7 @@ public void GenerateGitVersionInformation(GenerateGitVersionInformation task) public void WriteVersionInfoToBuildLog(WriteVersionInfoToBuildLog task) { - versionVariables = VersionVariables.FromFile(task.VersionFile, fileSystem); + versionVariables = VersionVariables.FromFile(task.VersionFile, fileSystem, log); gitVersionOutputTool.OutputVariables(versionVariables, false); } } From 2b9225c706ef7d8031a80fe4aca457808e65f758 Mon Sep 17 00:00:00 2001 From: Markus Horstmann Date: Thu, 4 Feb 2021 17:54:15 -0800 Subject: [PATCH 04/15] GitVersion.LibGit2Sharp: Add retries on locked files --- src/GitVersion.Core/Core/GitPreparer.cs | 5 +- .../Git/IReferenceCollection.cs | 3 +- src/GitVersion.Core/GitVersion.Core.csproj | 7 +- .../Git/GitRepository.cs | 162 ++++++++++-------- .../Git/ReferenceCollection.cs | 6 +- 5 files changed, 106 insertions(+), 77 deletions(-) diff --git a/src/GitVersion.Core/Core/GitPreparer.cs b/src/GitVersion.Core/Core/GitPreparer.cs index d6ee46e276..236fb1e726 100644 --- a/src/GitVersion.Core/Core/GitPreparer.cs +++ b/src/GitVersion.Core/Core/GitPreparer.cs @@ -3,6 +3,7 @@ using System.Linq; using GitVersion.Common; using GitVersion.Extensions; +using GitVersion.Helpers; using GitVersion.Logging; using GitVersion.Model.Configuration; using Microsoft.Extensions.Options; @@ -329,7 +330,7 @@ private void CreateOrUpdateLocalBranchesFromRemoteTrackingOnes(string remoteName } var remoteRefTipId = remoteTrackingReference.ReferenceTargetId; log.Info($"Updating local ref '{localRef.Name.Canonical}' to point at {remoteRefTipId}."); - repository.Refs.UpdateTarget(localRef, remoteRefTipId); + repository.Refs.UpdateTarget(localRef, remoteRefTipId, log); continue; } @@ -382,7 +383,7 @@ public void EnsureLocalBranchExistsForCurrentBranch(IRemote remote, string curre log.Info(isBranch ? $"Updating local branch {referenceName} to point at {repoTip}" : $"Updating local branch {referenceName} to match ref {currentBranch}"); var localRef = repository.Refs[localCanonicalName]; - repository.Refs.UpdateTarget(localRef, repoTipId); + repository.Refs.UpdateTarget(localRef, repoTipId, log); } repository.Checkout(localCanonicalName); diff --git a/src/GitVersion.Core/Git/IReferenceCollection.cs b/src/GitVersion.Core/Git/IReferenceCollection.cs index 8a60b4e191..935d507c5b 100644 --- a/src/GitVersion.Core/Git/IReferenceCollection.cs +++ b/src/GitVersion.Core/Git/IReferenceCollection.cs @@ -1,3 +1,4 @@ +using GitVersion.Logging; using System.Collections.Generic; namespace GitVersion @@ -7,7 +8,7 @@ public interface IReferenceCollection : IEnumerable IReference Head { get; } IReference this[string name] { get; } void Add(string name, string canonicalRefNameOrObjectish, bool allowOverwrite = false); - void UpdateTarget(IReference directRef, IObjectId targetId); + void UpdateTarget(IReference directRef, IObjectId targetId, ILog log); IEnumerable FromGlob(string prefix); } } diff --git a/src/GitVersion.Core/GitVersion.Core.csproj b/src/GitVersion.Core/GitVersion.Core.csproj index 60b4786406..0949789beb 100644 --- a/src/GitVersion.Core/GitVersion.Core.csproj +++ b/src/GitVersion.Core/GitVersion.Core.csproj @@ -8,10 +8,15 @@ GitVersion.Core GitVersion Derives SemVer information from a repository following GitFlow or GitHubFlow. This is the Core library which both GitVersion cli and Task use allowing programatic usage of GitVersion. - $(AssemblyName) + + + <_Parameter1>GitVersion.LibGit2Sharp + + + diff --git a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs index 56dc68981d..87b6a03005 100644 --- a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs +++ b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using GitVersion.Helpers; using GitVersion.Logging; using LibGit2Sharp; using LibGit2Sharp.Handlers; @@ -53,101 +54,114 @@ public void Dispose() public ICommit FindMergeBase(ICommit commit, ICommit otherCommit) { - var mergeBase = repositoryInstance.ObjectDatabase.FindMergeBase((Commit)commit, (Commit)otherCommit); - return new Commit(mergeBase); + return new OperationWithExponentialBackoff(new ThreadSleep(), log, () => + { + var mergeBase = repositoryInstance.ObjectDatabase.FindMergeBase((Commit)commit, (Commit)otherCommit); + return new Commit(mergeBase); + }, 6).ExecuteAsync().Result; } + public int GetNumberOfUncommittedChanges() { - // check if we have a branch tip at all to behave properly with empty repos - // => return that we have actually uncomitted changes because we are apparently - // running GitVersion on something which lives inside this brand new repo _/\Ö/\_ - if (repositoryInstance.Head?.Tip == null || repositoryInstance.Diff == null) + return new OperationWithExponentialBackoff(new ThreadSleep(), log, () => { - // this is a somewhat cumbersome way of figuring out the number of changes in the repo - // which is more expensive than to use the Diff as it gathers more info, but - // we can't use the other method when we are dealing with a new/empty repo - try - { - var status = repositoryInstance.RetrieveStatus(); - return status.Untracked.Count() + status.Staged.Count(); - } - catch (Exception) + // check if we have a branch tip at all to behave properly with empty repos + // => return that we have actually uncomitted changes because we are apparently + // running GitVersion on something which lives inside this brand new repo _/\Ö/\_ + if (repositoryInstance.Head?.Tip == null || repositoryInstance.Diff == null) { - return int.MaxValue; // this should be somewhat puzzling to see, - // so we may have reached our goal to show that - // that repo is really "Dirty"... + // this is a somewhat cumbersome way of figuring out the number of changes in the repo + // which is more expensive than to use the Diff as it gathers more info, but + // we can't use the other method when we are dealing with a new/empty repo + try + { + var status = repositoryInstance.RetrieveStatus(); + return status.Untracked.Count() + status.Staged.Count(); + } + catch (Exception) + { + return int.MaxValue; // this should be somewhat puzzling to see, + // so we may have reached our goal to show that + // that repo is really "Dirty"... + } } - } - // gets all changes of the last commit vs Staging area and WT - var changes = repositoryInstance.Diff.Compare(repositoryInstance.Head.Tip.Tree, + // gets all changes of the last commit vs Staging area and WT + var changes = repositoryInstance.Diff.Compare(repositoryInstance.Head.Tip.Tree, DiffTargets.Index | DiffTargets.WorkingDirectory); - return changes.Count; + return changes.Count; + }, 6).ExecuteAsync().Result; } public void CreateBranchForPullRequestBranch(AuthenticationInfo auth) { - var network = repositoryInstance.Network; - var remote = network.Remotes.Single(); + new OperationWithExponentialBackoff(new ThreadSleep(), log, () => + { + var network = repositoryInstance.Network; + var remote = network.Remotes.Single(); - log.Info("Fetching remote refs to see if there is a pull request ref"); - var credentialsProvider = GetCredentialsProvider(auth); - var remoteTips = (credentialsProvider != null - ? network.ListReferences(remote, credentialsProvider) - : network.ListReferences(remote)) - .Select(r => r.ResolveToDirectReference()).ToList(); + log.Info("Fetching remote refs to see if there is a pull request ref"); + var credentialsProvider = GetCredentialsProvider(auth); + var remoteTips = (credentialsProvider != null + ? network.ListReferences(remote, credentialsProvider) + : network.ListReferences(remote)) + .Select(r => r.ResolveToDirectReference()).ToList(); - log.Info($"Remote Refs:{System.Environment.NewLine}" + string.Join(System.Environment.NewLine, remoteTips.Select(r => r.CanonicalName))); + log.Info($"Remote Refs:{System.Environment.NewLine}" + string.Join(System.Environment.NewLine, remoteTips.Select(r => r.CanonicalName))); - var headTipSha = Head.Tip?.Sha; + var headTipSha = Head.Tip?.Sha; - var refs = remoteTips.Where(r => r.TargetIdentifier == headTipSha).ToList(); + var refs = remoteTips.Where(r => r.TargetIdentifier == headTipSha).ToList(); - if (refs.Count == 0) - { - var message = $"Couldn't find any remote tips from remote '{remote.Url}' pointing at the commit '{headTipSha}'."; - throw new WarningException(message); - } + if (refs.Count == 0) + { + var message = $"Couldn't find any remote tips from remote '{remote.Url}' pointing at the commit '{headTipSha}'."; + throw new WarningException(message); + } - if (refs.Count > 1) - { - var names = string.Join(", ", refs.Select(r => r.CanonicalName)); - var message = $"Found more than one remote tip from remote '{remote.Url}' pointing at the commit '{headTipSha}'. Unable to determine which one to use ({names})."; - throw new WarningException(message); - } + if (refs.Count > 1) + { + var names = string.Join(", ", refs.Select(r => r.CanonicalName)); + var message = $"Found more than one remote tip from remote '{remote.Url}' pointing at the commit '{headTipSha}'. Unable to determine which one to use ({names})."; + throw new WarningException(message); + } - var reference = refs.First(); - var canonicalName = reference.CanonicalName; - var referenceName = ReferenceName.Parse(reference.CanonicalName); - log.Info($"Found remote tip '{canonicalName}' pointing at the commit '{headTipSha}'."); + var reference = refs.First(); + var canonicalName = reference.CanonicalName; + var referenceName = ReferenceName.Parse(reference.CanonicalName); + log.Info($"Found remote tip '{canonicalName}' pointing at the commit '{headTipSha}'."); - if (referenceName.IsTag) - { - log.Info($"Checking out tag '{canonicalName}'"); - Checkout(reference.Target.Sha); - } - else if (referenceName.IsPullRequest) - { - var fakeBranchName = canonicalName.Replace("refs/pull/", "refs/heads/pull/").Replace("refs/pull-requests/", "refs/heads/pull-requests/"); + if (referenceName.IsTag) + { + log.Info($"Checking out tag '{canonicalName}'"); + Checkout(reference.Target.Sha); + } + else if (referenceName.IsPullRequest) + { + var fakeBranchName = canonicalName.Replace("refs/pull/", "refs/heads/pull/").Replace("refs/pull-requests/", "refs/heads/pull-requests/"); - log.Info($"Creating fake local branch '{fakeBranchName}'."); - Refs.Add(fakeBranchName, headTipSha); + log.Info($"Creating fake local branch '{fakeBranchName}'."); + Refs.Add(fakeBranchName, headTipSha); - log.Info($"Checking local branch '{fakeBranchName}' out."); - Checkout(fakeBranchName); - } - else - { - var message = $"Remote tip '{canonicalName}' from remote '{remote.Url}' doesn't look like a valid pull request."; - throw new WarningException(message); - } + log.Info($"Checking local branch '{fakeBranchName}' out."); + Checkout(fakeBranchName); + } + else + { + var message = $"Remote tip '{canonicalName}' from remote '{remote.Url}' doesn't look like a valid pull request."; + throw new WarningException(message); + } + }, 6).ExecuteAsync().Wait(); } public void Clone(string sourceUrl, string workdirPath, AuthenticationInfo auth) { try { - var path = Repository.Clone(sourceUrl, workdirPath, GetCloneOptions(auth)); - log.Info($"Returned path after repository clone: {path}"); + new OperationWithExponentialBackoff(new ThreadSleep(), log, () => + { + var path = Repository.Clone(sourceUrl, workdirPath, GetCloneOptions(auth)); + log.Info($"Returned path after repository clone: {path}"); + }, 6).ExecuteAsync().Wait(); } catch (LibGit2SharpException ex) { @@ -168,9 +182,15 @@ public void Clone(string sourceUrl, string workdirPath, AuthenticationInfo auth) throw new Exception("There was an unknown problem with the Git repository you provided", ex); } } - public void Checkout(string commitOrBranchSpec) => Commands.Checkout(repositoryInstance, commitOrBranchSpec); - public void Fetch(string remote, IEnumerable refSpecs, AuthenticationInfo auth, string logMessage) => - Commands.Fetch((Repository)repositoryInstance, remote, refSpecs, GetFetchOptions(auth), logMessage); + public void Checkout(string commitOrBranchSpec) + { + new OperationWithExponentialBackoff(new ThreadSleep(), log, () => Commands.Checkout(repositoryInstance, commitOrBranchSpec) , 6).ExecuteAsync().Wait(); + } + + public void Fetch(string remote, IEnumerable refSpecs, AuthenticationInfo auth, string logMessage) + { + new OperationWithExponentialBackoff(new ThreadSleep(), log, () => Commands.Fetch((Repository)repositoryInstance, remote, refSpecs, GetFetchOptions(auth), logMessage), 6).ExecuteAsync().Wait(); + } internal static string Discover(string path) => Repository.Discover(path); private static FetchOptions GetFetchOptions(AuthenticationInfo auth) diff --git a/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs b/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs index 5fede01e50..b09b3839ec 100644 --- a/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs @@ -1,3 +1,5 @@ +using GitVersion.Helpers; +using GitVersion.Logging; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -19,9 +21,9 @@ public void Add(string name, string canonicalRefNameOrObjectish, bool allowOverw innerCollection.Add(name, canonicalRefNameOrObjectish, allowOverwrite); } - public void UpdateTarget(IReference directRef, IObjectId targetId) + public void UpdateTarget(IReference directRef, IObjectId targetId, ILog log) { - innerCollection.UpdateTarget((Reference)directRef, (ObjectId)targetId); + new OperationWithExponentialBackoff(new ThreadSleep(), log, () => innerCollection.UpdateTarget((Reference)directRef, (ObjectId)targetId), maxRetries: 6).ExecuteAsync().Wait(); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); From 231e46a2839b2dfc9115a7dc27601b254ae0d05c Mon Sep 17 00:00:00 2001 From: Markus Horstmann Date: Thu, 4 Feb 2021 18:05:14 -0800 Subject: [PATCH 05/15] Fix whitespace formatting --- src/GitVersion.LibGit2Sharp/Git/GitRepository.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs index 87b6a03005..40eb86014f 100644 --- a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs +++ b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs @@ -81,8 +81,8 @@ public int GetNumberOfUncommittedChanges() catch (Exception) { return int.MaxValue; // this should be somewhat puzzling to see, - // so we may have reached our goal to show that - // that repo is really "Dirty"... + // so we may have reached our goal to show that + // that repo is really "Dirty"... } } @@ -184,7 +184,7 @@ public void Clone(string sourceUrl, string workdirPath, AuthenticationInfo auth) } public void Checkout(string commitOrBranchSpec) { - new OperationWithExponentialBackoff(new ThreadSleep(), log, () => Commands.Checkout(repositoryInstance, commitOrBranchSpec) , 6).ExecuteAsync().Wait(); + new OperationWithExponentialBackoff(new ThreadSleep(), log, () => Commands.Checkout(repositoryInstance, commitOrBranchSpec), 6).ExecuteAsync().Wait(); } public void Fetch(string remote, IEnumerable refSpecs, AuthenticationInfo auth, string logMessage) From 47eead0b93ce14163abc27b43a01c793d77c82f8 Mon Sep 17 00:00:00 2001 From: Markus Horstmann Date: Fri, 5 Feb 2021 11:12:48 -0800 Subject: [PATCH 06/15] Incorporate code review feedback --- src/GitVersion.Core/Core/ThreadSleep.cs | 2 +- src/GitVersion.Core/GitVersion.Core.csproj | 6 ------ .../Helpers/OperationWithExponentialBackoff.cs | 4 ++-- .../VersionConverters/OutputGenerator/OutputGenerator.cs | 2 +- src/GitVersion.MsBuild/GitVersionTaskExecutor.cs | 2 +- 5 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/GitVersion.Core/Core/ThreadSleep.cs b/src/GitVersion.Core/Core/ThreadSleep.cs index b2f7e68bce..db3c4809e4 100644 --- a/src/GitVersion.Core/Core/ThreadSleep.cs +++ b/src/GitVersion.Core/Core/ThreadSleep.cs @@ -2,7 +2,7 @@ namespace GitVersion { - internal class ThreadSleep : IThreadSleep + public class ThreadSleep : IThreadSleep { public async Task SleepAsync(int milliseconds) { diff --git a/src/GitVersion.Core/GitVersion.Core.csproj b/src/GitVersion.Core/GitVersion.Core.csproj index 0949789beb..747956f53f 100644 --- a/src/GitVersion.Core/GitVersion.Core.csproj +++ b/src/GitVersion.Core/GitVersion.Core.csproj @@ -11,12 +11,6 @@ $(AssemblyName) - - - <_Parameter1>GitVersion.LibGit2Sharp - - - diff --git a/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs b/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs index 373899d078..58d35b12ad 100644 --- a/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs +++ b/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs @@ -5,7 +5,7 @@ namespace GitVersion.Helpers { - internal class OperationWithExponentialBackoff : OperationWithExponentialBackoff where T : Exception + public class OperationWithExponentialBackoff : OperationWithExponentialBackoff where T : Exception { public OperationWithExponentialBackoff(IThreadSleep threadSleep, ILog log, Action operation, int maxRetries = 5) : base(threadSleep, log, () => { operation(); return false; }, maxRetries) @@ -18,7 +18,7 @@ public OperationWithExponentialBackoff(IThreadSleep threadSleep, ILog log, Actio } - internal class OperationWithExponentialBackoff where T : Exception + public class OperationWithExponentialBackoff where T : Exception { private readonly IThreadSleep threadSleep; private readonly ILog log; diff --git a/src/GitVersion.Core/VersionConverters/OutputGenerator/OutputGenerator.cs b/src/GitVersion.Core/VersionConverters/OutputGenerator/OutputGenerator.cs index 7d17c362de..9dabaab050 100644 --- a/src/GitVersion.Core/VersionConverters/OutputGenerator/OutputGenerator.cs +++ b/src/GitVersion.Core/VersionConverters/OutputGenerator/OutputGenerator.cs @@ -23,7 +23,7 @@ public class OutputGenerator : IOutputGenerator public OutputGenerator(ICurrentBuildAgent buildAgent, IConsole console, ILog log, IFileSystem fileSystem, IOptions options) { this.console = console ?? throw new ArgumentNullException(nameof(console)); - this.log = log; + this.log = log ?? throw new ArgumentNullException(nameof(log)); this.fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); this.options = options ?? throw new ArgumentNullException(nameof(options)); this.buildAgent = buildAgent; diff --git a/src/GitVersion.MsBuild/GitVersionTaskExecutor.cs b/src/GitVersion.MsBuild/GitVersionTaskExecutor.cs index 5bf5eee688..13472af54c 100644 --- a/src/GitVersion.MsBuild/GitVersionTaskExecutor.cs +++ b/src/GitVersion.MsBuild/GitVersionTaskExecutor.cs @@ -18,7 +18,7 @@ public class GitVersionTaskExecutor : IGitVersionTaskExecutor public GitVersionTaskExecutor(IFileSystem fileSystem, IGitVersionOutputTool gitVersionOutputTool, IOptions options, ILog log) { this.fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); - this.log = log; + this.log = log ?? throw new ArgumentNullException(nameof(log)); this.gitVersionOutputTool = gitVersionOutputTool ?? throw new ArgumentNullException(nameof(gitVersionOutputTool)); this.options = options ?? throw new ArgumentNullException(nameof(options)); } From 16880b094f2f1a4c8c37a4b39a7288465f2f477e Mon Sep 17 00:00:00 2001 From: Markus Horstmann Date: Fri, 5 Feb 2021 11:21:19 -0800 Subject: [PATCH 07/15] Code Review: remove logger from IReferenceCollection --- src/GitVersion.Core/Core/GitPreparer.cs | 4 ++-- src/GitVersion.Core/Git/IReferenceCollection.cs | 10 +++++++++- .../Git/ReferenceCollection.cs | 14 ++++++++++---- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/GitVersion.Core/Core/GitPreparer.cs b/src/GitVersion.Core/Core/GitPreparer.cs index 236fb1e726..becac24f98 100644 --- a/src/GitVersion.Core/Core/GitPreparer.cs +++ b/src/GitVersion.Core/Core/GitPreparer.cs @@ -330,7 +330,7 @@ private void CreateOrUpdateLocalBranchesFromRemoteTrackingOnes(string remoteName } var remoteRefTipId = remoteTrackingReference.ReferenceTargetId; log.Info($"Updating local ref '{localRef.Name.Canonical}' to point at {remoteRefTipId}."); - repository.Refs.UpdateTarget(localRef, remoteRefTipId, log); + new OperationWithExponentialBackoff(new ThreadSleep(), log, () => repository.Refs.UpdateTarget(localRef, remoteRefTipId), maxRetries: 6).ExecuteAsync().Wait(); continue; } @@ -383,7 +383,7 @@ public void EnsureLocalBranchExistsForCurrentBranch(IRemote remote, string curre log.Info(isBranch ? $"Updating local branch {referenceName} to point at {repoTip}" : $"Updating local branch {referenceName} to match ref {currentBranch}"); var localRef = repository.Refs[localCanonicalName]; - repository.Refs.UpdateTarget(localRef, repoTipId, log); + new OperationWithExponentialBackoff(new ThreadSleep(), log, () => repository.Refs.UpdateTarget(localRef, repoTipId), maxRetries: 6).ExecuteAsync().Wait(); } repository.Checkout(localCanonicalName); diff --git a/src/GitVersion.Core/Git/IReferenceCollection.cs b/src/GitVersion.Core/Git/IReferenceCollection.cs index 935d507c5b..9a7cf69d45 100644 --- a/src/GitVersion.Core/Git/IReferenceCollection.cs +++ b/src/GitVersion.Core/Git/IReferenceCollection.cs @@ -1,4 +1,5 @@ using GitVersion.Logging; +using System; using System.Collections.Generic; namespace GitVersion @@ -8,7 +9,14 @@ public interface IReferenceCollection : IEnumerable IReference Head { get; } IReference this[string name] { get; } void Add(string name, string canonicalRefNameOrObjectish, bool allowOverwrite = false); - void UpdateTarget(IReference directRef, IObjectId targetId, ILog log); + void UpdateTarget(IReference directRef, IObjectId targetId); IEnumerable FromGlob(string prefix); } + + public class LockedFileException : Exception + { + public LockedFileException(Exception inner) : base(inner.Message, inner) + { + } + } } diff --git a/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs b/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs index b09b3839ec..e77e2b9b1a 100644 --- a/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs @@ -1,5 +1,3 @@ -using GitVersion.Helpers; -using GitVersion.Logging; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -21,9 +19,17 @@ public void Add(string name, string canonicalRefNameOrObjectish, bool allowOverw innerCollection.Add(name, canonicalRefNameOrObjectish, allowOverwrite); } - public void UpdateTarget(IReference directRef, IObjectId targetId, ILog log) + public void UpdateTarget(IReference directRef, IObjectId targetId) { - new OperationWithExponentialBackoff(new ThreadSleep(), log, () => innerCollection.UpdateTarget((Reference)directRef, (ObjectId)targetId), maxRetries: 6).ExecuteAsync().Wait(); + try + { + innerCollection.UpdateTarget((Reference)directRef, (ObjectId)targetId); + } + catch (LibGit2Sharp.LockedFileException ex) + { + // Wrap this exception so that callers that want to catch it don't need to take a dependency on LibGit2Sharp. + throw new LockedFileException(ex); + } } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); From b1b581ecf26080cb8635d2ad563ba7ac615e8547 Mon Sep 17 00:00:00 2001 From: Markus Horstmann Date: Fri, 5 Feb 2021 11:34:33 -0800 Subject: [PATCH 08/15] Use default retry count (5) for locked files --- src/GitVersion.Core/Core/GitPreparer.cs | 4 ++-- src/GitVersion.Core/Model/VersionVariables.cs | 2 +- .../OutputGenerator/OutputGenerator.cs | 2 +- src/GitVersion.LibGit2Sharp/Git/GitRepository.cs | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/GitVersion.Core/Core/GitPreparer.cs b/src/GitVersion.Core/Core/GitPreparer.cs index becac24f98..bd4ea4d36e 100644 --- a/src/GitVersion.Core/Core/GitPreparer.cs +++ b/src/GitVersion.Core/Core/GitPreparer.cs @@ -330,7 +330,7 @@ private void CreateOrUpdateLocalBranchesFromRemoteTrackingOnes(string remoteName } var remoteRefTipId = remoteTrackingReference.ReferenceTargetId; log.Info($"Updating local ref '{localRef.Name.Canonical}' to point at {remoteRefTipId}."); - new OperationWithExponentialBackoff(new ThreadSleep(), log, () => repository.Refs.UpdateTarget(localRef, remoteRefTipId), maxRetries: 6).ExecuteAsync().Wait(); + new OperationWithExponentialBackoff(new ThreadSleep(), log, () => repository.Refs.UpdateTarget(localRef, remoteRefTipId)).ExecuteAsync().Wait(); continue; } @@ -383,7 +383,7 @@ public void EnsureLocalBranchExistsForCurrentBranch(IRemote remote, string curre log.Info(isBranch ? $"Updating local branch {referenceName} to point at {repoTip}" : $"Updating local branch {referenceName} to match ref {currentBranch}"); var localRef = repository.Refs[localCanonicalName]; - new OperationWithExponentialBackoff(new ThreadSleep(), log, () => repository.Refs.UpdateTarget(localRef, repoTipId), maxRetries: 6).ExecuteAsync().Wait(); + new OperationWithExponentialBackoff(new ThreadSleep(), log, () => repository.Refs.UpdateTarget(localRef, repoTipId)).ExecuteAsync().Wait(); } repository.Checkout(localCanonicalName); diff --git a/src/GitVersion.Core/Model/VersionVariables.cs b/src/GitVersion.Core/Model/VersionVariables.cs index 00cae5eb3d..407dea089c 100644 --- a/src/GitVersion.Core/Model/VersionVariables.cs +++ b/src/GitVersion.Core/Model/VersionVariables.cs @@ -166,7 +166,7 @@ public static VersionVariables FromJson(string json) public static VersionVariables FromFile(string filePath, IFileSystem fileSystem, ILog log) { - var retryOperation = new OperationWithExponentialBackoff(new ThreadSleep(), null, () => FromFileInternal(filePath, fileSystem), maxRetries: 6); + var retryOperation = new OperationWithExponentialBackoff(new ThreadSleep(), null, () => FromFileInternal(filePath, fileSystem)); var versionVariables = retryOperation.ExecuteAsync().Result; return versionVariables; diff --git a/src/GitVersion.Core/VersionConverters/OutputGenerator/OutputGenerator.cs b/src/GitVersion.Core/VersionConverters/OutputGenerator/OutputGenerator.cs index 9dabaab050..be9090d676 100644 --- a/src/GitVersion.Core/VersionConverters/OutputGenerator/OutputGenerator.cs +++ b/src/GitVersion.Core/VersionConverters/OutputGenerator/OutputGenerator.cs @@ -38,7 +38,7 @@ public void Execute(VersionVariables variables, OutputContext context) } if (gitVersionOptions.Output.Contains(OutputType.File)) { - var retryOperation = new OperationWithExponentialBackoff(new ThreadSleep(), log, () => fileSystem.WriteAllText(context.OutputFile, variables.ToString()), maxRetries: 6); + var retryOperation = new OperationWithExponentialBackoff(new ThreadSleep(), log, () => fileSystem.WriteAllText(context.OutputFile, variables.ToString())); retryOperation.ExecuteAsync().Wait(); } if (gitVersionOptions.Output.Contains(OutputType.Json)) diff --git a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs index 40eb86014f..9ce177fe47 100644 --- a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs +++ b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs @@ -58,7 +58,7 @@ public ICommit FindMergeBase(ICommit commit, ICommit otherCommit) { var mergeBase = repositoryInstance.ObjectDatabase.FindMergeBase((Commit)commit, (Commit)otherCommit); return new Commit(mergeBase); - }, 6).ExecuteAsync().Result; + }).ExecuteAsync().Result; } public int GetNumberOfUncommittedChanges() @@ -91,7 +91,7 @@ public int GetNumberOfUncommittedChanges() DiffTargets.Index | DiffTargets.WorkingDirectory); return changes.Count; - }, 6).ExecuteAsync().Result; + }).ExecuteAsync().Result; } public void CreateBranchForPullRequestBranch(AuthenticationInfo auth) { @@ -151,7 +151,7 @@ public void CreateBranchForPullRequestBranch(AuthenticationInfo auth) var message = $"Remote tip '{canonicalName}' from remote '{remote.Url}' doesn't look like a valid pull request."; throw new WarningException(message); } - }, 6).ExecuteAsync().Wait(); + }).ExecuteAsync().Wait(); } public void Clone(string sourceUrl, string workdirPath, AuthenticationInfo auth) { @@ -161,7 +161,7 @@ public void Clone(string sourceUrl, string workdirPath, AuthenticationInfo auth) { var path = Repository.Clone(sourceUrl, workdirPath, GetCloneOptions(auth)); log.Info($"Returned path after repository clone: {path}"); - }, 6).ExecuteAsync().Wait(); + }).ExecuteAsync().Wait(); } catch (LibGit2SharpException ex) { @@ -184,12 +184,12 @@ public void Clone(string sourceUrl, string workdirPath, AuthenticationInfo auth) } public void Checkout(string commitOrBranchSpec) { - new OperationWithExponentialBackoff(new ThreadSleep(), log, () => Commands.Checkout(repositoryInstance, commitOrBranchSpec), 6).ExecuteAsync().Wait(); + new OperationWithExponentialBackoff(new ThreadSleep(), log, () => Commands.Checkout(repositoryInstance, commitOrBranchSpec)).ExecuteAsync().Wait(); } public void Fetch(string remote, IEnumerable refSpecs, AuthenticationInfo auth, string logMessage) { - new OperationWithExponentialBackoff(new ThreadSleep(), log, () => Commands.Fetch((Repository)repositoryInstance, remote, refSpecs, GetFetchOptions(auth), logMessage), 6).ExecuteAsync().Wait(); + new OperationWithExponentialBackoff(new ThreadSleep(), log, () => Commands.Fetch((Repository)repositoryInstance, remote, refSpecs, GetFetchOptions(auth), logMessage)).ExecuteAsync().Wait(); } internal static string Discover(string path) => Repository.Discover(path); From a7be7e01b7880cc91068053e2eace1c855b0514b Mon Sep 17 00:00:00 2001 From: Markus Horstmann Date: Fri, 5 Feb 2021 12:21:33 -0800 Subject: [PATCH 09/15] Fix bad merge --- src/GitVersion.Core/Model/VersionVariables.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/GitVersion.Core/Model/VersionVariables.cs b/src/GitVersion.Core/Model/VersionVariables.cs index 407dea089c..39b4b214c4 100644 --- a/src/GitVersion.Core/Model/VersionVariables.cs +++ b/src/GitVersion.Core/Model/VersionVariables.cs @@ -1,3 +1,5 @@ +using GitVersion.Helpers; +using GitVersion.Logging; using System; using System.Collections; using System.Collections.Generic; From 643e50d4d7851c4c87f034d1d07eb1d8b7944ad4 Mon Sep 17 00:00:00 2001 From: Markus Horstmann Date: Fri, 5 Feb 2021 12:22:13 -0800 Subject: [PATCH 10/15] Fix unnecessary whitespace change --- src/GitVersion.Core/GitVersion.Core.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/GitVersion.Core/GitVersion.Core.csproj b/src/GitVersion.Core/GitVersion.Core.csproj index 747956f53f..8d63289831 100644 --- a/src/GitVersion.Core/GitVersion.Core.csproj +++ b/src/GitVersion.Core/GitVersion.Core.csproj @@ -8,6 +8,7 @@ GitVersion.Core GitVersion Derives SemVer information from a repository following GitFlow or GitHubFlow. This is the Core library which both GitVersion cli and Task use allowing programatic usage of GitVersion. + $(AssemblyName) From 95fe3b0cd293c99b5f1ea46cd05523b5473130ae Mon Sep 17 00:00:00 2001 From: Markus Horstmann Date: Fri, 5 Feb 2021 13:10:44 -0800 Subject: [PATCH 11/15] Fix WriteVersionInfoTest failure (pad parameter validation/passing) --- .../Helpers/OperationWithExponentialBackoff.cs | 2 +- src/GitVersion.Core/Model/VersionVariables.cs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs b/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs index 58d35b12ad..da12a7909d 100644 --- a/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs +++ b/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs @@ -31,7 +31,7 @@ public OperationWithExponentialBackoff(IThreadSleep threadSleep, ILog log, Func< throw new ArgumentOutOfRangeException(nameof(maxRetries)); this.threadSleep = threadSleep ?? throw new ArgumentNullException(nameof(threadSleep)); - this.log = log; + this.log = log ?? throw new ArgumentNullException(nameof(log)); this.operation = operation; this.maxRetries = maxRetries; } diff --git a/src/GitVersion.Core/Model/VersionVariables.cs b/src/GitVersion.Core/Model/VersionVariables.cs index 39b4b214c4..951de1a4d5 100644 --- a/src/GitVersion.Core/Model/VersionVariables.cs +++ b/src/GitVersion.Core/Model/VersionVariables.cs @@ -168,11 +168,9 @@ public static VersionVariables FromJson(string json) public static VersionVariables FromFile(string filePath, IFileSystem fileSystem, ILog log) { - var retryOperation = new OperationWithExponentialBackoff(new ThreadSleep(), null, () => FromFileInternal(filePath, fileSystem)); - + var retryOperation = new OperationWithExponentialBackoff(new ThreadSleep(), log, () => FromFileInternal(filePath, fileSystem)); var versionVariables = retryOperation.ExecuteAsync().Result; return versionVariables; - } private static VersionVariables FromFileInternal(string filePath, IFileSystem fileSystem) { From cdf74f280b644007c15e170b2614051fe7c53780 Mon Sep 17 00:00:00 2001 From: Markus Horstmann Date: Fri, 5 Feb 2021 13:51:50 -0800 Subject: [PATCH 12/15] Code cleanup for better diffability --- src/GitVersion.Core/GitVersion.Core.csproj | 2 +- .../OperationWithExponentialBackoff.cs | 2 +- .../Git/GitRepository.cs | 148 ++++++++++-------- 3 files changed, 82 insertions(+), 70 deletions(-) diff --git a/src/GitVersion.Core/GitVersion.Core.csproj b/src/GitVersion.Core/GitVersion.Core.csproj index 8d63289831..60b4786406 100644 --- a/src/GitVersion.Core/GitVersion.Core.csproj +++ b/src/GitVersion.Core/GitVersion.Core.csproj @@ -8,7 +8,7 @@ GitVersion.Core GitVersion Derives SemVer information from a repository following GitFlow or GitHubFlow. This is the Core library which both GitVersion cli and Task use allowing programatic usage of GitVersion. - + $(AssemblyName) diff --git a/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs b/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs index da12a7909d..c3a9ae4de8 100644 --- a/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs +++ b/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs @@ -11,12 +11,12 @@ public OperationWithExponentialBackoff(IThreadSleep threadSleep, ILog log, Actio : base(threadSleep, log, () => { operation(); return false; }, maxRetries) { } + public new Task ExecuteAsync() { return base.ExecuteAsync(); } - } public class OperationWithExponentialBackoff where T : Exception { diff --git a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs index 9ce177fe47..cc40361805 100644 --- a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs +++ b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs @@ -65,94 +65,106 @@ public int GetNumberOfUncommittedChanges() { return new OperationWithExponentialBackoff(new ThreadSleep(), log, () => { - // check if we have a branch tip at all to behave properly with empty repos - // => return that we have actually uncomitted changes because we are apparently - // running GitVersion on something which lives inside this brand new repo _/\Ö/\_ - if (repositoryInstance.Head?.Tip == null || repositoryInstance.Diff == null) + return GetNumberOfUncommittedChangesInternal(); + }).ExecuteAsync().Result; + } + + private int GetNumberOfUncommittedChangesInternal() + { + // check if we have a branch tip at all to behave properly with empty repos + // => return that we have actually uncomitted changes because we are apparently + // running GitVersion on something which lives inside this brand new repo _/\Ö/\_ + if (repositoryInstance.Head?.Tip == null || repositoryInstance.Diff == null) + { + // this is a somewhat cumbersome way of figuring out the number of changes in the repo + // which is more expensive than to use the Diff as it gathers more info, but + // we can't use the other method when we are dealing with a new/empty repo + try { - // this is a somewhat cumbersome way of figuring out the number of changes in the repo - // which is more expensive than to use the Diff as it gathers more info, but - // we can't use the other method when we are dealing with a new/empty repo - try - { - var status = repositoryInstance.RetrieveStatus(); - return status.Untracked.Count() + status.Staged.Count(); - } - catch (Exception) - { - return int.MaxValue; // this should be somewhat puzzling to see, - // so we may have reached our goal to show that - // that repo is really "Dirty"... - } + var status = repositoryInstance.RetrieveStatus(); + return status.Untracked.Count() + status.Staged.Count(); } + catch (Exception) + { + return int.MaxValue; // this should be somewhat puzzling to see, + // so we may have reached our goal to show that + // that repo is really "Dirty"... + } + } - // gets all changes of the last commit vs Staging area and WT - var changes = repositoryInstance.Diff.Compare(repositoryInstance.Head.Tip.Tree, - DiffTargets.Index | DiffTargets.WorkingDirectory); + // gets all changes of the last commit vs Staging area and WT + var changes = repositoryInstance.Diff.Compare(repositoryInstance.Head.Tip.Tree, + DiffTargets.Index | DiffTargets.WorkingDirectory); - return changes.Count; - }).ExecuteAsync().Result; + return changes.Count; } + public void CreateBranchForPullRequestBranch(AuthenticationInfo auth) { new OperationWithExponentialBackoff(new ThreadSleep(), log, () => { - var network = repositoryInstance.Network; - var remote = network.Remotes.Single(); + CreateBranchForPullRequestBranchInternal(auth); + }).ExecuteAsync().Wait(); + } - log.Info("Fetching remote refs to see if there is a pull request ref"); - var credentialsProvider = GetCredentialsProvider(auth); - var remoteTips = (credentialsProvider != null - ? network.ListReferences(remote, credentialsProvider) - : network.ListReferences(remote)) - .Select(r => r.ResolveToDirectReference()).ToList(); + private void CreateBranchForPullRequestBranchInternal(AuthenticationInfo auth) + { + var network = repositoryInstance.Network; + var remote = network.Remotes.Single(); - log.Info($"Remote Refs:{System.Environment.NewLine}" + string.Join(System.Environment.NewLine, remoteTips.Select(r => r.CanonicalName))); + log.Info("Fetching remote refs to see if there is a pull request ref"); + var credentialsProvider = GetCredentialsProvider(auth); + var remoteTips = (credentialsProvider != null + ? network.ListReferences(remote, credentialsProvider) + : network.ListReferences(remote)) + .Select(r => r.ResolveToDirectReference()).ToList(); - var headTipSha = Head.Tip?.Sha; + log.Info($"Remote Refs:{System.Environment.NewLine}" + string.Join(System.Environment.NewLine, remoteTips.Select(r => r.CanonicalName))); - var refs = remoteTips.Where(r => r.TargetIdentifier == headTipSha).ToList(); + var headTipSha = Head.Tip?.Sha; - if (refs.Count == 0) - { - var message = $"Couldn't find any remote tips from remote '{remote.Url}' pointing at the commit '{headTipSha}'."; - throw new WarningException(message); - } + var refs = remoteTips.Where(r => r.TargetIdentifier == headTipSha).ToList(); - if (refs.Count > 1) - { - var names = string.Join(", ", refs.Select(r => r.CanonicalName)); - var message = $"Found more than one remote tip from remote '{remote.Url}' pointing at the commit '{headTipSha}'. Unable to determine which one to use ({names})."; - throw new WarningException(message); - } + if (refs.Count == 0) + { + var message = $"Couldn't find any remote tips from remote '{remote.Url}' pointing at the commit '{headTipSha}'."; + throw new WarningException(message); + } - var reference = refs.First(); - var canonicalName = reference.CanonicalName; - var referenceName = ReferenceName.Parse(reference.CanonicalName); - log.Info($"Found remote tip '{canonicalName}' pointing at the commit '{headTipSha}'."); + if (refs.Count > 1) + { + var names = string.Join(", ", refs.Select(r => r.CanonicalName)); + var message = $"Found more than one remote tip from remote '{remote.Url}' pointing at the commit '{headTipSha}'. Unable to determine which one to use ({names})."; + throw new WarningException(message); + } - if (referenceName.IsTag) - { - log.Info($"Checking out tag '{canonicalName}'"); - Checkout(reference.Target.Sha); - } - else if (referenceName.IsPullRequest) - { - var fakeBranchName = canonicalName.Replace("refs/pull/", "refs/heads/pull/").Replace("refs/pull-requests/", "refs/heads/pull-requests/"); + var reference = refs.First(); + var canonicalName = reference.CanonicalName; + var referenceName = ReferenceName.Parse(reference.CanonicalName); + log.Info($"Found remote tip '{canonicalName}' pointing at the commit '{headTipSha}'."); + + if (referenceName.IsTag) + { + log.Info($"Checking out tag '{canonicalName}'"); + Checkout(reference.Target.Sha); + } + else if (referenceName.IsPullRequest) + { + var fakeBranchName = canonicalName.Replace("refs/pull/", "refs/heads/pull/").Replace("refs/pull-requests/", "refs/heads/pull-requests/"); - log.Info($"Creating fake local branch '{fakeBranchName}'."); - Refs.Add(fakeBranchName, headTipSha); + log.Info($"Creating fake local branch '{fakeBranchName}'."); + Refs.Add(fakeBranchName, headTipSha); - log.Info($"Checking local branch '{fakeBranchName}' out."); - Checkout(fakeBranchName); - } - else - { - var message = $"Remote tip '{canonicalName}' from remote '{remote.Url}' doesn't look like a valid pull request."; - throw new WarningException(message); - } - }).ExecuteAsync().Wait(); + log.Info($"Checking local branch '{fakeBranchName}' out."); + Checkout(fakeBranchName); + } + else + { + var message = $"Remote tip '{canonicalName}' from remote '{remote.Url}' doesn't look like a valid pull request."; + throw new WarningException(message); + } } + public void Clone(string sourceUrl, string workdirPath, AuthenticationInfo auth) { try From b98a9f7d05768edc070d3f91a534eaf452c8e376 Mon Sep 17 00:00:00 2001 From: Markus Horstmann Date: Fri, 5 Feb 2021 14:16:04 -0800 Subject: [PATCH 13/15] Make VersionVariables.FromFile work with no logger. Preserve exception. --- .../OperationWithExponentialBackoff.cs | 1 - src/GitVersion.Core/Model/VersionVariables.cs | 22 ++++++++++++++++--- .../Git/GitRepository.cs | 12 +++------- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs b/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs index c3a9ae4de8..e8e50e5e11 100644 --- a/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs +++ b/src/GitVersion.Core/Helpers/OperationWithExponentialBackoff.cs @@ -16,7 +16,6 @@ public OperationWithExponentialBackoff(IThreadSleep threadSleep, ILog log, Actio { return base.ExecuteAsync(); } - } public class OperationWithExponentialBackoff where T : Exception { diff --git a/src/GitVersion.Core/Model/VersionVariables.cs b/src/GitVersion.Core/Model/VersionVariables.cs index 951de1a4d5..18b257b38f 100644 --- a/src/GitVersion.Core/Model/VersionVariables.cs +++ b/src/GitVersion.Core/Model/VersionVariables.cs @@ -168,9 +168,25 @@ public static VersionVariables FromJson(string json) public static VersionVariables FromFile(string filePath, IFileSystem fileSystem, ILog log) { - var retryOperation = new OperationWithExponentialBackoff(new ThreadSleep(), log, () => FromFileInternal(filePath, fileSystem)); - var versionVariables = retryOperation.ExecuteAsync().Result; - return versionVariables; + try + { + if (log == null) + { + return FromFileInternal(filePath, fileSystem); + } + var retryOperation = new OperationWithExponentialBackoff(new ThreadSleep(), log, () => FromFileInternal(filePath, fileSystem)); + var versionVariables = retryOperation.ExecuteAsync().Result; + return versionVariables; + } + catch (AggregateException ex) + { + var lastException = ex.InnerExceptions?.LastOrDefault() ?? ex.InnerException; + if (lastException != null) + { + throw lastException; + } + throw; + } } private static VersionVariables FromFileInternal(string filePath, IFileSystem fileSystem) { diff --git a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs index cc40361805..4737d11884 100644 --- a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs +++ b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs @@ -60,7 +60,6 @@ public ICommit FindMergeBase(ICommit commit, ICommit otherCommit) return new Commit(mergeBase); }).ExecuteAsync().Result; } - public int GetNumberOfUncommittedChanges() { return new OperationWithExponentialBackoff(new ThreadSleep(), log, () => @@ -68,7 +67,6 @@ public int GetNumberOfUncommittedChanges() return GetNumberOfUncommittedChangesInternal(); }).ExecuteAsync().Result; } - private int GetNumberOfUncommittedChangesInternal() { // check if we have a branch tip at all to behave properly with empty repos @@ -87,18 +85,17 @@ private int GetNumberOfUncommittedChangesInternal() catch (Exception) { return int.MaxValue; // this should be somewhat puzzling to see, - // so we may have reached our goal to show that - // that repo is really "Dirty"... + // so we may have reached our goal to show that + // that repo is really "Dirty"... } } // gets all changes of the last commit vs Staging area and WT var changes = repositoryInstance.Diff.Compare(repositoryInstance.Head.Tip.Tree, - DiffTargets.Index | DiffTargets.WorkingDirectory); + DiffTargets.Index | DiffTargets.WorkingDirectory); return changes.Count; } - public void CreateBranchForPullRequestBranch(AuthenticationInfo auth) { new OperationWithExponentialBackoff(new ThreadSleep(), log, () => @@ -106,7 +103,6 @@ public void CreateBranchForPullRequestBranch(AuthenticationInfo auth) CreateBranchForPullRequestBranchInternal(auth); }).ExecuteAsync().Wait(); } - private void CreateBranchForPullRequestBranchInternal(AuthenticationInfo auth) { var network = repositoryInstance.Network; @@ -164,7 +160,6 @@ private void CreateBranchForPullRequestBranchInternal(AuthenticationInfo auth) throw new WarningException(message); } } - public void Clone(string sourceUrl, string workdirPath, AuthenticationInfo auth) { try @@ -198,7 +193,6 @@ public void Checkout(string commitOrBranchSpec) { new OperationWithExponentialBackoff(new ThreadSleep(), log, () => Commands.Checkout(repositoryInstance, commitOrBranchSpec)).ExecuteAsync().Wait(); } - public void Fetch(string remote, IEnumerable refSpecs, AuthenticationInfo auth, string logMessage) { new OperationWithExponentialBackoff(new ThreadSleep(), log, () => Commands.Fetch((Repository)repositoryInstance, remote, refSpecs, GetFetchOptions(auth), logMessage)).ExecuteAsync().Wait(); From c3f8356cf516f52a59cf3baee9934ccd07f60230 Mon Sep 17 00:00:00 2001 From: Markus Horstmann Date: Fri, 5 Feb 2021 15:06:35 -0800 Subject: [PATCH 14/15] Revert "#2582: GitVersionMsBuild.Targets: fix typo in GitVersion_PreReleaseLabelWithDash" This reverts commit f2b61316aefac8fb0de3e11f6b47475414b2d996. --- src/GitVersion.MsBuild/msbuild/tools/GitVersion.MsBuild.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GitVersion.MsBuild/msbuild/tools/GitVersion.MsBuild.targets b/src/GitVersion.MsBuild/msbuild/tools/GitVersion.MsBuild.targets index fa9b1db2d0..ce188f1c95 100644 --- a/src/GitVersion.MsBuild/msbuild/tools/GitVersion.MsBuild.targets +++ b/src/GitVersion.MsBuild/msbuild/tools/GitVersion.MsBuild.targets @@ -72,7 +72,7 @@ GitVersion_PreReleaseTag=$(GitVersion_PreReleaseTag);$(DefineConstants) GitVersion_PreReleaseTagWithDash=$(GitVersion_PreReleaseTagWithDash);$(DefineConstants) GitVersion_PreReleaseLabel=$(GitVersion_PreReleaseLabel);$(DefineConstants) - GitVersion_PreReleaseLabelWithDash=$(GitVersion_PreReleaseLabelWithDash);$(DefineConstants) + GitVersion_PreReleaseLabelWithDash=$(GitVersion_PreReleaseLabeWithDashl);$(DefineConstants) GitVersion_PreReleaseNumber=$(GitVersion_PreReleaseNumber);$(DefineConstants) GitVersion_WeightedPreReleaseNumber=$(GitVersion_WeightedPreReleaseNumber);$(DefineConstants) GitVersion_BuildMetaData=$(GitVersion_BuildMetaData);$(DefineConstants) From da7707cfd167ced4539f56811abf532e47a3effb Mon Sep 17 00:00:00 2001 From: Artur Date: Sun, 7 Feb 2021 13:51:34 +0100 Subject: [PATCH 15/15] moved the retry mechanism out of LibGit2Sharp classlib f2b613 --- src/GitVersion.Core/Core/GitPreparer.cs | 30 +++-- .../Git/IReferenceCollection.cs | 8 -- .../Model/Exceptions/LockedFileException.cs | 11 ++ .../Git/GitRepository.cs | 117 +++++++++--------- .../Git/ReferenceCollection.cs | 9 +- .../RepositoryExtensions.cs | 27 ++++ 6 files changed, 122 insertions(+), 80 deletions(-) create mode 100644 src/GitVersion.Core/Model/Exceptions/LockedFileException.cs diff --git a/src/GitVersion.Core/Core/GitPreparer.cs b/src/GitVersion.Core/Core/GitPreparer.cs index bd4ea4d36e..9e16df1438 100644 --- a/src/GitVersion.Core/Core/GitPreparer.cs +++ b/src/GitVersion.Core/Core/GitPreparer.cs @@ -149,7 +149,10 @@ private void CloneRepository(string repositoryUrl, string gitDirectory, Authenti { using (log.IndentLog($"Cloning repository from url '{repositoryUrl}'")) { - repository.Clone(repositoryUrl, gitDirectory, auth); + new OperationWithExponentialBackoff(new ThreadSleep(), log, () => + { + repository.Clone(repositoryUrl, gitDirectory, auth); + }).ExecuteAsync().Wait(); } } @@ -179,7 +182,9 @@ private void NormalizeGitDirectory(bool noFetch, string currentBranchName, bool { var refSpecs = string.Join(", ", remote.FetchRefSpecs.Select(r => r.Specification)); log.Info($"Fetching from remote '{remote.Name}' using the following refspecs: {refSpecs}."); - repository.Fetch(remote.Name, Enumerable.Empty(), authentication, null); + new OperationWithExponentialBackoff(new ThreadSleep(), log, + () => repository.Fetch(remote.Name, Enumerable.Empty(), authentication, null)) + .ExecuteAsync().Wait(); } EnsureLocalBranchExistsForCurrentBranch(remote, currentBranchName); @@ -221,7 +226,7 @@ private void NormalizeGitDirectory(bool noFetch, string currentBranchName, bool if (matchingCurrentBranch != null) { log.Info($"Checking out local branch '{currentBranchName}'."); - repository.Checkout(matchingCurrentBranch.Name.Canonical); + Checkout(matchingCurrentBranch.Name.Canonical); } else if (localBranchesWhereCommitShaIsHead.Count > 1) { @@ -234,7 +239,7 @@ private void NormalizeGitDirectory(bool noFetch, string currentBranchName, bool if (main != null) { log.Warning("Because one of the branches is 'main', will build main." + moveBranchMsg); - repository.Checkout(Config.MainBranchKey); + Checkout(Config.MainBranchKey); } else { @@ -243,7 +248,7 @@ private void NormalizeGitDirectory(bool noFetch, string currentBranchName, bool { var branchWithoutSeparator = branchesWithoutSeparators[0]; log.Warning($"Choosing {branchWithoutSeparator.Name.Canonical} as it is the only branch without / or - in it. " + moveBranchMsg); - repository.Checkout(branchWithoutSeparator.Name.Canonical); + Checkout(branchWithoutSeparator.Name.Canonical); } else { @@ -254,12 +259,15 @@ private void NormalizeGitDirectory(bool noFetch, string currentBranchName, bool else if (localBranchesWhereCommitShaIsHead.Count == 0) { log.Info($"No local branch pointing at the commit '{headSha}'. Fake branch needs to be created."); - repository.CreateBranchForPullRequestBranch(authentication); + new OperationWithExponentialBackoff(new ThreadSleep(), log, () => + { + repository.CreateBranchForPullRequestBranch(authentication); + }).ExecuteAsync().Wait(); } else { log.Info($"Checking out local branch 'refs/heads/{localBranchesWhereCommitShaIsHead[0]}'."); - repository.Checkout(localBranchesWhereCommitShaIsHead[0].Name.Friendly); + Checkout(localBranchesWhereCommitShaIsHead[0].Name.Friendly); } } finally @@ -386,7 +394,13 @@ public void EnsureLocalBranchExistsForCurrentBranch(IRemote remote, string curre new OperationWithExponentialBackoff(new ThreadSleep(), log, () => repository.Refs.UpdateTarget(localRef, repoTipId)).ExecuteAsync().Wait(); } - repository.Checkout(localCanonicalName); + Checkout(localCanonicalName); + } + + private void Checkout(string commitOrBranchSpec) + { + new OperationWithExponentialBackoff(new ThreadSleep(), log, () => + repository.Checkout(commitOrBranchSpec)).ExecuteAsync().Wait(); } } } diff --git a/src/GitVersion.Core/Git/IReferenceCollection.cs b/src/GitVersion.Core/Git/IReferenceCollection.cs index 9a7cf69d45..0a0a15a306 100644 --- a/src/GitVersion.Core/Git/IReferenceCollection.cs +++ b/src/GitVersion.Core/Git/IReferenceCollection.cs @@ -1,5 +1,3 @@ -using GitVersion.Logging; -using System; using System.Collections.Generic; namespace GitVersion @@ -13,10 +11,4 @@ public interface IReferenceCollection : IEnumerable IEnumerable FromGlob(string prefix); } - public class LockedFileException : Exception - { - public LockedFileException(Exception inner) : base(inner.Message, inner) - { - } - } } diff --git a/src/GitVersion.Core/Model/Exceptions/LockedFileException.cs b/src/GitVersion.Core/Model/Exceptions/LockedFileException.cs new file mode 100644 index 0000000000..f53226812e --- /dev/null +++ b/src/GitVersion.Core/Model/Exceptions/LockedFileException.cs @@ -0,0 +1,11 @@ +using System; + +namespace GitVersion +{ + public class LockedFileException : Exception + { + public LockedFileException(Exception inner) : base(inner.Message, inner) + { + } + } +} diff --git a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs index 4737d11884..95eb657ee6 100644 --- a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs +++ b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs @@ -98,77 +98,74 @@ private int GetNumberOfUncommittedChangesInternal() } public void CreateBranchForPullRequestBranch(AuthenticationInfo auth) { - new OperationWithExponentialBackoff(new ThreadSleep(), log, () => + RepositoryExtensions.RunSafe(() => { - CreateBranchForPullRequestBranchInternal(auth); - }).ExecuteAsync().Wait(); - } - private void CreateBranchForPullRequestBranchInternal(AuthenticationInfo auth) - { - var network = repositoryInstance.Network; - var remote = network.Remotes.Single(); + var network = repositoryInstance.Network; + var remote = network.Remotes.Single(); - log.Info("Fetching remote refs to see if there is a pull request ref"); - var credentialsProvider = GetCredentialsProvider(auth); - var remoteTips = (credentialsProvider != null - ? network.ListReferences(remote, credentialsProvider) - : network.ListReferences(remote)) - .Select(r => r.ResolveToDirectReference()).ToList(); + log.Info("Fetching remote refs to see if there is a pull request ref"); + var credentialsProvider = GetCredentialsProvider(auth); + var remoteTips = (credentialsProvider != null + ? network.ListReferences(remote, credentialsProvider) + : network.ListReferences(remote)) + .Select(r => r.ResolveToDirectReference()).ToList(); - log.Info($"Remote Refs:{System.Environment.NewLine}" + string.Join(System.Environment.NewLine, remoteTips.Select(r => r.CanonicalName))); + log.Info($"Remote Refs:{System.Environment.NewLine}" + string.Join(System.Environment.NewLine, remoteTips.Select(r => r.CanonicalName))); - var headTipSha = Head.Tip?.Sha; + var headTipSha = Head.Tip?.Sha; - var refs = remoteTips.Where(r => r.TargetIdentifier == headTipSha).ToList(); + var refs = remoteTips.Where(r => r.TargetIdentifier == headTipSha).ToList(); - if (refs.Count == 0) - { - var message = $"Couldn't find any remote tips from remote '{remote.Url}' pointing at the commit '{headTipSha}'."; - throw new WarningException(message); - } + if (refs.Count == 0) + { + var message = $"Couldn't find any remote tips from remote '{remote.Url}' pointing at the commit '{headTipSha}'."; + throw new WarningException(message); + } - if (refs.Count > 1) - { - var names = string.Join(", ", refs.Select(r => r.CanonicalName)); - var message = $"Found more than one remote tip from remote '{remote.Url}' pointing at the commit '{headTipSha}'. Unable to determine which one to use ({names})."; - throw new WarningException(message); - } + if (refs.Count > 1) + { + var names = string.Join(", ", refs.Select(r => r.CanonicalName)); + var message = $"Found more than one remote tip from remote '{remote.Url}' pointing at the commit '{headTipSha}'. Unable to determine which one to use ({names})."; + throw new WarningException(message); + } - var reference = refs.First(); - var canonicalName = reference.CanonicalName; - var referenceName = ReferenceName.Parse(reference.CanonicalName); - log.Info($"Found remote tip '{canonicalName}' pointing at the commit '{headTipSha}'."); + var reference = refs.First(); + var canonicalName = reference.CanonicalName; + var referenceName = ReferenceName.Parse(reference.CanonicalName); + log.Info($"Found remote tip '{canonicalName}' pointing at the commit '{headTipSha}'."); - if (referenceName.IsTag) - { - log.Info($"Checking out tag '{canonicalName}'"); - Checkout(reference.Target.Sha); - } - else if (referenceName.IsPullRequest) - { - var fakeBranchName = canonicalName.Replace("refs/pull/", "refs/heads/pull/").Replace("refs/pull-requests/", "refs/heads/pull-requests/"); + if (referenceName.IsTag) + { + log.Info($"Checking out tag '{canonicalName}'"); + Checkout(reference.Target.Sha); + } + else if (referenceName.IsPullRequest) + { + var fakeBranchName = canonicalName.Replace("refs/pull/", "refs/heads/pull/").Replace("refs/pull-requests/", "refs/heads/pull-requests/"); - log.Info($"Creating fake local branch '{fakeBranchName}'."); - Refs.Add(fakeBranchName, headTipSha); + log.Info($"Creating fake local branch '{fakeBranchName}'."); + Refs.Add(fakeBranchName, headTipSha); - log.Info($"Checking local branch '{fakeBranchName}' out."); - Checkout(fakeBranchName); - } - else - { - var message = $"Remote tip '{canonicalName}' from remote '{remote.Url}' doesn't look like a valid pull request."; - throw new WarningException(message); - } + log.Info($"Checking local branch '{fakeBranchName}' out."); + Checkout(fakeBranchName); + } + else + { + var message = $"Remote tip '{canonicalName}' from remote '{remote.Url}' doesn't look like a valid pull request."; + throw new WarningException(message); + } + }); } public void Clone(string sourceUrl, string workdirPath, AuthenticationInfo auth) { try { - new OperationWithExponentialBackoff(new ThreadSleep(), log, () => - { - var path = Repository.Clone(sourceUrl, workdirPath, GetCloneOptions(auth)); - log.Info($"Returned path after repository clone: {path}"); - }).ExecuteAsync().Wait(); + var path = Repository.Clone(sourceUrl, workdirPath, GetCloneOptions(auth)); + log.Info($"Returned path after repository clone: {path}"); + } + catch (LibGit2Sharp.LockedFileException ex) + { + throw new LockedFileException(ex); } catch (LibGit2SharpException ex) { @@ -191,11 +188,17 @@ public void Clone(string sourceUrl, string workdirPath, AuthenticationInfo auth) } public void Checkout(string commitOrBranchSpec) { - new OperationWithExponentialBackoff(new ThreadSleep(), log, () => Commands.Checkout(repositoryInstance, commitOrBranchSpec)).ExecuteAsync().Wait(); + RepositoryExtensions.RunSafe(() => + { + Commands.Checkout(repositoryInstance, commitOrBranchSpec); + }); } public void Fetch(string remote, IEnumerable refSpecs, AuthenticationInfo auth, string logMessage) { - new OperationWithExponentialBackoff(new ThreadSleep(), log, () => Commands.Fetch((Repository)repositoryInstance, remote, refSpecs, GetFetchOptions(auth), logMessage)).ExecuteAsync().Wait(); + RepositoryExtensions.RunSafe(() => + { + Commands.Fetch((Repository)repositoryInstance, remote, refSpecs, GetFetchOptions(auth), logMessage); + }); } internal static string Discover(string path) => Repository.Discover(path); diff --git a/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs b/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs index e77e2b9b1a..ba03760e91 100644 --- a/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs +++ b/src/GitVersion.LibGit2Sharp/Git/ReferenceCollection.cs @@ -21,15 +21,10 @@ public void Add(string name, string canonicalRefNameOrObjectish, bool allowOverw public void UpdateTarget(IReference directRef, IObjectId targetId) { - try + RepositoryExtensions.RunSafe(() => { innerCollection.UpdateTarget((Reference)directRef, (ObjectId)targetId); - } - catch (LibGit2Sharp.LockedFileException ex) - { - // Wrap this exception so that callers that want to catch it don't need to take a dependency on LibGit2Sharp. - throw new LockedFileException(ex); - } + }); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/src/GitVersion.LibGit2Sharp/RepositoryExtensions.cs b/src/GitVersion.LibGit2Sharp/RepositoryExtensions.cs index c4f25e845b..790139a5c6 100644 --- a/src/GitVersion.LibGit2Sharp/RepositoryExtensions.cs +++ b/src/GitVersion.LibGit2Sharp/RepositoryExtensions.cs @@ -1,3 +1,4 @@ +using System; using LibGit2Sharp; using Microsoft.Extensions.Options; @@ -7,5 +8,31 @@ public static class RepositoryExtensions { public static IGitRepository ToGitRepository(this IRepository repository) => new GitRepository(repository); public static IGitRepositoryInfo ToGitRepositoryInfo(IOptions options) => new GitRepositoryInfo(options); + + public static void RunSafe(Action operation) + { + try + { + operation(); + } + catch (LibGit2Sharp.LockedFileException ex) + { + // Wrap this exception so that callers that want to catch it don't need to take a dependency on LibGit2Sharp. + throw new LockedFileException(ex); + } + } + + public static T RunSafe(Func operation) + { + try + { + return operation(); + } + catch (LibGit2Sharp.LockedFileException ex) + { + // Wrap this exception so that callers that want to catch it don't need to take a dependency on LibGit2Sharp. + throw new LockedFileException(ex); + } + } } }