Skip to content
This repository has been archived by the owner on Feb 12, 2025. It is now read-only.

Git source control: added an optional 'revision' configuration property #163

Closed
wants to merge 2 commits 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions project/UnitTests/Core/SourceControl/GitTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ public void PopulateFromFullySpecifiedXml()
<tagNameFormat>{0}</tagNameFormat>
<committerName>Max Mustermann</committerName>
<committerEMail>[email protected]</committerEMail>
<revision>v1.0.2.0</revision>
</git>";

git = (Git)NetReflector.Read(xml);
Expand All @@ -92,7 +93,8 @@ public void PopulateFromFullySpecifiedXml()
Assert.AreEqual("[email protected]", git.CommitterEMail, "#B11");
Assert.AreEqual(true, git.CommitBuildModifications, "#B12");
Assert.AreEqual(true, git.CommitUntrackedFiles, "#B13");
}
Assert.AreEqual("v1.0.2.0", git.Revision, "#B14");
}

[Test]
public void PopulateFromMinimallySpecifiedXml()
Expand All @@ -115,7 +117,8 @@ public void PopulateFromMinimallySpecifiedXml()
Assert.AreEqual(null, git.CommitterEMail, "#C11");
Assert.AreEqual(false, git.CommitBuildModifications, "#C12");
Assert.AreEqual(false, git.CommitUntrackedFiles, "#C13");
}
Assert.AreEqual(null, git.Revision, "#C14");
}

[Test]
public void ShouldApplyLabelIfTagOnSuccessTrue()
Expand Down
69 changes: 46 additions & 23 deletions project/core/sourcecontrol/Git.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ namespace ThoughtWorks.CruiseControl.Core.Sourcecontrol
/// &lt;sourcecontrol type="git"&gt;
/// &lt;repository&gt;git://github.com/rails/rails.git&lt;/repository&gt;
/// &lt;branch&gt;master&lt;/branch&gt;
/// &lt;revision&gt;v1.0.2.0&lt;/revision&gt;
/// &lt;autoGetSource&gt;true&lt;/autoGetSource&gt;
/// &lt;fetchSubmodules&gt;true&lt;/fetchSubmodules&gt;
/// &lt;executable&gt;git&lt;/executable&gt;
Expand Down Expand Up @@ -65,17 +66,20 @@ namespace ThoughtWorks.CruiseControl.Core.Sourcecontrol
/// <para>
/// Once the repository is initialized the "git fetch origin" command is issued to fetch the remote changes. Next,
/// "git log $LastIntegrationCommit..origin/$BranchName --name-status -c",
/// or "git log $LastIntegrationCommit..$Revision --name-status -c" (if 'revision' property was specified)
/// is issued to get a list of commits and their changes, where $LastIntegrationCommit is the commit which was
/// checked out the last time an integration was run. If the project has not yet been integrated, a
/// "git log origin/$BranchName --name-status -c"
/// or "git log origin/$BranchName --name-status -c" (if 'revision' property was specified)
/// command is issued instead.
/// </para>
/// <para>
/// <b>Getting the source</b>
/// </para>
/// <para>
/// Once Cruise Control.NET has modifications detected and the 'autoGetSource' property is set to 'true' the "git checkout -f
/// origin/$NameOfTheBranch" command is issued. Also the "git clean -f -d -x" command to get a clean working copy to start a new build.
/// origin/$BranchName" (or "git checkout -f $Revision") command is issued.
/// Also the "git clean -f -d -x" command to get a clean working copy to start a new build.
/// If 'fetchSubmodules' is set to 'true' git submodules will be fetched and updated.
/// </para>
/// <para>
Expand Down Expand Up @@ -131,7 +135,7 @@ public class Git : ProcessSourceControl
private BuildProgressInformation _buildProgressInformation;

/// <summary>
/// Whether to fetch the updates from the repository and checkout the branch for a particular build.
/// Whether to fetch the updates from the repository and checkout the branch / revision for a particular build.
/// </summary>
/// <version>1.5</version>
/// <default>true</default>
Expand Down Expand Up @@ -237,6 +241,14 @@ public class Git : ProcessSourceControl
[ReflectorProperty("workingDirectory", Required = false)]
public string WorkingDirectory { get; set; }

/// <summary>
/// Repository revision (commit hash or tag name) to checkout and build.
/// </summary>
/// <version>1.9</version>
/// <default>none</default>
[ReflectorProperty("revision", Required = false)]
public string Revision { get; set; }

