Skip to content
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

[WIP] Allow skipping commits that don't contain changes relative to the project path. #223

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
35 changes: 27 additions & 8 deletions src/NerdBank.GitVersioning/GitExtensions.cs
Expand Up @@ -45,7 +45,7 @@ public static int GetVersionHeight(this Commit commit, string repoRelativeProjec
baseVersion = VersionFile.GetVersion(commit, repoRelativeProjectDirectory)?.Version?.Version ?? Version0;
}

int height = commit.GetHeight(c => CommitMatchesMajorMinorVersion(c, baseVersion, repoRelativeProjectDirectory));
int height = commit.GetHeight(null, c => CommitMatchesMajorMinorVersion(c, baseVersion, repoRelativeProjectDirectory));
return height;
}

Expand Down Expand Up @@ -100,20 +100,25 @@ public static int GetVersionHeight(this Branch branch, string repoRelativeProjec
/// the specified commit and the most distant ancestor (inclusive).
/// </summary>
/// <param name="commit">The commit to measure the height of.</param>
/// <param name="pathFilters">
/// An array of paths to filter the changes in commits on. If a commit,
/// after filtering, contains no changes, the commit does not contribute to the height.
/// May be null to disable filtering.</param>
/// <param name="continueStepping">
/// A function that returns <c>false</c> when we reach a commit that
/// should not be included in the height calculation.
/// May be null to count the height to the original commit.
/// </param>
/// <returns>The height of the commit. Always a positive integer.</returns>
public static int GetHeight(this Commit commit, Func<Commit, bool> continueStepping = null)
public static int GetHeight(this Commit commit, string[] pathFilters = null, Func<Commit, bool> continueStepping = null)
{
Requires.NotNull(commit, nameof(commit));

var heights = new Dictionary<ObjectId, int>();
return GetCommitHeight(commit, heights, continueStepping);
return GetCommitHeight(commit, heights, pathFilters, continueStepping);
}


/// <summary>
/// Gets the number of commits in the longest single path between
/// the specified branch's head and the most distant ancestor (inclusive).
Expand All @@ -127,7 +132,7 @@ public static int GetHeight(this Commit commit, Func<Commit, bool> continueStepp
/// <returns>The height of the branch.</returns>
public static int GetHeight(this Branch branch, Func<Commit, bool> continueStepping = null)
{
return GetHeight(branch.Commits.First(), continueStepping);
return GetHeight(branch.Commits.First(), null, continueStepping);
}

/// <summary>
Expand Down Expand Up @@ -282,7 +287,7 @@ public static IEnumerable<Commit> GetCommitsFromVersion(this Repository repo, Ve
var possibleCommits = from commit in GetCommitsReachableFromRefs(repo)
where version.Revision == -1 || commit.Id.StartsWith(objectIdLeadingValue, objectIdMask)
let buildNumberOffset = VersionFile.GetVersion(commit)?.BuildNumberOffsetOrDefault ?? 0
let versionHeight = commit.GetHeight(c => CommitMatchesMajorMinorVersion(c, version, repoRelativeProjectDirectory))
let versionHeight = commit.GetHeight(null, c => CommitMatchesMajorMinorVersion(c, version, repoRelativeProjectDirectory))
where versionHeight == version.Build - buildNumberOffset
select commit;

Expand Down Expand Up @@ -488,13 +493,17 @@ private static string EncodeAsHex(byte[] buffer)
/// </summary>
/// <param name="commit">The commit to measure the height of.</param>
/// <param name="heights">A cache of commits and their heights.</param>
/// <param name="pathFilters">
/// An array of paths to filter the changes in commits on. If a commit,
/// after filtering, contains no changes, the commit does not contribute to the height.
/// May be null to disable filtering.</param>
/// <param name="continueStepping">
/// A function that returns <c>false</c> when we reach a commit that
/// should not be included in the height calculation.
/// May be null to count the height to the original commit.
/// </param>
/// <returns>The height of the branch.</returns>
private static int GetCommitHeight(Commit commit, Dictionary<ObjectId, int> heights, Func<Commit, bool> continueStepping)
private static int GetCommitHeight(Commit commit, Dictionary<ObjectId, int> heights, string[] pathFilters, Func<Commit, bool> continueStepping)
{
Requires.NotNull(commit, nameof(commit));
Requires.NotNull(heights, nameof(heights));
Expand All @@ -505,10 +514,20 @@ private static int GetCommitHeight(Commit commit, Dictionary<ObjectId, int> heig
height = 0;
if (continueStepping == null || continueStepping(commit))
{
height = 1;
if (pathFilters != null)
{
var repo = ((IBelongToARepository)commit)
.Repository;

if (commit.Parents.SelectMany(parent => repo.Diff.Compare<TreeChanges>(parent.Tree, commit.Tree, pathFilters)).Any())
height = 1;
}
else
height = 1;

if (commit.Parents.Any())
{
height += commit.Parents.Max(p => GetCommitHeight(p, heights, continueStepping));
height += commit.Parents.Max(p => GetCommitHeight(p, heights, pathFilters, continueStepping));
}
}

Expand Down
14 changes: 8 additions & 6 deletions src/NerdBank.GitVersioning/VersionOracle.cs
Expand Up @@ -27,7 +27,7 @@ public class VersionOracle
/// <summary>
/// Initializes a new instance of the <see cref="VersionOracle"/> class.
/// </summary>
public static VersionOracle Create(string projectDirectory, string gitRepoDirectory = null, ICloudBuild cloudBuild = null, int? overrideBuildNumberOffset = null, string projectPathRelativeToGitRepoRoot = null)
public static VersionOracle Create(string projectDirectory, string gitRepoDirectory = null, ICloudBuild cloudBuild = null, int? overrideBuildNumberOffset = null, string projectPathRelativeToGitRepoRoot = null, bool ignoreCommitsWithoutChangesInProjectDirectory = false)
{
Requires.NotNull(projectDirectory, nameof(projectDirectory));
if (string.IsNullOrEmpty(gitRepoDirectory))
Expand All @@ -37,7 +37,7 @@ public static VersionOracle Create(string projectDirectory, string gitRepoDirect

using (var git = GitExtensions.OpenGitRepo(gitRepoDirectory))
{
return new VersionOracle(projectDirectory, git, null, cloudBuild, overrideBuildNumberOffset, projectPathRelativeToGitRepoRoot);
return new VersionOracle(projectDirectory, git, null, cloudBuild, overrideBuildNumberOffset, projectPathRelativeToGitRepoRoot, ignoreCommitsWithoutChangesInProjectDirectory);
}
}

Expand All @@ -52,7 +52,7 @@ public VersionOracle(string projectDirectory, LibGit2Sharp.Repository repo, IClo
/// <summary>
/// Initializes a new instance of the <see cref="VersionOracle"/> class.
/// </summary>
public VersionOracle(string projectDirectory, LibGit2Sharp.Repository repo, LibGit2Sharp.Commit head, ICloudBuild cloudBuild, int? overrideBuildNumberOffset = null, string projectPathRelativeToGitRepoRoot = null)
public VersionOracle(string projectDirectory, LibGit2Sharp.Repository repo, LibGit2Sharp.Commit head, ICloudBuild cloudBuild, int? overrideBuildNumberOffset = null, string projectPathRelativeToGitRepoRoot = null, bool ignoreCommitsWithoutChangesInProjectDirectory = false)
{
var repoRoot = repo?.Info?.WorkingDirectory?.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
var relativeRepoProjectDirectory = !string.IsNullOrWhiteSpace(repoRoot)
Expand Down Expand Up @@ -83,7 +83,7 @@ public VersionOracle(string projectDirectory, LibGit2Sharp.Repository repo, LibG
this.VersionOptions = committedVersion ?? workingVersion;

this.GitCommitId = commit?.Id.Sha ?? cloudBuild?.GitCommitId ?? null;
this.VersionHeight = CalculateVersionHeight(relativeRepoProjectDirectory, commit, committedVersion, workingVersion);
this.VersionHeight = CalculateVersionHeight(relativeRepoProjectDirectory, commit, committedVersion, workingVersion, ignoreCommitsWithoutChangesInProjectDirectory);
this.BuildingRef = cloudBuild?.BuildingTag ?? cloudBuild?.BuildingBranch ?? repo?.Head.CanonicalName;

// Override the typedVersion with the special build number and revision components, when available.
Expand Down Expand Up @@ -418,7 +418,7 @@ private static string MakePrereleaseSemVer1Compliant(string prerelease, int padd
return semver1;
}

private static int CalculateVersionHeight(string relativeRepoProjectDirectory, LibGit2Sharp.Commit headCommit, VersionOptions committedVersion, VersionOptions workingVersion)
private static int CalculateVersionHeight(string relativeRepoProjectDirectory, LibGit2Sharp.Commit headCommit, VersionOptions committedVersion, VersionOptions workingVersion, bool ignoreCommitsWithoutChangesInProjectDirectory)
{
var headCommitVersion = committedVersion?.Version?.Version ?? Version0;

Expand All @@ -434,7 +434,9 @@ private static int CalculateVersionHeight(string relativeRepoProjectDirectory, L
}
}

return headCommit?.GetHeight(c => c.CommitMatchesMajorMinorVersion(headCommitVersion, relativeRepoProjectDirectory)) ?? 0;
return headCommit?.GetHeight(
ignoreCommitsWithoutChangesInProjectDirectory ? new[] { relativeRepoProjectDirectory } : null,
c => c.CommitMatchesMajorMinorVersion(headCommitVersion, relativeRepoProjectDirectory)) ?? 0;
}

private static Version GetIdAsVersion(LibGit2Sharp.Commit headCommit, VersionOptions committedVersion, VersionOptions workingVersion, int versionHeight)
Expand Down
10 changes: 8 additions & 2 deletions src/Nerdbank.GitVersioning.Tasks/GetBuildVersion.cs
Expand Up @@ -37,6 +37,12 @@ public GetBuildVersion()
/// <value>Expected to be "true", "false", or empty.</value>
public string DefaultPublicRelease { get; set; }

/// <summary>
/// Gets or sets a value indicating whether only commits that contain changes
/// in the project path contribute to git version height.
/// </summary>
public string SkipCommitsWithoutChangesInPath { get; set; }

/// <summary>
/// Gets or sets the path to the repo root. If null or empty, behavior defaults to using Environment.CurrentDirectory and searching upwards.
/// </summary>
Expand Down Expand Up @@ -77,7 +83,7 @@ public GetBuildVersion()
/// </summary>
[Output]
public bool PublicRelease { get; private set; }

/// <summary>
/// Gets the version string to use in the compiled assemblies.
/// </summary>
Expand Down Expand Up @@ -188,7 +194,7 @@ protected override bool ExecuteInner()

var cloudBuild = CloudBuild.Active;
var overrideBuildNumberOffset = (this.OverrideBuildNumberOffset == int.MaxValue) ? (int?)null : this.OverrideBuildNumberOffset;
var oracle = VersionOracle.Create(Directory.GetCurrentDirectory(), this.GitRepoRoot, cloudBuild, overrideBuildNumberOffset, this.ProjectPathRelativeToGitRepoRoot);
var oracle = VersionOracle.Create(Directory.GetCurrentDirectory(), this.GitRepoRoot, cloudBuild, overrideBuildNumberOffset, this.ProjectPathRelativeToGitRepoRoot, string.Equals(this.SkipCommitsWithoutChangesInPath, "true", StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrEmpty(this.DefaultPublicRelease))
{
oracle.PublicRelease = string.Equals(this.DefaultPublicRelease, "true", StringComparison.OrdinalIgnoreCase);
Expand Down
Expand Up @@ -65,6 +65,7 @@
BuildMetadata="@(BuildMetadata)"
DefaultPublicRelease="$(PublicRelease)"
GitRepoRoot="$(GitRepoRoot)"
SkipCommitsWithoutChangesInPath="$(SkipCommitsWithoutChangesInPath)"
ProjectPathRelativeToGitRepoRoot="$(ProjectPathRelativeToGitRepoRoot)"
OverrideBuildNumberOffset="$(OverrideBuildNumberOffset)"
TargetsPath="$(MSBuildThisFileDirectory)">
Expand Down