It turns out that there is still no way to do this in TeamCity, so at the same time this problem was solved by running an additional MsBuild script at the beginning of the build process, which checks if the master branch is present in the current (local) repository and receives it if this is not true.
The script looks like this:
<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" DefaultTargets="Run" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <DirWorkspace>$(MSBuildProjectDirectory)</DirWorkspace> <DirRepository Condition=" '$(DirRepository)' == '' ">$(DirWorkspace)</DirRepository> <DirGit Condition=" '$(DirGit)' == '' ">c:\Program Files (x86)\Git\bin</DirGit> </PropertyGroup> <Import Project="$(DirWorkspace)\GitHasMasterBranch.msbuild" Condition="Exists('$(DirWorkspace)\GitHasMasterBranch.msbuild')"/> <Import Project="$(DirWorkspace)\GitGetMasterBranch.msbuild" Condition="Exists('$(DirWorkspace)\GitGetMasterBranch.msbuild')"/> <Target Name="Run" DependsOnTargets="_DisplayInfo;_FetchOriginMasterIfNotExists"> </Target> <Target Name="_DisplayInfo"> <Message Text="Preparing workspace ..." /> </Target> <PropertyGroup> <ExeGit>$(DirGit)\git.exe</ExeGit> </PropertyGroup> <Target Name="_FetchOriginMasterIfNotExists" DependsOnTargets="_DisplayInfo"> <GitHasMasterBranch LocalPath="$(DirRepository)"> <Output TaskParameter="HasMaster" PropertyName="HasMaster" /> </GitHasMasterBranch> <Message Text="Not fetching master branch because it already exists" Condition="($(HasMaster))" /> <Message Text="Fetching master branch because it does not exist" Condition="(!$(HasMaster))" /> <GitGetMasterBranch LocalPath="$(DirRepository)" Condition="(!$(HasMaster))"/> </Target> </Project>
In this script, the GitHasMasterBranch MsBuild inline script line looks like this:
<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' ToolsVersion="4.0"> <UsingTask TaskName="GitHasMasterBranch" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll"> <ParameterGroup> <LocalPath ParameterType="System.String" Required="true" /> <HasMaster ParameterType="System.Boolean" Output="true" /> </ParameterGroup> <Task> <Code Type="Method" Language="cs"> <![CDATA[ public override bool Execute() { var info = new System.Diagnostics.ProcessStartInfo { FileName = "git", Arguments = "branch", WorkingDirectory = LocalPath, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, }; var text = new System.Text.StringBuilder(); var process = new System.Diagnostics.Process(); process.StartInfo = info; process.OutputDataReceived += (s, e) => { text.Append(e.Data); }; process.ErrorDataReceived += (s, e) => { if (!string.IsNullOrWhiteSpace(e.Data)) { Log.LogError(e.Data); } }; process.Start(); process.BeginOutputReadLine(); process.BeginErrorReadLine(); process.WaitForExit(); HasMaster = text.ToString().Contains("* master");
And the GitGetMasterBranch MsBuild inline script looks like this:
<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' ToolsVersion="4.0"> <UsingTask TaskName="GitGetMasterBranch" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll"> <ParameterGroup> <LocalPath ParameterType="System.String" Required="true" /> </ParameterGroup> <Task> <Code Type="Method" Language="cs"> <![CDATA[ public override bool Execute() { // Get the name of the current branch var info = new System.Diagnostics.ProcessStartInfo { FileName = "git", Arguments = "symbolic-ref --short -q HEAD", WorkingDirectory = LocalPath, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, }; var text = new System.Text.StringBuilder(); var process = new System.Diagnostics.Process(); process.StartInfo = info; process.OutputDataReceived += (s, e) => { text.Append(e.Data); }; process.Start(); process.BeginOutputReadLine(); process.BeginErrorReadLine(); process.WaitForExit(); var currentBranch = text.ToString().Trim(); // git fetch info = new System.Diagnostics.ProcessStartInfo { FileName = "git", Arguments = "fetch origin", WorkingDirectory = LocalPath, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, }; process = new System.Diagnostics.Process(); process.StartInfo = info; process.OutputDataReceived += (s, e) => { if (!string.IsNullOrWhiteSpace(e.Data)) { Log.LogMessage(MessageImportance.High, e.Data); } }; process.Start(); process.BeginOutputReadLine(); process.BeginErrorReadLine(); process.WaitForExit(); // git checkout master info = new System.Diagnostics.ProcessStartInfo { FileName = "git", Arguments = "checkout master", WorkingDirectory = LocalPath, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, }; process = new System.Diagnostics.Process(); process.StartInfo = info; process.OutputDataReceived += (s, e) => { if (!string.IsNullOrWhiteSpace(e.Data)) { Log.LogMessage(MessageImportance.High, e.Data); } }; process.Start(); process.BeginOutputReadLine(); process.BeginErrorReadLine(); process.WaitForExit(); // git pull info = new System.Diagnostics.ProcessStartInfo { FileName = "git", Arguments = "pull", WorkingDirectory = LocalPath, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, }; process = new System.Diagnostics.Process(); process.StartInfo = info; process.OutputDataReceived += (s, e) => { if (!string.IsNullOrWhiteSpace(e.Data)) { Log.LogMessage(MessageImportance.High, e.Data); } }; process.Start(); process.BeginOutputReadLine(); process.BeginErrorReadLine(); process.WaitForExit(); // git checkout <CURRENT_BRANCH> info = new System.Diagnostics.ProcessStartInfo { FileName = "git", Arguments = string.Format("checkout {0}", currentBranch), WorkingDirectory = LocalPath, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, }; process = new System.Diagnostics.Process(); process.StartInfo = info; process.OutputDataReceived += (s, e) => { if (!string.IsNullOrWhiteSpace(e.Data)) { Log.LogMessage(MessageImportance.High, e.Data); } }; process.Start(); process.BeginOutputReadLine(); process.BeginErrorReadLine(); process.WaitForExit(); // Log.HasLoggedErrors is true if the task logged any errors -- even if they were logged // from a task constructor or property setter. As long as this task is written to always log an error // when it fails, we can reliably return HasLoggedErrors. return !Log.HasLoggedErrors; } ]]> </Code> </Task> </UsingTask> </Project>
Essentially, all this last script is to save the current branch name, do a GIT fetch to get all branches, do a GIT checkout main branch, and then do a GIT checkout original branch.
This is not the fastest approach, but it works now.