Automatically Associate work items with a shelve command.

This commit is contained in:
NathanLBCooper
2016-02-05 17:43:02 +00:00
parent 9399d4dd08
commit a7adafa261
15 changed files with 286 additions and 172 deletions
+23 -2
View File
@@ -2,6 +2,7 @@
using System.IO;
using NDesk.Options;
using Sep.Git.Tfs.Core;
using Sep.Git.Tfs.Util;
using StructureMap;
namespace Sep.Git.Tfs.Commands
@@ -13,14 +14,18 @@ namespace Sep.Git.Tfs.Commands
{
private readonly TextWriter _stdout;
private readonly CheckinOptions _checkinOptions;
private readonly ShelveSpecificCheckinOptionsFactory _checkinOptionsFactory;
private readonly TfsWriter _writer;
private readonly Globals _globals;
private bool EvaluateCheckinPolicies { get; set; }
public Shelve(TextWriter stdout, CheckinOptions checkinOptions, TfsWriter writer)
public Shelve(TextWriter stdout, CheckinOptions checkinOptions, TfsWriter writer, Globals globals)
{
_stdout = stdout;
_globals = globals;
_checkinOptions = checkinOptions;
_checkinOptionsFactory = new ShelveSpecificCheckinOptionsFactory(_stdout, _globals);
_writer = writer;
}
@@ -52,9 +57,25 @@ namespace Sep.Git.Tfs.Commands
_stdout.WriteLine("Shelveset \"" + shelvesetName + "\" already exists. Use -f to replace it.");
return GitTfsExitCodes.ForceRequired;
}
changeset.Remote.Shelve(shelvesetName, referenceToShelve, changeset, EvaluateCheckinPolicies);
var commit = _globals.Repository.GetCommit(refToShelve);
var message = commit != null
? BuildCommitMessage(commit, !_checkinOptions.NoGenerateCheckinComment,
changeset.Remote.MaxCommitHash)
: string.Empty;
var shelveSpecificCheckinOptions = _checkinOptionsFactory.BuildShelveSetSpecificCheckinOptions(_checkinOptions, message);
changeset.Remote.Shelve(shelvesetName, referenceToShelve, changeset, shelveSpecificCheckinOptions, EvaluateCheckinPolicies);
return GitTfsExitCodes.OK;
});
}
public string BuildCommitMessage(GitCommit commit, bool generateCheckinComment, string latest)
{
return generateCheckinComment
? _globals.Repository.GetCommitMessage(commit.Sha, latest)
: _globals.Repository.GetCommit(commit.Sha).Message;
}
}
}
+5
View File
@@ -230,6 +230,11 @@ namespace Sep.Git.Tfs.Core
throw DerivedRemoteException;
}
public void Shelve(string shelvesetName, string treeish, TfsChangesetInfo parentChangeset, CheckinOptions options, bool evaluateCheckinPolicies)
{
throw DerivedRemoteException;
}
public void Shelve(string shelvesetName, string treeish, TfsChangesetInfo parentChangeset, bool evaluateCheckinPolicies)
{
throw DerivedRemoteException;
+4 -4
View File
@@ -846,9 +846,9 @@ namespace Sep.Git.Tfs.Core
Repository.UpdateRef(destinationRef, commit, "Shelveset " + shelvesetName + " from " + shelvesetOwner);
}
public void Shelve(string shelvesetName, string head, TfsChangesetInfo parentChangeset, bool evaluateCheckinPolicies)
public void Shelve(string shelvesetName, string head, TfsChangesetInfo parentChangeset, CheckinOptions options, bool evaluateCheckinPolicies)
{
WithWorkspace(parentChangeset, workspace => Shelve(shelvesetName, head, parentChangeset, evaluateCheckinPolicies, workspace));
WithWorkspace(parentChangeset, workspace => Shelve(shelvesetName, head, parentChangeset, options, evaluateCheckinPolicies, workspace));
}
public bool HasShelveset(string shelvesetName)
@@ -856,10 +856,10 @@ namespace Sep.Git.Tfs.Core
return Tfs.HasShelveset(shelvesetName);
}
private void Shelve(string shelvesetName, string head, TfsChangesetInfo parentChangeset, bool evaluateCheckinPolicies, ITfsWorkspace workspace)
private void Shelve(string shelvesetName, string head, TfsChangesetInfo parentChangeset, CheckinOptions options, bool evaluateCheckinPolicies, ITfsWorkspace workspace)
{
PendChangesToWorkspace(head, parentChangeset.GitCommit, workspace);
workspace.Shelve(shelvesetName, evaluateCheckinPolicies, () => Repository.GetCommitMessage(head, parentChangeset.GitCommit));
workspace.Shelve(shelvesetName, evaluateCheckinPolicies, options);
}
public int CheckinTool(string head, TfsChangesetInfo parentChangeset)
+1 -1
View File
@@ -56,7 +56,7 @@ namespace Sep.Git.Tfs.Core
void QuickFetch();
void QuickFetch(int changesetId);
void Unshelve(string shelvesetOwner, string shelvesetName, string destinationBranch, Action<Exception> ignorableErrorHandler, bool force);
void Shelve(string shelvesetName, string treeish, TfsChangesetInfo parentChangeset, bool evaluateCheckinPolicies);
void Shelve(string shelvesetName, string head, TfsChangesetInfo parentChangeset, CheckinOptions options, bool evaluateCheckinPolicies);
bool HasShelveset(string shelvesetName);
int CheckinTool(string head, TfsChangesetInfo parentChangeset);
int Checkin(string treeish, TfsChangesetInfo parentChangeset, CheckinOptions options, string sourceTfsPath = null);
+1 -1
View File
@@ -25,7 +25,7 @@ namespace Sep.Git.Tfs.Core
/// <summary>
/// Shelves all pending changes, with the given shelveset name.
/// </summary>
void Shelve(string shelvesetName, bool evaluateCheckinPolicies, Func<string> generateCheckinComment);
void Shelve(string shelvesetName, bool evaluateCheckinPolicies, CheckinOptions checkinOptions);
/// <summary>
/// Evaluates check-in policies and checks in all pending changes.
/// </summary>
+4 -3
View File
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Sep.Git.Tfs.Commands;
@@ -33,7 +34,7 @@ namespace Sep.Git.Tfs.Core
this.Remote = remote;
}
public void Shelve(string shelvesetName, bool evaluateCheckinPolicies, Func<string> generateCheckinComment)
public void Shelve(string shelvesetName, bool evaluateCheckinPolicies, CheckinOptions checkinOptions)
{
var pendingChanges = _workspace.GetPendingChanges();
@@ -41,8 +42,8 @@ namespace Sep.Git.Tfs.Core
throw new GitTfsException("Nothing to shelve!");
var shelveset = _tfsHelper.CreateShelveset(_workspace, shelvesetName);
shelveset.Comment = string.IsNullOrWhiteSpace(_checkinOptions.CheckinComment) && !_checkinOptions.NoGenerateCheckinComment ? generateCheckinComment() : _checkinOptions.CheckinComment;
shelveset.WorkItemInfo = GetWorkItemInfos().ToArray();
shelveset.Comment = checkinOptions.CheckinComment;
shelveset.WorkItemInfo = GetWorkItemInfos(checkinOptions).ToArray();
if (evaluateCheckinPolicies)
{
foreach (var message in _policyEvaluator.EvaluateCheckin(_workspace, pendingChanges, shelveset.Comment, null, shelveset.WorkItemInfo).Messages)
+2
View File
@@ -202,6 +202,7 @@
<Compile Include="Util\AuthorsFile.cs" />
<Compile Include="Util\Bouncer.cs" />
<Compile Include="Util\ChangeSieve.cs" />
<Compile Include="Util\CheckinOptionsExtensions.cs" />
<Compile Include="Util\ConfigPropertyLoader.cs" />
<Compile Include="Util\ExportMetadatasInitializer.cs" />
<Compile Include="Util\PathResolver.cs" />
@@ -211,6 +212,7 @@
<Compile Include="Util\GitTfsCommandFactory.cs" />
<Compile Include="Util\GitTfsCommandRunner.cs" />
<Compile Include="Core\IGitTfsVersionProvider.cs" />
<Compile Include="Util\ShelveSpecificCheckinOptionsFactory.cs" />
<Compile Include="Util\TemporaryFile.cs" />
<Compile Include="Util\WhenDynamicDoesntWork.cs" />
<Compile Include="Util\PluggableWithAliases.cs" />
+146
View File
@@ -0,0 +1,146 @@
using System;
using System.IO;
using System.Text.RegularExpressions;
using Sep.Git.Tfs.Commands;
using Sep.Git.Tfs.Core;
namespace Sep.Git.Tfs.Util
{
public static class CheckinOptionsExtensions
{
public static CheckinOptions Clone(this CheckinOptions source, Globals globals)
{
CheckinOptions clone = new CheckinOptions();
clone.CheckinComment = source.CheckinComment;
clone.NoGenerateCheckinComment = source.NoGenerateCheckinComment;
clone.NoMerge = source.NoMerge;
clone.OverrideReason = source.OverrideReason;
clone.Force = source.Force;
clone.OverrideGatedCheckIn = source.OverrideGatedCheckIn;
clone.WorkItemsToAssociate.AddRange(source.WorkItemsToAssociate);
clone.WorkItemsToResolve.AddRange(source.WorkItemsToResolve);
clone.AuthorTfsUserId = source.AuthorTfsUserId;
try
{
string re = globals.Repository.GetConfig(GitTfsConstants.WorkItemAssociateRegexConfigKey);
if (String.IsNullOrEmpty(re))
clone.WorkItemAssociateRegex = GitTfsConstants.TfsWorkItemAssociateRegex;
else
clone.WorkItemAssociateRegex = new Regex(re);
}
catch (Exception)
{
clone.WorkItemAssociateRegex = null;
}
foreach (var note in source.CheckinNotes)
{
clone.CheckinNotes[note.Key] = note.Value;
}
return clone;
}
public static void ProcessWorkItemCommands(this CheckinOptions checkinOptions, TextWriter writer, bool isResolvable = true)
{
MatchCollection workitemMatches;
if ((workitemMatches = GitTfsConstants.TfsWorkItemRegex.Matches(checkinOptions.CheckinComment)).Count > 0)
{
foreach (Match match in workitemMatches)
{
if (isResolvable && match.Groups["action"].Value == "resolve")
{
writer.WriteLine("Resolving work item {0}", match.Groups["item_id"]);
checkinOptions.WorkItemsToResolve.Add(match.Groups["item_id"].Value);
}
else {
writer.WriteLine("Associating with work item {0}", match.Groups["item_id"]);
checkinOptions.WorkItemsToAssociate.Add(match.Groups["item_id"].Value);
}
}
checkinOptions.CheckinComment = GitTfsConstants.TfsWorkItemRegex.Replace(checkinOptions.CheckinComment, "").Trim(' ', '\r', '\n');
}
if (checkinOptions.WorkItemAssociateRegex != null)
{
var workitemAssociatedMatches = checkinOptions.WorkItemAssociateRegex.Matches(checkinOptions.CheckinComment);
if (workitemAssociatedMatches.Count != 0)
{
foreach (Match match in workitemAssociatedMatches)
{
var workitem = match.Groups["item_id"].Value;
if (!checkinOptions.WorkItemsToAssociate.Contains(workitem))
{
writer.WriteLine("Associating with work item {0}", workitem);
checkinOptions.WorkItemsToAssociate.Add(workitem);
}
}
}
}
}
public static void ProcessCheckinNoteCommands(this CheckinOptions checkinOptions, TextWriter writer)
{
foreach (Match match in GitTfsConstants.TfsReviewerRegex.Matches(checkinOptions.CheckinComment))
{
string reviewer = match.Groups["reviewer"].Value;
if (!string.IsNullOrWhiteSpace(reviewer))
{
switch (match.Groups["type"].Value)
{
case "code":
writer.WriteLine("Code reviewer: {0}", reviewer);
checkinOptions.CheckinNotes.Add("Code Reviewer", reviewer);
break;
case "security":
writer.WriteLine("Security reviewer: {0}", reviewer);
checkinOptions.CheckinNotes.Add("Security Reviewer", reviewer);
break;
case "performance":
writer.WriteLine("Performance reviewer: {0}", reviewer);
checkinOptions.CheckinNotes.Add("Performance Reviewer", reviewer);
break;
}
}
}
checkinOptions.CheckinComment = GitTfsConstants.TfsReviewerRegex.Replace(checkinOptions.CheckinComment, "").Trim(' ', '\r', '\n');
}
public static void ProcessForceCommand(this CheckinOptions checkinOptions, TextWriter writer)
{
MatchCollection workitemMatches;
if ((workitemMatches = GitTfsConstants.TfsForceRegex.Matches(checkinOptions.CheckinComment)).Count == 1)
{
string overrideReason = workitemMatches[0].Groups["reason"].Value;
if (!string.IsNullOrWhiteSpace(overrideReason))
{
writer.WriteLine("Forcing the checkin: {0}", overrideReason);
checkinOptions.Force = true;
checkinOptions.OverrideReason = overrideReason;
}
checkinOptions.CheckinComment = GitTfsConstants.TfsForceRegex.Replace(checkinOptions.CheckinComment, "").Trim(' ', '\r', '\n');
}
}
public static void ProcessAuthor(this CheckinOptions checkinOptions, TextWriter writer, GitCommit commit, AuthorsFile authors)
{
if (!authors.IsParseSuccessfull)
return;
Author a = authors.FindAuthor(commit.AuthorAndEmail);
if (a == null)
{
checkinOptions.AuthorTfsUserId = null;
return;
}
checkinOptions.AuthorTfsUserId = a.TfsUserId;
writer.WriteLine("Commit was authored by git user {0} {1} ({2})", a.Name, a.Email, a.TfsUserId);
}
}
}
@@ -1,9 +1,6 @@
using System;
using System.IO;
using Sep.Git.Tfs.Commands;
using Sep.Git.Tfs.Core;
using System.Text.RegularExpressions;
using StructureMap;
namespace Sep.Git.Tfs.Util
{
@@ -19,181 +16,38 @@ namespace Sep.Git.Tfs.Util
public class CommitSpecificCheckinOptionsFactory
{
private readonly TextWriter writer;
private readonly AuthorsFile authors;
private readonly Globals globals;
private AuthorsFile authors;
public CommitSpecificCheckinOptionsFactory(TextWriter writer, Globals globals, AuthorsFile authors)
{
this.writer = writer;
this.globals = globals;
this.authors = authors;
this.globals = globals;
}
public CheckinOptions BuildCommitSpecificCheckinOptions(CheckinOptions sourceCheckinOptions, string commitMessage)
{
var customCheckinOptions = Clone(sourceCheckinOptions);
var customCheckinOptions = sourceCheckinOptions.Clone(this.globals);
customCheckinOptions.CheckinComment = commitMessage;
ProcessWorkItemCommands(customCheckinOptions, writer);
customCheckinOptions.ProcessWorkItemCommands(writer);
ProcessCheckinNoteCommands(customCheckinOptions, writer);
customCheckinOptions.ProcessCheckinNoteCommands(writer);
ProcessForceCommand(customCheckinOptions, writer);
customCheckinOptions.ProcessForceCommand(writer);
return customCheckinOptions;
}
public CheckinOptions BuildCommitSpecificCheckinOptions(CheckinOptions sourceCheckinOptions, string commitMessage, GitCommit commit)
{
var customCheckinOptions = Clone(sourceCheckinOptions);
var customCheckinOptions = BuildCommitSpecificCheckinOptions(sourceCheckinOptions, commitMessage);
customCheckinOptions.CheckinComment = commitMessage;
ProcessWorkItemCommands(customCheckinOptions, writer);
ProcessCheckinNoteCommands(customCheckinOptions, writer);
ProcessForceCommand(customCheckinOptions, writer);
ProcessAuthor(customCheckinOptions, writer, commit);
customCheckinOptions.ProcessAuthor(writer, commit, authors);
return customCheckinOptions;
}
private CheckinOptions Clone(CheckinOptions source)
{
CheckinOptions clone = new CheckinOptions();
clone.CheckinComment = source.CheckinComment;
clone.NoGenerateCheckinComment = source.NoGenerateCheckinComment;
clone.NoMerge = source.NoMerge;
clone.OverrideReason = source.OverrideReason;
clone.Force = source.Force;
clone.OverrideGatedCheckIn = source.OverrideGatedCheckIn;
clone.WorkItemsToAssociate.AddRange(source.WorkItemsToAssociate);
clone.WorkItemsToResolve.AddRange(source.WorkItemsToResolve);
clone.AuthorTfsUserId = source.AuthorTfsUserId;
try
{
string re = globals.Repository.GetConfig(GitTfsConstants.WorkItemAssociateRegexConfigKey);
if (String.IsNullOrEmpty(re))
clone.WorkItemAssociateRegex = GitTfsConstants.TfsWorkItemAssociateRegex;
else
clone.WorkItemAssociateRegex = new Regex(re);
}
catch (Exception)
{
clone.WorkItemAssociateRegex = null;
}
foreach (var note in source.CheckinNotes)
{
clone.CheckinNotes[note.Key] = note.Value;
}
return clone;
}
private void ProcessWorkItemCommands(CheckinOptions checkinOptions, TextWriter writer)
{
MatchCollection workitemMatches;
if ((workitemMatches = GitTfsConstants.TfsWorkItemRegex.Matches(checkinOptions.CheckinComment)).Count > 0)
{
foreach (Match match in workitemMatches)
{
if (match.Groups["action"].Value == "resolve")
{
writer.WriteLine("Resolving work item {0}", match.Groups["item_id"]);
checkinOptions.WorkItemsToResolve.Add(match.Groups["item_id"].Value);
}else{
writer.WriteLine("Associating with work item {0}", match.Groups["item_id"]);
checkinOptions.WorkItemsToAssociate.Add(match.Groups["item_id"].Value);
}
}
checkinOptions.CheckinComment = GitTfsConstants.TfsWorkItemRegex.Replace(checkinOptions.CheckinComment, "").Trim(' ', '\r', '\n');
}
if (checkinOptions.WorkItemAssociateRegex != null)
{
var workitemAssociatedMatches = checkinOptions.WorkItemAssociateRegex.Matches(checkinOptions.CheckinComment);
if (workitemAssociatedMatches.Count != 0)
{
foreach (Match match in workitemAssociatedMatches)
{
var workitem = match.Groups["item_id"].Value;
if (!checkinOptions.WorkItemsToAssociate.Contains(workitem))
{
writer.WriteLine("Associating with work item {0}", workitem);
checkinOptions.WorkItemsToAssociate.Add(workitem);
}
}
}
}
}
private void ProcessCheckinNoteCommands(CheckinOptions checkinOptions, TextWriter writer)
{
foreach (Match match in GitTfsConstants.TfsReviewerRegex.Matches(checkinOptions.CheckinComment))
{
string reviewer = match.Groups["reviewer"].Value;
if (!string.IsNullOrWhiteSpace(reviewer))
{
switch (match.Groups["type"].Value)
{
case "code":
writer.WriteLine("Code reviewer: {0}", reviewer);
checkinOptions.CheckinNotes.Add("Code Reviewer", reviewer);
break;
case "security":
writer.WriteLine("Security reviewer: {0}", reviewer);
checkinOptions.CheckinNotes.Add("Security Reviewer", reviewer);
break;
case "performance":
writer.WriteLine("Performance reviewer: {0}", reviewer);
checkinOptions.CheckinNotes.Add("Performance Reviewer", reviewer);
break;
}
}
}
checkinOptions.CheckinComment = GitTfsConstants.TfsReviewerRegex.Replace(checkinOptions.CheckinComment, "").Trim(' ', '\r', '\n');
}
private void ProcessForceCommand(CheckinOptions checkinOptions, TextWriter writer)
{
MatchCollection workitemMatches;
if ((workitemMatches = GitTfsConstants.TfsForceRegex.Matches(checkinOptions.CheckinComment)).Count == 1)
{
string overrideReason = workitemMatches[0].Groups["reason"].Value;
if (!string.IsNullOrWhiteSpace(overrideReason))
{
writer.WriteLine("Forcing the checkin: {0}", overrideReason);
checkinOptions.Force = true;
checkinOptions.OverrideReason = overrideReason;
}
checkinOptions.CheckinComment = GitTfsConstants.TfsForceRegex.Replace(checkinOptions.CheckinComment, "").Trim(' ', '\r', '\n');
}
}
private void ProcessAuthor(CheckinOptions checkinOptions, TextWriter writer, GitCommit commit)
{
if (!authors.IsParseSuccessfull)
return;
Author a = authors.FindAuthor(commit.AuthorAndEmail);
if (a == null)
{
checkinOptions.AuthorTfsUserId = null;
return;
}
checkinOptions.AuthorTfsUserId = a.TfsUserId;
writer.WriteLine("Commit was authored by git user {0} {1} ({2})", a.Name, a.Email, a.TfsUserId);
}
}
}
@@ -0,0 +1,29 @@
using System.IO;
using Sep.Git.Tfs.Commands;
namespace Sep.Git.Tfs.Util
{
public class ShelveSpecificCheckinOptionsFactory
{
private readonly TextWriter writer;
private readonly Globals globals;
public ShelveSpecificCheckinOptionsFactory(TextWriter writer, Globals globals)
{
this.writer = writer;
this.globals = globals;
}
public CheckinOptions BuildShelveSetSpecificCheckinOptions(CheckinOptions sourceCheckinOptions,
string commitMessage)
{
var customCheckinOptions = sourceCheckinOptions.Clone(this.globals);
customCheckinOptions.CheckinComment = commitMessage;
customCheckinOptions.ProcessWorkItemCommands(writer, false);
return customCheckinOptions;
}
}
}
+6 -6
View File
@@ -118,8 +118,8 @@ namespace Sep.Git.Tfs.Test.Commands
mocks.ClassUnderTest.Run("shelveset name");
remote.AssertWasCalled(x => x.Shelve(null, null, null, false),
y => y.Constraints(Is.Equal("shelveset name"), Is.Equal("HEAD"), Is.Anything(), Is.Anything()));
remote.AssertWasCalled(x => x.Shelve(null, null, null, null, false),
y => y.Constraints(Is.Equal("shelveset name"), Is.Equal("HEAD"), Is.Anything(), Is.Anything(), Is.Anything()));
}
[Fact]
@@ -135,8 +135,8 @@ namespace Sep.Git.Tfs.Test.Commands
mocks.ClassUnderTest.Run("shelveset name", "treeish");
remote.AssertWasCalled(x => x.Shelve(null, null, null, false),
y => y.Constraints(Is.Equal("shelveset name"), Is.Equal("treeish"), Is.Anything(), Is.Anything()));
remote.AssertWasCalled(x => x.Shelve(null, null, null, null, false),
y => y.Constraints(Is.Equal("shelveset name"), Is.Equal("treeish"), Is.Anything(), Is.Anything(), Is.Anything()));
}
[Fact]
@@ -158,7 +158,7 @@ namespace Sep.Git.Tfs.Test.Commands
mocks.ClassUnderTest.Run("shelveset name", "treeish");
mocks.Get<IGitTfsRemote>().AssertWasNotCalled(
x => x.Shelve(Arg<string>.Is.Anything, Arg<string>.Is.Anything, Arg<TfsChangesetInfo>.Is.Anything, Arg<bool>.Is.Anything));
x => x.Shelve(Arg<string>.Is.Anything, Arg<string>.Is.Anything, Arg<TfsChangesetInfo>.Is.Anything, Arg<CheckinOptions>.Is.Anything, Arg<bool>.Is.Anything));
}
[Fact]
@@ -171,7 +171,7 @@ namespace Sep.Git.Tfs.Test.Commands
mocks.ClassUnderTest.Run("shelveset name", "treeish");
mocks.Get<IGitTfsRemote>().AssertWasCalled(
x => x.Shelve(Arg<string>.Is.Anything, Arg<string>.Is.Anything, Arg<TfsChangesetInfo>.Is.Anything, Arg<bool>.Is.Anything));
x => x.Shelve(Arg<string>.Is.Anything, Arg<string>.Is.Anything, Arg<TfsChangesetInfo>.Is.Anything, Arg<CheckinOptions>.Is.Anything, Arg<bool>.Is.Anything));
}
private TfsChangesetInfo ChangesetForRemote(string remoteId)
+1
View File
@@ -136,6 +136,7 @@
<Compile Include="Util\CommitSpecificCheckinOptionsFactoryTests.cs" />
<Compile Include="Util\GitTfsCommandRunnerTests.cs" />
<Compile Include="Util\BouncerTest.cs" />
<Compile Include="Util\ShelveSpecificCheckinOptionsFactoryTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GitTfs.VsFake\GitTfs.VsFake.csproj">
@@ -0,0 +1,47 @@
using System.IO;
using Sep.Git.Tfs.Commands;
using Sep.Git.Tfs.Core;
using Sep.Git.Tfs.Util;
using StructureMap.AutoMocking;
using Xunit;
namespace Sep.Git.Tfs.Test.Util
{
public class ShelveSpecificCheckinOptionsFactoryTests
{
private RhinoAutoMocker<ShelveSpecificCheckinOptionsFactory> mocks;
public ShelveSpecificCheckinOptionsFactoryTests()
{
mocks = new RhinoAutoMocker<ShelveSpecificCheckinOptionsFactory>();
mocks.Get<Globals>().Repository = mocks.Get<IGitRepository>();
}
[Fact]
public void Adds_work_item_to_associate_and_removes_checkin_command_comment()
{
string commitMessage = @"test message
formatted git commit message
git-tfs-work-item: 1234 associate";
string expectedCheckinComment = @"test message
formatted git commit message
";
var specificCheckinOptions = GetShelveSpecificCheckinOptions().BuildShelveSetSpecificCheckinOptions(new CheckinOptions(), commitMessage);
Assert.Equal(1, specificCheckinOptions.WorkItemsToAssociate.Count);
Assert.Contains("1234", specificCheckinOptions.WorkItemsToAssociate);
Assert.Equal(expectedCheckinComment, specificCheckinOptions.CheckinComment);
}
private ShelveSpecificCheckinOptionsFactory GetShelveSpecificCheckinOptions()
{
return new ShelveSpecificCheckinOptionsFactory(new StringWriter(), mocks.Get<Globals>());
}
}
}
+4
View File
@@ -1,6 +1,10 @@
## Summary
Creates a TFS shelveset from a Git branch.
## Features
[Special actions in commit messages](../special-actions-in-commit-messages.md) can be inserted, to associate TFS work items.
## Synopsis
Usage: git-tfs shelve [options] shelveset-name [ref-to-shelve]
where options are:
+5 -1
View File
@@ -1,4 +1,4 @@
The [rcheckin](commands/rcheckin.md) command examines the commmit message for additional TFS specific
The [rcheckin](commands/rcheckin.md) command examines the commit message for additional TFS specific
notifications. The following may be appended as separate lines to the end of the commit message:
* `git-tfs-work-item: <id>` will link the new changeset with the given work item and the default action type.
@@ -8,6 +8,10 @@ notifications. The following may be appended as separate lines to the end of the
* `git-tfs-performance-reviewer: <name>` sets the Performance Reviewer field.
* `git-tfs-force: <reason>` will force the checkin, overriding TFS checkin policies with the given reason.
The [shelve](commands/shelve.md) command also examines the commit message for the following TFS specific notifications, in the same matter as the rcheckin command:
* `git-tfs-work-item: <id>` will link the new changeset with the given work item and the default action type.
Additionally the text of the message is searched for work item IDs. If a string matching a # followed
by a valid work-item ID number is found, then the commit will be associated with the specified TFS work-item.
For cases where this may be undesirable, the default match may be overridden by setting the