diff --git a/project/UnitTests/Core/SourceControl/GitTest.cs b/project/UnitTests/Core/SourceControl/GitTest.cs
index e98b843c9..df1a1ffe4 100644
--- a/project/UnitTests/Core/SourceControl/GitTest.cs
+++ b/project/UnitTests/Core/SourceControl/GitTest.cs
@@ -76,6 +76,7 @@ public void PopulateFromFullySpecifiedXml()
{0}
Max Mustermann
max.mustermann@gmx.de
+ v1.0.2.0
";
git = (Git)NetReflector.Read(xml);
@@ -92,7 +93,8 @@ public void PopulateFromFullySpecifiedXml()
Assert.AreEqual("max.mustermann@gmx.de", 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()
@@ -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()
diff --git a/project/core/sourcecontrol/Git.cs b/project/core/sourcecontrol/Git.cs
index 0b672d035..d6b7700e6 100644
--- a/project/core/sourcecontrol/Git.cs
+++ b/project/core/sourcecontrol/Git.cs
@@ -27,6 +27,7 @@ namespace ThoughtWorks.CruiseControl.Core.Sourcecontrol
/// <sourcecontrol type="git">
/// <repository>git://github.com/rails/rails.git</repository>
/// <branch>master</branch>
+ /// <revision>v1.0.2.0</revision>
/// <autoGetSource>true</autoGetSource>
/// <fetchSubmodules>true</fetchSubmodules>
/// <executable>git</executable>
@@ -65,9 +66,11 @@ namespace ThoughtWorks.CruiseControl.Core.Sourcecontrol
///
/// 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.
///
///
@@ -75,7 +78,8 @@ namespace ThoughtWorks.CruiseControl.Core.Sourcecontrol
///
///
/// 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.
///
///
@@ -131,7 +135,7 @@ public class Git : ProcessSourceControl
private BuildProgressInformation _buildProgressInformation;
///
- /// 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.
///
/// 1.5
/// true
@@ -237,6 +241,14 @@ public class Git : ProcessSourceControl
[ReflectorProperty("workingDirectory", Required = false)]
public string WorkingDirectory { get; set; }
+ ///
+ /// Repository revision (commit hash or tag name) to checkout and build.
+ ///
+ /// 1.9
+ /// none
+ [ReflectorProperty("revision", Required = false)]
+ public string Revision { get; set; }
+
///
/// Initializes a new instance of the class.
///
@@ -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);
@@ -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)
@@ -470,39 +482,39 @@ private ProcessInfo NewProcessInfo(string args, IIntegrationResult result, Proce
///
/// Get the hash of the latest commit in the remote repository.
///
- /// Name of the branch.
+ /// Name of the branch or revision
/// IIntegrationResult of the current build.
- 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();
}
///
- /// Get the commit history including changes between and origin/
+ /// Get the commit history including changes between and
///
- /// Name of the branch.
+ /// Name of the branch or revision
/// The commit from which to start logging.
/// IIntegrationResult of the current build.
/// Result of the "git log" command.
- 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));
}
@@ -603,18 +615,29 @@ private void GitFetch(IIntegrationResult result)
ProcessExecutor.ProcessOutput -= ProcessExecutor_ProcessOutput;
}
+ ///
+ /// Get the target name, either a revision or a remote branch name
+ ///
+ /// remote branch name
+ /// optional revision (may be null or empty)
+ ///
+ private string GetBranchNameOrRevision(string branchName, string revision)
+ {
+ return string.IsNullOrEmpty(revision) ? string.Concat("origin/", branchName) : revision;
+ }
+
///
- /// 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.
///
- /// Name of the branch to checkout.
+ /// Name of the branch to checkout.
/// IIntegrationResult of the current build.
- 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);