Merge remote-tracking branch 'origin/master' into delete-orphans
This commit is contained in:
+20
-1
@@ -1,8 +1,27 @@
|
||||
language: c
|
||||
|
||||
notifications:
|
||||
irc:
|
||||
channels:
|
||||
- "irc.freenode.org#git-tfs"
|
||||
template:
|
||||
- "travis build %{build_number} (%{branch} - %{commit} - %{author}): %{message} #{build_url}"
|
||||
on_success: change
|
||||
on_failure: always
|
||||
|
||||
install:
|
||||
- sudo apt-get install mono-devel mono-gmcs
|
||||
- sudo apt-get install cmake mono-devel mono-gmcs
|
||||
|
||||
script:
|
||||
- git submodule update --init
|
||||
- cd lib/libgit2sharp
|
||||
- git submodule update --init
|
||||
- mkdir cmake-build
|
||||
- cd cmake-build
|
||||
- cmake -DTHREADSAFE=ON -DCMAKE_BUILD_TYPE=Release -DBUILD_CLAR=OFF -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=./libgit2-bin ../libgit2
|
||||
- export LD_LIBRARY_PATH=$PWD/libgit2-bin/lib
|
||||
- cmake --build . --target install
|
||||
- cp -Rpv libgit2-bin/lib ../Lib/NativeBinaries/
|
||||
- cd ..
|
||||
- cd ../..
|
||||
- xbuild CI.proj
|
||||
|
||||
@@ -66,6 +66,9 @@
|
||||
<Compile Include="..\CommonAssemblyInfo.cs">
|
||||
<Link>Properties\CommonAssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GitTfs.VsCommon\Wrappers.PostVs2010.cs">
|
||||
<Link>Wrappers.PostVs2010.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Version.cs">
|
||||
<Link>Properties\Version.cs</Link>
|
||||
</Compile>
|
||||
|
||||
@@ -104,6 +104,9 @@
|
||||
<Compile Include="..\GitTfs.VsCommon\Wrappers.cs">
|
||||
<Link>Wrappers.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GitTfs.VsCommon\Wrappers.PostVs2010.cs">
|
||||
<Link>Wrappers.PostVs2010.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Version.cs">
|
||||
<Link>Properties\Version.cs</Link>
|
||||
</Compile>
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Microsoft.TeamFoundation;
|
||||
using Microsoft.TeamFoundation.Server;
|
||||
using Microsoft.TeamFoundation.VersionControl.Client;
|
||||
using Microsoft.TeamFoundation.WorkItemTracking.Client;
|
||||
@@ -47,13 +48,6 @@ namespace Sep.Git.Tfs.VsCommon
|
||||
|
||||
private string[] _legacyUrls;
|
||||
|
||||
|
||||
public string[] LegacyUrls
|
||||
{
|
||||
get { return _legacyUrls ?? (_legacyUrls = new string[0]); }
|
||||
set { _legacyUrls = value; }
|
||||
}
|
||||
|
||||
protected NetworkCredential GetCredential()
|
||||
{
|
||||
var idx = Username.IndexOf('\\');
|
||||
@@ -108,11 +102,18 @@ namespace Sep.Git.Tfs.VsCommon
|
||||
get { return GetService<IGroupSecurityService>(); }
|
||||
}
|
||||
|
||||
private ILinking _linking;
|
||||
private ILinking Linking
|
||||
{
|
||||
get { return _linking ?? (_linking = GetService<ILinking>()); }
|
||||
}
|
||||
|
||||
public IEnumerable<ITfsChangeset> GetChangesets(string path, long startVersion, GitTfsRemote remote)
|
||||
{
|
||||
var changesets = VersionControl.QueryHistory(path, VersionSpec.Latest, 0, RecursionType.Full,
|
||||
null, new ChangesetVersionSpec((int) startVersion), VersionSpec.Latest, int.MaxValue, true,
|
||||
true, true);
|
||||
|
||||
return changesets.Cast<Changeset>()
|
||||
.OrderBy(changeset => changeset.ChangesetId)
|
||||
.Select(changeset => BuildTfsChangeset(changeset, remote));
|
||||
@@ -120,7 +121,12 @@ namespace Sep.Git.Tfs.VsCommon
|
||||
|
||||
public virtual bool CanGetBranchInformation { get { return false; } }
|
||||
|
||||
public virtual IEnumerable<string> GetAllTfsBranchesOrderedByCreation()
|
||||
public virtual IEnumerable<string> GetAllTfsRootBranchesOrderedByCreation()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual IEnumerable<IBranchObject> GetBranches()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
@@ -133,12 +139,25 @@ namespace Sep.Git.Tfs.VsCommon
|
||||
private ITfsChangeset BuildTfsChangeset(Changeset changeset, GitTfsRemote remote)
|
||||
{
|
||||
var tfsChangeset = _container.With<ITfsHelper>(this).With<IChangeset>(_bridge.Wrap<WrapperForChangeset, Changeset>(changeset)).GetInstance<TfsChangeset>();
|
||||
tfsChangeset.Summary = new TfsChangesetInfo {ChangesetId = changeset.ChangesetId, Remote = remote};
|
||||
tfsChangeset.Summary = new TfsChangesetInfo { ChangesetId = changeset.ChangesetId, Remote = remote };
|
||||
|
||||
if (changeset.WorkItems != null)
|
||||
{
|
||||
tfsChangeset.Summary.Workitems = changeset.WorkItems.Select(wi => new TfsWorkitem
|
||||
{
|
||||
Id = wi.Id,
|
||||
Title = wi.Title,
|
||||
Description = wi.Description,
|
||||
Url = Linking.GetArtifactUrl(wi.Uri.AbsoluteUri)
|
||||
});
|
||||
}
|
||||
|
||||
return tfsChangeset;
|
||||
}
|
||||
|
||||
public void WithWorkspace(string localDirectory, IGitTfsRemote remote, TfsChangesetInfo versionToFetch, Action<ITfsWorkspace> action)
|
||||
{
|
||||
Trace.WriteLine("Setting up a TFS workspace at " + localDirectory);
|
||||
var workspace = GetWorkspace(localDirectory, remote.TfsRepositoryPath);
|
||||
try
|
||||
{
|
||||
@@ -258,7 +277,7 @@ namespace Sep.Git.Tfs.VsCommon
|
||||
shelvesets = shelvesets.OrderBy(s => s.CreationDate);
|
||||
break;
|
||||
case "owner":
|
||||
shelvesets = shelvesets.OrderBy(s => s.OwnerName);
|
||||
shelvesets = shelvesets.OrderBy(s => s.OwnerName).ThenBy(s => s.CreationDate);
|
||||
break;
|
||||
case "name":
|
||||
shelvesets = shelvesets.OrderBy(s => s.Name);
|
||||
@@ -271,6 +290,9 @@ namespace Sep.Git.Tfs.VsCommon
|
||||
return GitTfsExitCodes.InvalidArguments;
|
||||
}
|
||||
}
|
||||
else
|
||||
shelvesets = shelvesets.OrderBy(s => s.CreationDate);
|
||||
|
||||
if (shelveList.FullFormat)
|
||||
WriteShelvesetsToStdoutDetailed(shelvesets);
|
||||
else
|
||||
@@ -282,7 +304,7 @@ namespace Sep.Git.Tfs.VsCommon
|
||||
{
|
||||
foreach (var shelveset in shelvesets)
|
||||
{
|
||||
_stdout.WriteLine(" {0,-20} {1,-20}", shelveset.OwnerName, shelveset.Name);
|
||||
_stdout.WriteLine("{0,-22} {1,-20}", shelveset.OwnerName, shelveset.Name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,11 +521,6 @@ namespace Sep.Git.Tfs.VsCommon
|
||||
return BuildTfsChangeset(VersionControl.GetChangeset(changesetId), remote);
|
||||
}
|
||||
|
||||
public bool MatchesUrl(string tfsUrl)
|
||||
{
|
||||
return Url == tfsUrl || LegacyUrls.Contains(tfsUrl);
|
||||
}
|
||||
|
||||
public IEnumerable<IWorkItemCheckinInfo> GetWorkItemInfos(IEnumerable<string> workItems, TfsWorkItemCheckinAction checkinAction)
|
||||
{
|
||||
return
|
||||
@@ -558,9 +575,10 @@ namespace Sep.Git.Tfs.VsCommon
|
||||
return new WorkItemCheckedInfo(Convert.ToInt32(workitem), true, checkinAction);
|
||||
}
|
||||
|
||||
public IEnumerable<TfsLabel> GetLabels(string tfsPathBranch)
|
||||
public IEnumerable<TfsLabel> GetLabels(string tfsPathBranch, string nameFilter = null)
|
||||
{
|
||||
var labels = VersionControl.QueryLabels(null, tfsPathBranch, null, true, tfsPathBranch, VersionSpec.Latest);
|
||||
var labels = VersionControl.QueryLabels(nameFilter, tfsPathBranch, null, true, tfsPathBranch, VersionSpec.Latest);
|
||||
|
||||
return labels.Select(e => new TfsLabel {
|
||||
Id = e.LabelId,
|
||||
Name = e.Name,
|
||||
@@ -573,4 +591,4 @@ namespace Sep.Git.Tfs.VsCommon
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,23 +4,36 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.TeamFoundation.VersionControl.Client;
|
||||
using Sep.Git.Tfs.Core;
|
||||
using Sep.Git.Tfs.Core.BranchVisitors;
|
||||
using Sep.Git.Tfs.Core.TfsInterop;
|
||||
using StructureMap;
|
||||
|
||||
namespace Sep.Git.Tfs.VsCommon
|
||||
{
|
||||
|
||||
public abstract class TfsHelperVs2010Base : TfsHelperBase
|
||||
{
|
||||
TfsApiBridge _bridge;
|
||||
|
||||
public TfsHelperVs2010Base(TextWriter stdout, TfsApiBridge bridge, IContainer container)
|
||||
: base(stdout, bridge, container)
|
||||
{
|
||||
_bridge = bridge;
|
||||
}
|
||||
|
||||
public override bool CanGetBranchInformation { get { return true; } }
|
||||
|
||||
public override IEnumerable<string> GetAllTfsBranchesOrderedByCreation()
|
||||
public override IEnumerable<string> GetAllTfsRootBranchesOrderedByCreation()
|
||||
{
|
||||
return VersionControl.QueryRootBranchObjects(RecursionType.Full).Select(b => b.Properties.RootItem.Item);
|
||||
return VersionControl.QueryRootBranchObjects(RecursionType.Full)
|
||||
.Where(b => b.Properties.ParentBranch == null)
|
||||
.Select(b => b.Properties.RootItem.Item);
|
||||
}
|
||||
|
||||
public override IEnumerable<IBranchObject> GetBranches()
|
||||
{
|
||||
var branches = VersionControl.QueryRootBranchObjects(RecursionType.Full)
|
||||
.Where(b => b.Properties.RootItem.IsDeleted == false);
|
||||
return _bridge.Wrap<WrapperForBranchObject, BranchObject>(branches);
|
||||
}
|
||||
|
||||
public override int GetRootChangesetForBranch(string tfsPathBranchToCreate, string tfsPathParentBranch = null)
|
||||
@@ -46,10 +59,12 @@ namespace Sep.Git.Tfs.VsCommon
|
||||
throw new GitTfsException("An unexpected error occured when trying to find the root changeset.\nFailed to find first changeset for " + tfsPathBranchToCreate);
|
||||
}
|
||||
|
||||
var mergedItemsToFirstChangesetInBranchToCreate =
|
||||
VersionControl.TrackMerges(new int[] {firstChangesetInBranchToCreate.ChangesetId},
|
||||
new ItemIdentifier(tfsPathBranchToCreate),
|
||||
new ItemIdentifier[] {new ItemIdentifier(tfsPathParentBranch),}, null);
|
||||
var mergedItemsToFirstChangesetInBranchToCreate = VersionControl
|
||||
.TrackMerges(new int[] {firstChangesetInBranchToCreate.ChangesetId},
|
||||
new ItemIdentifier(tfsPathBranchToCreate),
|
||||
new ItemIdentifier[] {new ItemIdentifier(tfsPathParentBranch),},
|
||||
null)
|
||||
.OrderBy(x => x.SourceChangeset.ChangesetId);
|
||||
|
||||
var lastChangesetsMergeFromParentBranch = mergedItemsToFirstChangesetInBranchToCreate.LastOrDefault();
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.TeamFoundation.VersionControl.Client;
|
||||
using Sep.Git.Tfs.Core.TfsInterop;
|
||||
|
||||
namespace Sep.Git.Tfs.VsCommon
|
||||
{
|
||||
public class WrapperForBranchObject : WrapperFor<BranchObject>, IBranchObject
|
||||
{
|
||||
BranchObject _branch;
|
||||
|
||||
public WrapperForBranchObject(BranchObject branch) : base(branch)
|
||||
{
|
||||
_branch = branch;
|
||||
}
|
||||
|
||||
public string Path
|
||||
{
|
||||
get { return _branch.Properties.RootItem.Item; }
|
||||
}
|
||||
|
||||
public bool IsRoot
|
||||
{
|
||||
get { return _branch.Properties.ParentBranch == null; }
|
||||
}
|
||||
|
||||
public string ParentPath
|
||||
{
|
||||
get { return _branch.Properties.ParentBranch.Item; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.TeamFoundation.Server;
|
||||
using Microsoft.TeamFoundation.VersionControl.Client;
|
||||
using Sep.Git.Tfs.Core;
|
||||
using Sep.Git.Tfs.Core.TfsInterop;
|
||||
using Sep.Git.Tfs.Util;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Sep.Git.Tfs.Commands;
|
||||
@@ -15,12 +16,14 @@ namespace Sep.Git.Tfs.VsFake
|
||||
#region misc/null
|
||||
|
||||
IContainer _container;
|
||||
private TextWriter _stdout;
|
||||
TextWriter _stdout;
|
||||
Script _script;
|
||||
|
||||
public TfsHelper(IContainer container, TextWriter stdout)
|
||||
public TfsHelper(IContainer container, TextWriter stdout, Script script)
|
||||
{
|
||||
_container = container;
|
||||
_stdout = stdout;
|
||||
_script = script;
|
||||
}
|
||||
|
||||
public string TfsClientLibraryVersion { get { return "(FAKE)"; } }
|
||||
@@ -28,7 +31,6 @@ namespace Sep.Git.Tfs.VsFake
|
||||
public string Url { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string[] LegacyUrls { get; set; }
|
||||
|
||||
public void EnsureAuthenticated() {}
|
||||
|
||||
@@ -50,12 +52,12 @@ namespace Sep.Git.Tfs.VsFake
|
||||
|
||||
public ITfsChangeset GetLatestChangeset(GitTfsRemote remote)
|
||||
{
|
||||
return TfsPlugin.Script.Changesets.LastOrDefault().AndAnd(x => BuildTfsChangeset(x, remote));
|
||||
return _script.Changesets.LastOrDefault().AndAnd(x => BuildTfsChangeset(x, remote));
|
||||
}
|
||||
|
||||
public IEnumerable<ITfsChangeset> GetChangesets(string path, long startVersion, GitTfsRemote remote)
|
||||
{
|
||||
return TfsPlugin.Script.Changesets.Where(x => x.Id > startVersion).Select(x => BuildTfsChangeset(x, remote));
|
||||
return _script.Changesets.Where(x => x.Id >= startVersion).Select(x => BuildTfsChangeset(x, remote));
|
||||
}
|
||||
|
||||
private ITfsChangeset BuildTfsChangeset(ScriptedChangeset changeset, GitTfsRemote remote)
|
||||
@@ -185,8 +187,14 @@ namespace Sep.Git.Tfs.VsFake
|
||||
|
||||
public void WithWorkspace(string directory, IGitTfsRemote remote, TfsChangesetInfo versionToFetch, Action<ITfsWorkspace> action)
|
||||
{
|
||||
Trace.WriteLine("Setting up a TFS workspace at " + directory);
|
||||
var fakeWorkspace = new FakeWorkspace(directory, remote.TfsRepositoryPath);
|
||||
var workspace = new TfsWorkspace(fakeWorkspace, directory, _stdout, versionToFetch, remote, null, this, null);
|
||||
var workspace = _container.With("localDirectory").EqualTo(directory)
|
||||
.With("remote").EqualTo(remote)
|
||||
.With("contextVersion").EqualTo(versionToFetch)
|
||||
.With("workspace").EqualTo(fakeWorkspace)
|
||||
.With("tfsHelper").EqualTo(this)
|
||||
.GetInstance<TfsWorkspace>();
|
||||
action(workspace);
|
||||
}
|
||||
|
||||
@@ -317,11 +325,6 @@ namespace Sep.Git.Tfs.VsFake
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool MatchesUrl(string tfsUrl)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool HasShelveset(string shelvesetName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
@@ -349,12 +352,17 @@ namespace Sep.Git.Tfs.VsFake
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetAllTfsBranchesOrderedByCreation()
|
||||
public IEnumerable<string> GetAllTfsRootBranchesOrderedByCreation()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<TfsLabel> GetLabels(string tfsPathBranch)
|
||||
public IEnumerable<IBranchObject> GetBranches()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<TfsLabel> GetLabels(string tfsPathBranch, string nameFilter = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -10,11 +10,12 @@ namespace Sep.Git.Tfs.VsFake
|
||||
{
|
||||
base.Initialize(scan);
|
||||
}
|
||||
*/
|
||||
|
||||
public override void Initialize(StructureMap.ConfigurationExpression config)
|
||||
{
|
||||
config.For<Script>().Use(() => Script.Load(ScriptPath));
|
||||
}
|
||||
*/
|
||||
|
||||
public override bool IsViable()
|
||||
{
|
||||
@@ -28,18 +29,5 @@ namespace Sep.Git.Tfs.VsFake
|
||||
return Environment.GetEnvironmentVariable(Script.EnvVar);
|
||||
}
|
||||
}
|
||||
|
||||
static Script _script;
|
||||
internal static Script Script
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_script == null)
|
||||
{
|
||||
_script = ScriptPath.AndAnd(Script.Load);
|
||||
}
|
||||
return _script;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{F52EAF
|
||||
.nuget\nuget.targets = .nuget\nuget.targets
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{4A46FEEE-B8A2-4445-B9D1-8160520C7301}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
docs\config.md = docs\config.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(TestCaseManagementSettings) = postSolution
|
||||
CategoryFile = GitTfs1.vsmdi
|
||||
|
||||
@@ -44,8 +44,15 @@ namespace Sep.Git.Tfs.Commands
|
||||
if (parent.Remote.IsDerived)
|
||||
{
|
||||
var remoteId = GetRemoteId(parent);
|
||||
_globals.Repository.CreateTfsRemote(remoteId, parent, _remoteOptions);
|
||||
_stdout.WriteLine("-> new remote " + remoteId);
|
||||
var remote = _globals.Repository.CreateTfsRemote(new RemoteInfo
|
||||
{
|
||||
Id = remoteId,
|
||||
Url = parent.Remote.TfsUrl,
|
||||
Repository = parent.Remote.TfsRepositoryPath,
|
||||
RemoteOptions = _remoteOptions,
|
||||
});
|
||||
remote.UpdateRef(parent.GitCommit, parent.ChangesetId);
|
||||
_stdout.WriteLine("-> new remote " + remote.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using NDesk.Options;
|
||||
using Sep.Git.Tfs.Core;
|
||||
using Sep.Git.Tfs.Core.TfsInterop;
|
||||
using StructureMap;
|
||||
|
||||
namespace Sep.Git.Tfs.Commands
|
||||
{
|
||||
[Pluggable("branch")]
|
||||
[Description("branch")]
|
||||
[RequiresValidGitRepository]
|
||||
public class Branch : GitTfsCommand
|
||||
{
|
||||
private Globals globals;
|
||||
private TextWriter stdout;
|
||||
public bool DisplayRemotes { get; set; }
|
||||
|
||||
public OptionSet OptionSet
|
||||
{
|
||||
get {
|
||||
return new OptionSet
|
||||
{
|
||||
{ "r|remotes", "Display all the TFS branch of the current TFS server", v => DisplayRemotes = (v != null) }
|
||||
}
|
||||
.Merge(globals.OptionSet);
|
||||
}
|
||||
}
|
||||
|
||||
public Branch(Globals globals, TextWriter stdout)
|
||||
{
|
||||
this.globals = globals;
|
||||
this.stdout = stdout;
|
||||
}
|
||||
|
||||
public int Run()
|
||||
{
|
||||
// should probably pull this from options so that it is settable from the command-line
|
||||
const string remoteId = GitTfsConstants.DefaultRepositoryId;
|
||||
|
||||
var tfsRemotes = globals.Repository.ReadAllTfsRemotes();
|
||||
if (DisplayRemotes)
|
||||
{
|
||||
WriteRemoteTfsBranchStructure(stdout, remoteId, tfsRemotes);
|
||||
return GitTfsExitCodes.OK;
|
||||
}
|
||||
|
||||
WriteTfsRemoteDetails(stdout, tfsRemotes);
|
||||
return GitTfsExitCodes.OK;
|
||||
}
|
||||
|
||||
private void WriteRemoteTfsBranchStructure(TextWriter writer, string remoteId, IEnumerable<IGitTfsRemote> tfsRemotes)
|
||||
{
|
||||
writer.WriteLine("\nTFS branch structure:");
|
||||
|
||||
var repo = globals.Repository;
|
||||
var remote = repo.ReadTfsRemote(remoteId);
|
||||
var root = remote.Tfs.GetRootTfsBranchForRemotePath(remote.TfsRepositoryPath);
|
||||
|
||||
var visitor = new WriteBranchStructureTreeVisitor(remote.TfsRepositoryPath, writer, tfsRemotes);
|
||||
root.AcceptVisitor(visitor);
|
||||
}
|
||||
|
||||
private void WriteTfsRemoteDetails(TextWriter writer, IEnumerable<IGitTfsRemote> tfsRemotes)
|
||||
{
|
||||
writer.WriteLine("\nGit-tfs remote details:");
|
||||
foreach (var remote in tfsRemotes)
|
||||
{
|
||||
writer.WriteLine("\n {0} -> {1} {2}", remote.Id, remote.TfsUrl, remote.TfsRepositoryPath);
|
||||
writer.WriteLine(" {0} - {1} @ {2}", remote.RemoteRef, remote.MaxCommitHash, remote.MaxChangesetId);
|
||||
}
|
||||
}
|
||||
|
||||
private class WriteBranchStructureTreeVisitor : IBranchTreeVisitor
|
||||
{
|
||||
private readonly TextWriter _stdout;
|
||||
private readonly string _targetPath;
|
||||
private readonly IEnumerable<IGitTfsRemote> _tfsRemotes;
|
||||
|
||||
public WriteBranchStructureTreeVisitor(string targetPath, TextWriter writer, IEnumerable<IGitTfsRemote> tfsRemotes = null)
|
||||
{
|
||||
_targetPath = targetPath;
|
||||
_stdout = writer;
|
||||
_tfsRemotes = tfsRemotes;
|
||||
}
|
||||
|
||||
public void Visit(BranchTree branch, int level)
|
||||
{
|
||||
for (var i = 0; i < level; i++ )
|
||||
_stdout.Write(" | ");
|
||||
|
||||
_stdout.WriteLine();
|
||||
|
||||
for (var i = 0; i < level - 1; i++)
|
||||
_stdout.Write(" | ");
|
||||
|
||||
if (level > 0)
|
||||
_stdout.Write(" +-");
|
||||
|
||||
_stdout.Write(" {0}", branch.Path);
|
||||
|
||||
if (_tfsRemotes != null)
|
||||
{
|
||||
var remote = _tfsRemotes.FirstOrDefault(r => r.TfsRepositoryPath == branch.Path);
|
||||
if (remote != null)
|
||||
_stdout.Write(" -> " + remote.Id);
|
||||
}
|
||||
|
||||
if (branch.Path.Equals(_targetPath))
|
||||
_stdout.Write(" [*]");
|
||||
|
||||
_stdout.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,12 @@ namespace Sep.Git.Tfs.Commands
|
||||
public class Cleanup : GitTfsCommand
|
||||
{
|
||||
private readonly CleanupWorkspaces _cleanupWorkspaces;
|
||||
private readonly CleanupWorkspaceLocal _cleanupWorkspaceLocal;
|
||||
|
||||
public Cleanup(CleanupWorkspaces cleanupWorkspaces)
|
||||
public Cleanup(CleanupWorkspaces cleanupWorkspaces, CleanupWorkspaceLocal cleanupWorkspaceLocal)
|
||||
{
|
||||
_cleanupWorkspaces = cleanupWorkspaces;
|
||||
_cleanupWorkspaceLocal = cleanupWorkspaceLocal;
|
||||
}
|
||||
|
||||
public OptionSet OptionSet
|
||||
@@ -24,15 +26,21 @@ namespace Sep.Git.Tfs.Commands
|
||||
get { return _cleanupWorkspaces.OptionSet; }
|
||||
}
|
||||
|
||||
|
||||
public int Run()
|
||||
{
|
||||
return Choose(_cleanupWorkspaces.Run());
|
||||
return RunAll(_cleanupWorkspaces.Run, _cleanupWorkspaceLocal.Run);
|
||||
}
|
||||
|
||||
private int Choose(params int[] results)
|
||||
private int RunAll(params Func<int>[] cleaners)
|
||||
{
|
||||
return results.Where(x => x != GitTfsExitCodes.OK).FirstOr(GitTfsExitCodes.OK);
|
||||
var result = GitTfsExitCodes.OK;
|
||||
foreach (var cleaner in cleaners)
|
||||
{
|
||||
result = cleaner();
|
||||
if (result != GitTfsExitCodes.OK)
|
||||
return result;
|
||||
}
|
||||
return GitTfsExitCodes.OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using NDesk.Options;
|
||||
using Sep.Git.Tfs.Core;
|
||||
using StructureMap;
|
||||
|
||||
namespace Sep.Git.Tfs.Commands
|
||||
{
|
||||
[Pluggable("cleanup-workspace-local")]
|
||||
[Description("cleanup-workspace-local [tfs-remote-id]...")]
|
||||
[RequiresValidGitRepository]
|
||||
public class CleanupWorkspaceLocal : GitTfsCommand
|
||||
{
|
||||
private readonly TextWriter _stdout;
|
||||
private readonly Globals _globals;
|
||||
private readonly CleanupOptions _cleanupOptions;
|
||||
|
||||
public CleanupWorkspaceLocal(TextWriter stdout, Globals globals, CleanupOptions cleanupOptions)
|
||||
{
|
||||
_stdout = stdout;
|
||||
_globals = globals;
|
||||
_cleanupOptions = cleanupOptions;
|
||||
}
|
||||
|
||||
public OptionSet OptionSet
|
||||
{
|
||||
get { return _cleanupOptions.OptionSet; }
|
||||
}
|
||||
|
||||
public int Run()
|
||||
{
|
||||
_cleanupOptions.Init();
|
||||
foreach(var remote in _globals.Repository.ReadAllTfsRemotes())
|
||||
{
|
||||
Cleanup(remote);
|
||||
}
|
||||
return GitTfsExitCodes.OK;
|
||||
}
|
||||
|
||||
public int Run(IList<string> remoteIds)
|
||||
{
|
||||
_cleanupOptions.Init();
|
||||
foreach (var remoteId in remoteIds)
|
||||
{
|
||||
var remote = _globals.Repository.ReadTfsRemote(remoteId);
|
||||
Cleanup(remote);
|
||||
}
|
||||
return GitTfsExitCodes.OK;
|
||||
}
|
||||
|
||||
private void Cleanup(IGitTfsRemote remote)
|
||||
{
|
||||
_stdout.WriteLine("Cleaning up workspaces directory for TFS remote " + remote.Id);
|
||||
remote.CleanupWorkspaceDirectory();
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
-21
@@ -8,6 +8,7 @@ using NDesk.Options;
|
||||
using Sep.Git.Tfs.Core;
|
||||
using StructureMap;
|
||||
using Sep.Git.Tfs.Util;
|
||||
using Sep.Git.Tfs.Core.TfsInterop;
|
||||
|
||||
namespace Sep.Git.Tfs.Commands
|
||||
{
|
||||
@@ -50,15 +51,17 @@ namespace Sep.Git.Tfs.Commands
|
||||
var currentDir = Environment.CurrentDirectory;
|
||||
var repositoryDirCreated = InitGitDir(gitRepositoryPath);
|
||||
|
||||
// TFS string representations of repository paths do not end in trailing slashes
|
||||
tfsRepositoryPath = (tfsRepositoryPath ?? string.Empty).TrimEnd('/');
|
||||
|
||||
try
|
||||
{
|
||||
var retVal = 0;
|
||||
retVal = init.Run(tfsUrl, tfsRepositoryPath, gitRepositoryPath);
|
||||
var retVal = init.Run(tfsUrl, tfsRepositoryPath, gitRepositoryPath);
|
||||
|
||||
VerifyTfsPathToClone(tfsRepositoryPath);
|
||||
|
||||
if (retVal == 0) retVal = fetch.Run();
|
||||
if (retVal == 0) globals.Repository.CommandNoisy("merge", globals.Repository.ReadAllTfsRemotes().First().RemoteRef);
|
||||
if (retVal == 0) globals.Repository.CommandNoisy("merge", globals.Repository.ReadTfsRemote(globals.RemoteId).RemoteRef);
|
||||
if (retVal == 0 && withBranches && initBranch != null)
|
||||
{
|
||||
initBranch.CloneAllBranches = true;
|
||||
@@ -101,30 +104,29 @@ namespace Sep.Git.Tfs.Commands
|
||||
var remote = globals.Repository.ReadTfsRemote(GitTfsConstants.DefaultRepositoryId);
|
||||
if (!remote.Tfs.CanGetBranchInformation)
|
||||
return;
|
||||
var tfsBranchesPath = remote.Tfs.GetAllTfsBranchesOrderedByCreation().ToList();
|
||||
var tfsTrunkRepository = remote.Tfs.GetRootTfsBranchForRemotePath(tfsRepositoryPath, false);
|
||||
if (tfsTrunkRepository == null)
|
||||
{
|
||||
var tfsRootBranches = remote.Tfs.GetAllTfsRootBranchesOrderedByCreation();
|
||||
var cloneMsg = " => If you want to manage branches with git-tfs, clone one of this branch instead :\n"
|
||||
+ " - " + tfsRootBranches.Aggregate((s1, s2) => s1 + @"\n - " + s2);
|
||||
|
||||
if (withBranches)
|
||||
throw new GitTfsException("error: cloning the whole repository or too high in the repository path doesn't permit to manage branches!\n" + cloneMsg);
|
||||
stdout.WriteLine("warning: you are going to clone the whole repository or too high in the repository path !\n" + cloneMsg);
|
||||
}
|
||||
|
||||
var tfsBranchesPath = tfsTrunkRepository.GetAllChildren();
|
||||
var tfsPathToClone = tfsRepositoryPath.TrimEnd('/').ToLower();
|
||||
var tfsTrunkRepositoryPath = tfsBranchesPath.First();
|
||||
var tfsTrunkRepositoryPath = tfsTrunkRepository.Path;
|
||||
if (tfsPathToClone != tfsTrunkRepositoryPath.ToLower())
|
||||
{
|
||||
if (tfsBranchesPath.Select(e=>e.ToLower()).Contains(tfsPathToClone))
|
||||
if (tfsBranchesPath.Select(e=>e.Path.ToLower()).Contains(tfsPathToClone))
|
||||
stdout.WriteLine("info: you are going to clone a branch instead of the trunk ( {0} )\n"
|
||||
+ " => If you want to manage branches with git-tfs, clone {0} with '--with-branches' option instead...)", tfsTrunkRepositoryPath);
|
||||
else
|
||||
{
|
||||
if (tfsTrunkRepositoryPath.ToLower().IndexOf(tfsPathToClone) == 0)
|
||||
{
|
||||
if (withBranches)
|
||||
throw new GitTfsException("error: cloning the whole repository doesn't permit to manage branches!\n"
|
||||
+ " =>If you want to manage branches with git-tfs, clone " + tfsTrunkRepositoryPath + " instead...");
|
||||
stdout.WriteLine("warning: you are going to clone the whole repository!\n"
|
||||
+ " =>If you want to manage branches with git-tfs, clone " + tfsTrunkRepositoryPath + " instead...");
|
||||
}
|
||||
else
|
||||
{
|
||||
stdout.WriteLine("warning: you are going to clone a subdirectory of a branch and won't be able to manage branches :(\n"
|
||||
+ " => If you want to manage branches with git-tfs, clone " + tfsTrunkRepositoryPath + " with '--with-branches' option instead...)");
|
||||
}
|
||||
}
|
||||
stdout.WriteLine("warning: you are going to clone a subdirectory of a branch and won't be able to manage branches :(\n"
|
||||
+ " => If you want to manage branches with git-tfs, clone " + tfsTrunkRepositoryPath + " with '--with-branches' option instead...)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,9 +81,13 @@ namespace Sep.Git.Tfs.Commands
|
||||
|
||||
private void GitTfsInit(string tfsUrl, string tfsRepositoryPath)
|
||||
{
|
||||
gitHelper.SetConfig("core.autocrlf", initOptions.GitInitAutoCrlf);
|
||||
gitHelper.SetConfig("core.ignorecase", "false");
|
||||
globals.Repository.CreateTfsRemote(globals.RemoteId, tfsUrl, tfsRepositoryPath, remoteOptions);
|
||||
globals.Repository.CreateTfsRemote(new RemoteInfo
|
||||
{
|
||||
Id = globals.RemoteId,
|
||||
Url = tfsUrl,
|
||||
Repository = tfsRepositoryPath,
|
||||
RemoteOptions = remoteOptions,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using NDesk.Options;
|
||||
using Sep.Git.Tfs.Core;
|
||||
using StructureMap;
|
||||
using Sep.Git.Tfs.Util;
|
||||
using Sep.Git.Tfs.Core.TfsInterop;
|
||||
|
||||
namespace Sep.Git.Tfs.Commands
|
||||
{
|
||||
@@ -57,9 +58,11 @@ namespace Sep.Git.Tfs.Commands
|
||||
|
||||
public int Run(string tfsBranchPath, string gitBranchNameExpected)
|
||||
{
|
||||
|
||||
var defaultRemote = InitFromDefaultRemote();
|
||||
|
||||
// TFS representations of repository paths do not have trailing slashes
|
||||
tfsBranchPath = (tfsBranchPath ?? string.Empty).TrimEnd('/');
|
||||
|
||||
var allRemotes = _globals.Repository.ReadAllTfsRemotes();
|
||||
|
||||
tfsBranchPath.AssertValidTfsPath();
|
||||
@@ -78,26 +81,23 @@ namespace Sep.Git.Tfs.Commands
|
||||
|
||||
var allRemotes = _globals.Repository.ReadAllTfsRemotes();
|
||||
|
||||
bool first = true;
|
||||
var allTfsBranches = defaultRemote.Tfs.GetAllTfsBranchesOrderedByCreation();
|
||||
var rootBranch = defaultRemote.Tfs.GetRootTfsBranchForRemotePath(defaultRemote.TfsRepositoryPath);
|
||||
if (rootBranch == null)
|
||||
throw new GitTfsException(string.Format("error: Init all the branches is only possible when 'git tfs clone' was done from the trunk!!! '{0}' is not a TFS branch!", defaultRemote.TfsRepositoryPath));
|
||||
if (defaultRemote.TfsRepositoryPath.ToLower() != rootBranch.Path.ToLower())
|
||||
throw new GitTfsException(string.Format("error: Init all the branches is only possible when 'git tfs clone' was done from the trunk!!! Please clone again from '{0}'...", rootBranch.Path));
|
||||
|
||||
var childBranchPaths = rootBranch.GetAllChildren().Select(b=>b.Path);
|
||||
|
||||
_stdout.WriteLine("Tfs branches found:");
|
||||
foreach (var tfsBranch in allTfsBranches)
|
||||
foreach (var tfsBranchPath in childBranchPaths)
|
||||
{
|
||||
_stdout.WriteLine("- " + tfsBranch);
|
||||
_stdout.WriteLine("- " + tfsBranchPath);
|
||||
}
|
||||
|
||||
foreach (var tfsBranch in allTfsBranches)
|
||||
foreach (var tfsBranchPath in childBranchPaths)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
if (defaultRemote.TfsRepositoryPath.ToLower() != tfsBranch.ToLower())
|
||||
throw new GitTfsException("error: Init all the branches is only possible when 'git tfs clone' was done from the trunk!!! Please clone again from the trunk...");
|
||||
|
||||
first = false;
|
||||
continue;
|
||||
}
|
||||
var result = CreateBranch(defaultRemote, tfsBranch, allRemotes);
|
||||
var result = CreateBranch(defaultRemote, tfsBranchPath, allRemotes);
|
||||
if (result < 0)
|
||||
return result;
|
||||
}
|
||||
@@ -131,6 +131,9 @@ namespace Sep.Git.Tfs.Commands
|
||||
{
|
||||
Trace.WriteLine("=> Working on TFS branch : " + tfsRepositoryPath);
|
||||
|
||||
// TFS string representations of repository paths do not end in trailing slashes
|
||||
tfsRepositoryPath = (tfsRepositoryPath ?? string.Empty).TrimEnd('/');
|
||||
|
||||
if (allRemotes.Count(r => r.TfsRepositoryPath.ToLower() == tfsRepositoryPath.ToLower()) != 0)
|
||||
{
|
||||
Trace.WriteLine("There is already a remote for this tfs branch. Branch ignored!");
|
||||
@@ -169,8 +172,7 @@ namespace Sep.Git.Tfs.Commands
|
||||
Trace.WriteLine("Commit found! sha1 : " + sha1RootCommit);
|
||||
|
||||
Trace.WriteLine("Try creating remote...");
|
||||
_globals.Repository.CreateTfsRemote(gitBranchName, defaultRemote.TfsUrl, tfsRepositoryPath, _remoteOptions);
|
||||
var tfsRemote = _globals.Repository.ReadTfsRemote(gitBranchName);
|
||||
var tfsRemote = _globals.Repository.CreateTfsRemote(new RemoteInfo { Id = gitBranchName, Url = defaultRemote.TfsUrl, Repository = tfsRepositoryPath, RemoteOptions = _remoteOptions });
|
||||
if (!_globals.Repository.CreateBranch(tfsRemote.RemoteRef, sha1RootCommit))
|
||||
throw new GitTfsException("error: Fail to create remote branch ref file!");
|
||||
Trace.WriteLine("Remote created!");
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using NDesk.Options;
|
||||
using Sep.Git.Tfs.Core;
|
||||
using StructureMap;
|
||||
@@ -24,6 +25,8 @@ namespace Sep.Git.Tfs.Commands
|
||||
public string TfsPassword { get; set; }
|
||||
public string ParentBranch { get; set; }
|
||||
public bool LabelAllBranches { get; set; }
|
||||
public string NameFilter { get; set; }
|
||||
public string ExcludeNameFilter { get; set; }
|
||||
string AuthorsFilePath { get; set; }
|
||||
|
||||
public Labels(TextWriter stdout, Globals globals, AuthorsFile authors)
|
||||
@@ -40,6 +43,8 @@ namespace Sep.Git.Tfs.Commands
|
||||
return new OptionSet
|
||||
{
|
||||
{ "all|fetch-all", "Fetch all the labels on all the TFS remotes (For TFS 2010 and later)", v => LabelAllBranches = v != null },
|
||||
{ "n|label-name=", "Fetch all the labels respecting this name filter", v => NameFilter = v },
|
||||
{ "e|exclude-label-name=", "Exclude all the labels respecting this regex name filter", v => ExcludeNameFilter = v },
|
||||
{ "u|username=", "TFS username", v => TfsUsername = v },
|
||||
{ "p|password=", "TFS password", v => TfsPassword = v },
|
||||
{ "a|authors=", "Path to an Authors file to map TFS users to Git users", v => AuthorsFilePath = v },
|
||||
@@ -83,12 +88,25 @@ namespace Sep.Git.Tfs.Commands
|
||||
|
||||
private int CreateLabelsForTfsBranch(IGitTfsRemote tfsRemote)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(NameFilter))
|
||||
NameFilter = null;
|
||||
else
|
||||
NameFilter = NameFilter.Trim();
|
||||
|
||||
UpdateRemote(tfsRemote);
|
||||
_stdout.WriteLine("Looking for label on " + tfsRemote.TfsRepositoryPath + "...");
|
||||
var labels = tfsRemote.Tfs.GetLabels(tfsRemote.TfsRepositoryPath);
|
||||
var labels = tfsRemote.Tfs.GetLabels(tfsRemote.TfsRepositoryPath, NameFilter);
|
||||
_stdout.WriteLine(labels.Count() +" labels found!");
|
||||
|
||||
Regex exludeRegex = null;
|
||||
if (ExcludeNameFilter != null)
|
||||
exludeRegex = new Regex(ExcludeNameFilter);
|
||||
|
||||
foreach (var label in labels)
|
||||
{
|
||||
if (ExcludeNameFilter != null && exludeRegex.IsMatch(label.Name))
|
||||
continue;
|
||||
|
||||
Trace.WriteLine("LabelId:" + label.Id + "/ChangesetId:" + label.ChangesetId + "/LabelName:" + label.Name + "/Owner:" + label.Owner);
|
||||
Trace.WriteLine("Try to find changeset in git repository...");
|
||||
string sha1TagCommit = _globals.Repository.FindCommitHashByCommitMessage("git-tfs-id: .*;C" + label.ChangesetId + "[^0-9]");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using NDesk.Options;
|
||||
@@ -21,7 +21,7 @@ namespace Sep.Git.Tfs.Commands
|
||||
get
|
||||
{
|
||||
return fetch.OptionSet
|
||||
.Add("rebase", "rebase your modifications on tfs changes", v => _shouldRebase = v != null );
|
||||
.Add("r|rebase", "rebase your modifications on tfs changes", v => _shouldRebase = v != null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Sep.Git.Tfs.Commands
|
||||
throw new GitTfsException("error: You have local changes; rebase-workflow only possible with clean working directory.")
|
||||
.WithRecommendation("Try 'git stash' to stash your local changes and pull again.");
|
||||
}
|
||||
globals.Repository.CommandNoisy("rebase", remote.RemoteRef);
|
||||
globals.Repository.CommandNoisy("rebase", "--preserve-merges", remote.RemoteRef);
|
||||
}
|
||||
else
|
||||
globals.Repository.CommandNoisy("merge", remote.RemoteRef);
|
||||
|
||||
@@ -17,8 +17,6 @@ namespace Sep.Git.Tfs.Commands
|
||||
{
|
||||
{ "ignore-regex=", "a regex of files to ignore",
|
||||
v => IgnoreRegex = v },
|
||||
{ "no-metadata", "leave out the 'git-tfs-id:' tag in commit messages\nUse this when you're exporting from TFS and don't need to put data back into TFS.",
|
||||
v => NoMetaData = v != null },
|
||||
{ "u|username=", "TFS username",
|
||||
v => Username = v },
|
||||
{ "p|password=", "TFS password",
|
||||
@@ -29,8 +27,6 @@ namespace Sep.Git.Tfs.Commands
|
||||
|
||||
public string IgnoreRegex { get; set; }
|
||||
|
||||
public bool NoMetaData { get; set; }
|
||||
|
||||
public string Username { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Sep.Git.Tfs.Commands
|
||||
|
||||
public int Run()
|
||||
{
|
||||
var remote = _globals.Repository.ReadAllTfsRemotes().First();
|
||||
var remote = _globals.Repository.ReadTfsRemote(_globals.RemoteId);
|
||||
return remote.Tfs.ListShelvesets(this, remote);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Sep.Git.Tfs.Core.TfsInterop;
|
||||
|
||||
namespace Sep.Git.Tfs.Core.BranchVisitors
|
||||
{
|
||||
public class BranchTreeContainsPathVisitor : IBranchTreeVisitor
|
||||
{
|
||||
private string searchPath;
|
||||
private bool searchExactPath;
|
||||
|
||||
public BranchTreeContainsPathVisitor(string searchPath, bool searchExactPath)
|
||||
{
|
||||
this.searchPath = searchPath;
|
||||
this.searchExactPath = searchExactPath;
|
||||
}
|
||||
|
||||
public bool Found { get; private set; }
|
||||
|
||||
public void Visit(BranchTree childBranch, int level)
|
||||
{
|
||||
if (Found == false
|
||||
&& ((searchExactPath && searchPath.ToLower() == childBranch.Path.ToLower())
|
||||
|| (!searchExactPath && searchPath.ToLower().IndexOf(childBranch.Path.ToLower()) == 0)))
|
||||
{
|
||||
Found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,6 +207,11 @@ namespace Sep.Git.Tfs.Core
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void CleanupWorkspaceDirectory()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ITfsChangeset GetChangeset(long changesetId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
@@ -222,6 +227,11 @@ namespace Sep.Git.Tfs.Core
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool MatchesUrlAndRepositoryPath(string tfsUrl, string tfsRepositoryPath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,13 @@ namespace Sep.Git.Tfs.Core
|
||||
}
|
||||
}
|
||||
|
||||
public static T GetOrAdd<K, T>(this Dictionary<K, T> dictionary, K key) where T : new()
|
||||
{
|
||||
if (!dictionary.ContainsKey(key))
|
||||
dictionary.Add(key, new T());
|
||||
return dictionary[key];
|
||||
}
|
||||
|
||||
public static T FirstOr<T>(this IEnumerable<T> e, T defaultValue)
|
||||
{
|
||||
foreach (var x in e) return x;
|
||||
@@ -130,5 +137,10 @@ namespace Sep.Git.Tfs.Core
|
||||
public static StreamWriter WithEncoding(this StreamWriter stream, Encoding encoding) {
|
||||
return new StreamWriter(stream.BaseStream, encoding);
|
||||
}
|
||||
|
||||
public static bool Contains(this IEnumerable<string> list, string toCheck, StringComparison comp)
|
||||
{
|
||||
return list.Any(listMember => listMember.IndexOf(toCheck, comp) >= 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+63
-112
@@ -5,6 +5,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Sep.Git.Tfs.Commands;
|
||||
using Sep.Git.Tfs.Core.TfsInterop;
|
||||
using StructureMap;
|
||||
using LibGit2Sharp;
|
||||
|
||||
@@ -17,14 +18,16 @@ namespace Sep.Git.Tfs.Core
|
||||
private static readonly Regex configLineRegex = new Regex("^tfs-remote\\.(?<id>.+)\\.(?<key>[^.=]+)=(?<value>.*)$");
|
||||
private IDictionary<string, IGitTfsRemote> _cachedRemotes;
|
||||
private Repository _repository;
|
||||
private RemoteConfigConverter _remoteConfigReader;
|
||||
|
||||
public GitRepository(TextWriter stdout, string gitDir, IContainer container, Globals globals)
|
||||
public GitRepository(TextWriter stdout, string gitDir, IContainer container, Globals globals, RemoteConfigConverter remoteConfigReader)
|
||||
: base(stdout, container)
|
||||
{
|
||||
_container = container;
|
||||
_globals = globals;
|
||||
GitDir = gitDir;
|
||||
_repository = new LibGit2Sharp.Repository(GitDir);
|
||||
_remoteConfigReader = remoteConfigReader;
|
||||
}
|
||||
|
||||
~GitRepository()
|
||||
@@ -52,9 +55,18 @@ namespace Sep.Git.Tfs.Core
|
||||
gitCommand.WorkingDirectory = Path.Combine(gitCommand.WorkingDirectory, WorkingCopySubdir);
|
||||
}
|
||||
|
||||
public string GetConfig(string key)
|
||||
{
|
||||
return _repository.Config.Get<string>(key, null);
|
||||
}
|
||||
|
||||
public IEnumerable<IGitTfsRemote> ReadAllTfsRemotes()
|
||||
{
|
||||
return GetTfsRemotes().Values;
|
||||
var remotes = GetTfsRemotes().Values;
|
||||
foreach (var remote in remotes)
|
||||
remote.EnsureTfsAuthenticated();
|
||||
|
||||
return remotes;
|
||||
}
|
||||
|
||||
public IGitTfsRemote ReadTfsRemote(string remoteId)
|
||||
@@ -62,7 +74,9 @@ namespace Sep.Git.Tfs.Core
|
||||
if (!HasRemote(remoteId))
|
||||
throw new GitTfsException("Unable to locate git-tfs remote with id = " + remoteId)
|
||||
.WithRecommendation("Try using `git tfs bootstrap` to auto-init TFS remotes.");
|
||||
return GetTfsRemotes()[remoteId];
|
||||
var remote = GetTfsRemotes()[remoteId];
|
||||
remote.EnsureTfsAuthenticated();
|
||||
return remote;
|
||||
}
|
||||
|
||||
private IGitTfsRemote ReadTfsRemote(string tfsUrl, string tfsRepositoryPath, bool includeStubRemotes)
|
||||
@@ -70,7 +84,7 @@ namespace Sep.Git.Tfs.Core
|
||||
var allRemotes = GetTfsRemotes();
|
||||
var matchingRemotes =
|
||||
allRemotes.Values.Where(
|
||||
remote => remote.Tfs.MatchesUrl(tfsUrl) && remote.TfsRepositoryPath == tfsRepositoryPath);
|
||||
remote => remote.MatchesUrlAndRepositoryPath(tfsUrl, tfsRepositoryPath));
|
||||
switch (matchingRemotes.Count())
|
||||
{
|
||||
case 0:
|
||||
@@ -80,7 +94,10 @@ namespace Sep.Git.Tfs.Core
|
||||
.WithRecommendation("Try setting a legacy-url for an existing remote.");
|
||||
return new DerivedGitTfsRemote(tfsUrl, tfsRepositoryPath);
|
||||
case 1:
|
||||
return matchingRemotes.First();
|
||||
Trace.WriteLine("One remote matched");
|
||||
var remote = matchingRemotes.First();
|
||||
remote.EnsureTfsAuthenticated();
|
||||
return remote;
|
||||
default:
|
||||
Trace.WriteLine("More than one remote matched!");
|
||||
goto case 1;
|
||||
@@ -92,11 +109,43 @@ namespace Sep.Git.Tfs.Core
|
||||
return _cachedRemotes ?? (_cachedRemotes = ReadTfsRemotes());
|
||||
}
|
||||
|
||||
public IGitTfsRemote CreateTfsRemote(RemoteInfo remote)
|
||||
{
|
||||
if (HasRemote(remote.Id))
|
||||
throw new GitTfsException("A remote with id \"" + remote.Id + "\" already exists.");
|
||||
|
||||
// These help the new (if it's new) git repository to behave more sanely.
|
||||
_repository.Config.Set("core.autocrlf", "false");
|
||||
_repository.Config.Set("core.ignorecase", "false");
|
||||
|
||||
foreach (var entry in _remoteConfigReader.Dump(remote))
|
||||
{
|
||||
if (entry.Value != null)
|
||||
{
|
||||
_repository.Config.Set(entry.Key, entry.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
_repository.Config.Unset(entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
var gitTfsRemote = BuildRemote(remote);
|
||||
gitTfsRemote.EnsureTfsAuthenticated();
|
||||
|
||||
return _cachedRemotes[remote.Id] = gitTfsRemote;
|
||||
}
|
||||
|
||||
private IDictionary<string, IGitTfsRemote> ReadTfsRemotes()
|
||||
{
|
||||
var remotes = new Dictionary<string, IGitTfsRemote>();
|
||||
CommandOutputPipe(stdout => ParseRemoteConfig(stdout, remotes), "config", "--list");
|
||||
return remotes;
|
||||
// does this need to ensuretfsauthenticated?
|
||||
_repository.Config.Set("tfs.touch", "1"); // reload configuration, because `git tfs init` and `git tfs clone` use Process.Start to update the config, so _repository's copy is out of date.
|
||||
return _remoteConfigReader.Load(_repository.Config).Select(x => BuildRemote(x)).ToDictionary(x => x.Id);
|
||||
}
|
||||
|
||||
private IGitTfsRemote BuildRemote(RemoteInfo remoteInfo)
|
||||
{
|
||||
return _container.With(remoteInfo).With<IGitRepository>(this).GetInstance<IGitTfsRemote>();
|
||||
}
|
||||
|
||||
public bool HasRemote(string remoteId)
|
||||
@@ -123,110 +172,6 @@ namespace Sep.Git.Tfs.Core
|
||||
}
|
||||
}
|
||||
|
||||
public void CreateTfsRemote(string remoteId, TfsChangesetInfo tfsHead, RemoteOptions remoteOptions)
|
||||
{
|
||||
CreateTfsRemote(remoteId, tfsHead.Remote.TfsUrl, tfsHead.Remote.TfsRepositoryPath, remoteOptions);
|
||||
ReadTfsRemote(remoteId).UpdateRef(tfsHead.GitCommit, tfsHead.ChangesetId);
|
||||
}
|
||||
|
||||
public void CreateTfsRemote(string remoteId, string tfsUrl, string tfsRepositoryPath, RemoteOptions remoteOptions)
|
||||
{
|
||||
if (HasRemote(remoteId))
|
||||
throw new GitTfsException("A remote with id \"" + remoteId + "\" already exists.");
|
||||
|
||||
if (remoteOptions != null)
|
||||
{
|
||||
if (remoteOptions.NoMetaData) SetTfsConfig(remoteId, "no-meta-data", 1);
|
||||
if (remoteOptions.IgnoreRegex != null) SetTfsConfig(remoteId, "ignore-paths", remoteOptions.IgnoreRegex);
|
||||
if (!string.IsNullOrEmpty(remoteOptions.Username)) SetTfsConfig(remoteId, "username", remoteOptions.Username);
|
||||
if (!string.IsNullOrEmpty(remoteOptions.Password)) SetTfsConfig(remoteId, "password", remoteOptions.Password);
|
||||
}
|
||||
|
||||
SetTfsConfig(remoteId, "url", tfsUrl);
|
||||
SetTfsConfig(remoteId, "repository", tfsRepositoryPath);
|
||||
SetTfsConfig(remoteId, "fetch", "refs/remotes/" + remoteId + "/master");
|
||||
|
||||
Directory.CreateDirectory(Path.Combine(GitDir, "tfs"));
|
||||
_cachedRemotes = null;
|
||||
}
|
||||
|
||||
private void SetTfsConfig(string remoteId, string subkey, object value)
|
||||
{
|
||||
this.SetConfig(_globals.RemoteConfigKey(remoteId, subkey), value);
|
||||
}
|
||||
|
||||
private void ParseRemoteConfig(TextReader stdout, IDictionary<string, IGitTfsRemote> remotes)
|
||||
{
|
||||
string line;
|
||||
while ((line = stdout.ReadLine()) != null)
|
||||
{
|
||||
TryParseRemoteConfigLine(line, remotes);
|
||||
}
|
||||
foreach (var gitTfsRemotePair in remotes)
|
||||
{
|
||||
var remote = gitTfsRemotePair.Value;
|
||||
remote.EnsureTfsAuthenticated();
|
||||
}
|
||||
}
|
||||
|
||||
private void TryParseRemoteConfigLine(string line, IDictionary<string, IGitTfsRemote> remotes)
|
||||
{
|
||||
var match = configLineRegex.Match(line);
|
||||
if (match.Success)
|
||||
{
|
||||
var key = match.Groups["key"].Value;
|
||||
var value = match.Groups["value"].Value;
|
||||
var remoteId = match.Groups["id"].Value;
|
||||
var remote = remotes.ContainsKey(remoteId)
|
||||
? remotes[remoteId]
|
||||
: (remotes[remoteId] = BuildRemote(remoteId));
|
||||
try
|
||||
{
|
||||
SetRemoteConfigValue(remote, key, value);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new GitTfsException("Malformed value for " + key + ": " + value, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IGitTfsRemote BuildRemote(string id)
|
||||
{
|
||||
var remote = _container.GetInstance<IGitTfsRemote>();
|
||||
remote.Repository = this;
|
||||
remote.Id = id;
|
||||
return remote;
|
||||
}
|
||||
|
||||
private void SetRemoteConfigValue(IGitTfsRemote remote, string key, string value)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case "url":
|
||||
remote.TfsUrl = value;
|
||||
break;
|
||||
case "legacy-urls":
|
||||
remote.Tfs.LegacyUrls = value.Split(',');
|
||||
break;
|
||||
case "repository":
|
||||
remote.TfsRepositoryPath = value;
|
||||
break;
|
||||
case "ignore-paths":
|
||||
remote.IgnoreRegexExpression = value;
|
||||
break;
|
||||
case "username":
|
||||
remote.TfsUsername = value;
|
||||
break;
|
||||
case "password":
|
||||
remote.TfsPassword = value;
|
||||
break;
|
||||
case "autotag":
|
||||
remote.Autotag = bool.Parse(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public GitCommit GetCommit(string commitish)
|
||||
{
|
||||
return new GitCommit(_repository.Lookup<Commit>(commitish));
|
||||
@@ -443,5 +388,11 @@ namespace Sep.Git.Tfs.Core
|
||||
if (_repository.Tags[name] == null)
|
||||
_repository.ApplyTag(name, sha, new Signature(Owner, emailOwner, new DateTimeOffset(creationDate)), comment);
|
||||
}
|
||||
|
||||
public void CreateNote(string sha, string content, string owner, string emailOwner, DateTime creationDate)
|
||||
{
|
||||
Signature author = new Signature(owner, emailOwner, creationDate);
|
||||
_repository.Notes.Add(new ObjectId(sha), content, author, author, "commits");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,18 +19,32 @@ namespace Sep.Git.Tfs.Core
|
||||
private readonly RemoteOptions remoteOptions;
|
||||
private long? maxChangesetId;
|
||||
private string maxCommitHash;
|
||||
private bool isTfsAuthenticated;
|
||||
|
||||
public GitTfsRemote(RemoteOptions remoteOptions, Globals globals, ITfsHelper tfsHelper, TextWriter stdout)
|
||||
public GitTfsRemote(RemoteInfo info, IGitRepository repository, RemoteOptions remoteOptions, Globals globals, ITfsHelper tfsHelper, TextWriter stdout)
|
||||
{
|
||||
this.remoteOptions = remoteOptions;
|
||||
this.globals = globals;
|
||||
this.stdout = stdout;
|
||||
Tfs = tfsHelper;
|
||||
Repository = repository;
|
||||
|
||||
Id = info.Id;
|
||||
TfsUrl = info.Url;
|
||||
TfsRepositoryPath = info.Repository;
|
||||
TfsUsername = info.Username;
|
||||
TfsPassword = info.Password;
|
||||
Aliases = (info.Aliases ?? Enumerable.Empty<string>()).ToArray();
|
||||
IgnoreRegexExpression = info.IgnoreRegex;
|
||||
Autotag = info.Autotag;
|
||||
}
|
||||
|
||||
public void EnsureTfsAuthenticated()
|
||||
{
|
||||
if (isTfsAuthenticated)
|
||||
return;
|
||||
Tfs.EnsureAuthenticated();
|
||||
isTfsAuthenticated = true;
|
||||
}
|
||||
|
||||
public bool IsDerived
|
||||
@@ -46,6 +60,8 @@ namespace Sep.Git.Tfs.Core
|
||||
set { Tfs.Url = value; }
|
||||
}
|
||||
|
||||
private string[] Aliases { get; set; }
|
||||
|
||||
public bool Autotag { get; set; }
|
||||
|
||||
public string TfsUsername
|
||||
@@ -114,7 +130,7 @@ namespace Sep.Git.Tfs.Core
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(Dir, "workspace");
|
||||
return Repository.GetConfig("git-tfs.workspace-dir") ?? Path.Combine(Dir, "workspace");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +139,22 @@ namespace Sep.Git.Tfs.Core
|
||||
Tfs.CleanupWorkspaces(WorkingDirectory);
|
||||
}
|
||||
|
||||
public void CleanupWorkspaceDirectory()
|
||||
{
|
||||
try
|
||||
{
|
||||
var allFiles = Directory.EnumerateFiles(WorkingDirectory, "*", SearchOption.AllDirectories);
|
||||
foreach (var file in allFiles)
|
||||
File.SetAttributes(file, File.GetAttributes(file) & ~FileAttributes.ReadOnly);
|
||||
|
||||
Directory.Delete(WorkingDirectory, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShouldSkip(string path)
|
||||
{
|
||||
return IsInDotGit(path) ||
|
||||
@@ -168,7 +200,17 @@ namespace Sep.Git.Tfs.Core
|
||||
log.CommitParents.Add(parent);
|
||||
}
|
||||
}
|
||||
UpdateRef(Commit(log), changeset.Summary.ChangesetId);
|
||||
var commitSha = Commit(log);
|
||||
UpdateRef(commitSha, changeset.Summary.ChangesetId);
|
||||
if(changeset.Summary.Workitems.Count() != 0)
|
||||
{
|
||||
string workitemNote = "Workitems:\n";
|
||||
foreach(var workitem in changeset.Summary.Workitems)
|
||||
{
|
||||
workitemNote += String.Format("[{0}] {1}\n {2}\n", workitem.Id, workitem.Title, workitem.Url);
|
||||
}
|
||||
Repository.CreateNote(commitSha, workitemNote, log.AuthorName, log.AuthorEmail, log.Date);
|
||||
}
|
||||
DoGcIfNeeded();
|
||||
}
|
||||
}
|
||||
@@ -495,5 +537,15 @@ namespace Sep.Git.Tfs.Core
|
||||
PendChangesToWorkspace(head, parent, workspace);
|
||||
return workspace.Checkin(options);
|
||||
}
|
||||
|
||||
public bool MatchesUrlAndRepositoryPath(string tfsUrl, string tfsRepositoryPath)
|
||||
{
|
||||
return MatchesTfsUrl(tfsUrl) && TfsRepositoryPath.Equals(tfsRepositoryPath, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private bool MatchesTfsUrl(string tfsUrl)
|
||||
{
|
||||
return TfsUrl.Equals(tfsUrl, StringComparison.OrdinalIgnoreCase) || Aliases.Contains(tfsUrl, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Sep.Git.Tfs.Core.TfsInterop;
|
||||
|
||||
namespace Sep.Git.Tfs.Core
|
||||
{
|
||||
public interface IBranchTreeVisitor
|
||||
{
|
||||
void Visit(BranchTree childBranch, int level);
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,4 @@ namespace Sep.Git.Tfs.Core
|
||||
void WrapGitCommandErrors(string exceptionMessage, Action action);
|
||||
IGitRepository MakeRepository(string dir);
|
||||
}
|
||||
|
||||
public static partial class Ext
|
||||
{
|
||||
public static void SetConfig(this IGitHelpers gitHelpers, string configKey, object value)
|
||||
{
|
||||
gitHelpers.CommandNoisy("config", configKey, value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Sep.Git.Tfs.Commands;
|
||||
|
||||
@@ -7,10 +8,10 @@ namespace Sep.Git.Tfs.Core
|
||||
public interface IGitRepository : IGitHelpers
|
||||
{
|
||||
string GitDir { get; set; }
|
||||
string GetConfig(string key);
|
||||
IEnumerable<IGitTfsRemote> ReadAllTfsRemotes();
|
||||
IGitTfsRemote ReadTfsRemote(string remoteId);
|
||||
void /*or IGitTfsRemote*/ CreateTfsRemote(string remoteId, string tfsUrl, string tfsRepositoryPath, RemoteOptions remoteOptions);
|
||||
void /*or IGitTfsRemote*/ CreateTfsRemote(string remoteId, TfsChangesetInfo tfsHead, RemoteOptions remoteOptions);
|
||||
IGitTfsRemote CreateTfsRemote(RemoteInfo remoteInfo);
|
||||
bool HasRemote(string remoteId);
|
||||
bool HasRef(string gitRef);
|
||||
void MoveTfsRefForwardIfNeeded(IGitTfsRemote remote);
|
||||
@@ -28,5 +29,6 @@ namespace Sep.Git.Tfs.Core
|
||||
bool CreateBranch(string gitBranchName, string target);
|
||||
string FindCommitHashByCommitMessage(string patternToFind);
|
||||
void CreateTag(string name, string sha, string comment, string Owner, string emailOwner, System.DateTime creationDate);
|
||||
void CreateNote(string sha, string content, string owner, string emailOwner, DateTime creationDate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,8 +38,10 @@ namespace Sep.Git.Tfs.Core
|
||||
/// </summary>
|
||||
long Checkin(string head, string parent, TfsChangesetInfo parentChangeset, CheckinOptions options);
|
||||
void CleanupWorkspace();
|
||||
void CleanupWorkspaceDirectory();
|
||||
ITfsChangeset GetChangeset(long changesetId);
|
||||
void UpdateRef(string commitHash, long changesetId);
|
||||
void EnsureTfsAuthenticated();
|
||||
bool MatchesUrlAndRepositoryPath(string tfsUrl, string tfsRepositoryPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Sep.Git.Tfs.Core
|
||||
{
|
||||
public interface ITfsWorkitem
|
||||
{
|
||||
int Id { get; set; }
|
||||
string Title { get; set; }
|
||||
string Description { get; set; }
|
||||
string Url { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LibGit2Sharp;
|
||||
|
||||
namespace Sep.Git.Tfs.Core
|
||||
{
|
||||
public class RemoteConfigConverter
|
||||
{
|
||||
public IEnumerable<RemoteInfo> Load(IEnumerable<ConfigurationEntry> config)
|
||||
{
|
||||
var remotes = new Dictionary<string, RemoteInfo>();
|
||||
foreach (var entry in config)
|
||||
{
|
||||
var keyParts = entry.Key.Split('.');
|
||||
if (keyParts.Length == 3 && keyParts[0] == "tfs-remote")
|
||||
{
|
||||
var id = keyParts[1];
|
||||
var key = keyParts[2];
|
||||
var remote = remotes.GetOrAdd(id);
|
||||
remote.Id = id;
|
||||
if (key == "url")
|
||||
remote.Url = entry.Value;
|
||||
else if (key == "repository")
|
||||
remote.Repository = entry.Value;
|
||||
else if (key == "username")
|
||||
remote.Username = entry.Value;
|
||||
else if (key == "password")
|
||||
remote.Password = entry.Value;
|
||||
else if (key == "ignore-paths")
|
||||
remote.IgnoreRegex = entry.Value;
|
||||
else if (key == "legacy-urls")
|
||||
remote.Aliases = entry.Value.Split(',');
|
||||
else if (key == "autotag")
|
||||
remote.Autotag = bool.Parse(entry.Value);
|
||||
}
|
||||
}
|
||||
return remotes.Values;
|
||||
}
|
||||
|
||||
public IEnumerable<ConfigurationEntry> Dump(RemoteInfo remote)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(remote.Id))
|
||||
{
|
||||
var prefix = "tfs-remote." + remote.Id + ".";
|
||||
yield return c(prefix + "url", remote.Url);
|
||||
yield return c(prefix + "repository", remote.Repository);
|
||||
yield return c(prefix + "username", remote.Username);
|
||||
yield return c(prefix + "password", remote.Password);
|
||||
yield return c(prefix + "ignore-paths", remote.IgnoreRegex);
|
||||
yield return c(prefix + "legacy-urls", remote.Aliases == null ? null : string.Join(",", remote.Aliases));
|
||||
yield return c(prefix + "autotag", remote.Autotag ? "true" : null);
|
||||
}
|
||||
}
|
||||
|
||||
private ConfigurationEntry c(string key, string value)
|
||||
{
|
||||
return new ConfigurationEntry(key, value, ConfigurationLevel.Local);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Sep.Git.Tfs.Commands;
|
||||
|
||||
namespace Sep.Git.Tfs.Core
|
||||
{
|
||||
public class RemoteInfo
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string Repository { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string IgnoreRegex { get; set; }
|
||||
public IEnumerable<string> Aliases { get; set; }
|
||||
public bool Autotag { get; set; }
|
||||
|
||||
public RemoteOptions RemoteOptions
|
||||
{
|
||||
get { return new RemoteOptions { IgnoreRegex = IgnoreRegex, Username = Username, Password = Password }; }
|
||||
set { IgnoreRegex = value.IgnoreRegex; Username = value.Username; Password = value.Password; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,18 @@
|
||||
namespace Sep.Git.Tfs.Core
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Sep.Git.Tfs.Core
|
||||
{
|
||||
public class TfsChangesetInfo
|
||||
{
|
||||
public IGitTfsRemote Remote { get; set; }
|
||||
public long ChangesetId { get; set; }
|
||||
public string GitCommit { get; set; }
|
||||
public IEnumerable<ITfsWorkitem> Workitems { get; set; }
|
||||
|
||||
public TfsChangesetInfo()
|
||||
{
|
||||
Workitems = Enumerable.Empty<ITfsWorkitem>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Sep.Git.Tfs.Core.BranchVisitors;
|
||||
|
||||
namespace Sep.Git.Tfs.Core.TfsInterop
|
||||
{
|
||||
public interface IBranchObject
|
||||
{
|
||||
string Path { get; }
|
||||
string ParentPath { get; }
|
||||
bool IsRoot { get; }
|
||||
}
|
||||
|
||||
public class BranchTree
|
||||
{
|
||||
public BranchTree(IBranchObject branch)
|
||||
: this(branch, new List<BranchTree>())
|
||||
{
|
||||
}
|
||||
|
||||
public BranchTree(IBranchObject branch, IEnumerable<BranchTree> childBranches)
|
||||
: this(branch, childBranches.ToList())
|
||||
{
|
||||
}
|
||||
|
||||
public BranchTree(IBranchObject branch, List<BranchTree> childBranches)
|
||||
{
|
||||
if (childBranches == null)
|
||||
throw new ArgumentNullException("childBranches");
|
||||
Branch = branch;
|
||||
ChildBranches = childBranches;
|
||||
}
|
||||
|
||||
public IBranchObject Branch { get; private set; }
|
||||
|
||||
public List<BranchTree> ChildBranches { get; private set; }
|
||||
|
||||
public string Path { get { return Branch.Path; } }
|
||||
public string ParentPath { get { return Branch.ParentPath; } }
|
||||
public bool IsRoot { get { return Branch.IsRoot; } }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0} [{1} children]", this.Path, this.ChildBranches.Count);
|
||||
}
|
||||
}
|
||||
|
||||
public static class BranchExtensions
|
||||
{
|
||||
public static BranchTree GetRootTfsBranchForRemotePath(this ITfsHelper tfs, string remoteTfsPath, bool searchExactPath = true)
|
||||
{
|
||||
var branches = tfs.GetBranches();
|
||||
var branchTrees = branches.Aggregate(new Dictionary<string, BranchTree>(StringComparer.OrdinalIgnoreCase), (dict, branch) => dict.Tap(d => d.Add(branch.Path, new BranchTree(branch))));
|
||||
foreach(var branch in branchTrees.Values)
|
||||
{
|
||||
if(!branch.IsRoot)
|
||||
{
|
||||
//in some strange cases there might be a branch which is not marked as IsRoot
|
||||
//but the parent for this branch is missing.
|
||||
if (branchTrees.ContainsKey(branch.ParentPath))
|
||||
branchTrees[branch.ParentPath].ChildBranches.Add(branch);
|
||||
}
|
||||
}
|
||||
var roots = branchTrees.Values.Where(b => b.IsRoot);
|
||||
return roots.FirstOrDefault(b =>
|
||||
{
|
||||
var visitor = new BranchTreeContainsPathVisitor(remoteTfsPath, searchExactPath);
|
||||
b.AcceptVisitor(visitor);
|
||||
return visitor.Found;
|
||||
});
|
||||
}
|
||||
|
||||
public static void AcceptVisitor(this BranchTree branch, IBranchTreeVisitor treeVisitor, int level = 0)
|
||||
{
|
||||
treeVisitor.Visit(branch, level);
|
||||
foreach (var childBranch in branch.ChildBranches)
|
||||
{
|
||||
childBranch.AcceptVisitor(treeVisitor, level + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<BranchTree> GetAllChildren(this BranchTree branch)
|
||||
{
|
||||
if (branch == null) return Enumerable.Empty<BranchTree>();
|
||||
|
||||
var childrenBranches = new List<BranchTree>(branch.ChildBranches);
|
||||
foreach (var childBranch in branch.ChildBranches)
|
||||
{
|
||||
childrenBranches.AddRange(childBranch.GetAllChildren());
|
||||
}
|
||||
return childrenBranches;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ namespace Sep.Git.Tfs.Core.TfsInterop
|
||||
string Url { get; set; }
|
||||
string Username { get; set; }
|
||||
string Password { get; set; }
|
||||
string[] LegacyUrls { get; set; }
|
||||
IEnumerable<ITfsChangeset> GetChangesets(string path, long startVersion, GitTfsRemote remote);
|
||||
void WithWorkspace(string directory, IGitTfsRemote remote, TfsChangesetInfo versionToFetch, Action<ITfsWorkspace> action);
|
||||
IShelveset CreateShelveset(IWorkspace workspace, string shelvesetName);
|
||||
@@ -21,7 +20,6 @@ namespace Sep.Git.Tfs.Core.TfsInterop
|
||||
ITfsChangeset GetLatestChangeset(GitTfsRemote remote);
|
||||
ITfsChangeset GetChangeset(int changesetId, GitTfsRemote remote);
|
||||
IChangeset GetChangeset(int changesetId);
|
||||
bool MatchesUrl(string tfsUrl);
|
||||
bool HasShelveset(string shelvesetName);
|
||||
ITfsChangeset GetShelvesetData(IGitTfsRemote remote, string shelvesetOwner, string shelvesetName);
|
||||
int ListShelvesets(ShelveList shelveList, IGitTfsRemote remote);
|
||||
@@ -29,9 +27,10 @@ namespace Sep.Git.Tfs.Core.TfsInterop
|
||||
long ShowCheckinDialog(IWorkspace workspace, IPendingChange[] pendingChanges, IEnumerable<IWorkItemCheckedInfo> checkedInfos, string checkinComment);
|
||||
void CleanupWorkspaces(string workingDirectory);
|
||||
int GetRootChangesetForBranch(string tfsPathBranchToCreate, string tfsPathParentBranch = null);
|
||||
IEnumerable<TfsLabel> GetLabels(string tfsPathBranch);
|
||||
IEnumerable<TfsLabel> GetLabels(string tfsPathBranch, string nameFilter = null);
|
||||
bool CanGetBranchInformation { get; }
|
||||
IEnumerable<string> GetAllTfsBranchesOrderedByCreation();
|
||||
IEnumerable<string> GetAllTfsRootBranchesOrderedByCreation();
|
||||
IEnumerable<IBranchObject> GetBranches();
|
||||
void EnsureAuthenticated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Sep.Git.Tfs.Core.TfsInterop
|
||||
{
|
||||
public IEnumerable<Exception> InnerExceptions { get; private set; }
|
||||
|
||||
public PluginLoaderException(string message, IEnumerable<Exception> failures) : base(message, failures.Last())
|
||||
public PluginLoaderException(string message, IEnumerable<Exception> failures) : base(message, failures.LastOrDefault())
|
||||
{
|
||||
InnerExceptions = failures;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
namespace Sep.Git.Tfs.Core.TfsInterop
|
||||
{
|
||||
public class WrapperFor<T>
|
||||
public class WrapperFor<TFS_TYPE>
|
||||
{
|
||||
private readonly T _wrapped;
|
||||
private readonly TFS_TYPE _wrapped;
|
||||
|
||||
public WrapperFor(T wrapped)
|
||||
public WrapperFor(TFS_TYPE wrapped)
|
||||
{
|
||||
_wrapped = wrapped;
|
||||
}
|
||||
|
||||
public T Unwrap()
|
||||
public TFS_TYPE Unwrap()
|
||||
{
|
||||
return _wrapped;
|
||||
}
|
||||
|
||||
public static T Unwrap(object wrapper)
|
||||
public static TFS_TYPE Unwrap(object wrapper)
|
||||
{
|
||||
return ((WrapperFor<T>)wrapper).Unwrap();
|
||||
return ((WrapperFor<TFS_TYPE>)wrapper).Unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Sep.Git.Tfs.Core
|
||||
{
|
||||
public class TfsWorkitem : ITfsWorkitem
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Url { get; set; }
|
||||
}
|
||||
}
|
||||
+7
-7
@@ -33,30 +33,30 @@ namespace Sep.Git.Tfs
|
||||
_globals = globals;
|
||||
}
|
||||
|
||||
public void Run(IList<string> args)
|
||||
public int Run(IList<string> args)
|
||||
{
|
||||
InitializeGlobals();
|
||||
var command = ExtractCommand(args);
|
||||
if(RequiresValidGitRepository(command)) AssertValidGitRepository();
|
||||
var unparsedArgs = ParseOptions(command, args);
|
||||
Main(command, unparsedArgs);
|
||||
return Main(command, unparsedArgs);
|
||||
}
|
||||
|
||||
public void Main(GitTfsCommand command, IList<string> unparsedArgs)
|
||||
public int Main(GitTfsCommand command, IList<string> unparsedArgs)
|
||||
{
|
||||
Trace.WriteLine(_gitTfsVersionProvider.GetVersionString());
|
||||
if(_globals.ShowHelp)
|
||||
{
|
||||
Environment.ExitCode = _help.ShowHelp(command);
|
||||
return _help.ShowHelp(command);
|
||||
}
|
||||
else if(_globals.ShowVersion)
|
||||
{
|
||||
_container.GetInstance<TextWriter>().WriteLine(_gitTfsVersionProvider.GetVersionString());
|
||||
Environment.ExitCode = GitTfsExitCodes.OK;
|
||||
return GitTfsExitCodes.OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
Environment.ExitCode = _runner.Run(command, unparsedArgs);
|
||||
//PostFetchCheckout();
|
||||
return _runner.Run(command, unparsedArgs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-1
@@ -131,11 +131,17 @@
|
||||
<Link>Properties\Version.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Core\DirectoryTidier.cs" />
|
||||
<Compile Include="Core\ITfsWorkitem.cs" />
|
||||
<Compile Include="Commands\Branch.cs" />
|
||||
<Compile Include="Core\BranchVisitors\BranchTreeContainsPathVisitor.cs" />
|
||||
<Compile Include="Core\IBranchTreeVisitor.cs" />
|
||||
<Compile Include="Core\TfsInterop\IBranch.cs" />
|
||||
<Compile Include="Commands\Bootstrap.cs" />
|
||||
<Compile Include="Commands\Checkin.cs" />
|
||||
<Compile Include="Commands\CheckinTool.cs" />
|
||||
<Compile Include="Commands\CheckinOptions.cs" />
|
||||
<Compile Include="Commands\CheckinBase.cs" />
|
||||
<Compile Include="Commands\CleanupWorkspaceLocal.cs" />
|
||||
<Compile Include="Commands\Labels.cs" />
|
||||
<Compile Include="Commands\InitBranch.cs" />
|
||||
<Compile Include="Commands\Info.cs" />
|
||||
@@ -169,6 +175,8 @@
|
||||
<Compile Include="Core\GitChangeInfo.cs" />
|
||||
<Compile Include="Core\GitCommit.cs" />
|
||||
<Compile Include="Core\GitObject.cs" />
|
||||
<Compile Include="Core\RemoteConfigConverter.cs" />
|
||||
<Compile Include="Core\RemoteInfo.cs" />
|
||||
<Compile Include="Core\GitTreeEntry.cs" />
|
||||
<Compile Include="Core\IGitChangedFile.cs" />
|
||||
<Compile Include="Core\IGitTfsRemote.cs" />
|
||||
@@ -200,6 +208,7 @@
|
||||
<Compile Include="Core\TfsInterop\NullIdentity.cs" />
|
||||
<Compile Include="Core\TfsInterop\IChangeset.cs" />
|
||||
<Compile Include="Core\TfsTreeEntry.cs" />
|
||||
<Compile Include="Core\TfsWorkitem.cs" />
|
||||
<Compile Include="Core\TfsWorkspace.cs" />
|
||||
<Compile Include="Core\TfsWriter.cs" />
|
||||
<Compile Include="Core\GitTfsException.cs" />
|
||||
@@ -291,4 +300,4 @@
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -63,22 +63,6 @@ namespace Sep.Git.Tfs
|
||||
|
||||
public string RemoteId { get; set; }
|
||||
|
||||
private string GetRemoteConfigPrefix()
|
||||
{
|
||||
if (RemoteId == null) return null;
|
||||
return GetRemoteConfigPrefix(RemoteId);
|
||||
}
|
||||
|
||||
private string GetRemoteConfigPrefix(string remoteId)
|
||||
{
|
||||
return "tfs-remote." + remoteId;
|
||||
}
|
||||
|
||||
public string RemoteConfigKey(string remoteId, string parameter)
|
||||
{
|
||||
return GetRemoteConfigPrefix(remoteId) + "." + parameter;
|
||||
}
|
||||
|
||||
public string GitDir
|
||||
{
|
||||
get { return Environment.GetEnvironmentVariable("GIT_DIR"); }
|
||||
|
||||
+7
-3
@@ -19,9 +19,7 @@ namespace Sep.Git.Tfs
|
||||
{
|
||||
try
|
||||
{
|
||||
//Trace.Listeners.Add(new ConsoleTraceListener());
|
||||
var container = Initialize();
|
||||
container.GetInstance<GitTfs>().Run(new List<string>(args));
|
||||
Environment.ExitCode = MainCore(args);
|
||||
}
|
||||
catch(GitTfsException e)
|
||||
{
|
||||
@@ -44,6 +42,12 @@ namespace Sep.Git.Tfs
|
||||
}
|
||||
}
|
||||
|
||||
public static int MainCore(string[] args)
|
||||
{
|
||||
var container = Initialize();
|
||||
return container.GetInstance<GitTfs>().Run(new List<string>(args));
|
||||
}
|
||||
|
||||
private static void ReportException(Exception e)
|
||||
{
|
||||
Trace.WriteLine(e);
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Sep.Git.Tfs.Test.Commands
|
||||
remote.TfsPassword = "pwd";
|
||||
remote.TfsRepositoryPath = "$/MyProject/Trunk";
|
||||
remote.TfsUrl = "http://myTfsServer:8080/tfs";
|
||||
remote.Tfs = new VsFake.TfsHelper(mocks.Container, null);
|
||||
remote.Tfs = new VsFake.TfsHelper(mocks.Container, null, null);
|
||||
gitRepository.Stub(r => r.GitDir).Return(".");
|
||||
|
||||
newBranchRemote = MockRepository.GenerateStub<IGitTfsRemote>();
|
||||
@@ -53,7 +53,7 @@ namespace Sep.Git.Tfs.Test.Commands
|
||||
ShouldInitBranch("MyBranch");
|
||||
}
|
||||
|
||||
public void ShouldInitBranch(string expectedGitBranchName)
|
||||
private void ShouldInitBranch(string expectedGitBranchName)
|
||||
{
|
||||
const string GIT_BRANCH_TO_INIT = "MyBranch";
|
||||
|
||||
@@ -67,8 +67,7 @@ namespace Sep.Git.Tfs.Test.Commands
|
||||
gitRepository.Expect(x => x.ReadAllTfsRemotes()).Return(new List<IGitTfsRemote> { remote }).Repeat.Once();
|
||||
gitRepository.Expect(x => x.AssertValidBranchName(GIT_BRANCH_TO_INIT)).Return(GIT_BRANCH_TO_INIT).Repeat.Once();
|
||||
gitRepository.Expect(x => x.FindCommitHashByCommitMessage(Arg<string>.Is.Anything)).Return("sha1BeforeFetch").Repeat.Once();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(Arg<string>.Is.Same(GIT_BRANCH_TO_INIT), Arg<string>.Is.Same("http://myTfsServer:8080/tfs"), Arg<string>.Is.Same("$/MyProject/MyBranch"), Arg<RemoteOptions>.Is.Anything)).Repeat.Once();
|
||||
gitRepository.Expect(x => x.ReadTfsRemote(GIT_BRANCH_TO_INIT)).Return(newBranchRemote).Repeat.Once();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(null)).Callback<RemoteInfo>((info) => info.Id == GIT_BRANCH_TO_INIT && info.Url == "http://myTfsServer:8080/tfs" && info.Repository == "$/MyProject/MyBranch").Return(newBranchRemote).Repeat.Once();
|
||||
|
||||
newBranchRemote.Expect(r => r.RemoteRef).Return("refs/remote/tfs/" + GIT_BRANCH_TO_INIT).Repeat.Once();
|
||||
newBranchRemote.Expect(r => r.Fetch()).Repeat.Once();
|
||||
@@ -99,7 +98,7 @@ namespace Sep.Git.Tfs.Test.Commands
|
||||
|
||||
gitRepository.Expect(x => x.AssertValidBranchName(GIT_BRANCH_TO_INIT)).Throw(new GitTfsException("The name specified for the new git branch is not allowed. Choose another one!"));
|
||||
gitRepository.Expect(x => x.FindCommitHashByCommitMessage(Arg<string>.Is.Anything)).Return("9ee6a5ab4abd0a96a5e90a6a99988ce59af7964a").Repeat.Never();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(Arg<string>.Is.Same("myBranch"), Arg<string>.Is.Same("http://myTfsServer:8080/tfs"), Arg<string>.Is.Same("$/MyProject/MyBranch"), Arg<RemoteOptions>.Is.Anything)).Repeat.Never();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(null)).Callback<RemoteInfo>((info) => info.Id == "myBranch" && info.Url == "http://myTfsServer:8080/tfs" && info.Repository == "$/MyProject/MyBranch").Repeat.Never();
|
||||
|
||||
Assert.Throws(typeof(GitTfsException), ()=>mocks.ClassUnderTest.Run("$/MyProject/MyBranch", GIT_BRANCH_TO_INIT));
|
||||
|
||||
@@ -122,14 +121,14 @@ namespace Sep.Git.Tfs.Test.Commands
|
||||
existingBranchRemote.TfsPassword = "pwd";
|
||||
existingBranchRemote.TfsRepositoryPath = "$/MyProject/MyBranch";
|
||||
existingBranchRemote.TfsUrl = "http://myTfsServer:8080/tfs";
|
||||
existingBranchRemote.Tfs = new VsFake.TfsHelper(mocks.Container, null);
|
||||
existingBranchRemote.Tfs = new VsFake.TfsHelper(mocks.Container, null, null);
|
||||
|
||||
gitRepository.Expect(x => x.ReadTfsRemote("default")).Return(remote).Repeat.Once();
|
||||
gitRepository.Expect(x => x.ReadAllTfsRemotes()).Return(new List<IGitTfsRemote> { remote, existingBranchRemote }).Repeat.Once();
|
||||
|
||||
gitRepository.Expect(x => x.AssertValidBranchName(GIT_BRANCH_TO_INIT)).Return(GIT_BRANCH_TO_INIT).Repeat.Never();
|
||||
gitRepository.Expect(x => x.FindCommitHashByCommitMessage(Arg<string>.Is.Anything)).Return("9ee6a5ab4abd0a96a5e90a6a99988ce59af7964a").Repeat.Never();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(Arg<string>.Is.Same("myBranch"), Arg<string>.Is.Same("http://myTfsServer:8080/tfs"), Arg<string>.Is.Same("$/MyProject/MyBranch"), Arg<RemoteOptions>.Is.Anything)).Repeat.Never();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(null)).Callback<RemoteInfo>((info) => info.Id == "myBranch" && info.Url == "http://myTfsServer:8080/tfs" && info.Repository == "$/MyProject/MyBranch").Repeat.Never();
|
||||
|
||||
Assert.Equal(GitTfsExitCodes.InvalidArguments, mocks.ClassUnderTest.Run("$/MyProject/MyBranch", GIT_BRANCH_TO_INIT));
|
||||
|
||||
@@ -151,7 +150,7 @@ namespace Sep.Git.Tfs.Test.Commands
|
||||
gitRepository.Expect(x => x.ReadAllTfsRemotes()).Return(new List<IGitTfsRemote> { remote }).Repeat.Once();
|
||||
gitRepository.Expect(x => x.AssertValidBranchName(GIT_BRANCH_TO_INIT)).Return(GIT_BRANCH_TO_INIT).Repeat.Once();
|
||||
gitRepository.Expect(x => x.CommandOneline(Arg<string[]>.Is.Anything)).Return("foo!").Repeat.Never();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(Arg<string>.Is.Same(GIT_BRANCH_TO_INIT), Arg<string>.Is.Same("http://myTfsServer:8080/tfs"), Arg<string>.Is.Same("$/MyProject/MyBranch"), Arg<RemoteOptions>.Is.Anything)).Repeat.Never();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(null)).Callback<RemoteInfo>((info) => info.Id == GIT_BRANCH_TO_INIT && info.Url == "http://myTfsServer:8080/tfs" && info.Repository == "$/MyProject/MyBranch").Repeat.Never();
|
||||
gitRepository.Expect(x => x.ReadTfsRemote(GIT_BRANCH_TO_INIT)).Return(newBranchRemote).Repeat.Never();
|
||||
|
||||
Assert.Throws(typeof(GitTfsException), () => mocks.ClassUnderTest.Run("$/MyProject/MyBranch"));
|
||||
@@ -174,7 +173,7 @@ namespace Sep.Git.Tfs.Test.Commands
|
||||
gitRepository.Expect(x => x.ReadAllTfsRemotes()).Return(new List<IGitTfsRemote> { remote }).Repeat.Once();
|
||||
gitRepository.Expect(x => x.AssertValidBranchName(GIT_BRANCH_TO_INIT)).Return(GIT_BRANCH_TO_INIT).Repeat.Once();
|
||||
gitRepository.Expect(x => x.FindCommitHashByCommitMessage(Arg<string>.Is.Anything)).Return("").Repeat.Once();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(Arg<string>.Is.Same(GIT_BRANCH_TO_INIT), Arg<string>.Is.Same("http://myTfsServer:8080/tfs"), Arg<string>.Is.Same("$/MyProject/MyBranch"), Arg<RemoteOptions>.Is.Anything)).Repeat.Never();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(null)).Callback<RemoteInfo>((info) => info.Id == GIT_BRANCH_TO_INIT && info.Url == "http://myTfsServer:8080/tfs" && info.Repository == "$/MyProject/MyBranch").Repeat.Never();
|
||||
gitRepository.Expect(x => x.ReadTfsRemote(GIT_BRANCH_TO_INIT)).Return(newBranchRemote).Repeat.Never();
|
||||
|
||||
|
||||
@@ -202,8 +201,7 @@ namespace Sep.Git.Tfs.Test.Commands
|
||||
gitRepository.Expect(x => x.ReadAllTfsRemotes()).Return(new List<IGitTfsRemote> { remote }).Repeat.Once();
|
||||
gitRepository.Expect(x => x.AssertValidBranchName(GIT_BRANCH_TO_INIT)).Return(GIT_BRANCH_TO_INIT).Repeat.Once();
|
||||
gitRepository.Expect(x => x.FindCommitHashByCommitMessage(Arg<string>.Is.Anything)).Return("sha1BeforeFetch").Repeat.Once();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(Arg<string>.Is.Same(GIT_BRANCH_TO_INIT), Arg<string>.Is.Same("http://myTfsServer:8080/tfs"), Arg<string>.Is.Same("$/MyProject/MyBranch"), Arg<RemoteOptions>.Is.Anything)).Repeat.Once();
|
||||
gitRepository.Expect(x => x.ReadTfsRemote(GIT_BRANCH_TO_INIT)).Return(newBranchRemote).Repeat.Once();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(null)).Callback<RemoteInfo>((info) => info.Id == GIT_BRANCH_TO_INIT && info.Url == "http://myTfsServer:8080/tfs" && info.Repository == "$/MyProject/MyBranch").Return(newBranchRemote).Repeat.Once();
|
||||
|
||||
newBranchRemote.Expect(r => r.RemoteRef).Return("refs/remote/tfs/" + GIT_BRANCH_TO_INIT).Repeat.Once();
|
||||
newBranchRemote.Expect(r => r.Fetch()).Repeat.Once();
|
||||
@@ -235,7 +233,7 @@ namespace Sep.Git.Tfs.Test.Commands
|
||||
gitRepository.Expect(x => x.AssertValidBranchName(GIT_BRANCH_TO_INIT)).Return(GIT_BRANCH_TO_INIT).Repeat.Once();
|
||||
gitRepository.Expect(x => x.ReadAllTfsRemotes()).Return(new List<IGitTfsRemote> { remote }).Repeat.Once();
|
||||
gitRepository.Expect(x => x.FindCommitHashByCommitMessage(Arg<string>.Is.Anything)).Return("9ee6a5ab4abd0a96a5e90a6a99988ce59af7964a").Repeat.Never();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(Arg<string>.Is.Same(GIT_BRANCH_TO_INIT), Arg<string>.Is.Same("http://myTfsServer:8080/tfs"), Arg<string>.Is.Same("$/MyProject/MyBranch"), Arg<RemoteOptions>.Is.Anything)).Repeat.Never();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(null)).Callback<RemoteInfo>((info) => info.Id == GIT_BRANCH_TO_INIT && info.Url == "http://myTfsServer:8080/tfs" && info.Repository == "$/MyProject/MyBranch").Repeat.Never();
|
||||
gitRepository.Expect(x => x.ReadTfsRemote(GIT_BRANCH_TO_INIT)).Return(newBranchRemote).Repeat.Never();
|
||||
|
||||
Assert.Throws(typeof(GitTfsException), ()=>mocks.ClassUnderTest.Run("$/MyProject/MyBranch"));
|
||||
@@ -245,6 +243,16 @@ namespace Sep.Git.Tfs.Test.Commands
|
||||
#endregion
|
||||
|
||||
#region Init All branches
|
||||
|
||||
public class MockBranchObject : IBranchObject
|
||||
{
|
||||
public string Path { get; set; }
|
||||
|
||||
public string ParentPath { get; set; }
|
||||
|
||||
public bool IsRoot { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldInitAllBranches()
|
||||
{
|
||||
@@ -259,7 +267,12 @@ namespace Sep.Git.Tfs.Test.Commands
|
||||
remote.Tfs = mocks.Get<ITfsHelper>();
|
||||
var tfsPathBranch1 = "$/MyProject/MyBranch1";
|
||||
var tfsPathBranch2 = "$/MyProject/MyBranch2";
|
||||
remote.Tfs.Stub(t => t.GetAllTfsBranchesOrderedByCreation()).Return(new List<string> { remote.TfsRepositoryPath, tfsPathBranch1, tfsPathBranch2 });
|
||||
remote.Tfs.Stub(t => t.GetBranches()).Return(new IBranchObject[] {
|
||||
new MockBranchObject() { IsRoot = true, Path = remote.TfsRepositoryPath },
|
||||
new MockBranchObject() { ParentPath = remote.TfsRepositoryPath, Path = tfsPathBranch1 },
|
||||
new MockBranchObject() { ParentPath = remote.TfsRepositoryPath, Path = tfsPathBranch2 },
|
||||
});
|
||||
remote.Tfs.Stub(t => t.GetAllTfsRootBranchesOrderedByCreation()).Return(new List<string> { remote.TfsRepositoryPath });
|
||||
|
||||
gitRepository.Expect(x => x.ReadTfsRemote("default")).Return(remote).Repeat.Once();
|
||||
gitRepository.Expect(x => x.ReadAllTfsRemotes()).Return(new List<IGitTfsRemote> { remote }).Repeat.Once();
|
||||
@@ -270,8 +283,7 @@ namespace Sep.Git.Tfs.Test.Commands
|
||||
|
||||
gitRepository.Expect(x => x.AssertValidBranchName(GIT_BRANCH_TO_INIT1)).Return(GIT_BRANCH_TO_INIT1).Repeat.Once();
|
||||
gitRepository.Expect(x => x.FindCommitHashByCommitMessage("git-tfs-id: .*;C" + rootChangeSetB1 + "[^0-9]")).Return("ShaBeforeFetch_Branch1").Repeat.Once();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(Arg<string>.Is.Same(GIT_BRANCH_TO_INIT1), Arg<string>.Is.Same("http://myTfsServer:8080/tfs"), Arg<string>.Is.Same(tfsPathBranch1), Arg<RemoteOptions>.Is.Anything)).Repeat.Once();
|
||||
gitRepository.Expect(x => x.ReadTfsRemote(GIT_BRANCH_TO_INIT1)).Return(newBranch1Remote).Repeat.Once();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(null)).Callback<RemoteInfo>((info) => info.Id == GIT_BRANCH_TO_INIT1 && info.Url == "http://myTfsServer:8080/tfs" && info.Repository == tfsPathBranch1).Return(newBranch1Remote).Repeat.Once();
|
||||
|
||||
newBranch1Remote.Expect(r => r.RemoteRef).Return("refs/remote/tfs/" + GIT_BRANCH_TO_INIT1).Repeat.Once();
|
||||
newBranch1Remote.Expect(r => r.Fetch()).Repeat.Once();
|
||||
@@ -290,8 +302,7 @@ namespace Sep.Git.Tfs.Test.Commands
|
||||
|
||||
gitRepository.Expect(x => x.AssertValidBranchName(GIT_BRANCH_TO_INIT2)).Return(GIT_BRANCH_TO_INIT2).Repeat.Once();
|
||||
gitRepository.Expect(x => x.FindCommitHashByCommitMessage("git-tfs-id: .*;C" + rootChangeSetB2 + "[^0-9]")).Return("ShaBeforeFetch_Branch2").Repeat.Once();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(Arg<string>.Is.Same(GIT_BRANCH_TO_INIT2), Arg<string>.Is.Same("http://myTfsServer:8080/tfs"), Arg<string>.Is.Same(tfsPathBranch2), Arg<RemoteOptions>.Is.Anything)).Repeat.Once();
|
||||
gitRepository.Expect(x => x.ReadTfsRemote(GIT_BRANCH_TO_INIT2)).Return(newBranch2Remote).Repeat.Once();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(null)).Callback<RemoteInfo>((info) => info.Id == GIT_BRANCH_TO_INIT2 && info.Url == "http://myTfsServer:8080/tfs" && info.Repository == tfsPathBranch2).Return(newBranch2Remote).Repeat.Once();
|
||||
|
||||
newBranch2Remote.Expect(r => r.RemoteRef).Return("refs/remote/tfs/" + GIT_BRANCH_TO_INIT2).Repeat.Once();
|
||||
newBranch2Remote.Expect(r => r.Fetch()).Repeat.Once();
|
||||
@@ -306,12 +317,11 @@ namespace Sep.Git.Tfs.Test.Commands
|
||||
gitRepository.VerifyAllExpectations();
|
||||
newBranch1Remote.VerifyAllExpectations();
|
||||
newBranch2Remote.VerifyAllExpectations();
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldFailInitAllBranchesBecauseNeedCloneWasMadeFromTrunk()
|
||||
{
|
||||
|
||||
const string GIT_BRANCH_TO_INIT1 = "MyBranch1";
|
||||
const string GIT_BRANCH_TO_INIT2 = "MyBranch2";
|
||||
|
||||
@@ -322,7 +332,12 @@ namespace Sep.Git.Tfs.Test.Commands
|
||||
remote.Tfs = mocks.Get<ITfsHelper>();
|
||||
var tfsPathBranch1 = "$/MyProject/MyBranch1";
|
||||
var tfsPathBranch2 = "$/MyProject/MyBranch2";
|
||||
remote.Tfs.Stub(t => t.GetAllTfsBranchesOrderedByCreation()).Return(new List<string> { "$/MyProject/TheCloneWasNotMadeFromTheTrunk!", tfsPathBranch1, tfsPathBranch2 });
|
||||
remote.Tfs.Stub(t => t.GetBranches()).Return(new IBranchObject[] {
|
||||
new MockBranchObject() { IsRoot = true, Path = "$/MyProject/TheCloneWasNotMadeFromTheTrunk!" },
|
||||
new MockBranchObject() { ParentPath = "$/MyProject/TheCloneWasNotMadeFromTheTrunk!", Path = tfsPathBranch1 },
|
||||
new MockBranchObject() { ParentPath = "$/MyProject/TheCloneWasNotMadeFromTheTrunk!", Path = tfsPathBranch2 },
|
||||
new MockBranchObject() { ParentPath = "$/MyProject/TheCloneWasNotMadeFromTheTrunk!", Path = remote.TfsRepositoryPath },
|
||||
});
|
||||
|
||||
gitRepository.Expect(x => x.ReadTfsRemote("default")).Return(remote).Repeat.Once();
|
||||
gitRepository.Expect(x => x.ReadAllTfsRemotes()).Return(new List<IGitTfsRemote> { remote }).Repeat.Once();
|
||||
@@ -333,7 +348,61 @@ namespace Sep.Git.Tfs.Test.Commands
|
||||
|
||||
gitRepository.Expect(x => x.AssertValidBranchName(GIT_BRANCH_TO_INIT1)).Return(GIT_BRANCH_TO_INIT1).Repeat.Never();
|
||||
gitRepository.Expect(x => x.FindCommitHashByCommitMessage("git-tfs-id: .*;C" + rootChangeSetB1 + "[^0-9]")).Return("Sha_Branch1").Repeat.Never();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(Arg<string>.Is.Same(GIT_BRANCH_TO_INIT1), Arg<string>.Is.Same("http://myTfsServer:8080/tfs"), Arg<string>.Is.Same(tfsPathBranch1), Arg<RemoteOptions>.Is.Anything)).Repeat.Never();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(null)).Callback<RemoteInfo>((info) => info.Id == GIT_BRANCH_TO_INIT1 && info.Url == "http://myTfsServer:8080/tfs" && info.Repository == tfsPathBranch1).Repeat.Never();
|
||||
#endregion
|
||||
|
||||
#region Branch2
|
||||
var newBranch2Remote = MockRepository.GenerateStub<IGitTfsRemote>();
|
||||
newBranch2Remote.Id = GIT_BRANCH_TO_INIT2;
|
||||
|
||||
var rootChangeSetB2 = 2000;
|
||||
remote.Tfs.Stub(t => t.GetRootChangesetForBranch(tfsPathBranch2)).Return(rootChangeSetB2);
|
||||
|
||||
gitRepository.Expect(x => x.AssertValidBranchName(GIT_BRANCH_TO_INIT2)).Return(GIT_BRANCH_TO_INIT2).Repeat.Never();
|
||||
gitRepository.Expect(x => x.FindCommitHashByCommitMessage("git-tfs-id: .*;C" + rootChangeSetB2 + "[^0-9]")).Return("Sha_Branch2").Repeat.Never();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(null)).Callback<RemoteInfo>((info) => info.Id == GIT_BRANCH_TO_INIT2 && info.Url == "http://myTfsServer:8080/tfs" && info.Repository == tfsPathBranch2).Repeat.Never();
|
||||
#endregion
|
||||
|
||||
var ex = Assert.Throws(typeof(GitTfsException), ()=>mocks.ClassUnderTest.Run());
|
||||
|
||||
Assert.Equal("error: Init all the branches is only possible when 'git tfs clone' was done from the trunk!!! Please clone again from '$/MyProject/TheCloneWasNotMadeFromTheTrunk!'...", ex.Message);
|
||||
|
||||
gitRepository.VerifyAllExpectations();
|
||||
|
||||
newBranch1Remote.VerifyAllExpectations();
|
||||
newBranch2Remote.VerifyAllExpectations();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldFailInitAllBranchesBecauseCloneWasNotMadeFromABranch()
|
||||
{
|
||||
const string GIT_BRANCH_TO_INIT1 = "MyBranch1";
|
||||
const string GIT_BRANCH_TO_INIT2 = "MyBranch2";
|
||||
|
||||
IGitRepository gitRepository; IGitTfsRemote remote; IGitTfsRemote newBranch1Remote;
|
||||
InitMocks4Tests(GIT_BRANCH_TO_INIT1, out gitRepository, out remote, out newBranch1Remote);
|
||||
|
||||
mocks.ClassUnderTest.CloneAllBranches = true;
|
||||
remote.Tfs = mocks.Get<ITfsHelper>();
|
||||
var tfsPathBranch1 = "$/MyProject/MyBranch1";
|
||||
var tfsPathBranch2 = "$/MyProject/MyBranch2";
|
||||
remote.Tfs.Stub(t => t.GetBranches()).Return(new IBranchObject[] {
|
||||
new MockBranchObject() { IsRoot = true, Path = "$/MyProject/TheCloneWasNotMadeFromTheTrunk!" },
|
||||
new MockBranchObject() { ParentPath = "$/MyProject/TheCloneWasNotMadeFromTheTrunk!", Path = tfsPathBranch1 },
|
||||
new MockBranchObject() { ParentPath = "$/MyProject/TheCloneWasNotMadeFromTheTrunk!", Path = tfsPathBranch2 },
|
||||
// Note the remote TfsRepositoryPath is NOT included!
|
||||
});
|
||||
|
||||
gitRepository.Expect(x => x.ReadTfsRemote("default")).Return(remote).Repeat.Once();
|
||||
gitRepository.Expect(x => x.ReadAllTfsRemotes()).Return(new List<IGitTfsRemote> { remote }).Repeat.Once();
|
||||
|
||||
#region Branch1
|
||||
var rootChangeSetB1 = 1000;
|
||||
remote.Tfs.Stub(t => t.GetRootChangesetForBranch(tfsPathBranch1)).Return(rootChangeSetB1);
|
||||
|
||||
gitRepository.Expect(x => x.AssertValidBranchName(GIT_BRANCH_TO_INIT1)).Return(GIT_BRANCH_TO_INIT1).Repeat.Never();
|
||||
gitRepository.Expect(x => x.FindCommitHashByCommitMessage("git-tfs-id: .*;C" + rootChangeSetB1 + "[^0-9]")).Return("Sha_Branch1").Repeat.Never();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(null)).Callback<RemoteInfo>((info) => info.Id == GIT_BRANCH_TO_INIT1 && info.Url == "http://myTfsServer:8080/tfs" && info.Repository == tfsPathBranch1).Repeat.Never();
|
||||
gitRepository.Expect(x => x.ReadTfsRemote(GIT_BRANCH_TO_INIT1)).Return(newBranch1Remote).Repeat.Never();
|
||||
#endregion
|
||||
|
||||
@@ -346,18 +415,20 @@ namespace Sep.Git.Tfs.Test.Commands
|
||||
|
||||
gitRepository.Expect(x => x.AssertValidBranchName(GIT_BRANCH_TO_INIT2)).Return(GIT_BRANCH_TO_INIT2).Repeat.Never();
|
||||
gitRepository.Expect(x => x.FindCommitHashByCommitMessage("git-tfs-id: .*;C" + rootChangeSetB2 + "[^0-9]")).Return("Sha_Branch2").Repeat.Never();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(Arg<string>.Is.Same(GIT_BRANCH_TO_INIT2), Arg<string>.Is.Same("http://myTfsServer:8080/tfs"), Arg<string>.Is.Same(tfsPathBranch2), Arg<RemoteOptions>.Is.Anything)).Repeat.Never();
|
||||
gitRepository.Expect(x => x.CreateTfsRemote(null)).Callback<RemoteInfo>((info) => info.Id == GIT_BRANCH_TO_INIT2 && info.Url == "http://myTfsServer:8080/tfs" && info.Repository == tfsPathBranch2).Repeat.Never();
|
||||
gitRepository.Expect(x => x.ReadTfsRemote(GIT_BRANCH_TO_INIT2)).Return(newBranch2Remote).Repeat.Never();
|
||||
#endregion
|
||||
|
||||
Assert.Throws(typeof(GitTfsException), ()=>mocks.ClassUnderTest.Run());
|
||||
var ex = Assert.Throws(typeof(GitTfsException), () => mocks.ClassUnderTest.Run());
|
||||
|
||||
Assert.Equal("error: Init all the branches is only possible when 'git tfs clone' was done from the trunk!!! '$/MyProject/Trunk' is not a TFS branch!", ex.Message);
|
||||
|
||||
gitRepository.VerifyAllExpectations();
|
||||
|
||||
newBranch1Remote.VerifyAllExpectations();
|
||||
newBranch2Remote.VerifyAllExpectations();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Help Command
|
||||
@@ -371,7 +442,7 @@ namespace Sep.Git.Tfs.Test.Commands
|
||||
remote.TfsPassword = "pwd";
|
||||
remote.TfsRepositoryPath = "$/MyProject/Trunk";
|
||||
remote.TfsUrl = "http://myTfsServer:8080/tfs";
|
||||
remote.Tfs = new VsFake.TfsHelper(mocks.Container, null);
|
||||
remote.Tfs = new VsFake.TfsHelper(mocks.Container, null, null);
|
||||
gitRepository.Expect(x => x.ReadTfsRemote("default")).Return(remote).Repeat.Never();
|
||||
|
||||
//Not Very Clean!!! Don't know how to test that :(
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Sep.Git.Tfs.Core.BranchVisitors;
|
||||
using Sep.Git.Tfs.Core.TfsInterop;
|
||||
using Sep.Git.Tfs.Test.Commands;
|
||||
using Xunit;
|
||||
|
||||
namespace Sep.Git.Tfs.Test.Core.BranchVisitors
|
||||
{
|
||||
public class BranchContainsPathVisitorTest
|
||||
{
|
||||
private BranchTree branch;
|
||||
|
||||
public BranchContainsPathVisitorTest()
|
||||
{
|
||||
branch = new BranchTree(new InitBranchTest.MockBranchObject { Path = @"$/Scratch/Source/Main" });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InexactMatch_WithoutTrailingSlash_IsFound()
|
||||
{
|
||||
var visitor = new BranchTreeContainsPathVisitor(@"$/Scratch/Source/Main", false);
|
||||
|
||||
branch.AcceptVisitor(visitor);
|
||||
|
||||
Assert.True(visitor.Found);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InexactMatch_WithTrailingSlash_IsFound()
|
||||
{
|
||||
var visitor = new BranchTreeContainsPathVisitor(@"$/Scratch/Source/Main/", false);
|
||||
|
||||
branch.AcceptVisitor(visitor);
|
||||
|
||||
Assert.True(visitor.Found);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExactMatch_WithoutTrailingSlash_IsFound()
|
||||
{
|
||||
var visitor = new BranchTreeContainsPathVisitor(@"$/Scratch/Source/Main", true);
|
||||
|
||||
branch.AcceptVisitor(visitor);
|
||||
|
||||
Assert.True(visitor.Found);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExactMatch_WithTrailingSlash_IsNotFound()
|
||||
{
|
||||
var visitor = new BranchTreeContainsPathVisitor(@"$/Scratch/Source/Main/", true);
|
||||
|
||||
branch.AcceptVisitor(visitor);
|
||||
|
||||
Assert.False(visitor.Found);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System.IO;
|
||||
using Rhino.Mocks;
|
||||
using Sep.Git.Tfs.Core;
|
||||
using Sep.Git.Tfs.Core.TfsInterop;
|
||||
using Sep.Git.Tfs.Util;
|
||||
using StructureMap.AutoMocking;
|
||||
using Xunit;
|
||||
|
||||
namespace Sep.Git.Tfs.Test.Core
|
||||
{
|
||||
public class GitTfsRemoteTests
|
||||
{
|
||||
[Fact]
|
||||
public void MatchesUrlAndRepositoryPath_should_be_case_insensitive_for_tfs_url()
|
||||
{
|
||||
var remote = BuildRemote(url: "http://testvcs:8080/tfs/test", repository: "test");
|
||||
Assert.True(remote.MatchesUrlAndRepositoryPath("http://testvcs:8080/tfs/Test", "test"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesUrlAndRepositoryPath_should_be_false_if_no_match_for_tfs_url()
|
||||
{
|
||||
var remote = BuildRemote(url: "http://testvcs:8080/tfs/test", repository: "test");
|
||||
Assert.False(remote.MatchesUrlAndRepositoryPath("http://adifferenturl:8080/tfs/Test", "test"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesUrlAndRepositoryPath_should_be_case_insensitive_for_legacy_urls()
|
||||
{
|
||||
var remote = BuildRemote(legacyUrls: new[] { "http://testvcs:8080/tfs/test", "AnotherUrlThatDoesntMatch" }, repository: "test");
|
||||
Assert.True(remote.MatchesUrlAndRepositoryPath("http://testvcs:8080/tfs/Test", "test"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesUrlAndRepositoryPath_should_be_case_insensitive_for_tfs_repository_path()
|
||||
{
|
||||
var remote = BuildRemote(url: "test", repository: "$/Test");
|
||||
Assert.True(remote.MatchesUrlAndRepositoryPath("test", "$/test"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchesUrlAndRepositoryPath_should_be_false_if_no_match_for_tfs_repository_path()
|
||||
{
|
||||
var remote = BuildRemote(url: "test", repository: "$/Test");
|
||||
Assert.False(remote.MatchesUrlAndRepositoryPath("test", "$/shouldnotmatch"));
|
||||
}
|
||||
|
||||
private GitTfsRemote BuildRemote(string repository, string url = "", string[] legacyUrls = null)
|
||||
{
|
||||
if (legacyUrls == null)
|
||||
legacyUrls = new string[0];
|
||||
var info = new RemoteInfo
|
||||
{
|
||||
Url = url,
|
||||
Repository = repository,
|
||||
Aliases = legacyUrls,
|
||||
};
|
||||
var mocks = new RhinoAutoMocker<GitTfsRemote>();
|
||||
mocks.Inject<TextWriter>(new StringWriter());
|
||||
mocks.Inject<RemoteInfo>(info);
|
||||
mocks.Inject<ITfsHelper>(MockRepository.GenerateStub<ITfsHelper>()); // GitTfsRemote backs the TfsUrl with this.
|
||||
return mocks.ClassUnderTest;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LibGit2Sharp;
|
||||
using Xunit;
|
||||
using Sep.Git.Tfs.Core;
|
||||
|
||||
namespace Sep.Git.Tfs.Test.Core
|
||||
{
|
||||
public class RemoteConfigConverterDumpTests
|
||||
{
|
||||
RemoteConfigConverter _dumper = new RemoteConfigConverter();
|
||||
|
||||
[Fact]
|
||||
public void DumpsNothingWithNoId()
|
||||
{
|
||||
var remote = new RemoteInfo { Url = "http://server/path", Repository = "$/Project" };
|
||||
Assert.Empty(_dumper.Dump(remote));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DumpsNothingWithBlankId()
|
||||
{
|
||||
var remote = new RemoteInfo { Id = " ", Url = "http://server/path", Repository = "$/Project" };
|
||||
Assert.Empty(_dumper.Dump(remote));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DumpsMinimalRemote()
|
||||
{
|
||||
var remote = new RemoteInfo { Id = "default", Url = "http://server/path", Repository = "$/Project" };
|
||||
var config = _dumper.Dump(remote);
|
||||
AssertContainsConfig("tfs-remote.default.url", "http://server/path", config);
|
||||
AssertContainsConfig("tfs-remote.default.repository", "$/Project", config);
|
||||
AssertContainsConfig("tfs-remote.default.username", null, config);
|
||||
AssertContainsConfig("tfs-remote.default.password", null, config);
|
||||
AssertContainsConfig("tfs-remote.default.ignore-paths", null, config);
|
||||
AssertContainsConfig("tfs-remote.default.legacy-urls", null, config);
|
||||
AssertContainsConfig("tfs-remote.default.autotag", null, config);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DumpsCompleteRemote()
|
||||
{
|
||||
var remote = new RemoteInfo {
|
||||
Id = "default",
|
||||
Url = "http://server/path",
|
||||
Repository = "$/Project",
|
||||
Username = "user",
|
||||
Password = "pass",
|
||||
IgnoreRegex = "abc",
|
||||
Autotag = true,
|
||||
Aliases = new string[] { "http://abc", "http://def" },
|
||||
};
|
||||
var config = _dumper.Dump(remote);
|
||||
AssertContainsConfig("tfs-remote.default.url", "http://server/path", config);
|
||||
AssertContainsConfig("tfs-remote.default.repository", "$/Project", config);
|
||||
AssertContainsConfig("tfs-remote.default.username", "user", config);
|
||||
AssertContainsConfig("tfs-remote.default.password", "pass", config);
|
||||
AssertContainsConfig("tfs-remote.default.ignore-paths", "abc", config);
|
||||
AssertContainsConfig("tfs-remote.default.legacy-urls", "http://abc,http://def", config);
|
||||
AssertContainsConfig("tfs-remote.default.autotag", "true", config);
|
||||
}
|
||||
|
||||
private void AssertContainsConfig(string key, string value, IEnumerable<ConfigurationEntry> configs)
|
||||
{
|
||||
Assert.Contains(new ConfigurationEntry(key, value, ConfigurationLevel.Local), configs, configComparer);
|
||||
}
|
||||
|
||||
static IEqualityComparer<ConfigurationEntry> configComparer = new ConfigurationEntryComparer();
|
||||
|
||||
class ConfigurationEntryComparer : IEqualityComparer<ConfigurationEntry>
|
||||
{
|
||||
bool IEqualityComparer<ConfigurationEntry>.Equals(ConfigurationEntry x, ConfigurationEntry y)
|
||||
{
|
||||
return x.Key == y.Key && x.Value == y.Value;
|
||||
}
|
||||
|
||||
int IEqualityComparer<ConfigurationEntry>.GetHashCode(ConfigurationEntry obj)
|
||||
{
|
||||
return obj.Key.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LibGit2Sharp;
|
||||
using Sep.Git.Tfs.Core;
|
||||
using Xunit;
|
||||
|
||||
namespace Sep.Git.Tfs.Test.Core
|
||||
{
|
||||
public class RemoteConfigConverterLoadTests
|
||||
{
|
||||
RemoteConfigConverter _reader = new RemoteConfigConverter();
|
||||
Dictionary<string, string> _config = new Dictionary<string, string>();
|
||||
|
||||
IEnumerable<ConfigurationEntry> _gitConfig { get { return _config.Select(x => new ConfigurationEntry(x.Key, x.Value, ConfigurationLevel.Local)); } }
|
||||
IEnumerable<RemoteInfo> _remotes { get { return _reader.Load(_gitConfig); } }
|
||||
RemoteInfo _firstRemote { get { return _remotes.FirstOrDefault(); } }
|
||||
|
||||
public RemoteConfigConverterLoadTests()
|
||||
{
|
||||
// Set some normal-ish config params. This makes sure that there is no barfing on extra config entries.
|
||||
_config["core.autocrlf"] = "true";
|
||||
_config["ui.color"] = "auto";
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoConfig()
|
||||
{
|
||||
Assert.Empty(_remotes);
|
||||
}
|
||||
|
||||
void SetUpMinimalRemote()
|
||||
{
|
||||
_config["tfs-remote.default.url"] = "http://server/path";
|
||||
_config["tfs-remote.default.repository"] = "$/project";
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MinimalRemote()
|
||||
{
|
||||
SetUpMinimalRemote();
|
||||
Assert.Equal(1, _remotes.Count());
|
||||
Assert.Equal("default", _firstRemote.Id);
|
||||
Assert.Equal("http://server/path", _firstRemote.Url);
|
||||
Assert.Equal("$/project", _firstRemote.Repository);
|
||||
Assert.Null(_firstRemote.Username);
|
||||
Assert.Null(_firstRemote.Password);
|
||||
Assert.Null(_firstRemote.IgnoreRegex);
|
||||
}
|
||||
|
||||
void SetUpCompleteRemote()
|
||||
{
|
||||
SetUpMinimalRemote();
|
||||
_config["tfs-remote.default.username"] = "theuser";
|
||||
_config["tfs-remote.default.password"] = "thepassword";
|
||||
_config["tfs-remote.default.ignore-paths"] = "ignorethis.zip";
|
||||
_config["tfs-remote.default.legacy-urls"] = "http://old:8080/,http://other/";
|
||||
_config["tfs-remote.default.autotag"] = "true";
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoteWithEverything()
|
||||
{
|
||||
SetUpCompleteRemote();
|
||||
Assert.Equal("default", _firstRemote.Id);
|
||||
Assert.Equal("http://server/path", _firstRemote.Url);
|
||||
Assert.Equal("$/project", _firstRemote.Repository);
|
||||
Assert.Equal("theuser", _firstRemote.Username);
|
||||
Assert.Equal("thepassword", _firstRemote.Password);
|
||||
Assert.Equal("ignorethis.zip", _firstRemote.IgnoreRegex);
|
||||
Assert.Equal(new string[] { "http://old:8080/", "http://other/" }, _firstRemote.Aliases);
|
||||
Assert.True(_firstRemote.Autotag);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Sep.Git.Tfs.Core.TfsInterop;
|
||||
using Xunit;
|
||||
|
||||
namespace Sep.Git.Tfs.Test.Core.TfsInterop
|
||||
{
|
||||
public class BranchExtensionsTest
|
||||
{
|
||||
[Fact]
|
||||
public void AllChildrenAlwaysReturnsAnEnumerable()
|
||||
{
|
||||
IEnumerable<BranchTree> result = ((BranchTree) null).GetAllChildren();
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,18 +111,25 @@
|
||||
<Compile Include="Commands\InitOptionsTest.cs" />
|
||||
<Compile Include="Commands\InitBranchTest.cs" />
|
||||
<Compile Include="Core\DirectoryTidierTests.cs" />
|
||||
<Compile Include="Core\GitTfsRemoteTests.cs" />
|
||||
<Compile Include="Core\BranchVisitors\BranchContainsPathVisitorTest.cs" />
|
||||
<Compile Include="Core\TfsInterop\BranchExtensionsTest.cs" />
|
||||
<Compile Include="FactExceptOnUnix.cs" />
|
||||
<Compile Include="Commands\HelpTest.cs" />
|
||||
<Compile Include="Commands\ShelveTest.cs" />
|
||||
<Compile Include="Core\DelimitedReaderTests.cs" />
|
||||
<Compile Include="Core\ExtTests.cs" />
|
||||
<Compile Include="Core\GitChangeInfoTests.cs" />
|
||||
<Compile Include="Core\RemoteConfigConverterDumpTests.cs" />
|
||||
<Compile Include="Core\RemoteConfigConverterLoadTests.cs" />
|
||||
<Compile Include="Core\ModeTests.cs" />
|
||||
<Compile Include="Core\TfsApiBridgeTest.cs" />
|
||||
<Compile Include="Core\StubbedCheckinEvaluationResult.cs" />
|
||||
<Compile Include="Core\TfsWorkspaceTests.cs" />
|
||||
<Compile Include="GitTfsRegexTests.cs" />
|
||||
<Compile Include="Integration\BootstrapTests.cs" />
|
||||
<Compile Include="Integration\CloneTests.cs" />
|
||||
<Compile Include="Integration\FetchTests.cs" />
|
||||
<Compile Include="Integration\IntegrationHelper.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="TestHelpers\ExtensionMethods.cs" />
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using Xunit;
|
||||
|
||||
namespace Sep.Git.Tfs.Test.Integration
|
||||
{
|
||||
public class BootstrapTests : IDisposable
|
||||
{
|
||||
IntegrationHelper h = new IntegrationHelper();
|
||||
|
||||
public BootstrapTests()
|
||||
{
|
||||
h.SetupFake(_ => { });
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
h.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BootstrapWithNoRemotes()
|
||||
{
|
||||
h.SetupGitRepo("repo", g =>
|
||||
{
|
||||
g.Commit("A sample commit.");
|
||||
});
|
||||
h.RunIn("repo", "bootstrap");
|
||||
h.AssertNoRef("repo", "tfs/default");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BootstrapWithARemoteAtHead()
|
||||
{
|
||||
string c1 = null;
|
||||
h.SetupGitRepo("repo", g =>
|
||||
{
|
||||
c1 = g.Commit("A sample commit from TFS.\n\ngit-tfs-id: [http://server/tfs]$/MyProject;C1");
|
||||
});
|
||||
h.RunIn("repo", "bootstrap");
|
||||
h.AssertRef("repo", "tfs/default", c1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BootstrapWithARemoteAsAParentOfHead()
|
||||
{
|
||||
string c1 = null;
|
||||
h.SetupGitRepo("repo", g =>
|
||||
{
|
||||
c1 = g.Commit("A sample commit from TFS.\n\ngit-tfs-id: [http://server/tfs]$/MyProject;C1");
|
||||
g.Commit("Another sample commit.");
|
||||
});
|
||||
h.RunIn("repo", "bootstrap");
|
||||
h.AssertRef("repo", "tfs/default", c1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ namespace Sep.Git.Tfs.Test.Integration
|
||||
{
|
||||
}
|
||||
|
||||
[FactExceptOnUnix(Skip="eventually")]
|
||||
[FactExceptOnUnix]
|
||||
public void ClonesEmptyProject()
|
||||
{
|
||||
h.SetupFake(r =>
|
||||
@@ -36,10 +36,10 @@ namespace Sep.Git.Tfs.Test.Integration
|
||||
});
|
||||
h.Run("clone", h.TfsUrl, "$/MyProject");
|
||||
h.AssertGitRepo("MyProject");
|
||||
const string expectedSha = "tbd";
|
||||
const string expectedSha = "4053764b2868a2be71ae7f5f113ad84dff8a052a";
|
||||
h.AssertRef("MyProject", "HEAD", expectedSha);
|
||||
h.AssertRef("MyProject", "master", expectedSha);
|
||||
h.AssertRef("MyProject", "tfs/default", expectedSha);
|
||||
h.AssertRef("MyProject", "refs/remotes/tfs/default", expectedSha);
|
||||
h.AssertEmptyWorkspace("MyProject");
|
||||
}
|
||||
|
||||
@@ -57,10 +57,10 @@ namespace Sep.Git.Tfs.Test.Integration
|
||||
});
|
||||
h.Run("clone", h.TfsUrl, "$/MyProject");
|
||||
h.AssertGitRepo("MyProject");
|
||||
const string expectedSha = "72a03802ac5f864a40a9bee13608f85e0e2ad05b";
|
||||
const string expectedSha = "d64d883266eca65bede947c79529318718a0d8eb";
|
||||
h.AssertRef("MyProject", "HEAD", expectedSha);
|
||||
h.AssertRef("MyProject", "master", expectedSha);
|
||||
h.AssertRef("MyProject", "tfs/default", expectedSha);
|
||||
h.AssertRef("MyProject", "refs/remotes/tfs/default", expectedSha);
|
||||
h.AssertFileInWorkspace("MyProject", "Folder/File.txt", "File contents");
|
||||
h.AssertFileInWorkspace("MyProject", "README", "tldr");
|
||||
}
|
||||
@@ -78,7 +78,7 @@ namespace Sep.Git.Tfs.Test.Integration
|
||||
});
|
||||
h.Run("clone", h.TfsUrl, "$/MyProject");
|
||||
h.AssertGitRepo("MyProject");
|
||||
AssertRefs("ea7ed178fb4cce7f46d2c84b907a88fa9d194014");
|
||||
AssertRefs("4faa9a5f32e6af118b84071a537228d3f7da7d9d");
|
||||
h.AssertFileInWorkspace("MyProject", "ÆØÅ/äöü.txt", "File contents");
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace Sep.Git.Tfs.Test.Integration
|
||||
});
|
||||
h.Run("clone", h.TfsUrl, "$/MyProject");
|
||||
h.AssertGitRepo("MyProject");
|
||||
AssertRefs("78f0490e22ae245a63238744de2d96f0675880a0");
|
||||
AssertRefs("5bd7660fa145ce0c38b5c279502478ce205a0cfb");
|
||||
h.AssertFileInWorkspace("MyProject", "Folder/File.txt", "Blåbærsyltetøy er godt!");
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace Sep.Git.Tfs.Test.Integration
|
||||
});
|
||||
h.Run("clone", h.TfsUrl, "$/MyProject");
|
||||
h.AssertGitRepo("MyProject");
|
||||
AssertRefs("9a73fe007130ca91517283aafe1d442f406df973");
|
||||
AssertRefs("cd14e6e28abffd625412dae36d9d9659bf6cb68c");
|
||||
h.AssertFileInWorkspace("MyProject", "Folder/File.txt", "File contents");
|
||||
|
||||
var expectedCommitMessage = new System.Text.StringBuilder();
|
||||
@@ -127,7 +127,7 @@ namespace Sep.Git.Tfs.Test.Integration
|
||||
{
|
||||
h.AssertRef("MyProject", "HEAD", expectedSha);
|
||||
h.AssertRef("MyProject", "master", expectedSha);
|
||||
h.AssertRef("MyProject", "tfs/default", expectedSha);
|
||||
h.AssertRef("MyProject", "refs/remotes/tfs/default", expectedSha);
|
||||
}
|
||||
|
||||
[FactExceptOnUnix]
|
||||
@@ -148,7 +148,7 @@ namespace Sep.Git.Tfs.Test.Integration
|
||||
h.Run("clone", h.TfsUrl, "$/MyProject");
|
||||
h.AssertGitRepo("MyProject");
|
||||
h.AssertCleanWorkspace("MyProject");
|
||||
AssertRefs("70cdbdca83c3808e60bc1f8cde7e155055447df7");
|
||||
AssertRefs("175420603e41cd0175e3c25581754726bd21cb96");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using Sep.Git.Tfs.Core.TfsInterop;
|
||||
using Xunit;
|
||||
|
||||
namespace Sep.Git.Tfs.Test.Integration
|
||||
{
|
||||
public class FetchTests : IDisposable
|
||||
{
|
||||
private readonly IntegrationHelper integrationHelper;
|
||||
|
||||
public FetchTests()
|
||||
{
|
||||
integrationHelper = new IntegrationHelper();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
integrationHelper.Dispose();
|
||||
}
|
||||
|
||||
[FactExceptOnUnix]
|
||||
public void CanFetchWithMixedUpCasingForTfsServerUrl()
|
||||
{
|
||||
CloneRepoWithTwoCommits();
|
||||
AddNewCommitToFakeTfsServer();
|
||||
string tfsUrlInUpperCase = integrationHelper.TfsUrl.ToUpper();
|
||||
integrationHelper.ChangeConfigSetting("MyProject", "tfs-remote.default.url", tfsUrlInUpperCase);
|
||||
|
||||
integrationHelper.RunIn("MyProject", "pull");
|
||||
|
||||
Assert.Equal(3, integrationHelper.GetCommitCount("MyProject"));
|
||||
}
|
||||
|
||||
[FactExceptOnUnix]
|
||||
public void CanFetchWithMixedUpCasingForLegacyTfsServerUrl()
|
||||
{
|
||||
CloneRepoWithTwoCommits();
|
||||
AddNewCommitToFakeTfsServer();
|
||||
string tfsUrlInUpperCase = integrationHelper.TfsUrl.ToUpper();
|
||||
integrationHelper.ChangeConfigSetting("MyProject", "tfs-remote.default.url", "nomatch");
|
||||
integrationHelper.ChangeConfigSetting("MyProject", "tfs-remote.default.legacy-urls", tfsUrlInUpperCase + ",aDifferentUrl");
|
||||
|
||||
integrationHelper.RunIn("MyProject", "pull");
|
||||
|
||||
Assert.Equal(3, integrationHelper.GetCommitCount("MyProject"));
|
||||
}
|
||||
|
||||
[FactExceptOnUnix]
|
||||
public void CanFetchWithMixedUpCasingForTfsRepositoryPath()
|
||||
{
|
||||
CloneRepoWithTwoCommits();
|
||||
AddNewCommitToFakeTfsServer();
|
||||
const string repoUrlInUpperCase = "$/MYPROJECT";
|
||||
integrationHelper.ChangeConfigSetting("MyProject", "tfs-remote.default.repository", repoUrlInUpperCase);
|
||||
|
||||
integrationHelper.RunIn("MyProject", "pull");
|
||||
|
||||
Assert.Equal(3, integrationHelper.GetCommitCount("MyProject"));
|
||||
}
|
||||
|
||||
private void CloneRepoWithTwoCommits()
|
||||
{
|
||||
integrationHelper.SetupFake(r =>
|
||||
{
|
||||
r.Changeset(1, "Project created from template", DateTime.Parse("2012-01-01 12:12:12 -05:00"))
|
||||
.Change(TfsChangeType.Add, TfsItemType.Folder, "$/MyProject");
|
||||
r.Changeset(2, "Add Readme", DateTime.Parse("2012-01-02 12:12:12 -05:00"))
|
||||
.Change(TfsChangeType.Add, TfsItemType.Folder, "$/MyProject/Folder")
|
||||
.Change(TfsChangeType.Add, TfsItemType.File, "$/MyProject/Folder/File.txt", "File contents")
|
||||
.Change(TfsChangeType.Add, TfsItemType.File, "$/MyProject/README", "tldr");
|
||||
});
|
||||
integrationHelper.Run("clone", integrationHelper.TfsUrl, "$/MyProject");
|
||||
integrationHelper.AssertGitRepo("MyProject");
|
||||
}
|
||||
|
||||
private void AddNewCommitToFakeTfsServer()
|
||||
{
|
||||
integrationHelper.SetupFake(r => CreateAChangeset(r));
|
||||
}
|
||||
|
||||
private static IntegrationHelper.FakeChangesetBuilder CreateAChangeset(IntegrationHelper.FakeHistoryBuilder r)
|
||||
{
|
||||
return r.Changeset(3, "Add a file", DateTime.Parse("2012-01-03 12:12:12 -05:00"))
|
||||
.Change(TfsChangeType.Add, TfsItemType.Folder, "$/MyProject/Foo")
|
||||
.Change(TfsChangeType.Add, TfsItemType.Folder, "$/MyProject/Foo/Bar")
|
||||
.Change(TfsChangeType.Add, TfsItemType.File, "$/MyProject/Foo/Bar/File.txt", "File contents");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,13 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using LibGit2Sharp;
|
||||
using Sep.Git.Tfs.Core;
|
||||
using Sep.Git.Tfs.Core.TfsInterop;
|
||||
using Sep.Git.Tfs.VsFake;
|
||||
using Xunit;
|
||||
using Xunit.Sdk;
|
||||
using LibGit2Sharp;
|
||||
|
||||
namespace Sep.Git.Tfs.Test.Integration
|
||||
{
|
||||
@@ -34,6 +36,12 @@ namespace Sep.Git.Tfs.Test.Integration
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
while (!_repositories.Empty())
|
||||
{
|
||||
var repo = _repositories.First();
|
||||
repo.Value.Dispose();
|
||||
_repositories.Remove(repo.Key);
|
||||
}
|
||||
if (_workdir != null)
|
||||
{
|
||||
try
|
||||
@@ -47,6 +55,43 @@ namespace Sep.Git.Tfs.Test.Integration
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string, Repository> _repositories = new Dictionary<string,Repository>();
|
||||
public Repository Repository(string path)
|
||||
{
|
||||
path = Path.Combine(Workdir, path);
|
||||
if (!_repositories.ContainsKey(path))
|
||||
_repositories.Add(path, new Repository(path));
|
||||
return _repositories[path];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region set up a git repository
|
||||
|
||||
public void SetupGitRepo(string path, Action<RepoBuilder> buildIt)
|
||||
{
|
||||
using (var repo = LibGit2Sharp.Repository.Init(Path.Combine(Workdir, path)))
|
||||
buildIt(new RepoBuilder(repo));
|
||||
}
|
||||
|
||||
public class RepoBuilder
|
||||
{
|
||||
private Repository _repo;
|
||||
|
||||
public RepoBuilder(Repository repo)
|
||||
{
|
||||
_repo = repo;
|
||||
}
|
||||
|
||||
public string Commit(string message)
|
||||
{
|
||||
File.WriteAllText(Path.Combine(_repo.Info.WorkingDirectory, "README.txt"), message);
|
||||
_repo.Index.Stage("README.txt");
|
||||
var committer = new Signature("Test User", "test@example.com", new DateTimeOffset(DateTime.Now));
|
||||
return _repo.Commit(message, committer, committer).Id.Sha;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region set up vsfake script
|
||||
@@ -71,7 +116,7 @@ namespace Sep.Git.Tfs.Test.Integration
|
||||
|
||||
public FakeChangesetBuilder Changeset(int changesetId, string message, DateTime checkinDate)
|
||||
{
|
||||
var changeset =new ScriptedChangeset
|
||||
var changeset = new ScriptedChangeset
|
||||
{
|
||||
Id = changesetId,
|
||||
Comment = message,
|
||||
@@ -110,22 +155,46 @@ namespace Sep.Git.Tfs.Test.Integration
|
||||
|
||||
public string TfsUrl { get { return "http://does/not/matter"; } }
|
||||
|
||||
public void Run(params string[] args)
|
||||
public int Run(params string[] args)
|
||||
{
|
||||
var startInfo = new ProcessStartInfo();
|
||||
startInfo.WorkingDirectory = Workdir;
|
||||
startInfo.EnvironmentVariables["GIT_TFS_CLIENT"] = "Fake";
|
||||
startInfo.EnvironmentVariables[Script.EnvVar] = FakeScript;
|
||||
startInfo.EnvironmentVariables["Path"] = CurrentBuildPath + ";" + Environment.GetEnvironmentVariable("Path");
|
||||
startInfo.FileName = "cmd";
|
||||
startInfo.Arguments = "/c git tfs --debug " + String.Join(" ", args);
|
||||
startInfo.UseShellExecute = false;
|
||||
startInfo.RedirectStandardOutput = true;
|
||||
Console.WriteLine("PATH: " + startInfo.EnvironmentVariables["Path"]);
|
||||
Console.WriteLine(">> " + startInfo.FileName + " " + startInfo.Arguments);
|
||||
var process = Process.Start(startInfo);
|
||||
Console.Out.Write(process.StandardOutput.ReadToEnd());
|
||||
process.WaitForExit();
|
||||
return RunIn(".", args);
|
||||
}
|
||||
|
||||
public int RunIn(string workPath, params string[] args)
|
||||
{
|
||||
var origPwd = Environment.CurrentDirectory;
|
||||
var origClient = Environment.GetEnvironmentVariable("GIT_TFS_CLIENT");
|
||||
var origScript = Environment.GetEnvironmentVariable(Script.EnvVar);
|
||||
try
|
||||
{
|
||||
Environment.CurrentDirectory = Path.Combine(Workdir, workPath);
|
||||
Environment.SetEnvironmentVariable("GIT_TFS_CLIENT", "Fake");
|
||||
Environment.SetEnvironmentVariable(Script.EnvVar, FakeScript);
|
||||
Console.WriteLine(">> git tfs " + QuoteArgs(args));
|
||||
var argsWithDebug = new List<string>();
|
||||
argsWithDebug.Add("--debug");
|
||||
argsWithDebug.AddRange(args);
|
||||
return Program.MainCore(argsWithDebug.ToArray());
|
||||
}
|
||||
finally
|
||||
{
|
||||
Environment.SetEnvironmentVariable("GIT_TFS_CLIENT", origClient);
|
||||
Environment.SetEnvironmentVariable(Script.EnvVar, origScript);
|
||||
Environment.CurrentDirectory = origPwd;
|
||||
}
|
||||
}
|
||||
|
||||
private string QuoteArgs(string[] args)
|
||||
{
|
||||
return string.Join(" ", args.Select(arg => QuoteArg(arg)).ToArray());
|
||||
}
|
||||
|
||||
private string QuoteArg(string arg)
|
||||
{
|
||||
// This is not complete, but it is adequate for these tests.
|
||||
if (arg.Contains(' '))
|
||||
return '"' + arg + '"';
|
||||
return arg;
|
||||
}
|
||||
|
||||
private string CurrentBuildPath
|
||||
@@ -137,10 +206,22 @@ namespace Sep.Git.Tfs.Test.Integration
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeConfigSetting(string repodir, string key, string value)
|
||||
{
|
||||
var repo = new LibGit2Sharp.Repository(Path.Combine(Workdir, repodir));
|
||||
repo.Config.Set(key, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region assertions
|
||||
|
||||
public int GetCommitCount(string repodir)
|
||||
{
|
||||
var repo = new LibGit2Sharp.Repository(Path.Combine(Workdir, repodir));
|
||||
return repo.Commits.Count();
|
||||
}
|
||||
|
||||
public void AssertGitRepo(string repodir)
|
||||
{
|
||||
var path = Path.Combine(Workdir, repodir);
|
||||
@@ -148,21 +229,21 @@ namespace Sep.Git.Tfs.Test.Integration
|
||||
Assert.True(Directory.Exists(Path.Combine(path, ".git")), path + " should have a .git dir inside of it");
|
||||
}
|
||||
|
||||
public void AssertNoRef(string repodir, string gitref)
|
||||
{
|
||||
AssertEqual(null, RevParse(repodir, gitref), "Expected no ref " + gitref);
|
||||
}
|
||||
|
||||
public void AssertRef(string repodir, string gitref, string expectedSha)
|
||||
{
|
||||
Assert.NotNull(expectedSha);
|
||||
AssertEqual(expectedSha, RevParse(repodir, gitref), "Expected " + gitref + " to be " + expectedSha);
|
||||
}
|
||||
|
||||
private string RevParse(string repodir, string gitref)
|
||||
{
|
||||
// This really should delegate to libgit2, which isn't yet a part of GitTfs.
|
||||
var gitpath = Path.Combine(Workdir, repodir, ".git");
|
||||
var resolved = ReadIfPresent(Path.Combine(gitpath, gitref)) ??
|
||||
ReadIfPresent(Path.Combine(gitpath, "refs", "heads", gitref)) ??
|
||||
ReadIfPresent(Path.Combine(gitpath, "refs", "remotes", gitref));
|
||||
if (resolved != null && resolved.StartsWith("ref:"))
|
||||
return RevParse(repodir, resolved.Replace("ref:", "").Trim());
|
||||
return resolved;
|
||||
var parsed = Repository(repodir).Lookup<Commit>(gitref);
|
||||
return parsed == null ? null : parsed.Sha;
|
||||
}
|
||||
|
||||
private string ReadIfPresent(string path)
|
||||
@@ -173,16 +254,13 @@ namespace Sep.Git.Tfs.Test.Integration
|
||||
public void AssertEmptyWorkspace(string repodir)
|
||||
{
|
||||
var entries = new List<string>(Directory.GetFileSystemEntries(Path.Combine(Workdir, repodir)));
|
||||
entries.Remove(".");
|
||||
entries.Remove("..");
|
||||
entries.Remove(".git");
|
||||
entries = entries.Where(f => Path.GetFileName(f) != ".git").ToList();
|
||||
AssertEqual(new List<string>(), entries, "entries in " + repodir);
|
||||
}
|
||||
|
||||
public void AssertCleanWorkspace(string repodir)
|
||||
{
|
||||
var repo = new LibGit2Sharp.Repository(Path.Combine(Workdir, repodir));
|
||||
var status = repo.Index.RetrieveStatus();
|
||||
var status = Repository(repodir).Index.RetrieveStatus();
|
||||
AssertEqual(new List<string>(), status.Select(statusEntry => "" + statusEntry.State + ": " + statusEntry.FilePath).ToList(), "repo status");
|
||||
}
|
||||
|
||||
@@ -195,11 +273,10 @@ namespace Sep.Git.Tfs.Test.Integration
|
||||
|
||||
public void AssertCommitMessage(string repodir, string commitish, string message)
|
||||
{
|
||||
var repo = new LibGit2Sharp.Repository(Path.Combine(Workdir, repodir));
|
||||
var commit = LibGit2Sharp.RepositoryExtensions.Lookup<LibGit2Sharp.Commit>(repo, commitish);
|
||||
var commit = Repository(repodir).Lookup<Commit>(commitish);
|
||||
AssertEqual(message, commit.Message, "Commit message of " + commitish);
|
||||
}
|
||||
|
||||
|
||||
private void AssertEqual<T>(T expected, T actual, string message)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# Git-tfs config values
|
||||
|
||||
Git-tfs uses git's configuration system to track most of the important
|
||||
information about repositories.
|
||||
|
||||
## Repository-wide configuration
|
||||
|
||||
By default, git-tfs sets these configuration values for the repository
|
||||
during `git tfs init`.
|
||||
|
||||
* `core.ignorecase` is set to `true`, in an attempt to deal with
|
||||
casing issues.
|
||||
* `core.autocrlf` is set to `false`. This will make git preserve all
|
||||
characters (including CR and LF) in all files. The reason for doing
|
||||
this is to make the result of `git tfs clone` as nearly identical,
|
||||
byte-wise, as possible, to the version in TFS.
|
||||
|
||||
## Per-TFS remote
|
||||
|
||||
Git-tfs can map multiple TFS branches to git branches. Each TFS
|
||||
branch is tracked as a separate "remote", and several config values
|
||||
are stored for each branch.
|
||||
|
||||
Each git-tfs remote is assigned an ID. All of a remote's config keys
|
||||
are prefixed with `tfs-remote.<id>.` So, for example, the full `url`
|
||||
key for the remote `default` is `tfs-remote.default.url`.
|
||||
|
||||
* `url`
|
||||
is the URL of the TFS project collection.
|
||||
* `legacy-urls`
|
||||
is a list, comma-separated, of previous URLs of the TFS project
|
||||
collection. For example, if you started your git-tfs clone from
|
||||
a 2005 or 2008 TFS server ('http://tfs:8080/tfs'), and the server
|
||||
migrated to 2010 or later, moving your project into a project
|
||||
collection ('http://tfs:8080/tfs/DefaultCollection'), then the
|
||||
`url` for your git-tfs remote should be the current url, and
|
||||
`legacy-urls` would be the old url.
|
||||
* `repository`
|
||||
is the TFS repository path that was cloned to the root of your
|
||||
git-tfs project. Typically this is a TFS project path
|
||||
(`$/MyProject`), but it can be a subdirectory (`$/MyProject/Dir`)
|
||||
or a branch (`$/MyProject/trunk`).
|
||||
* `username` and `password`
|
||||
are your TFS credentials. Normally, if you connect to a TFS
|
||||
server on your local Windows domain, you won't need to provide
|
||||
these values, because git-tfs defaults to using integrated
|
||||
authentication.
|
||||
* `ignore-paths`
|
||||
is a regular expression of TFS paths to ignore when fetching.
|
||||
* `autotag`
|
||||
can be set to `true` to make git-tfs create a tag for each
|
||||
TFS commit. This is disabled by default, because creating
|
||||
a lot of tags will slow down your git operations.
|
||||
Reference in New Issue
Block a user