diff --git a/docs/input/docs/reference/build-servers/azure-devops.md b/docs/input/docs/reference/build-servers/azure-devops.md index 15fba0b50b..b48840fbb1 100644 --- a/docs/input/docs/reference/build-servers/azure-devops.md +++ b/docs/input/docs/reference/build-servers/azure-devops.md @@ -14,6 +14,14 @@ either using the Command Line build step or install an extension / custom build step. The custom build step requires a one-time setup to import the GitVersion task into your TFS or Azure DevOps Pipeline instance. +:::{.alert .alert-danger} +**Important** + +You must disable shallow fetch, either in the pipeline settings UI or by setting `fetchDepth: 0` in your `checkout` step; +without it, Azure DevOps Pipelines will perform a shallow clone, which will cause GitVersion to display an error message. +See [the Azure DevOps documentation](https://learn.microsoft.com/en-us/azure/devops/pipelines/yaml-schema/steps-checkout?view=azure-pipelines#shallow-fetch) for more information. +::: + ## Executing GitVersion ### Using GitVersion with the MSBuild Task NuGet Package diff --git a/src/GitVersion.Core.Tests/Core/GitVersionExecutorTests.cs b/src/GitVersion.Core.Tests/Core/GitVersionExecutorTests.cs index 9e32c4ef1a..bfb8cad0ba 100644 --- a/src/GitVersion.Core.Tests/Core/GitVersionExecutorTests.cs +++ b/src/GitVersion.Core.Tests/Core/GitVersionExecutorTests.cs @@ -544,6 +544,27 @@ public void CalculateVersionVariables_TwoBranchHasSameCommitHeadDetachedAndTagge version.Sha.ShouldBe(commits.First().Sha); } + [Test] + public void CalculateVersionVariables_ShallowFetch_ThrowException() + { + // Setup + using var fixture = new RemoteRepositoryFixture(); + fixture.LocalRepositoryFixture.MakeShallow(); + + using var worktreeFixture = new LocalRepositoryFixture(new Repository(fixture.LocalRepositoryFixture.RepositoryPath)); + var gitVersionOptions = new GitVersionOptions { WorkingDirectory = worktreeFixture.RepositoryPath }; + + var environment = new TestEnvironment(); + environment.SetEnvironmentVariable(AzurePipelines.EnvironmentVariableName, "true"); + + this.sp = GetServiceProvider(gitVersionOptions, environment: environment); + var sut = sp.GetRequiredService(); + + // Execute & Verify + var exception = Assert.Throws(() => sut.CalculateVersionVariables()); + exception?.Message.ShouldBe("Repository is a shallow clone. Git repositories must contain the full history. See https://gitversion.net/docs/reference/requirements#unshallow for more info."); + } + private IGitVersionCalculateTool GetGitVersionCalculator(GitVersionOptions gitVersionOptions, ILog? logger = null, IGitRepository? repository = null, IFileSystem? fs = null) { this.sp = GetServiceProvider(gitVersionOptions, logger, repository, fs); diff --git a/src/GitVersion.Core/Core/GitPreparer.cs b/src/GitVersion.Core/Core/GitPreparer.cs index 5735e018b1..abe952432a 100644 --- a/src/GitVersion.Core/Core/GitPreparer.cs +++ b/src/GitVersion.Core/Core/GitPreparer.cs @@ -183,6 +183,11 @@ private void NormalizeGitDirectory(bool noFetch, string? currentBranchName, bool EnsureHeadIsAttachedToBranch(currentBranchName, authentication); EnsureRepositoryHeadDuringNormalisation(nameof(EnsureHeadIsAttachedToBranch), expectedSha); + + if (this.repository.IsShallow) + { + throw new WarningException("Repository is a shallow clone. Git repositories must contain the full history. See https://gitversion.net/docs/reference/requirements#unshallow for more info."); + } } private void EnsureRepositoryHeadDuringNormalisation(string occasion, string? expectedSha) diff --git a/src/GitVersion.Core/Git/IGitRepository.cs b/src/GitVersion.Core/Git/IGitRepository.cs index ab6c226df4..abc2317c3a 100644 --- a/src/GitVersion.Core/Git/IGitRepository.cs +++ b/src/GitVersion.Core/Git/IGitRepository.cs @@ -5,6 +5,7 @@ public interface IGitRepository : IDisposable string Path { get; } string WorkingDirectory { get; } bool IsHeadDetached { get; } + bool IsShallow { get; } IBranch Head { get; } ITagCollection Tags { get; } IReferenceCollection Refs { get; } diff --git a/src/GitVersion.Core/PublicAPI.Unshipped.txt b/src/GitVersion.Core/PublicAPI.Unshipped.txt index 5aaae6a8e9..62f0097a29 100644 --- a/src/GitVersion.Core/PublicAPI.Unshipped.txt +++ b/src/GitVersion.Core/PublicAPI.Unshipped.txt @@ -335,6 +335,7 @@ GitVersion.IGitRepository.FindMergeBase(GitVersion.ICommit! commit, GitVersion.I GitVersion.IGitRepository.GetNumberOfUncommittedChanges() -> int GitVersion.IGitRepository.Head.get -> GitVersion.IBranch! GitVersion.IGitRepository.IsHeadDetached.get -> bool +GitVersion.IGitRepository.IsShallow.get -> bool GitVersion.IGitRepository.Path.get -> string! GitVersion.IGitRepository.Refs.get -> GitVersion.IReferenceCollection! GitVersion.IGitRepository.Remotes.get -> GitVersion.IRemoteCollection! diff --git a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs index 2b48c30762..18c0f0ffe5 100644 --- a/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs +++ b/src/GitVersion.LibGit2Sharp/Git/GitRepository.cs @@ -20,6 +20,7 @@ private IRepository RepositoryInstance public string Path => RepositoryInstance.Info.Path; public string WorkingDirectory => RepositoryInstance.Info.WorkingDirectory; public bool IsHeadDetached => RepositoryInstance.Info.IsHeadDetached; + public bool IsShallow => RepositoryInstance.Info.IsShallow; public IBranch Head => new Branch(RepositoryInstance.Head); public ITagCollection Tags => new TagCollection(RepositoryInstance.Tags); public IReferenceCollection Refs => new ReferenceCollection(RepositoryInstance.Refs); diff --git a/src/GitVersion.Testing/Fixtures/RepositoryFixtureBase.cs b/src/GitVersion.Testing/Fixtures/RepositoryFixtureBase.cs index b599c97377..d02fbe671c 100644 --- a/src/GitVersion.Testing/Fixtures/RepositoryFixtureBase.cs +++ b/src/GitVersion.Testing/Fixtures/RepositoryFixtureBase.cs @@ -127,6 +127,15 @@ public LocalRepositoryFixture CloneRepository() return new LocalRepositoryFixture(new Repository(localPath)); } + /// + /// Pulls with a depth of 1 and prunes all older commits, making the repository shallow. + /// + public void MakeShallow() + { + GitTestExtensions.ExecuteGitCmd($"-C {RepositoryPath} pull --depth 1"); + GitTestExtensions.ExecuteGitCmd($"-C {RepositoryPath} gc --prune=all"); + } + public void Fetch(string remote, FetchOptions? options = null) => Commands.Fetch(Repository, remote, Array.Empty(), options, null); }