diff --git a/README.md b/README.md
index d96ed04..6f4e7ad 100644
--- a/README.md
+++ b/README.md
@@ -256,3 +256,67 @@ Minor bumps can be achieved using either of the following within one of the comm
```
Patch bumps occur per-commit, and do not require specific messages to occur.
+
+## Advanced Use Cases
+
+### Using Shallow Clones
+
+For very large repositories with long histories it is often desirable to be able to use shallow clones.
+This is supported via the `--AllowShallowClone=true` argument, however it is critical to understand that there must be enough commits to find a previous version to use as the base.
+In a scenario where the tags for the previous version are always on your main branch's `HEAD` and you always branch from there.
+
+> [!WARNING]
+> if you cannot guarantee that the tags will be present, the generated version is likely to be incorrect.
+
+
+ Example: Bash
+
+```bash
+# Optionally, also use sparse checkout to only fetch some static, known, file that is small since the contents of the repo are unimportant for octoversion to run.
+# Only for cases where you don't need to checkout the whole repo
+git sparse-checkout set reporoot
+
+DEPTH=20
+while true; do
+git fetch --depth=$DEPTH --tags origin main featureBranch
+if git describe --tags --abbrev=0 featureBranch 2>/dev/null; then
+ break
+fi
+DEPTH=$((DEPTH * 2))
+if [ $DEPTH -gt 1000 ]; then
+ echo "Reached maximum depth without finding a tag, stopping fetch"
+ exit 1
+fi
+done
+```
+
+
+
+ Example: GitHub Actions
+
+```yaml
+- name: checkout repo source code with fast checkout
+ uses: actions/checkout@v6
+ with:
+ fetch-depth: 1
+ persist-credentials: true
+ token: ${{ secrets.GITHUB_TOKEN }}
+ sparse-checkout: reporoot
+
+- name: fast-checkout checking out default branch to find common history
+ run: |
+ DEPTH=20
+ while true; do
+ git fetch --depth=$DEPTH --tags origin ${{ github.event.repository.default_branch }} +${{ github.sha }}:${{ github.ref }}
+ if git describe --tags --abbrev=0 ${{ github.ref }} 2>/dev/null; then
+ break
+ fi
+ DEPTH=$((DEPTH * 2))
+ if [ $DEPTH -gt 1000 ]; then
+ echo "Reached maximum depth without finding a tag, stopping fetch"
+ exit 1
+ fi
+ done
+
+```
+
diff --git a/source/OctoVersion.Core/Configuration/AppSettings.cs b/source/OctoVersion.Core/Configuration/AppSettings.cs
index ebb915e..7c00f18 100644
--- a/source/OctoVersion.Core/Configuration/AppSettings.cs
+++ b/source/OctoVersion.Core/Configuration/AppSettings.cs
@@ -37,6 +37,9 @@ public class AppSettings : IAppSettings, IValidatableObject
public string? OutputJsonFile { get; set; }
+ // defaults to false - throw an exception if a shallow clone is detected
+ public bool AllowShallowClone { get; set; }
+
public void ApplyDefaultsIfRequired()
{
if (!NonPreReleaseTags.Any())
@@ -85,4 +88,4 @@ public IEnumerable Validate(ValidationContext validationContex
if (string.IsNullOrWhiteSpace(CurrentBranch) && string.IsNullOrWhiteSpace(FullSemVer))
yield return new ValidationResult($"At least one of {nameof(CurrentBranch)} or {nameof(FullSemVer)} must be provided.", new[] { nameof(CurrentBranch), nameof(FullSemVer) });
}
-}
\ No newline at end of file
+}
diff --git a/source/OctoVersion.Core/OctoVersionRunner.cs b/source/OctoVersion.Core/OctoVersionRunner.cs
index 7fdaf18..6f8b7b3 100644
--- a/source/OctoVersion.Core/OctoVersionRunner.cs
+++ b/source/OctoVersion.Core/OctoVersionRunner.cs
@@ -68,7 +68,7 @@ public void Run(out OctoVersionInfo versionInfo)
? currentDirectory
: appSettings.RepositoryPath;
- var versionCalculatorFactory = new VersionCalculatorFactory(repositorySearchPath);
+ var versionCalculatorFactory = new VersionCalculatorFactory(repositorySearchPath, appSettings.AllowShallowClone);
var calculator = versionCalculatorFactory.Create();
var version = calculator.GetVersion();
var currentSha = calculator.CurrentCommitHash;
diff --git a/source/OctoVersion.Core/VersionNumberCalculation/VersionCalculatorFactory.cs b/source/OctoVersion.Core/VersionNumberCalculation/VersionCalculatorFactory.cs
index a4ec6e1..2bd3dd8 100644
--- a/source/OctoVersion.Core/VersionNumberCalculation/VersionCalculatorFactory.cs
+++ b/source/OctoVersion.Core/VersionNumberCalculation/VersionCalculatorFactory.cs
@@ -12,20 +12,32 @@ public class VersionCalculatorFactory
{
readonly ILogger _logger = Log.ForContext();
readonly Repository _repository;
+ readonly bool _allowShallowClone;
public VersionCalculatorFactory(string repositorySearchPath)
+ : this(repositorySearchPath, allowShallowClone: false)
+ {
+ }
+
+ public VersionCalculatorFactory(string repositorySearchPath, bool allowShallowClone)
{
var gitRepositoryPath = Repository.Discover(repositorySearchPath);
if (gitRepositoryPath == null)
throw new RepositoryNotFoundException("Unable to resolve Git repository path.");
_logger.Debug("Located Git repository in {GitRepositoryPath}", gitRepositoryPath);
_repository = new Repository(gitRepositoryPath);
+ _allowShallowClone = allowShallowClone;
}
public VersionCalculator Create()
{
if (_repository.Info.IsShallow)
- throw new RepositoryIsShallowCloneException("This repository is a shallow clone; it does not contain enough history to resolve the version correctly.");
+ {
+ if (_allowShallowClone)
+ _logger.Warning("This repository is a shallow clone - version calculation may be inaccurate if the commit history does not reach a commit containing version information");
+ else
+ throw new RepositoryIsShallowCloneException("This repository is a shallow clone; it does not contain enough history to resolve the version correctly.");
+ }
Commit[] allCommits;
using (_logger.BeginTimedOperation("Loading commits"))
@@ -52,7 +64,15 @@ public VersionCalculator Create()
var simpleCommit = commits[commit.Sha];
foreach (var parent in commit.Parents)
{
- var simpleParent = commits[parent.Sha];
+ if (!commits.TryGetValue(parent.Sha, out var simpleParent))
+ {
+ // In a shallow clone, boundary commits may reference parents outside the available history. Skip them rather than blowing up with a KeyNotFoundException.
+ if (_allowShallowClone)
+ continue;
+
+ throw new KeyNotFoundException("Unable to find parent commit with hash " + parent.Sha);
+ }
+
simpleCommit.AddParent(simpleParent);
}
}
@@ -84,4 +104,4 @@ public VersionCalculator Create()
var calculator = new VersionCalculator(commits.Values.ToArray(), currentCommitHash);
return calculator;
}
-}
\ No newline at end of file
+}
diff --git a/source/OctoVersion.Tests/ConfigurationBootstrapperFixture.cs b/source/OctoVersion.Tests/ConfigurationBootstrapperFixture.cs
index f0ea542..a43e0a5 100644
--- a/source/OctoVersion.Tests/ConfigurationBootstrapperFixture.cs
+++ b/source/OctoVersion.Tests/ConfigurationBootstrapperFixture.cs
@@ -154,4 +154,33 @@ public static IEnumerable