diff --git a/doc/nbgv-cli.md b/doc/nbgv-cli.md
index b7e58610e..dfe4f55a5 100644
--- a/doc/nbgv-cli.md
+++ b/doc/nbgv-cli.md
@@ -165,6 +165,23 @@ For each branch, the following properties are provided:
**Note:** When the current branch is already the release branch for the current version, no new branch will be created.
In that case, the `NewBranch` property will be `null`.
+### Customizing the `prepare-release` commit message
+
+By default, the `prepare-release` command generates a commit message with the format "Set version to {version}".
+A switch allows you to customize the commit message, using `{0}` as a placeholder for the version.
+
+For example, running the following command:
+
+```
+nbgv prepare-release --commit-message-pattern "Custom commit message pattern - {0} custom message"
+```
+
+So your commit message is going to be this:
+
+```
+Custom commit message pattern - 1.0 custom message
+```
+
## Creating a version tag
The `tag` command automates the task of tagging a commit with a version.
diff --git a/src/NerdBank.GitVersioning/ReleaseManager.cs b/src/NerdBank.GitVersioning/ReleaseManager.cs
index 9dc198010..e937bc2d2 100644
--- a/src/NerdBank.GitVersioning/ReleaseManager.cs
+++ b/src/NerdBank.GitVersioning/ReleaseManager.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System.Globalization;
using LibGit2Sharp;
using Nerdbank.GitVersioning.LibGit2;
using Newtonsoft.Json;
@@ -127,7 +128,10 @@ public enum ReleaseManagerOutputMode
///
/// The output format to use for writing to stdout.
///
- public void PrepareRelease(string projectDirectory, string releaseUnstableTag = null, Version nextVersion = null, VersionOptions.ReleaseVersionIncrement? versionIncrement = null, ReleaseManagerOutputMode outputMode = default)
+ ///
+ /// An optional, custom message to use for the commit that sets the new version number. May use {0} to substitute the new version number.
+ ///
+ public void PrepareRelease(string projectDirectory, string releaseUnstableTag = null, Version nextVersion = null, VersionOptions.ReleaseVersionIncrement? versionIncrement = null, ReleaseManagerOutputMode outputMode = default, string unformattedCommitMessage = null)
{
Requires.NotNull(projectDirectory, nameof(projectDirectory));
@@ -168,7 +172,7 @@ public void PrepareRelease(string projectDirectory, string releaseUnstableTag =
this.WriteToOutput(releaseInfo);
}
- this.UpdateVersion(context, versionOptions.Version, releaseVersion);
+ this.UpdateVersion(context, versionOptions.Version, releaseVersion, unformattedCommitMessage);
return;
}
@@ -192,7 +196,7 @@ public void PrepareRelease(string projectDirectory, string releaseUnstableTag =
// create release branch and update version
Branch releaseBranch = repository.CreateBranch(releaseBranchName);
global::LibGit2Sharp.Commands.Checkout(repository, releaseBranch);
- this.UpdateVersion(context, versionOptions.Version, releaseVersion);
+ this.UpdateVersion(context, versionOptions.Version, releaseVersion, unformattedCommitMessage);
if (outputMode == ReleaseManagerOutputMode.Text)
{
@@ -201,7 +205,7 @@ public void PrepareRelease(string projectDirectory, string releaseUnstableTag =
// update version on main branch
global::LibGit2Sharp.Commands.Checkout(repository, originalBranchName);
- this.UpdateVersion(context, versionOptions.Version, nextDevVersion);
+ this.UpdateVersion(context, versionOptions.Version, nextDevVersion, unformattedCommitMessage);
if (outputMode == ReleaseManagerOutputMode.Text)
{
@@ -261,7 +265,7 @@ private string GetReleaseBranchName(VersionOptions versionOptions)
return branchNameFormat.Replace("{version}", versionOptions.Version.Version.ToString());
}
- private void UpdateVersion(LibGit2Context context, SemanticVersion oldVersion, SemanticVersion newVersion)
+ private void UpdateVersion(LibGit2Context context, SemanticVersion oldVersion, SemanticVersion newVersion, string unformattedCommitMessage)
{
Requires.NotNull(context, nameof(context));
@@ -290,7 +294,13 @@ private void UpdateVersion(LibGit2Context context, SemanticVersion oldVersion, S
// Author a commit only if we effectively changed something.
if (!context.Repository.Head.Tip.Tree.Equals(context.Repository.Index.WriteToTree()))
{
- context.Repository.Commit($"Set version to '{versionOptions.Version}'", signature, signature, new CommitOptions() { AllowEmptyCommit = false });
+ if (string.IsNullOrEmpty(unformattedCommitMessage))
+ {
+ unformattedCommitMessage = "Set version to '{0}'";
+ }
+
+ string commitMessage = string.Format(CultureInfo.CurrentCulture, unformattedCommitMessage, versionOptions.Version);
+ context.Repository.Commit(commitMessage, signature, signature, new CommitOptions() { AllowEmptyCommit = false });
}
}
}
diff --git a/src/nbgv/Program.cs b/src/nbgv/Program.cs
index 960e2062f..5eaa19cb1 100644
--- a/src/nbgv/Program.cs
+++ b/src/nbgv/Program.cs
@@ -10,6 +10,7 @@
using System.IO;
using System.Linq;
using System.Reflection;
+using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Build.Construction;
@@ -69,6 +70,7 @@ private enum ExitCodes
ShallowClone,
InternalError,
InvalidTagNameSetting,
+ InvalidUnformattedCommitMessage,
}
private static bool AlwaysUseLibGit2 => string.Equals(Environment.GetEnvironmentVariable("NBGV_GitEngine"), "LibGit2", StringComparison.Ordinal);
@@ -217,6 +219,7 @@ private static Parser BuildCommandLine()
var nextVersion = new Option("--nextVersion", "The version to set for the current branch. If omitted, the next version is determined automatically by incrementing the current version.");
var versionIncrement = new Option("--versionIncrement", "Overrides the 'versionIncrement' setting set in version.json for determining the next version of the current branch.");
var format = new Option(new[] { "--format", "-f" }, $"The format to write information about the release. Allowed values are: {string.Join(", ", SupportedFormats)}. The default is {DefaultOutputFormat}.").FromAmong(SupportedFormats);
+ var unformattedCommitMessage = new Option("--commit-message-pattern", "A custom message to use for the commit that changes the version number. May include {0} for the version number. If not specified, the default is \"Set version to '{0}'\".");
var tagArgument = new Argument("tag", "The prerelease tag to apply on the release branch (if any). If not specified, any existing prerelease tag will be removed. The preceding hyphen may be omitted.")
{
Arity = ArgumentArity.ZeroOrOne,
@@ -227,10 +230,11 @@ private static Parser BuildCommandLine()
nextVersion,
versionIncrement,
format,
+ unformattedCommitMessage,
tagArgument,
};
- prepareRelease.SetHandler(OnPrepareReleaseCommand, project, nextVersion, versionIncrement, format, tagArgument);
+ prepareRelease.SetHandler(OnPrepareReleaseCommand, project, nextVersion, versionIncrement, format, tagArgument, unformattedCommitMessage);
}
var root = new RootCommand($"{ThisAssembly.AssemblyTitle} v{ThisAssembly.AssemblyInformationalVersion}")
@@ -710,7 +714,7 @@ private static Task OnCloudCommand(string project, string[] metadata, strin
return Task.FromResult((int)ExitCodes.OK);
}
- private static Task OnPrepareReleaseCommand(string project, string nextVersion, string versionIncrement, string format, string tag)
+ private static Task OnPrepareReleaseCommand(string project, string nextVersion, string versionIncrement, string format, string tag, string unformattedCommitMessage)
{
// validate project path property
string searchPath = GetSpecifiedOrCurrentDirectoryPath(project);
@@ -763,11 +767,24 @@ private static Task OnPrepareReleaseCommand(string project, string nextVers
return Task.FromResult((int)ExitCodes.UnsupportedFormat);
}
+ if (!string.IsNullOrEmpty(unformattedCommitMessage))
+ {
+ try
+ {
+ string.Format(unformattedCommitMessage, "FormatValidator");
+ }
+ catch (FormatException ex)
+ {
+ Console.Error.WriteLine($"Invalid commit message pattern: {ex.Message}");
+ return Task.FromResult((int)ExitCodes.InvalidUnformattedCommitMessage);
+ }
+ }
+
// run prepare-release
try
{
var releaseManager = new ReleaseManager(Console.Out, Console.Error);
- releaseManager.PrepareRelease(searchPath, tag, nextVersionParsed, versionIncrementParsed, outputMode);
+ releaseManager.PrepareRelease(searchPath, tag, nextVersionParsed, versionIncrementParsed, outputMode, unformattedCommitMessage);
return Task.FromResult((int)ExitCodes.OK);
}
catch (ReleaseManager.ReleasePreparationException ex)
diff --git a/test/Nerdbank.GitVersioning.Tests/ReleaseManagerTests.cs b/test/Nerdbank.GitVersioning.Tests/ReleaseManagerTests.cs
index 9ced1c7a2..0c170b81d 100644
--- a/test/Nerdbank.GitVersioning.Tests/ReleaseManagerTests.cs
+++ b/test/Nerdbank.GitVersioning.Tests/ReleaseManagerTests.cs
@@ -638,6 +638,34 @@ public void PrepareRelease_ResetsVersionHeightOffset()
Assert.Equal(expectedReleaseVersionOptions, releaseVersion);
}
+ [Theory]
+ [InlineData("1.0-beta", "{0} Custom commit message pattern", "1.0 Custom commit message pattern")]
+ [InlineData("1.0-beta", "Custom commit message pattern - {0} custom message", "Custom commit message pattern - 1.0 custom message")]
+ [InlineData("1.0-beta", "Custom commit message pattern - {0}", "Custom commit message pattern - 1.0")]
+ [InlineData("1.0-beta", "{0}", "1.0")]
+ public void PrepareRelease_WithCustomCommitMessagePattern(string initialVersion, string commitMessagePattern, string expectedCommitMessage)
+ {
+ // Create and configure the repository
+ this.InitializeSourceControl();
+
+ var versionOptions = new VersionOptions()
+ {
+ Version = SemanticVersion.Parse(initialVersion),
+ };
+
+ this.WriteVersionFile(versionOptions);
+
+ // Run PrepareRelease with the custom commit message pattern
+ var releaseManager = new ReleaseManager();
+ releaseManager.PrepareRelease(this.RepoPath, unformattedCommitMessage: commitMessagePattern);
+
+ // Verify that the commit message on the release branch matches the expected pattern
+ string releaseBranchName = "v1.0";
+ Branch releaseBranch = this.LibGit2Repository.Branches[releaseBranchName];
+ Commit releaseBranchCommit = releaseBranch.Tip;
+ Assert.Equal(expectedCommitMessage, releaseBranchCommit.MessageShort);
+ }
+
///
protected override void InitializeSourceControl(bool withInitialCommit = true)
{