diff --git a/GitTfs.VsCommon/TfsHelper.Common.cs b/GitTfs.VsCommon/TfsHelper.Common.cs index fd71e267..7d48e995 100644 --- a/GitTfs.VsCommon/TfsHelper.Common.cs +++ b/GitTfs.VsCommon/TfsHelper.Common.cs @@ -195,10 +195,11 @@ namespace Sep.Git.Tfs.VsCommon return tfsChangeset; } - public void WithWorkspace(string localDirectory, IGitTfsRemote remote, TfsChangesetInfo versionToFetch, Action action) + public void WithWorkspace(string localDirectory, IGitTfsRemote remote, IEnumerable> mappings, TfsChangesetInfo versionToFetch, Action action) { - Trace.WriteLine("Setting up a TFS workspace at " + localDirectory); - var workspace = GetWorkspace(localDirectory, remote.TfsRepositoryPath); + Trace.WriteLine("Setting up a TFS workspace for multiple remotes"); + var folders = mappings.Select(x => new WorkingFolder(Path.Combine(localDirectory, x.Item1), x.Item2)).ToArray(); + var workspace = GetWorkspace(folders); try { var tfsWorkspace = _container.With("localDirectory").EqualTo(localDirectory) @@ -215,12 +216,33 @@ namespace Sep.Git.Tfs.VsCommon } } - private Workspace GetWorkspace(string localDirectory, string repositoryPath) + public void WithWorkspace(string localDirectory, IGitTfsRemote remote, TfsChangesetInfo versionToFetch, Action action) + { + Trace.WriteLine("Setting up a TFS workspace at " + localDirectory); + var workspace = GetWorkspace(new WorkingFolder(localDirectory, remote.TfsRepositoryPath)); + try + { + var tfsWorkspace = _container.With("localDirectory").EqualTo(localDirectory) + .With("remote").EqualTo(remote) + .With("contextVersion").EqualTo(versionToFetch) + .With("workspace").EqualTo(_bridge.Wrap(workspace)) + .With("tfsHelper").EqualTo(this) + .GetInstance(); + action(tfsWorkspace); + } + finally + { + workspace.Delete(); + } + } + + private Workspace GetWorkspace(params WorkingFolder[] folders) { var workspace = VersionControl.CreateWorkspace(GenerateWorkspaceName()); try { - workspace.CreateMapping(new WorkingFolder(repositoryPath, localDirectory)); + foreach(WorkingFolder folder in folders) + workspace.CreateMapping(folder); } catch (MappingConflictException e) { diff --git a/GitTfs/Commands/Subtree.cs b/GitTfs/Commands/Subtree.cs index 014b3699..b9ef8fda 100644 --- a/GitTfs/Commands/Subtree.cs +++ b/GitTfs/Commands/Subtree.cs @@ -90,26 +90,48 @@ namespace Sep.Git.Tfs.Commands var fetch = Squash ? this._quickFetch : this._fetch; - string remoteId = "subtree/" + Prefix; - IGitTfsRemote remote; - if (_globals.Repository.HasRemote(remoteId)) - { - remote = _globals.Repository.ReadTfsRemote(remoteId); - } - else - { - //create a remote for the new subtree - remote = _globals.Repository.CreateTfsRemote(new RemoteInfo - { - Id = remoteId, - Url = tfsUrl, - Repository = tfsRepositoryPath, - RemoteOptions = _remoteOptions, - }); - _stdout.WriteLine("-> new remote " + remote.Id); - } + //create a remote for the new subtree + string remoteId = "subtree/" + Prefix; + IGitTfsRemote remote = _globals.Repository.CreateTfsRemote(new RemoteInfo + { + Id = remoteId, + Url = tfsUrl, + Repository = tfsRepositoryPath, + RemoteOptions = _remoteOptions, + }); + _stdout.WriteLine("-> new remote " + remote.Id); + var tfsUri = new Uri(tfsUrl); + IGitTfsRemote owner = _globals.Repository.ReadAllTfsRemotes().FirstOrDefault(x => !x.Id.StartsWith("subtree/") && tfsUri.Equals(x.TfsUrl)); + if (owner == null) + { + try + { + owner = _globals.Repository.CreateTfsRemote(new RemoteInfo + { + Id = "origin", + Url = tfsUrl, + Repository = "", + RemoteOptions = _remoteOptions + }); + } + catch (Exception) + { + //need to clean up our subtree + try + { + _globals.Repository.DeleteTfsRemote(remote); + } + catch + { + + } + + throw; + } + } + int result = fetch.Run(remote.Id); if (result == GitTfsExitCodes.OK) diff --git a/GitTfs/Core/GitRepository.cs b/GitTfs/Core/GitRepository.cs index 8a9ff86a..c4c5506d 100644 --- a/GitTfs/Core/GitRepository.cs +++ b/GitTfs/Core/GitRepository.cs @@ -475,5 +475,19 @@ namespace Sep.Git.Tfs.Core { _repository.Reset(resetOptions, sha); } + + /// + /// Gets all configured "subtree" remotes which point to the same Tfs URL as the given remote. + /// If the given remote is itself a subtree, an empty enumerable is returned. + /// + public IEnumerable GetSubtrees(IGitTfsRemote owner) + { + //a subtree remote cannot have subtrees itself. + if (owner.Id.StartsWith("subtree/")) + return Enumerable.Empty(); + + var uri = new Uri(owner.TfsUrl); + return ReadAllTfsRemotes().Where(x => x.Id.StartsWith("subtree/") && uri.Equals(x.TfsUrl)); + } } } diff --git a/GitTfs/Core/GitTfsRemote.cs b/GitTfs/Core/GitTfsRemote.cs index b7eca3c3..3e811e69 100644 --- a/GitTfs/Core/GitTfsRemote.cs +++ b/GitTfs/Core/GitTfsRemote.cs @@ -546,7 +546,16 @@ namespace Sep.Git.Tfs.Core // with it. Tfs.CleanupWorkspaces(DefaultWorkingDirectory); - Tfs.WithWorkspace(WorkingDirectory, this, parentChangeset, action); + //are there any subtrees? + var subtrees = globals.Repository.GetSubtrees(this); + if (subtrees.Any()) + { + Tfs.WithWorkspace(WorkingDirectory, this, subtrees.Select(x => new Tuple(x.Id.Substring("subtree/".Length), x.TfsRepositoryPath)), parentChangeset, action); + } + else + { + Tfs.WithWorkspace(WorkingDirectory, this, parentChangeset, action); + } } private long Checkin(string head, string parent, ITfsWorkspace workspace, CheckinOptions options) diff --git a/GitTfs/Core/IGitRepository.cs b/GitTfs/Core/IGitRepository.cs index f5b74917..a4919af9 100644 --- a/GitTfs/Core/IGitRepository.cs +++ b/GitTfs/Core/IGitRepository.cs @@ -38,5 +38,10 @@ namespace Sep.Git.Tfs.Core void CreateNote(string sha, string content, string owner, string emailOwner, DateTime creationDate); void MoveRemote(string oldRemoteName, string newRemoteName); void Reset(string sha, ResetOptions resetOptions); + /// + /// Gets all configured "subtree" remotes which point to the same Tfs URL as the given remote. + /// If the given remote is itself a subtree, an empty enumerable is returned. + /// + IEnumerable GetSubtrees(IGitTfsRemote owner); } } diff --git a/GitTfs/Core/TfsInterop/ITfsHelper.cs b/GitTfs/Core/TfsInterop/ITfsHelper.cs index 8ed4434a..5464b089 100644 --- a/GitTfs/Core/TfsInterop/ITfsHelper.cs +++ b/GitTfs/Core/TfsInterop/ITfsHelper.cs @@ -33,5 +33,16 @@ namespace Sep.Git.Tfs.Core.TfsInterop IEnumerable GetBranches(); void EnsureAuthenticated(); void CreateBranch(string sourcePath, string targetPath, int changesetId, string comment = null); + + /// + /// Creates and maps a workspace for the given remote with the given local -> server directory mappings, at the given Tfs version, + /// and then performs the action. + /// + /// The local base directory containing all the mappings + /// The owning remote + /// The workspace mappings to create. Item1 is the relative path from the localDirectory, and Item2 is the TfsRepositoryPath + /// The TFS version to fetch from the server + /// The action to perform + void WithWorkspace(string localDirectory, IGitTfsRemote remote, IEnumerable> mappings, TfsChangesetInfo versionToFetch, Action action); } }