diff --git a/GitVersion.sln.GhostDoc.xml b/GitVersion.sln.GhostDoc.xml
index f671bde190..81bf4da447 100644
--- a/GitVersion.sln.GhostDoc.xml
+++ b/GitVersion.sln.GhostDoc.xml
@@ -23,6 +23,7 @@
false
false
true
+ true
false
diff --git a/GitVersionCore/BuildServers/GitHelper.cs b/GitVersionCore/BuildServers/GitHelper.cs
index f434efaca7..7616126c69 100644
--- a/GitVersionCore/BuildServers/GitHelper.cs
+++ b/GitVersionCore/BuildServers/GitHelper.cs
@@ -73,13 +73,25 @@ public static bool LooksLikeAValidPullRequestNumber(string issueNumber)
public static string ExtractIssueNumber(string mergeMessage)
{
+ // Dynamic: refs/heads/pr/5
// Github Message: refs/heads/pull/5/merge
// Stash Message: refs/heads/pull-requests/5/merge
- var regex = new Regex(MergeMessageRegexPattern);
- var match = regex.Match(mergeMessage);
+ // Note by @GeertvanHorrik: sorry, I suck at regex so did a quick hack, feel free to replace by regex
+ if (mergeMessage.Contains("refs/heads/pr/"))
+ {
+ var issueNumber = mergeMessage.Replace("refs/heads/pr/", string.Empty);
+ return issueNumber;
+ }
+ else
+ {
+ var regex = new Regex(MergeMessageRegexPattern);
+ var match = regex.Match(mergeMessage);
- return match.Groups["issuenumber"].Value;
+ var issueNumber = match.Groups["issuenumber"].Value;
+
+ return issueNumber;
+ }
}
static void AddMissingRefSpecs(Repository repo, Remote remote)
diff --git a/GitVersionCore/ExtensionMethods.cs b/GitVersionCore/Extensions/ExtensionMethods.cs
similarity index 98%
rename from GitVersionCore/ExtensionMethods.cs
rename to GitVersionCore/Extensions/ExtensionMethods.cs
index dc658d7928..55c36d2381 100644
--- a/GitVersionCore/ExtensionMethods.cs
+++ b/GitVersionCore/Extensions/ExtensionMethods.cs
@@ -6,7 +6,7 @@ namespace GitVersion
using System.Text.RegularExpressions;
using JetBrains.Annotations;
- static class ExtensionMethods
+ static partial class ExtensionMethods
{
public static bool IsOdd(this int number)
{
diff --git a/GitVersionCore/Extensions/ExtensionMethods.git.cs b/GitVersionCore/Extensions/ExtensionMethods.git.cs
new file mode 100644
index 0000000000..9fb298023c
--- /dev/null
+++ b/GitVersionCore/Extensions/ExtensionMethods.git.cs
@@ -0,0 +1,86 @@
+namespace GitVersion
+{
+ using System;
+
+ static partial class ExtensionMethods
+ {
+ public static string GetCanonicalBranchName(this string branchName)
+ {
+ if (branchName.IsPullRequest())
+ {
+ branchName = branchName.Replace("pull-requests", "pull");
+ branchName = branchName.Replace("pr", "pull");
+
+ return string.Format("refs/{0}/head", branchName);
+ }
+
+ return string.Format("refs/heads/{0}", branchName);
+ }
+
+ public static bool IsHotfix(this string branchName)
+ {
+ return branchName.StartsWith("hotfix-") || branchName.StartsWith("hotfix/");
+ }
+
+ public static string GetHotfixSuffix(this string branchName)
+ {
+ return branchName.TrimStart("hotfix-").TrimStart("hotfix/");
+ }
+
+ public static bool IsRelease(this string branchName)
+ {
+ return branchName.StartsWith("release-") || branchName.StartsWith("release/");
+ }
+
+ public static string GetReleaseSuffix(this string branchName)
+ {
+ return branchName.TrimStart("release-").TrimStart("release/");
+ }
+
+ public static string GetUnknownBranchSuffix(this string branchName)
+ {
+ var unknownBranchSuffix = branchName.Split('-', '/');
+ if (unknownBranchSuffix.Length == 1)
+ return branchName;
+ return unknownBranchSuffix[1];
+ }
+
+ public static string GetSuffix(this string branchName, BranchType branchType)
+ {
+ switch (branchType)
+ {
+ case BranchType.Hotfix:
+ return branchName.GetHotfixSuffix();
+
+ case BranchType.Release:
+ return branchName.GetReleaseSuffix();
+
+ case BranchType.Unknown:
+ return branchName.GetUnknownBranchSuffix();
+
+ default:
+ throw new NotSupportedException(string.Format("Unexpected branch type {0}.", branchType));
+ }
+ }
+
+ public static bool IsDevelop(this string branchName)
+ {
+ return branchName == "develop";
+ }
+
+ public static bool IsMaster(this string branchName)
+ {
+ return branchName == "master";
+ }
+
+ public static bool IsPullRequest(this string branchName)
+ {
+ return branchName.Contains("pull/") || branchName.Contains("pull-requests/") || branchName.Contains("pr/");
+ }
+
+ public static bool IsSupport(this string branchName)
+ {
+ return branchName.ToLower().StartsWith("support-");
+ }
+ }
+}
diff --git a/GitVersionCore/GitFlow/BranchClassifier.cs b/GitVersionCore/GitFlow/BranchClassifier.cs
index de728a6951..5c7c3c7101 100644
--- a/GitVersionCore/GitFlow/BranchClassifier.cs
+++ b/GitVersionCore/GitFlow/BranchClassifier.cs
@@ -4,35 +4,54 @@ namespace GitVersion
static class BranchClassifier
{
-
public static bool IsHotfix(this Branch branch)
{
- return branch.Name.StartsWith("hotfix-") || branch.Name.StartsWith("hotfix/");
+ return branch.Name.IsHotfix();
+ }
+
+ public static string GetHotfixSuffix(this Branch branch)
+ {
+ return branch.Name.GetHotfixSuffix();
}
public static bool IsRelease(this Branch branch)
{
- return branch.Name.StartsWith("release-") || branch.Name.StartsWith("release/");
+ return branch.Name.IsRelease();
+ }
+
+ public static string GetReleaseSuffix(this Branch branch)
+ {
+ return branch.Name.GetReleaseSuffix();
+ }
+
+ public static string GetUnknownBranchSuffix(this Branch branch)
+ {
+ return branch.Name.GetUnknownBranchSuffix();
+ }
+
+ public static string GetSuffix(this Branch branch, BranchType branchType)
+ {
+ return branch.CanonicalName.GetSuffix(branchType);
}
public static bool IsDevelop(this Branch branch)
{
- return branch.Name == "develop";
+ return branch.Name.IsDevelop();
}
public static bool IsMaster(this Branch branch)
{
- return branch.Name == "master";
+ return branch.Name.IsMaster();
}
public static bool IsPullRequest(this Branch branch)
{
- return branch.CanonicalName.Contains("/pull/") || branch.CanonicalName.Contains("/pull-requests/");
+ return branch.CanonicalName.IsPullRequest();
}
public static bool IsSupport(this Branch branch)
{
- return branch.Name.ToLower().StartsWith("support-");
+ return branch.Name.IsSupport();
}
}
}
diff --git a/GitVersionCore/GitHubFlow/GitHubFlowVersionFinder.cs b/GitVersionCore/GitHubFlow/GitHubFlowVersionFinder.cs
index 66beb34564..eeaedb16b2 100644
--- a/GitVersionCore/GitHubFlow/GitHubFlowVersionFinder.cs
+++ b/GitVersionCore/GitHubFlow/GitHubFlowVersionFinder.cs
@@ -4,7 +4,7 @@ public class GitHubFlowVersionFinder
{
public SemanticVersion FindVersion(GitVersionContext context)
{
- var repositoryDirectory = context.Repository.Info.WorkingDirectory;
+ var repositoryDirectory = context.Repository.GetRepositoryDirectory();
var lastTaggedReleaseFinder = new LastTaggedReleaseFinder(context);
var nextVersionTxtFileFinder = new NextVersionTxtFileFinder(repositoryDirectory);
var nextSemverCalculator = new NextSemverCalculator(nextVersionTxtFileFinder, lastTaggedReleaseFinder, context);
diff --git a/GitVersionCore/GitVersionCore.csproj b/GitVersionCore/GitVersionCore.csproj
index f89842d377..e3073e75f3 100644
--- a/GitVersionCore/GitVersionCore.csproj
+++ b/GitVersionCore/GitVersionCore.csproj
@@ -63,13 +63,15 @@
+
+
-
+
@@ -110,7 +112,6 @@
-
diff --git a/GitVersionCore/LibGitExtensions.cs b/GitVersionCore/LibGitExtensions.cs
index 7bc4f92f89..2385fbeb05 100644
--- a/GitVersionCore/LibGitExtensions.cs
+++ b/GitVersionCore/LibGitExtensions.cs
@@ -61,15 +61,16 @@ public static bool IsDetachedHead(this Branch branch)
return branch.CanonicalName.Equals("(no branch)", StringComparison.OrdinalIgnoreCase);
}
- public static string GetRepositoryDirectory(this IRepository repository)
+ public static string GetRepositoryDirectory(this IRepository repository, bool omitGitPostFix = true)
{
var gitDirectory = repository.Info.Path;
gitDirectory = gitDirectory.TrimEnd('\\');
- if (gitDirectory.EndsWith(".git"))
+ if (omitGitPostFix && gitDirectory.EndsWith(".git"))
{
gitDirectory = gitDirectory.Substring(0, gitDirectory.Length - ".git".Length);
+ gitDirectory = gitDirectory.TrimEnd('\\');
}
return gitDirectory;
diff --git a/GitVersionCore/SemanticVersion.cs b/GitVersionCore/SemanticVersion.cs
index a1f1bd83f6..578f672ae5 100644
--- a/GitVersionCore/SemanticVersion.cs
+++ b/GitVersionCore/SemanticVersion.cs
@@ -5,6 +5,8 @@ namespace GitVersion
public class SemanticVersion : IFormattable, IComparable
{
+ public static SemanticVersion Empty = new SemanticVersion();
+
static Regex ParseSemVer = new Regex(
@"[vV]?(?(?\d+)(\.(?\d+))?(\.(?\d+))?)(\.(?\d+))?(-(?[^\+]*))?(\+(?.*))?",
RegexOptions.Compiled);
@@ -34,6 +36,11 @@ public bool Equals(SemanticVersion obj)
BuildMetaData == obj.BuildMetaData;
}
+ public bool IsEmpty()
+ {
+ return Equals(Empty);
+ }
+
public static bool operator ==(SemanticVersion v1, SemanticVersion v2)
{
if (ReferenceEquals(v1, null))
diff --git a/GitVersionExe.Tests/GitPreparerTests.cs b/GitVersionExe.Tests/GitPreparerTests.cs
new file mode 100644
index 0000000000..0a1eb6ab94
--- /dev/null
+++ b/GitVersionExe.Tests/GitPreparerTests.cs
@@ -0,0 +1,71 @@
+namespace GitVersionExe.Tests
+{
+ using System.IO;
+ using GitVersion;
+ using LibGit2Sharp;
+ using NUnit.Framework;
+
+ [TestFixture]
+ public class GitPreparerTests
+ {
+ public GitPreparerTests()
+ {
+ Logger.WriteInfo = s => { };
+ Logger.WriteWarning = s => { };
+ Logger.WriteError = s => { };
+ }
+
+ const string RemoteRepositoryUrl = "https://github.com/ParticularLabs/GitVersion.git";
+ const string DefaultBranchName = "master";
+ const string SpecificBranchName = "gh-pages";
+
+ [Explicit]
+ [TestCase(null, DefaultBranchName)]
+ [TestCase(SpecificBranchName, SpecificBranchName)]
+ public void WorksCorrectlyWithRemoteRepository(string branchName, string expectedBranchName)
+ {
+ var tempDir = Path.GetTempPath();
+
+ var arguments = new Arguments
+ {
+ TargetPath = tempDir,
+ TargetUrl = RemoteRepositoryUrl
+ };
+
+ if (!string.IsNullOrWhiteSpace(branchName))
+ {
+ arguments.TargetBranch = branchName;
+ }
+
+ var gitPreparer = new GitPreparer(arguments);
+ var dynamicRepositoryPath = gitPreparer.Prepare();
+
+ Assert.AreEqual(Path.Combine(tempDir, "_dynamicrepository", ".git"), dynamicRepositoryPath);
+ Assert.IsTrue(gitPreparer.IsDynamicGitRepository);
+
+ using (var repository = new Repository(dynamicRepositoryPath))
+ {
+ var currentBranch = repository.Head.CanonicalName;
+
+ Assert.IsTrue(currentBranch.EndsWith(expectedBranchName));
+ }
+ }
+
+ [Test]
+ public void WorksCorrectlyWithLocalRepository()
+ {
+ var tempDir = Path.GetTempPath();
+
+ var arguments = new Arguments
+ {
+ TargetPath = tempDir
+ };
+
+ var gitPreparer = new GitPreparer(arguments);
+ var dynamicRepositoryPath = gitPreparer.Prepare();
+
+ Assert.AreEqual(null, dynamicRepositoryPath);
+ Assert.IsFalse(gitPreparer.IsDynamicGitRepository);
+ }
+ }
+}
diff --git a/GitVersionExe.Tests/GitVersionExe.Tests.csproj b/GitVersionExe.Tests/GitVersionExe.Tests.csproj
index 7aa9e365ce..df0e9e7533 100644
--- a/GitVersionExe.Tests/GitVersionExe.Tests.csproj
+++ b/GitVersionExe.Tests/GitVersionExe.Tests.csproj
@@ -76,6 +76,7 @@
+
diff --git a/GitVersionExe/GitPreparer.cs b/GitVersionExe/GitPreparer.cs
index fa1ea63394..6d6cab9a9f 100644
--- a/GitVersionExe/GitPreparer.cs
+++ b/GitVersionExe/GitPreparer.cs
@@ -1,6 +1,7 @@
namespace GitVersion
{
using System.IO;
+ using System.Linq;
using LibGit2Sharp;
public class GitPreparer
@@ -33,12 +34,13 @@ public string Prepare()
string GetGitInfoFromUrl()
{
- var gitDirectory = Path.Combine(arguments.TargetPath, "_dynamicrepository", ".git");
- if (Directory.Exists(gitDirectory))
+ var gitRootDirectory = Path.Combine(arguments.TargetPath, "_dynamicrepository");
+ var gitDirectory = Path.Combine(gitRootDirectory, ".git");
+ if (Directory.Exists(gitRootDirectory))
{
- Logger.WriteInfo(string.Format("Deleting existing .git folder from '{0}' to force new checkout from url", gitDirectory));
+ Logger.WriteInfo(string.Format("Deleting existing .git folder from '{0}' to force new checkout from url", gitRootDirectory));
- DeleteHelper.DeleteGitRepository(gitDirectory);
+ DeleteHelper.DeleteGitRepository(gitRootDirectory);
}
Credentials credentials = null;
@@ -48,16 +50,21 @@ string GetGitInfoFromUrl()
Logger.WriteInfo(string.Format("Setting up credentials using name '{0}'", authentication.Username));
credentials = new UsernamePasswordCredentials
- {
- Username = authentication.Username,
- Password = authentication.Password
+ {
+ Username = authentication.Username,
+ Password = authentication.Password
};
}
Logger.WriteInfo(string.Format("Retrieving git info from url '{0}'", arguments.TargetUrl));
- Repository.Clone(arguments.TargetUrl, gitDirectory,
- new CloneOptions { IsBare = true, Checkout = false, Credentials = credentials});
+ Repository.Clone(arguments.TargetUrl, gitDirectory,
+ new CloneOptions
+ {
+ IsBare = true,
+ Checkout = false,
+ CredentialsProvider = (url, usernameFromUrl, types) => credentials
+ });
if (!string.IsNullOrWhiteSpace(arguments.TargetBranch))
{
@@ -66,12 +73,33 @@ string GetGitInfoFromUrl()
using (var repository = new Repository(gitDirectory))
{
- var targetBranchName = string.Format("refs/heads/{0}", arguments.TargetBranch);
- if (!string.Equals(repository.Head.CanonicalName, targetBranchName))
+ Reference newHead = null;
+
+ var localReference = GetLocalReference(repository, arguments.TargetBranch);
+ if (localReference != null)
+ {
+ newHead = localReference;
+ }
+
+ if (newHead == null)
+ {
+ var remoteReference = GetRemoteReference(repository, arguments.TargetBranch, arguments.TargetUrl);
+ if (remoteReference != null)
+ {
+ repository.Network.Fetch(arguments.TargetUrl, new[]
+ {
+ string.Format("{0}:{1}", remoteReference.CanonicalName, arguments.TargetBranch)
+ });
+
+ newHead = repository.Refs[string.Format("refs/heads/{0}", arguments.TargetBranch)];
+ }
+ }
+
+ if (newHead != null)
{
Logger.WriteInfo(string.Format("Switching to branch '{0}'", arguments.TargetBranch));
- repository.Refs.UpdateTarget("HEAD", targetBranchName);
+ repository.Refs.UpdateTarget(repository.Refs.Head, newHead);
}
repository.CheckoutFilesIfExist("NextVersion.txt");
@@ -82,5 +110,20 @@ string GetGitInfoFromUrl()
return gitDirectory;
}
+
+ private static Reference GetLocalReference(Repository repository, string branchName)
+ {
+ var targetBranchName = branchName.GetCanonicalBranchName();
+
+ return repository.Refs.FirstOrDefault(localRef => string.Equals(localRef.CanonicalName, targetBranchName));
+ }
+
+ private static DirectReference GetRemoteReference(Repository repository, string branchName, string repositoryUrl)
+ {
+ var targetBranchName = branchName.GetCanonicalBranchName();
+ var remoteReferences = repository.Network.ListReferences(repositoryUrl);
+
+ return remoteReferences.FirstOrDefault(remoteRef => string.Equals(remoteRef.CanonicalName, targetBranchName));
+ }
}
}
\ No newline at end of file