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 d07f4ae641..c92b2a66e8 100644
--- a/GitVersionCore/BuildServers/GitHelper.cs
+++ b/GitVersionCore/BuildServers/GitHelper.cs
@@ -7,7 +7,7 @@ namespace GitVersion
public static class GitHelper
{
- const string MergeMessageRegexPattern = "refs/heads/pull(-requests)?/(?[0-9]*)/merge";
+ const string MergeMessageRegexPattern = "refs/heads/(pr|pull(-requests)?/(?[0-9]*)/merge)";
public static void NormalizeGitDirectory(string gitDirectory, Authentication authentication)
{
@@ -32,14 +32,14 @@ public static void NormalizeGitDirectory(string gitDirectory, Authentication aut
Logger.WriteInfo(string.Format("HEAD points at branch '{0}'.", headSha));
return;
}
-
+
Logger.WriteInfo(string.Format("HEAD is detached and points at commit '{0}'.", headSha));
-
+
// In order to decide whether a fake branch is required or not, first check to see if any local branches have the same commit SHA of the head SHA.
// If they do, go ahead and checkout that branch
// If no, go ahead and check out a new branch, using the known commit SHA as the pointer
var localBranchesWhereCommitShaIsHead = repo.Branches.Where(b => !b.IsRemote && b.Tip.Sha == headSha).ToList();
-
+
if (localBranchesWhereCommitShaIsHead.Count > 1)
{
var names = string.Join(", ", localBranchesWhereCommitShaIsHead.Select(r => r.CanonicalName));
@@ -55,7 +55,7 @@ public static void NormalizeGitDirectory(string gitDirectory, Authentication aut
else
{
Logger.WriteInfo(string.Format("Checking out local branch 'refs/heads/{0}'.", localBranchesWhereCommitShaIsHead[0].Name));
- repo.Branches[localBranchesWhereCommitShaIsHead[0].Name].Checkout();
+ repo.Branches[localBranchesWhereCommitShaIsHead[0].Name].Checkout();
}
}
}
@@ -73,13 +73,15 @@ 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);
- return match.Groups["issuenumber"].Value;
+ var issueNumber = match.Groups["issuenumber"].Value;
+
+ return issueNumber;
}
static void AddMissingRefSpecs(Repository repo, Remote remote)
@@ -101,7 +103,7 @@ static FetchOptions BuildFetchOptions(string username, string password)
if (!string.IsNullOrEmpty(username))
{
- fetchOptions.CredentialsProvider = (url, user, types) => new UsernamePasswordCredentials
+ fetchOptions.CredentialsProvider = (url, user, types) => new UsernamePasswordCredentials
{
Username = username,
Password = password
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..a9099dc71a
--- /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-") || branchName.ToLower().StartsWith("support/");
+ }
+ }
+}
diff --git a/GitVersionCore/GitFlow/BranchClassifier.cs b/GitVersionCore/GitFlow/BranchClassifier.cs
index e8a696639a..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-") || branch.Name.StartsWith("support/");
+ return branch.Name.IsSupport();
}
}
}
diff --git a/GitVersionCore/GitHubFlow/GitHubFlowVersionFinder.cs b/GitVersionCore/GitHubFlow/GitHubFlowVersionFinder.cs
index 264b83760d..145e2d2936 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, context.Configuration);
var nextSemverCalculator = new NextSemverCalculator(nextVersionTxtFileFinder, lastTaggedReleaseFinder, context);
diff --git a/GitVersionCore/GitVersionCore.csproj b/GitVersionCore/GitVersionCore.csproj
index 39fb6b0717..28ec915c62 100644
--- a/GitVersionCore/GitVersionCore.csproj
+++ b/GitVersionCore/GitVersionCore.csproj
@@ -73,14 +73,15 @@
-
+
+
-
+
diff --git a/GitVersionCore/LibGitExtensions.cs b/GitVersionCore/LibGitExtensions.cs
index 7374897b2b..b91357c8b1 100644
--- a/GitVersionCore/LibGitExtensions.cs
+++ b/GitVersionCore/LibGitExtensions.cs
@@ -78,15 +78,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 a7fa414232..d52a0adad3 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(
@"(?(?\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 override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
diff --git a/GitVersionExe.Tests/GitPreparerTests.cs b/GitVersionExe.Tests/GitPreparerTests.cs
new file mode 100644
index 0000000000..06bff07bde
--- /dev/null
+++ b/GitVersionExe.Tests/GitPreparerTests.cs
@@ -0,0 +1,75 @@
+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 DefaultBranchName = "master";
+ const string SpecificBranchName = "feature/foo";
+
+ [Test]
+ [TestCase(null, DefaultBranchName)]
+ [TestCase(SpecificBranchName, SpecificBranchName)]
+ public void WorksCorrectlyWithRemoteRepository(string branchName, string expectedBranchName)
+ {
+ var tempDir = Path.GetTempPath();
+
+ using (var fixture = new EmptyRepositoryFixture(new Config()))
+ {
+ fixture.Repository.MakeCommits(5);
+ fixture.Repository.CreateBranch("feature/foo");
+ var arguments = new Arguments
+ {
+ TargetPath = tempDir,
+ TargetUrl = fixture.RepositoryPath
+ };
+
+ 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 62dcc0c050..a0ee7f29d0 100644
--- a/GitVersionExe.Tests/GitVersionExe.Tests.csproj
+++ b/GitVersionExe.Tests/GitVersionExe.Tests.csproj
@@ -80,6 +80,7 @@
+
diff --git a/GitVersionExe/GitPreparer.cs b/GitVersionExe/GitPreparer.cs
index b5d2c2a1c4..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, CredentialsProvider = (url, user, types) => 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
diff --git a/GitVersionTask.Tests/GitVersionTask.Tests.v2.ncrunchproject b/GitVersionTask.Tests/GitVersionTask.Tests.v2.ncrunchproject
index e8a87eec3c..6e25143127 100644
--- a/GitVersionTask.Tests/GitVersionTask.Tests.v2.ncrunchproject
+++ b/GitVersionTask.Tests/GitVersionTask.Tests.v2.ncrunchproject
@@ -22,6 +22,11 @@
AutoDetect
STA
x86
+
+
+ IntegrationTests
+
+
..\Packages\LibGit2Sharp.0.19.0.0\lib\net40\NativeBinaries\**.*
PostBuildEventDisabled
\ No newline at end of file