/// <summary>
/// Initializes a new instance of the <see cref="Git" /> class.
/// </summary>
Expand Down Expand Up @@ -280,17 +292,17 @@ public override Modification[] GetModifications(IIntegrationResult from, IIntegr
string lastCommit;
if (revisionData.TryGetValue(COMMIT_KEY, out lastCommit))
{
logResult = GitLogHistory(Branch, lastCommit, to);
logResult = GitLogHistory(GetBranchNameOrRevision(Branch, Revision), lastCommit, to);
}
else
{
Log.Debug(string.Concat("[Git] last integrated commit not found, using all ancestors of origin/",
Branch, " as the set of modifications."));
logResult = GitLogHistory(Branch, to);
Log.Debug(string.Concat("[Git] last integrated commit not found, using all ancestors of ",
GetBranchNameOrRevision(Branch, Revision), " as the set of modifications."));
logResult = GitLogHistory(GetBranchNameOrRevision(Branch, Revision), to);
}

// Get the hash of the origin head, and store it against the integration result.
string originHeadHash = GitLogOriginHash(Branch, to);
string originHeadHash = GitLogOriginHash(GetBranchNameOrRevision(Branch, Revision), to);
revisionData[COMMIT_KEY] = originHeadHash;
to.SourceControlData.Clear();
NameValuePair.Copy(revisionData, to.SourceControlData);
Expand All @@ -308,8 +320,8 @@ public override void GetSource(IIntegrationResult result)
if (!AutoGetSource)
return;

// checkout remote branch
GitCheckoutRemoteBranch(Branch, result);
// checkout revision or remote branch
GitCheckoutRemoteBranch(GetBranchNameOrRevision(Branch, Revision), result);

// update submodules
if (FetchSubmodules)
Expand Down Expand Up @@ -470,39 +482,39 @@ private ProcessInfo NewProcessInfo(string args, IIntegrationResult result, Proce
/// <summary>
/// Get the hash of the latest commit in the remote repository.
/// </summary>
/// <param name="branchName">Name of the branch.</param>
/// <param name="branchNameOrRevision">Name of the branch or revision</param>
/// <param name="result">IIntegrationResult of the current build.</param>
private string GitLogOriginHash(string branchName, IIntegrationResult result)
private string GitLogOriginHash(string branchNameOrRevision, IIntegrationResult result)
{
ProcessArgumentBuilder buffer = new ProcessArgumentBuilder();
buffer.AddArgument("log");
buffer.AddArgument(string.Concat("origin/", branchName));
buffer.AddArgument(branchNameOrRevision);
buffer.AddArgument("-1");
buffer.AddArgument("--pretty=format:\"%H\"");
return Execute(NewProcessInfo(buffer.ToString(), result)).StandardOutput.Trim();
}

/// <summary>
/// Get the commit history including changes between <paramref name="from"/> and origin/<paramref name="branchName"/>
/// Get the commit history including changes between <paramref name="from"/> and <paramref name="branchNameOrRevision"/>
/// </summary>
/// <param name="branchName">Name of the branch.</param>
/// <param name="branchNameOrRevision">Name of the branch or revision</param>
/// <param name="from">The commit from which to start logging.</param>
/// <param name="to">IIntegrationResult of the current build.</param>
/// <returns>Result of the "git log" command.</returns>
private ProcessResult GitLogHistory(string branchName, string from, IIntegrationResult to)
private ProcessResult GitLogHistory(string branchNameOrRevision, string from, IIntegrationResult to)
{
ProcessArgumentBuilder buffer = new ProcessArgumentBuilder();
buffer.AddArgument("log");
buffer.AddArgument(string.Concat(from, "..origin/", branchName));
AppendLogOptions(buffer);
buffer.AddArgument(from + ".." + branchNameOrRevision);
AppendLogOptions(buffer);
return Execute(NewProcessInfo(buffer.ToString(), to));
}

private ProcessResult GitLogHistory(string branchName, IIntegrationResult to)
private ProcessResult GitLogHistory(string branchNameOrRevision, IIntegrationResult to)
{
ProcessArgumentBuilder buffer = new ProcessArgumentBuilder();
buffer.AddArgument("log");
buffer.AddArgument(string.Concat("origin/", branchName));
buffer.AddArgument(branchNameOrRevision);
AppendLogOptions(buffer);
return Execute(NewProcessInfo(buffer.ToString(), to));
}
Expand Down Expand Up @@ -603,18 +615,29 @@ private void GitFetch(IIntegrationResult result)
ProcessExecutor.ProcessOutput -= ProcessExecutor_ProcessOutput;
}

/// <summary>
/// Get the target name, either a revision or a remote branch name
/// </summary>
/// <param name="branchName">remote branch name</param>
/// <param name="revision">optional revision (may be null or empty)</param>
/// <returns></returns>
private string GetBranchNameOrRevision(string branchName, string revision)
{
return string.IsNullOrEmpty(revision) ? string.Concat("origin/", branchName) : revision;
}

/// <summary>
/// Checkout a remote branch with the "git checkout -q -f 'origin/branchName'" command.
/// Checkout a remote branch or revision with the "git checkout -q -f 'origin/branchName'" or "git checkout -q -f 'revision'" command.
/// </summary>
/// <param name="branchName">Name of the branch to checkout.</param>
/// <param name="branchOrRevision">Name of the branch to checkout.</param>
/// <param name="result">IIntegrationResult of the current build.</param>
private void GitCheckoutRemoteBranch(string branchName, IIntegrationResult result)
private void GitCheckoutRemoteBranch(string branchOrRevision, IIntegrationResult result)
{
ProcessArgumentBuilder buffer = new ProcessArgumentBuilder();
buffer.AddArgument("checkout");
buffer.AddArgument("-q");
buffer.AddArgument("-f");
buffer.AddArgument(string.Concat("origin/", branchName));
buffer.AddArgument(branchOrRevision);

// initialize progress information
var bpi = GetBuildProgressInformation(result);
Expand Down