Merge remote-tracking branch 'origin/master' into delete-orphans

This commit is contained in:
Matt Burke
2013-01-25 13:15:41 -05:00
55 changed files with 1574 additions and 344 deletions
+20 -1
View File
@@ -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
+3
View File
@@ -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>
+3
View File
@@ -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>
+37 -19
View File
@@ -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
}
}
}
}
+22 -7
View File
@@ -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();
+33
View File
@@ -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; }
}
}
}
+1
View File
@@ -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;
+21 -13
View File
@@ -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();
}
+2 -14
View File
@@ -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;
}
}
}
}
+5
View File
@@ -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
+9 -2
View File
@@ -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
{
+118
View File
@@ -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 -5
View File
@@ -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;
}
}
}
+59
View File
@@ -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
View File
@@ -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...)");
}
}
}
+7 -3
View File
@@ -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,
});
}
}
+19 -17
View File
@@ -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!");
+19 -1
View File
@@ -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]");
+3 -3
View File
@@ -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);
-4
View File
@@ -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; }
+1 -1
View File
@@ -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;
}
}
}
}
+10
View File
@@ -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
}
}
+12
View File
@@ -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
View File
@@ -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");
}
}
}
+55 -3
View File
@@ -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);
}
}
}
+11
View File
@@ -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);
}
}
-8
View File
@@ -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());
}
}
}
+5 -3
View File
@@ -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);
}
}
+2
View File
@@ -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);
}
}
+10
View File
@@ -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; }
}
}
+60
View File
@@ -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);
}
}
}
+26
View File
@@ -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; }
}
}
}
+10 -1
View File
@@ -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>();
}
}
}
+95
View File
@@ -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;
}
}
}
+4 -5
View File
@@ -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();
}
}
}
+1 -1
View File
@@ -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;
}
+6 -6
View File
@@ -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();
}
}
}
+10
View File
@@ -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
View File
@@ -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
View File
@@ -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>
-16
View File
@@ -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
View File
@@ -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);
+96 -25
View File
@@ -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);
}
}
}
+65
View File
@@ -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);
}
}
}
+7
View File
@@ -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" />
+56
View File
@@ -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);
}
}
}
+10 -10
View File
@@ -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");
}
}
}
+89
View File
@@ -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");
}
}
}
+109 -32
View File
@@ -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
+53
View File
@@ -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.