diff --git a/.gitignore b/.gitignore index a3a8ec3..e8fcf3a 100644 --- a/.gitignore +++ b/.gitignore @@ -106,3 +106,5 @@ $RECYCLE.BIN/ # ide temp *.sln.ide/ +/.vs +/src/.vs/AutoMerge/v15/Server/sqlite3 diff --git a/src/AutoMerge.Tests/AutoMerge.Tests.csproj b/src/AutoMerge.Tests/AutoMerge.Tests.csproj index 8869404..e1b7b79 100644 --- a/src/AutoMerge.Tests/AutoMerge.Tests.csproj +++ b/src/AutoMerge.Tests/AutoMerge.Tests.csproj @@ -14,6 +14,7 @@ v4.6.2 512 ea07ad34 + true @@ -70,6 +71,9 @@ AutoMerge + + + diff --git a/src/AutoMerge/AutoMerge.csproj b/src/AutoMerge/AutoMerge.csproj index af59e48..11c43e0 100644 --- a/src/AutoMerge/AutoMerge.csproj +++ b/src/AutoMerge/AutoMerge.csproj @@ -1,424 +1,448 @@ - + - - $(VisualStudioVersion) - 12.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - Program - $(DevEnvDir)\devenv.exe - /rootsuffix Exp - - - - - Debug - AnyCPU - 2.0 - {726ED85E-2274-4D95-B822-B2CFE2CE44B9} - {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Library - Properties - AutoMerge - AutoMerge - True - Key.snk - v4.5 - v4.6.2 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - true - - - - - ..\..\lib\System.Windows.Interactivity.WPF.2.0.20525\lib\net40\Microsoft.Expression.Interactions.dll - True - - - False - - - False - - - False - - - False - - - False - - - true - - - true - - - False - - - False - - - False - - - False - - - False - - - - - - - - - - - - - ..\..\lib\System.Windows.Interactivity.WPF.2.0.20525\lib\net40\System.Windows.Interactivity.dll - True - - - - - - - - - - - - - - - - - - - - False + + $(VisualStudioVersion) + 12.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + Program + $(DevEnvDir)\devenv.exe + /rootsuffix Exp + + + + + Debug + AnyCPU + 2.0 + {726ED85E-2274-4D95-B822-B2CFE2CE44B9} + {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + Properties + AutoMerge + AutoMerge + True + Key.snk + v4.5 + v4.6.2 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + + + + + ..\..\lib\System.Windows.Interactivity.WPF.2.0.20525\lib\net40\Microsoft.Expression.Interactions.dll + True - - False + + False - - False + + False - - False + + False - - False + + False - - False + + False - - - - - - C:\Program Files\Common Files\microsoft shared\Team Foundation Server\14.0\Microsoft.TeamFoundation.Client.dll - False + + true - - C:\Program Files\Common Files\microsoft shared\Team Foundation Server\14.0\Microsoft.TeamFoundation.Common.dll - False + + true - - C:\Program Files\Common Files\microsoft shared\Team Foundation Server\14.0\Microsoft.TeamFoundation.Controls.dll - False + + False - - C:\Program Files\Common Files\microsoft shared\Team Foundation Server\14.0\Microsoft.TeamFoundation.VersionControl.Client.dll - False + + False - - C:\Program Files\Common Files\microsoft shared\Team Foundation Server\14.0\Microsoft.TeamFoundation.VersionControl.Common.dll - False + + False - - C:\Program Files\Common Files\microsoft shared\Team Foundation Server\14.0\Microsoft.TeamFoundation.WorkItemTracking.Client.dll - False + + False - - - - - - - C:\Program Files\Common Files\microsoft shared\Team Foundation Server\15.0\Microsoft.TeamFoundation.Client.dll - False + + False - - C:\Program Files\Common Files\microsoft shared\Team Foundation Server\15.0\Microsoft.TeamFoundation.Common.dll - False + + + + + + + + + + + + ..\..\lib\System.Windows.Interactivity.WPF.2.0.20525\lib\net40\System.Windows.Interactivity.dll + True - - C:\Program Files\Common Files\microsoft shared\Team Foundation Server\15.0\Microsoft.TeamFoundation.Controls.dll - False + + + + + + + + + + + + + + + + + + + False + + + False + + + False + + + False + + + False + + + False + + + + + + + C:\Program Files\Common Files\microsoft shared\Team Foundation Server\14.0\Microsoft.TeamFoundation.Client.dll + False + + + C:\Program Files\Common Files\microsoft shared\Team Foundation Server\14.0\Microsoft.TeamFoundation.Common.dll + False + + + C:\Program Files\Common Files\microsoft shared\Team Foundation Server\14.0\Microsoft.TeamFoundation.Controls.dll + False + + + C:\Program Files\Common Files\microsoft shared\Team Foundation Server\14.0\Microsoft.TeamFoundation.VersionControl.Client.dll + False + + + C:\Program Files\Common Files\microsoft shared\Team Foundation Server\14.0\Microsoft.TeamFoundation.VersionControl.Common.dll + False + + + C:\Program Files\Common Files\microsoft shared\Team Foundation Server\14.0\Microsoft.TeamFoundation.WorkItemTracking.Client.dll + False + + + + + + + + C:\Program Files\Common Files\microsoft shared\Team Foundation Server\15.0\Microsoft.TeamFoundation.Client.dll + False + + + C:\Program Files\Common Files\microsoft shared\Team Foundation Server\15.0\Microsoft.TeamFoundation.Common.dll + False + + + C:\Program Files\Common Files\microsoft shared\Team Foundation Server\15.0\Microsoft.TeamFoundation.Controls.dll + False + + + C:\Program Files\Common Files\microsoft shared\Team Foundation Server\15.0\Microsoft.TeamFoundation.VersionControl.Client.dll + False + + + C:\Program Files\Common Files\microsoft shared\Team Foundation Server\15.0\Microsoft.TeamFoundation.VersionControl.Common.dll + False + + + C:\Program Files\Common Files\microsoft shared\Team Foundation Server\15.0\Microsoft.TeamFoundation.WorkItemTracking.Client.dll + False + + + + + + + True + ..\..\lib\EnvDTE.8.0.1\lib\net10\EnvDTE.dll + True - - C:\Program Files\Common Files\microsoft shared\Team Foundation Server\15.0\Microsoft.TeamFoundation.VersionControl.Client.dll - False + + True + ..\..\lib\EnvDTE100.10.0.1\lib\net20\EnvDTE100.dll + True - - C:\Program Files\Common Files\microsoft shared\Team Foundation Server\15.0\Microsoft.TeamFoundation.VersionControl.Common.dll - False + + True + ..\..\lib\EnvDTE80.8.0.1\lib\net10\EnvDTE80.dll + True - - C:\Program Files\Common Files\microsoft shared\Team Foundation Server\15.0\Microsoft.TeamFoundation.WorkItemTracking.Client.dll - False + + True + ..\..\lib\EnvDTE90.9.0.1\lib\net10\EnvDTE90.dll + True - - - - - - True - ..\..\lib\EnvDTE.8.0.1\lib\net10\EnvDTE.dll - True - - - True - ..\..\lib\EnvDTE100.10.0.1\lib\net20\EnvDTE100.dll - True - - - True - ..\..\lib\EnvDTE80.8.0.1\lib\net10\EnvDTE80.dll - True - - - True - ..\..\lib\EnvDTE90.9.0.1\lib\net10\EnvDTE90.dll - True - - - True - ..\..\lib\EnvDTE90a.9.0.1\lib\net10\EnvDTE90a.dll - True - - - {1CBA492E-7263-47BB-87FE-639000619B15} - 8 - 0 - 0 - primary - False - False - - - {00020430-0000-0000-C000-000000000046} - 2 - 0 - 0 - primary - False - False - - - - - - - - - - - - - - - - BranchesView.xaml - - - - - - - - - - - - - - - - - - - - - - - - - - - SplitButton.xaml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - RecentChangesetsView.xaml - - - - - True - True - Resources.resx - - - - - - - - - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - true - VSPackage - - - - - - - - LICENSE.txt - true - - - RELEASE_NOTES.md - true - - - Always - true - - - - Always - true - - - - - - Designer - - - - - - - - - - - - - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - - - true - - - - - - - - - - + + + + + + + + + + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + + + true + + + + + + + + + + - \ No newline at end of file + diff --git a/src/AutoMerge/AutoMergeTeamNavigationItem.cs b/src/AutoMerge/AutoMergeTeamNavigationItem.cs new file mode 100644 index 0000000..953d60d --- /dev/null +++ b/src/AutoMerge/AutoMergeTeamNavigationItem.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.Composition; +using AutoMerge.Base; +using AutoMerge.VersionControl; +using Microsoft.TeamFoundation.Controls; +using Microsoft.VisualStudio.Shell; + +namespace AutoMerge +{ + [TeamExplorerNavigationItem(GuidList.AutoMergeTeamNavigationItemId, 211, TargetPageId = GuidList.AutoMergeTeamPageId)] + class AutoMergeTeamNavigationItem : TeamExplorerNavigationItemBase + { + [ImportingConstructor] + public AutoMergeTeamNavigationItem( + [Import(typeof(SVsServiceProvider))] + IServiceProvider serviceProvider) + : base(serviceProvider, GuidList.AutoMergeTeamPageId, VersionControlProvider.TeamFoundation) + { + Text = Resources.AutoMergeTeamPageName; + Image = Resources.MergeImage; + } + } +} diff --git a/src/AutoMerge/AutoMergeTeamPage.cs b/src/AutoMerge/AutoMergeTeamPage.cs new file mode 100644 index 0000000..1e70b1f --- /dev/null +++ b/src/AutoMerge/AutoMergeTeamPage.cs @@ -0,0 +1,14 @@ +using AutoMerge.Base; +using Microsoft.TeamFoundation.Controls; + +namespace AutoMerge +{ + [TeamExplorerPage(GuidList.AutoMergeTeamPageId)] + public class AutoMergeTeamPage : TeamExplorerPageBase + { + public AutoMergeTeamPage() + { + Title = Resources.AutoMergeTeamPageName; + } + } +} diff --git a/src/AutoMerge/Base/TeamExplorerSectionViewModelBase.cs b/src/AutoMerge/Base/TeamExplorerSectionViewModelBase.cs index 5bfb91e..736c256 100644 --- a/src/AutoMerge/Base/TeamExplorerSectionViewModelBase.cs +++ b/src/AutoMerge/Base/TeamExplorerSectionViewModelBase.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using Microsoft.TeamFoundation.Client; using Microsoft.TeamFoundation.Common.Internal; @@ -33,11 +33,9 @@ protected virtual Task InitializeAsync(object sender, SectionInitializeEventArgs return _emptyTask; } - public async override void Initialize(object sender, SectionInitializeEventArgs e) + public override async void Initialize(object sender, SectionInitializeEventArgs e) { - ShowBusy(); - - try + await SetBusyWhileExecutingAsync(async () => { base.Initialize(sender, e); if (ServiceProvider != null) @@ -52,22 +50,24 @@ public async override void Initialize(object sender, SectionInitializeEventArgs } } await InitializeAsync(sender, e); - } - catch (Exception ex) - { - ShowError(ex.Message); - } + }); + } - HideBusy(); + public override async void Refresh() + { + await SetBusyWhileExecutingAsync(async () => + { + await RefreshAsync(); + }); } - public async override void Refresh() + protected async Task SetBusyWhileExecutingAsync(Func task) { ShowBusy(); try { - await RefreshAsync(); + await task(); } catch (Exception ex) { diff --git a/src/AutoMerge/Branches/Branch.cs b/src/AutoMerge/Branches/Branch.cs new file mode 100644 index 0000000..5b536ee --- /dev/null +++ b/src/AutoMerge/Branches/Branch.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace AutoMerge.Branches +{ + public class Branch + { + public string Name { get; set; } + + public List Branches { get; set; } + } +} diff --git a/src/AutoMerge/Branches/BranchesViewModel.cs b/src/AutoMerge/Branches/BranchesViewModel.cs index a80d6d5..8b12fde 100644 --- a/src/AutoMerge/Branches/BranchesViewModel.cs +++ b/src/AutoMerge/Branches/BranchesViewModel.cs @@ -177,16 +177,6 @@ public ObservableCollection MergeModes public DelegateCommand OpenSourceControlExplorerCommand { get; set; } - private static ObservableCollection GetWorkspaces(VersionControlServer versionControl, TfsTeamProjectCollection tfs) - { - var queryWorkspaces = versionControl.QueryWorkspaces(null, tfs.AuthorizedIdentity.UniqueName, Environment.MachineName); - if (queryWorkspaces.Length > 1) - { - return new ObservableCollection(queryWorkspaces.OrderBy(w => w.Name)); - } - return new ObservableCollection(queryWorkspaces); - } - protected async override Task InitializeAsync(object sender, SectionInitializeEventArgs e) { Logger.Debug("Start initilize branches section"); @@ -204,7 +194,7 @@ protected async override Task InitializeAsync(object sender, SectionInitializeEv if (e.Context == null) { - Workspaces = GetWorkspaces(versionControl, tfs); + Workspaces = WorkspaceHelper.GetWorkspaces(versionControl, tfs); if (Workspaces.Count > 0) { Workspace = WorkspaceHelper.GetWorkspace(versionControl, Workspaces); @@ -1252,7 +1242,7 @@ private void RefreshWorkspaces(object sender, WorkspaceEventArgs e) var tfs = Context.TeamProjectCollection; var versionControl = tfs.GetService(); - Workspaces = GetWorkspaces(versionControl, tfs); + Workspaces = WorkspaceHelper.GetWorkspaces(versionControl, tfs); if (Workspaces.Count > 0) { Workspace = Workspaces[0]; diff --git a/src/AutoMerge/RecentChangesets/ChangesetCommentConverter.cs b/src/AutoMerge/Changesets/ChangesetCommentConverter.cs similarity index 100% rename from src/AutoMerge/RecentChangesets/ChangesetCommentConverter.cs rename to src/AutoMerge/Changesets/ChangesetCommentConverter.cs diff --git a/src/AutoMerge/RecentChangesets/RecentChangesetFocusableControlNames.cs b/src/AutoMerge/Changesets/ChangesetFocusableControlNames.cs similarity index 72% rename from src/AutoMerge/RecentChangesets/RecentChangesetFocusableControlNames.cs rename to src/AutoMerge/Changesets/ChangesetFocusableControlNames.cs index 2f9b966..e445736 100644 --- a/src/AutoMerge/RecentChangesets/RecentChangesetFocusableControlNames.cs +++ b/src/AutoMerge/Changesets/ChangesetFocusableControlNames.cs @@ -1,9 +1,9 @@ -namespace AutoMerge +namespace AutoMerge { - public class RecentChangesetFocusableControlNames + public class ChangesetFocusableControlNames { public const string ChangesetIdTextBox = "changesetIdTextBox"; public const string AddChangesetByIdLink = "addChangesetByIdLink"; public const string ChangesetList = "changesetList"; } -} \ No newline at end of file +} diff --git a/src/AutoMerge/RecentChangesets/ChangesetViewModel.cs b/src/AutoMerge/Changesets/ChangesetViewModel.cs similarity index 100% rename from src/AutoMerge/RecentChangesets/ChangesetViewModel.cs rename to src/AutoMerge/Changesets/ChangesetViewModel.cs diff --git a/src/AutoMerge/Changesets/ChangesetsViewModel.cs b/src/AutoMerge/Changesets/ChangesetsViewModel.cs new file mode 100644 index 0000000..143e2b2 --- /dev/null +++ b/src/AutoMerge/Changesets/ChangesetsViewModel.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using AutoMerge.Events; +using AutoMerge.Prism.Command; +using AutoMerge.Prism.Events; +using Microsoft.TeamFoundation; +using Microsoft.TeamFoundation.Client; +using Microsoft.TeamFoundation.Controls; +using Microsoft.TeamFoundation.Controls.WPF.TeamExplorer; +using Task = System.Threading.Tasks.Task; + +using TeamExplorerSectionViewModelBase = AutoMerge.Base.TeamExplorerSectionViewModelBase; + +namespace AutoMerge +{ + public abstract class ChangesetsViewModel : TeamExplorerSectionViewModelBase + { + private readonly IEventAggregator _eventAggregator; + + protected ChangesetsViewModel(ILogger logger) + : base(logger) + { + Title = BaseTitle; + IsVisible = true; + IsExpanded = true; + IsBusy = false; + Changesets = new ObservableCollection(); + + _eventAggregator = EventAggregatorFactory.Get(); + _eventAggregator.GetEvent() + .Subscribe(OnMergeComplete); + + ViewChangesetDetailsCommand = new DelegateCommand(ViewChangesetDetailsExecute, ViewChangesetDetailsCanExecute); + } + + public ChangesetViewModel SelectedChangeset + { + get + { + return _selectedChangeset; + } + set + { + _selectedChangeset = value; + RaisePropertyChanged("SelectedChangeset"); + _eventAggregator.GetEvent().Publish(value); + } + } + private ChangesetViewModel _selectedChangeset; + + public ObservableCollection Changesets + { + get + { + return _changesets; + } + protected set + { + _changesets = value; + RaisePropertyChanged("Changesets"); + } + } + private ObservableCollection _changesets; + + + + public DelegateCommand ViewChangesetDetailsCommand { get; private set; } + + private void ViewChangesetDetailsExecute() + { + var changesetId = SelectedChangeset.ChangesetId; + TeamExplorerUtils.Instance.NavigateToPage(TeamExplorerPageIds.ChangesetDetails, ServiceProvider, changesetId); + } + + private bool ViewChangesetDetailsCanExecute() + { + return SelectedChangeset != null; + } + + private async void OnMergeComplete(bool obj) + { + await RefreshAsync(); + } + + protected override async Task InitializeAsync(object sender, SectionInitializeEventArgs e) + { + if (e.Context == null) + { + await RefreshAsync(); + } + else + { + RestoreContext(e); + } + } + + protected async Task GetChangesetAndUpdateTitleAsync() + { + Changesets.Clear(); + + Logger.Info("Getting changesets ..."); + var changesets = await GetChangesetsAsync(); + Logger.Info("Getting changesets end"); + + Changesets = new ObservableCollection(changesets); + UpdateTitle(); + + if (Changesets.Count > 0) + { + if (SelectedChangeset == null || SelectedChangeset.ChangesetId != Changesets[0].ChangesetId) + SelectedChangeset = Changesets[0]; + } + } + + public abstract Task> GetChangesetsAsync(); + + protected void UpdateTitle() + { + Title = Changesets.Count > 0 + ? string.Format("{0} ({1})", BaseTitle, Changesets.Count) + : BaseTitle; + } + + protected virtual void InvalidateCommands() + { + ViewChangesetDetailsCommand.RaiseCanExecuteChanged(); + } + + public override void Dispose() + { + base.Dispose(); + _eventAggregator.GetEvent().Unsubscribe(OnMergeComplete); + } + + protected abstract void RestoreContext(SectionInitializeEventArgs e); + + protected override void OnContextChanged(object sender, ContextChangedEventArgs e) + { + Refresh(); + } + + protected abstract string BaseTitle { get; } + } +} diff --git a/src/AutoMerge/Changesets/ChangesetsViewModelContext.cs b/src/AutoMerge/Changesets/ChangesetsViewModelContext.cs new file mode 100644 index 0000000..1b4fe88 --- /dev/null +++ b/src/AutoMerge/Changesets/ChangesetsViewModelContext.cs @@ -0,0 +1,14 @@ +using System.Collections.ObjectModel; + +namespace AutoMerge +{ + public class ChangesetsViewModelContext + { + public ObservableCollection Changesets { get; set; } + + public string Title { get; set; } + + public ChangesetViewModel SelectedChangeset { get; set; } + } + +} diff --git a/src/AutoMerge/Changesets/Providers/ChangesetByIdChangesetProvider.cs b/src/AutoMerge/Changesets/Providers/ChangesetByIdChangesetProvider.cs new file mode 100644 index 0000000..2d38b38 --- /dev/null +++ b/src/AutoMerge/Changesets/Providers/ChangesetByIdChangesetProvider.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AutoMerge +{ + public class ChangesetByIdChangesetProvider : ChangesetProviderBase + { + private readonly IEnumerable _changesetIds; + + public ChangesetByIdChangesetProvider(IServiceProvider serviceProvider, IEnumerable changesetIds) + : base(serviceProvider) + { + if (changesetIds == null) + throw new ArgumentNullException("changesetIds"); + + _changesetIds = changesetIds; + } + + protected override List GetChangesetsInternal() + { + var changesets = new List(); + var changesetService = GetChangesetService(); + if (changesetService != null) + { + changesets = _changesetIds + .Select(changesetService.GetChangeset) + .Where(c => c != null) + .Select(tfsChangeset => ToChangesetViewModel(tfsChangeset, changesetService)) + .ToList(); + } + + return changesets; + } + } +} diff --git a/src/AutoMerge/RecentChangesets/ChangesetProviderBase.cs b/src/AutoMerge/Changesets/Providers/ChangesetProviderBase.cs similarity index 79% rename from src/AutoMerge/RecentChangesets/ChangesetProviderBase.cs rename to src/AutoMerge/Changesets/Providers/ChangesetProviderBase.cs index 17627e5..fc8446c 100644 --- a/src/AutoMerge/RecentChangesets/ChangesetProviderBase.cs +++ b/src/AutoMerge/Changesets/Providers/ChangesetProviderBase.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -17,12 +17,12 @@ protected ChangesetProviderBase(IServiceProvider serviceProvider) _changesetService = new Lazy(InitChangesetService); } - public Task> GetChangesets(string userLogin) + public Task> GetChangesets() { - return Task.Run(() => GetChangesetsInternal(userLogin)); + return Task.Run(() => GetChangesetsInternal()); } - protected abstract List GetChangesetsInternal(string userLogin); + protected abstract List GetChangesetsInternal(); protected ChangesetViewModel ToChangesetViewModel(Changeset tfsChangeset, ChangesetService changesetService) { @@ -57,14 +57,9 @@ private ChangesetService InitChangesetService() return null; } - protected string GetProjectName() - { - var context = VersionControlNavigationHelper.GetTeamFoundationContext(_serviceProvider); - if (context != null) - { - return context.TeamProjectName; - } - return null; + protected IServiceProvider ServiceProvider() + { + return _serviceProvider; } } } diff --git a/src/AutoMerge/Changesets/Providers/IChangesetProvider.cs b/src/AutoMerge/Changesets/Providers/IChangesetProvider.cs new file mode 100644 index 0000000..e41a9c5 --- /dev/null +++ b/src/AutoMerge/Changesets/Providers/IChangesetProvider.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace AutoMerge +{ + public interface IChangesetProvider + { + Task> GetChangesets(); + } +} diff --git a/src/AutoMerge/RecentChangesets/MyChangesetChangesetProvider.cs b/src/AutoMerge/Changesets/Providers/MyChangesetChangesetProvider.cs similarity index 71% rename from src/AutoMerge/RecentChangesets/MyChangesetChangesetProvider.cs rename to src/AutoMerge/Changesets/Providers/MyChangesetChangesetProvider.cs index 733d923..ea3b9b1 100644 --- a/src/AutoMerge/RecentChangesets/MyChangesetChangesetProvider.cs +++ b/src/AutoMerge/Changesets/Providers/MyChangesetChangesetProvider.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; @@ -7,25 +7,27 @@ namespace AutoMerge public class MyChangesetChangesetProvider : ChangesetProviderBase { private readonly int _maxChangesetCount; + private readonly string _userLogin; - public MyChangesetChangesetProvider(IServiceProvider serviceProvider, int maxChangesetCount) + public MyChangesetChangesetProvider(IServiceProvider serviceProvider, int maxChangesetCount, string userLogin) : base(serviceProvider) { _maxChangesetCount = maxChangesetCount; + _userLogin = userLogin; } - protected override List GetChangesetsInternal(string userLogin) + protected override List GetChangesetsInternal() { var changesets = new List(); - if (!string.IsNullOrEmpty(userLogin)) + if (!string.IsNullOrEmpty(_userLogin)) { var changesetService = GetChangesetService(); if (changesetService != null) { - var projectName = GetProjectName(); - var tfsChangesets = changesetService.GetUserChangesets(projectName, userLogin, _maxChangesetCount); + var projectName = ProjectNameHelper.GetProjectName(ServiceProvider()); + var tfsChangesets = changesetService.GetUserChangesets(projectName, _userLogin, _maxChangesetCount); changesets = tfsChangesets .Select(tfsChangeset => ToChangesetViewModel(tfsChangeset, changesetService)) .ToList(); diff --git a/src/AutoMerge/Changesets/Providers/TeamChangesetChangesetProvider.cs b/src/AutoMerge/Changesets/Providers/TeamChangesetChangesetProvider.cs new file mode 100644 index 0000000..c896de4 --- /dev/null +++ b/src/AutoMerge/Changesets/Providers/TeamChangesetChangesetProvider.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using AutoMerge.Branches; +using Microsoft.TeamFoundation.VersionControl.Client; + + +namespace AutoMerge +{ + public class TeamChangesetChangesetProvider : ChangesetProviderBase + { + private string _sourceBranch; + private string _targetBranch; + + + public TeamChangesetChangesetProvider(IServiceProvider serviceProvider) : base(serviceProvider) + { + } + + public List ListProjects() + { + List result = new List(); + + var changesetService = GetChangesetService(); + + if (changesetService != null) + { + result.AddRange(changesetService.ListTfsProjects()); + } + + return result; + } + + public List ListBranches(string projectName) + { + var result = new List(); + + var changesetService = GetChangesetService(); + + if (changesetService != null) + { + var branches = changesetService.ListBranches(projectName); + + foreach (var branchObject in branches) + { + var branch = new Branch(); + + branch.Name = branchObject.Properties.RootItem.Item; + branch.Branches = branchObject.ChildBranches.Where(x => !x.IsDeleted).Select(x => x.Item).ToList(); + + if (branchObject.Properties.ParentBranch != null) + { + branch.Branches.Add(branchObject.Properties.ParentBranch.Item); + } + + result.Add(branch); + } + } + + return result; + + } + + public void SetSourceAndTargetBranch(string sourceBranch, string targetBranch) + { + _sourceBranch = sourceBranch; + _targetBranch = targetBranch; + } + + protected override List GetChangesetsInternal() + { + var changesets = new List(); + + var changesetService = GetChangesetService(); + + if (changesetService != null) + { + var tfsChangesets = changesetService.GetMergeCandidates(_sourceBranch, _targetBranch); + + changesets = tfsChangesets + .Select(tfsChangeset => ToChangesetViewModel(tfsChangeset, changesetService)) + .ToList(); + } + + return changesets; + } + + } +} diff --git a/src/AutoMerge/RecentChangesets/RecentChangesetsSection.cs b/src/AutoMerge/Changesets/RecentChangesetsSection.cs similarity index 75% rename from src/AutoMerge/RecentChangesets/RecentChangesetsSection.cs rename to src/AutoMerge/Changesets/RecentChangesetsSection.cs index 77b2d79..f0a6c58 100644 --- a/src/AutoMerge/RecentChangesets/RecentChangesetsSection.cs +++ b/src/AutoMerge/Changesets/RecentChangesetsSection.cs @@ -1,4 +1,5 @@ -using AutoMerge.Base; +using AutoMerge.Base; +using AutoMerge.RecentChangesets.Solo; using Microsoft.TeamFoundation.Controls; namespace AutoMerge @@ -8,14 +9,14 @@ public class RecentChangesetsSection : TeamExplorerSectionBase { protected override ITeamExplorerSection CreateViewModel(SectionInitializeEventArgs e) { - var viewModel = base.CreateViewModel(e) ?? new RecentChangesetsViewModel(new VsLogger(ServiceProvider)); + var viewModel = base.CreateViewModel(e) ?? new RecentChangesetsSoloViewModel(new VsLogger(ServiceProvider)); return viewModel; } protected override object CreateView(SectionInitializeEventArgs e) { - return new RecentChangesetsView(); + return new RecentChangesetsSoloView(); } } } diff --git a/src/AutoMerge/RecentChangesets/RecentChangesetsView.xaml b/src/AutoMerge/Changesets/Solo/RecentChangesetsSoloView.xaml similarity index 71% rename from src/AutoMerge/RecentChangesets/RecentChangesetsView.xaml rename to src/AutoMerge/Changesets/Solo/RecentChangesetsSoloView.xaml index 93a6afc..d4694b4 100644 --- a/src/AutoMerge/RecentChangesets/RecentChangesetsView.xaml +++ b/src/AutoMerge/Changesets/Solo/RecentChangesetsSoloView.xaml @@ -1,4 +1,4 @@ - + - - - - - - - - - - - - - - + diff --git a/src/AutoMerge/RecentChangesets/RecentChangesetsView.xaml.cs b/src/AutoMerge/Changesets/Solo/RecentChangesetsSoloView.xaml.cs similarity index 70% rename from src/AutoMerge/RecentChangesets/RecentChangesetsView.xaml.cs rename to src/AutoMerge/Changesets/Solo/RecentChangesetsSoloView.xaml.cs index 4fe095b..80ef617 100644 --- a/src/AutoMerge/RecentChangesets/RecentChangesetsView.xaml.cs +++ b/src/AutoMerge/Changesets/Solo/RecentChangesetsSoloView.xaml.cs @@ -1,4 +1,4 @@ -using System.Windows.Controls; +using System.Windows.Controls; using Microsoft.TeamFoundation.Controls.MVVM; namespace AutoMerge @@ -6,9 +6,9 @@ namespace AutoMerge /// /// Interaction logic for RecentChangesetsView.xaml /// - public partial class RecentChangesetsView : UserControl, IFocusService + public partial class RecentChangesetsSoloView : UserControl, IFocusService { - public RecentChangesetsView() + public RecentChangesetsSoloView() { InitializeComponent(); } @@ -17,14 +17,14 @@ public void SetFocus(string id, params object[] args) { switch (id) { - case RecentChangesetFocusableControlNames.AddChangesetByIdLink: + case ChangesetFocusableControlNames.AddChangesetByIdLink: addChangesetByIdLink.Focus(); break; - case RecentChangesetFocusableControlNames.ChangesetIdTextBox: + case ChangesetFocusableControlNames.ChangesetIdTextBox: changesetIdTextBox.FocusTextBox(); changesetIdTextBox.TextBoxControl.SelectionStart = changesetIdTextBox.TextBoxControl.Text.Length; break; - case RecentChangesetFocusableControlNames.ChangesetList: + case ChangesetFocusableControlNames.ChangesetList: if (changesetList.SelectedItem != null) { changesetList.UpdateLayout(); diff --git a/src/AutoMerge/RecentChangesets/RecentChangesetsViewModel.cs b/src/AutoMerge/Changesets/Solo/RecentChangesetsSoloViewModel.cs similarity index 54% rename from src/AutoMerge/RecentChangesets/RecentChangesetsViewModel.cs rename to src/AutoMerge/Changesets/Solo/RecentChangesetsSoloViewModel.cs index c5c5567..e1b5d8b 100644 --- a/src/AutoMerge/RecentChangesets/RecentChangesetsViewModel.cs +++ b/src/AutoMerge/Changesets/Solo/RecentChangesetsSoloViewModel.cs @@ -1,72 +1,24 @@ -using System; +using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using AutoMerge.Events; +using System.Threading.Tasks; using AutoMerge.Prism.Command; -using AutoMerge.Prism.Events; using Microsoft.TeamFoundation; -using Microsoft.TeamFoundation.Client; using Microsoft.TeamFoundation.Controls; -using Microsoft.TeamFoundation.Controls.WPF.TeamExplorer; -using Task = System.Threading.Tasks.Task; -using TeamExplorerSectionViewModelBase = AutoMerge.Base.TeamExplorerSectionViewModelBase; - -namespace AutoMerge +namespace AutoMerge.RecentChangesets.Solo { - public sealed class RecentChangesetsViewModel : TeamExplorerSectionViewModelBase + public class RecentChangesetsSoloViewModel : ChangesetsViewModel { - private readonly string _baseTitle; - private readonly IEventAggregator _eventAggregator; - - public RecentChangesetsViewModel(ILogger logger) - : base(logger) + public RecentChangesetsSoloViewModel(ILogger logger) : base(logger) { - Title = Resources.RecentChangesetSectionName; - IsVisible = true; - IsExpanded = true; - IsBusy = false; - Changesets = new ObservableCollection(); - _baseTitle = Title; - - _eventAggregator = EventAggregatorFactory.Get(); - _eventAggregator.GetEvent() - .Subscribe(OnMergeComplete); - - ViewChangesetDetailsCommand = new DelegateCommand(ViewChangesetDetailsExecute, ViewChangesetDetailsCanExecute); ToggleAddByIdCommand = new DelegateCommand(ToggleAddByIdExecute, ToggleAddByIdCanExecute); CancelAddChangesetByIdCommand = new DelegateCommand(CancelAddByIdExecute); AddChangesetByIdCommand = new DelegateCommand(AddChangesetByIdExecute, AddChangesetByIdCanExecute); } - public ChangesetViewModel SelectedChangeset - { - get - { - return _selectedChangeset; - } - set - { - _selectedChangeset = value; - RaisePropertyChanged("SelectedChangeset"); - _eventAggregator.GetEvent().Publish(value); - } - } - private ChangesetViewModel _selectedChangeset; - - public ObservableCollection Changesets - { - get - { - return _changesets; - } - private set - { - _changesets = value; - RaisePropertyChanged("Changesets"); - } - } - private ObservableCollection _changesets; + public DelegateCommand ToggleAddByIdCommand { get; private set; } + public DelegateCommand CancelAddChangesetByIdCommand { get; private set; } + public DelegateCommand AddChangesetByIdCommand { get; private set; } public bool ShowAddByIdChangeset { @@ -97,68 +49,12 @@ public string ChangesetIdsText } private string _changesetIdsText; - public DelegateCommand ViewChangesetDetailsCommand { get; private set; } - - public DelegateCommand ToggleAddByIdCommand { get; private set; } - - public DelegateCommand AddChangesetByIdCommand { get; private set; } - - public DelegateCommand CancelAddChangesetByIdCommand { get; private set; } - - private void ViewChangesetDetailsExecute() - { - var changesetId = SelectedChangeset.ChangesetId; - TeamExplorerUtils.Instance.NavigateToPage(TeamExplorerPageIds.ChangesetDetails, ServiceProvider, changesetId); - } - - private bool ViewChangesetDetailsCanExecute() - { - return SelectedChangeset != null; - } - - private async void OnMergeComplete(bool obj) - { - await RefreshAsync(); - } - - protected override async Task InitializeAsync(object sender, SectionInitializeEventArgs e) + public override async Task> GetChangesetsAsync() { - if (e.Context == null) - { - await RefreshAsync(); - } - else - { - RestoreContext(e); - } - } - - protected override async Task RefreshAsync() - { - Changesets.Clear(); - - var changesetProvider = new MyChangesetChangesetProvider(ServiceProvider, Settings.Instance.ChangesetCount); var userLogin = VersionControlNavigationHelper.GetAuthorizedUser(ServiceProvider); + var changesetProvider = new MyChangesetChangesetProvider(ServiceProvider, Settings.Instance.ChangesetCount, userLogin); - Logger.Info("Getting changesets ..."); - var changesets = await changesetProvider.GetChangesets(userLogin); - Logger.Info("Getting changesets end"); - - Changesets = new ObservableCollection(changesets); - UpdateTitle(); - - if (Changesets.Count > 0) - { - if (SelectedChangeset == null || SelectedChangeset.ChangesetId != Changesets[0].ChangesetId) - SelectedChangeset = Changesets[0]; - } - } - - private void UpdateTitle() - { - Title = Changesets.Count > 0 - ? string.Format("{0} ({1})", _baseTitle, Changesets.Count) - : _baseTitle; + return await changesetProvider.GetChangesets(); } private void ToggleAddByIdExecute() @@ -168,7 +64,7 @@ private void ToggleAddByIdExecute() ShowAddByIdChangeset = true; InvalidateCommands(); ResetAddById(); - SetMvvmFocus(RecentChangesetFocusableControlNames.ChangesetIdTextBox); + SetMvvmFocus(ChangesetFocusableControlNames.ChangesetIdTextBox); } catch (Exception ex) { @@ -188,7 +84,7 @@ private void CancelAddByIdExecute() { ShowAddByIdChangeset = false; InvalidateCommands(); - SetMvvmFocus(RecentChangesetFocusableControlNames.AddChangesetByIdLink); + SetMvvmFocus(ChangesetFocusableControlNames.AddChangesetByIdLink); ResetAddById(); } catch (Exception ex) @@ -202,6 +98,11 @@ private void ResetAddById() ChangesetIdsText = string.Empty; } + protected override async Task RefreshAsync() + { + await GetChangesetAndUpdateTitleAsync(); + } + private async void AddChangesetByIdExecute() { ShowBusy(); @@ -211,13 +112,13 @@ private async void AddChangesetByIdExecute() if (changesetIds.Count > 0) { var changesetProvider = new ChangesetByIdChangesetProvider(ServiceProvider, changesetIds); - var changesets = await changesetProvider.GetChangesets(null); + var changesets = await changesetProvider.GetChangesets(); if (changesets.Count > 0) { Changesets.Add(changesets[0]); SelectedChangeset = changesets[0]; - SetMvvmFocus(RecentChangesetFocusableControlNames.ChangesetList); + SetMvvmFocus(ChangesetFocusableControlNames.ChangesetList); UpdateTitle(); } ShowAddByIdChangeset = false; @@ -260,24 +161,20 @@ private static List GeChangesetIdsToAdd(string text) return list; } - private void InvalidateCommands() + protected override void InvalidateCommands() { - ViewChangesetDetailsCommand.RaiseCanExecuteChanged(); + base.InvalidateCommands(); + ToggleAddByIdCommand.RaiseCanExecuteChanged(); CancelAddChangesetByIdCommand.RaiseCanExecuteChanged(); AddChangesetByIdCommand.RaiseCanExecuteChanged(); } - public override void Dispose() - { - base.Dispose(); - _eventAggregator.GetEvent().Unsubscribe(OnMergeComplete); - } - public override void SaveContext(object sender, SectionSaveContextEventArgs e) { base.SaveContext(sender, e); - var context = new RecentChangesetsViewModelContext + + var context = new RecentChangesetsSoloViewModelContext { ChangesetIdsText = ChangesetIdsText, Changesets = Changesets, @@ -289,9 +186,10 @@ public override void SaveContext(object sender, SectionSaveContextEventArgs e) e.Context = context; } - private void RestoreContext(SectionInitializeEventArgs e) + protected override void RestoreContext(SectionInitializeEventArgs e) { - var context = (RecentChangesetsViewModelContext)e.Context; + var context = (RecentChangesetsSoloViewModelContext)e.Context; + ChangesetIdsText = context.ChangesetIdsText; Changesets = context.Changesets; SelectedChangeset = context.SelectedChangeset; @@ -299,9 +197,12 @@ private void RestoreContext(SectionInitializeEventArgs e) Title = context.Title; } - protected override void OnContextChanged(object sender, ContextChangedEventArgs e) + protected override string BaseTitle { - Refresh(); + get + { + return Resources.RecentChangesetSectionName; + } } } } diff --git a/src/AutoMerge/Changesets/Solo/RecentChangesetsSoloViewModelContext.cs b/src/AutoMerge/Changesets/Solo/RecentChangesetsSoloViewModelContext.cs new file mode 100644 index 0000000..6a7853b --- /dev/null +++ b/src/AutoMerge/Changesets/Solo/RecentChangesetsSoloViewModelContext.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AutoMerge +{ + public class RecentChangesetsSoloViewModelContext : ChangesetsViewModelContext + { + public bool ShowAddByIdChangeset { get; set; } + + public string ChangesetIdsText { get; set; } + } +} diff --git a/src/AutoMerge/Changesets/Team/TeamChangesetsView.xaml b/src/AutoMerge/Changesets/Team/TeamChangesetsView.xaml new file mode 100644 index 0000000..11bee45 --- /dev/null +++ b/src/AutoMerge/Changesets/Team/TeamChangesetsView.xaml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/AutoMerge/Changesets/Team/TeamChangesetsView.xaml.cs b/src/AutoMerge/Changesets/Team/TeamChangesetsView.xaml.cs new file mode 100644 index 0000000..97d2867 --- /dev/null +++ b/src/AutoMerge/Changesets/Team/TeamChangesetsView.xaml.cs @@ -0,0 +1,16 @@ +using System.Windows.Controls; +using Microsoft.TeamFoundation.Controls.MVVM; + +namespace AutoMerge +{ + /// + /// Interaction logic for TeamChangesetsView.xaml + /// + public partial class TeamChangesetsView : UserControl + { + public TeamChangesetsView() + { + InitializeComponent(); + } + } +} diff --git a/src/AutoMerge/Changesets/Team/TeamChangesetsViewModel.cs b/src/AutoMerge/Changesets/Team/TeamChangesetsViewModel.cs new file mode 100644 index 0000000..cfb2fe7 --- /dev/null +++ b/src/AutoMerge/Changesets/Team/TeamChangesetsViewModel.cs @@ -0,0 +1,225 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using AutoMerge.Branches; +using AutoMerge.Helpers; +using AutoMerge.Prism.Command; +using Microsoft.TeamFoundation.Controls; + +namespace AutoMerge +{ + public class TeamChangesetsViewModel : ChangesetsViewModel + { + private BranchTeamService _branchTeamService; + private TeamChangesetChangesetProvider _teamChangesetChangesetProvider; + private List _currentBranches; + + public TeamChangesetsViewModel(ILogger logger) : base(logger) + { + SelectedChangesets = new ObservableCollection(); + SourcesBranches = new ObservableCollection(); + TargetBranches = new ObservableCollection(); + ProjectNames = new ObservableCollection(); + + MergeCommand = DelegateCommand.FromAsyncHandler(MergeAsync, CanMerge); + FetchChangesetsCommand = DelegateCommand.FromAsyncHandler(FetchChangesetsAsync, CanFetchChangesets); + } + + public DelegateCommand MergeCommand { get; private set; } + public DelegateCommand FetchChangesetsCommand { get; private set; } + + public ObservableCollection ProjectNames { get; set; } + public ObservableCollection SourcesBranches { get; set; } + public ObservableCollection TargetBranches { get; set; } + + private string _selectedProjectName; + + public string SelectedProjectName + { + get { return _selectedProjectName; } + set + { + _selectedProjectName = value; + RaisePropertyChanged("SelectedProjectName"); + + _currentBranches = _teamChangesetChangesetProvider.ListBranches(SelectedProjectName); + + Changesets.Clear(); + SourcesBranches.Clear(); + TargetBranches.Clear(); + SourcesBranches.AddRange(_currentBranches.Select(x => x.Name)); + + UpdateTitle(); + } + } + + private string _sourceBranch; + + public string SourceBranch + { + get { return _sourceBranch; } + set + { + _sourceBranch = value; + RaisePropertyChanged("SourceBranch"); + InitializeTargetBranches(); + + FetchChangesetsCommand.RaiseCanExecuteChanged(); + } + } + + private string _targetBranch; + + public string TargetBranch + { + get { return _targetBranch; } + set + { + _targetBranch = value; + RaisePropertyChanged("TargetBranch"); + + FetchChangesetsCommand.RaiseCanExecuteChanged(); + } + } + + private ObservableCollection _selectedChangesets; + + public ObservableCollection SelectedChangesets + { + get { return _selectedChangesets; } + set + { + _selectedChangesets = value; + RaisePropertyChanged("SelectedChangesets"); + + if (_selectedChangesets != null) + { + _selectedChangesets.CollectionChanged += SelectedChangesets_CollectionChanged; + } + } + } + + private void SelectedChangesets_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + MergeCommand.RaiseCanExecuteChanged(); + } + + private async Task MergeAsync() + { + await SetBusyWhileExecutingAsync(async () => + { + var orderedSelectedChangesets = SelectedChangesets.OrderBy(x => x.ChangesetId).ToList(); + + await Task.Run(() => _branchTeamService.MergeBranches(SourceBranch, TargetBranch, orderedSelectedChangesets.First().ChangesetId, orderedSelectedChangesets.Last().ChangesetId)); + _branchTeamService.AddWorkItemsAndNavigate(orderedSelectedChangesets.Select(x => x.ChangesetId)); + }); + } + + private bool CanMerge() + { + return SelectedChangesets != null + && !IsBusy + && SelectedChangesets.Any() + && Changesets.Count(x => x.ChangesetId >= SelectedChangesets.Min(y => y.ChangesetId) && + x.ChangesetId <= SelectedChangesets.Max(y => y.ChangesetId)) == SelectedChangesets.Count; + } + + private async Task FetchChangesetsAsync() + { + await SetBusyWhileExecutingAsync(async () => await GetChangesetAndUpdateTitleAsync()); + + MergeCommand.RaiseCanExecuteChanged(); + } + + private bool CanFetchChangesets() + { + return SourceBranch != null && TargetBranch != null && !IsBusy; + } + + protected override Task InitializeAsync(object sender, SectionInitializeEventArgs e) + { + _branchTeamService = new BranchTeamService(Context.TeamProjectCollection, (ITeamExplorer)ServiceProvider.GetService(typeof(ITeamExplorer))); + _teamChangesetChangesetProvider = new TeamChangesetChangesetProvider(ServiceProvider); + + var projectNames = _teamChangesetChangesetProvider.ListProjects(); + projectNames.ForEach(x => ProjectNames.Add(x.Name)); + + return base.InitializeAsync(sender, e); + } + + public void InitializeTargetBranches() + { + TargetBranches.Clear(); + + if (SourceBranch != null) + { + TargetBranches.AddRange(_currentBranches.Single(x => x.Name == SourceBranch).Branches); + } + } + + public override async Task> GetChangesetsAsync() + { + _teamChangesetChangesetProvider.SetSourceAndTargetBranch(SourceBranch, TargetBranch); + return await _teamChangesetChangesetProvider.GetChangesets(); + } + + public override void SaveContext(object sender, SectionSaveContextEventArgs e) + { + base.SaveContext(sender, e); + + var context = new TeamChangesetsViewModelContext + { + SelectedProjectName = SelectedProjectName, + Changesets = Changesets, + Title = Title, + SourceBranch = SourceBranch, + TargetBranch = TargetBranch + }; + + e.Context = context; + } + + protected override void RestoreContext(SectionInitializeEventArgs e) + { + var context = (TeamChangesetsViewModelContext)e.Context; + + SelectedProjectName = context.SelectedProjectName; + Changesets = context.Changesets; + Title = context.Title; + SourceBranch = context.SourceBranch; + TargetBranch = context.TargetBranch; + } + + public override void Loaded(object sender, SectionLoadedEventArgs e) + { + base.Loaded(sender, e); + + //manually set to false because apparently HideBusy will set isBusy on false much later... + IsBusy = false; + + MergeCommand.RaiseCanExecuteChanged(); + FetchChangesetsCommand.RaiseCanExecuteChanged(); + } + + public override void Dispose() + { + base.Dispose(); + + if (_selectedChangesets != null) + { + _selectedChangesets.CollectionChanged -= SelectedChangesets_CollectionChanged; + } + } + + protected override string BaseTitle + { + get + { + return "Project name: " + SelectedProjectName; + } + } + + } +} diff --git a/src/AutoMerge/Changesets/Team/TeamChangesetsViewModelContext.cs b/src/AutoMerge/Changesets/Team/TeamChangesetsViewModelContext.cs new file mode 100644 index 0000000..8771ee3 --- /dev/null +++ b/src/AutoMerge/Changesets/Team/TeamChangesetsViewModelContext.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AutoMerge +{ + public class TeamChangesetsViewModelContext : ChangesetsViewModelContext + { + public string SourceBranch { get; set; } + + public string TargetBranch { get; set; } + + public string SelectedProjectName { get; set; } + } +} diff --git a/src/AutoMerge/Changesets/TeamChangesetsSection.cs b/src/AutoMerge/Changesets/TeamChangesetsSection.cs new file mode 100644 index 0000000..f0ca209 --- /dev/null +++ b/src/AutoMerge/Changesets/TeamChangesetsSection.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using AutoMerge.Base; +using Microsoft.TeamFoundation.Controls; +using Microsoft.VisualStudio.Shell; + +namespace AutoMerge.RecentChangesets +{ + [TeamExplorerSection(GuidList.TeamChangesetsSectionId, GuidList.AutoMergeTeamPageId, 11)] + public class TeamChangesetsSection : TeamExplorerSectionBase + { + protected override ITeamExplorerSection CreateViewModel(SectionInitializeEventArgs e) + { + return base.CreateViewModel(e) ?? new TeamChangesetsViewModel(new VsLogger(ServiceProvider)); + } + + protected override object CreateView(SectionInitializeEventArgs e) + { + return new TeamChangesetsView(); + } + } +} diff --git a/src/AutoMerge/Guids.cs b/src/AutoMerge/Guids.cs index 661cd47..297dae0 100644 --- a/src/AutoMerge/Guids.cs +++ b/src/AutoMerge/Guids.cs @@ -1,4 +1,4 @@ -// Guids.cs +// Guids.cs // MUST match guids.h using System; @@ -12,9 +12,12 @@ public static class GuidList public static readonly Guid guidAutoMergeCmdSet = new Guid(guidAutoMergeCmdSetString); public const string AutoMergeNavigationItemId = "02A9D8B3-287B-4C55-83E7-7BFDB435546D"; - public const string AutoMergePageId = "3B582638-5F12-4715-8719-5E5777AB4581"; - public const string RecentChangesetsSectionId = "8DA59790-3996-465E-A13F-27D64B3C2A9D"; + public const string AutoMergeTeamNavigationItemId = "3439f733-0177-4db4-b12b-85f19a4ac78a"; + public const string AutoMergePageId = "3B582638-5F12-4715-8719-5E5777AB4581"; + public const string AutoMergeTeamPageId = "246ccc66-d988-44d4-8d1e-e84ee846acd5"; + public const string RecentChangesetsSectionId = "8DA59790-3996-465E-A13F-27D64B3C2A9D"; + public const string TeamChangesetsSectionId = "b7dc6fbe-c3b1-47d8-805c-cce8c3dbedfb"; - public const string BranchesSectionId = "36BF6F52-F4AC-44A0-9985-817B2A65B3B0"; + public const string BranchesSectionId = "36BF6F52-F4AC-44A0-9985-817B2A65B3B0"; }; -} \ No newline at end of file +} diff --git a/src/AutoMerge/Helpers/CollectionExtensions.cs b/src/AutoMerge/Helpers/CollectionExtensions.cs new file mode 100644 index 0000000..eff17c1 --- /dev/null +++ b/src/AutoMerge/Helpers/CollectionExtensions.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace AutoMerge.Helpers +{ + public static class CollectionExtensions + { + public static void AddRange(this Collection list, IEnumerable itemsToAdd) + { + foreach(var itemToAdd in itemsToAdd) + { + list.Add(itemToAdd); + } + } + } +} diff --git a/src/AutoMerge/Helpers/ProjectNameHelper.cs b/src/AutoMerge/Helpers/ProjectNameHelper.cs new file mode 100644 index 0000000..e876ad2 --- /dev/null +++ b/src/AutoMerge/Helpers/ProjectNameHelper.cs @@ -0,0 +1,18 @@ +using System; + +namespace AutoMerge +{ + public static class ProjectNameHelper + { + public static string GetProjectName(IServiceProvider serviceProvider) + { + var context = VersionControlNavigationHelper.GetTeamFoundationContext(serviceProvider); + + if (context != null) + { + return context.TeamProjectName; + } + return null; + } + } +} diff --git a/src/AutoMerge/Helpers/WorkspaceHelper.cs b/src/AutoMerge/Helpers/WorkspaceHelper.cs index 4612c12..cf18cf7 100644 --- a/src/AutoMerge/Helpers/WorkspaceHelper.cs +++ b/src/AutoMerge/Helpers/WorkspaceHelper.cs @@ -1,5 +1,6 @@ -using System; +using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using Microsoft.TeamFoundation.Client; using Microsoft.TeamFoundation.VersionControl.Client; @@ -107,5 +108,15 @@ private static RegistryKey GetFeatureServerKey(string featurePath, string server } return null; } + + public static ObservableCollection GetWorkspaces(VersionControlServer versionControl, TfsTeamProjectCollection tfs) + { + var queryWorkspaces = versionControl.QueryWorkspaces(null, tfs.AuthorizedIdentity.UniqueName, Environment.MachineName); + if (queryWorkspaces.Length > 1) + { + return new ObservableCollection(queryWorkspaces.OrderBy(w => w.Name)); + } + return new ObservableCollection(queryWorkspaces); + } } } diff --git a/src/AutoMerge/RecentChangesets/ChangesetByIdChangesetProvider.cs b/src/AutoMerge/RecentChangesets/ChangesetByIdChangesetProvider.cs deleted file mode 100644 index e8d65f2..0000000 --- a/src/AutoMerge/RecentChangesets/ChangesetByIdChangesetProvider.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace AutoMerge -{ - public class ChangesetByIdChangesetProvider : ChangesetProviderBase - { - private readonly IEnumerable _changesetIds; - - public ChangesetByIdChangesetProvider(IServiceProvider serviceProvider, IEnumerable changesetIds) - : base(serviceProvider) - { - if (changesetIds == null) - throw new ArgumentNullException("changesetIds"); - - _changesetIds = changesetIds; - } - - protected override List GetChangesetsInternal(string userLogin) - { - var changesets = new List(); - var changesetService = GetChangesetService(); - if (changesetService != null) - { - changesets = _changesetIds - .Select(changesetService.GetChangeset) - .Where(c => c != null) - .Select(tfsChangeset => ToChangesetViewModel(tfsChangeset, changesetService)) - .ToList(); - } - - return changesets; - } - } -} \ No newline at end of file diff --git a/src/AutoMerge/RecentChangesets/IChangesetProvider.cs b/src/AutoMerge/RecentChangesets/IChangesetProvider.cs deleted file mode 100644 index e140326..0000000 --- a/src/AutoMerge/RecentChangesets/IChangesetProvider.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace AutoMerge -{ - public interface IChangesetProvider - { - Task> GetChangesets(string userLogin); - } -} \ No newline at end of file diff --git a/src/AutoMerge/RecentChangesets/RecentChangesetsViewModelContext.cs b/src/AutoMerge/RecentChangesets/RecentChangesetsViewModelContext.cs deleted file mode 100644 index 06ba726..0000000 --- a/src/AutoMerge/RecentChangesets/RecentChangesetsViewModelContext.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.ObjectModel; - -namespace AutoMerge -{ - public class RecentChangesetsViewModelContext - { - public ChangesetViewModel SelectedChangeset { get; set; } - - public ObservableCollection Changesets { get; set; } - - public bool ShowAddByIdChangeset { get; set; } - - public string ChangesetIdsText { get; set; } - - public string Title { get; set; } - } -} diff --git a/src/AutoMerge/Resources.Designer.cs b/src/AutoMerge/Resources.Designer.cs index c2905fa..16c14ba 100644 --- a/src/AutoMerge/Resources.Designer.cs +++ b/src/AutoMerge/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace AutoMerge { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -69,6 +69,15 @@ internal static string AutoMergePageName { } } + /// + /// Looks up a localized string similar to Auto Merge (Team). + /// + internal static string AutoMergeTeamPageName { + get { + return ResourceManager.GetString("AutoMergeTeamPageName", resourceCulture); + } + } + /// /// Looks up a localized string similar to Target branches. /// diff --git a/src/AutoMerge/Resources.resx b/src/AutoMerge/Resources.resx index 27b993e..6534a6f 100644 --- a/src/AutoMerge/Resources.resx +++ b/src/AutoMerge/Resources.resx @@ -1,17 +1,17 @@  - @@ -130,4 +130,7 @@ Resources\merge.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - + + Auto Merge (Team) + + \ No newline at end of file diff --git a/src/AutoMerge/Services/BranchTeamService.cs b/src/AutoMerge/Services/BranchTeamService.cs new file mode 100644 index 0000000..7b150ed --- /dev/null +++ b/src/AutoMerge/Services/BranchTeamService.cs @@ -0,0 +1,54 @@ +using Microsoft.TeamFoundation.Client; +using Microsoft.TeamFoundation.Controls; +using Microsoft.TeamFoundation.Controls.WPF.TeamExplorer; +using Microsoft.TeamFoundation.VersionControl.Client; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace AutoMerge +{ + public class BranchTeamService + { + private readonly Workspace _workspace; + private readonly ITeamExplorer _teamExplorer; + private readonly ChangesetService _changesetService; + + public BranchTeamService(TfsTeamProjectCollection tfs, ITeamExplorer teamExplorer) + { + var versionControlServer = tfs.GetService(); + + _workspace = WorkspaceHelper.GetWorkspace(versionControlServer, WorkspaceHelper.GetWorkspaces(versionControlServer, tfs)); + _teamExplorer = teamExplorer; + _changesetService = new ChangesetService(versionControlServer); + } + + public void MergeBranches(string source, string target, int from, int to) + { + _workspace.Merge(source, target, new ChangesetVersionSpec(from), new ChangesetVersionSpec(to), LockLevel.None, RecursionType.Full, MergeOptions.None); + } + + public void AddWorkItemsAndNavigate(IEnumerable changesetIds) + { + var workItemIds = new List(); + + foreach(var changesetId in changesetIds) + { + var changeSet = _changesetService.GetChangeset(changesetId); + + if (changeSet != null && changeSet.AssociatedWorkItems != null) + { + workItemIds.AddRange(changeSet.AssociatedWorkItems.Select(x => x.Id)); + } + } + + var pendingChangePage = (TeamExplorerPageBase) _teamExplorer.NavigateToPage(new Guid(TeamExplorerPageIds.PendingChanges), null); + var pendingChangeModel = (IPendingCheckin) pendingChangePage.Model; + + var modelType = pendingChangeModel.GetType(); + var method = modelType.GetMethod("AddWorkItemsByIdAsync", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); + method.Invoke(pendingChangeModel, new object[] { workItemIds.ToArray(), 1 }); + } + } +} diff --git a/src/AutoMerge/Services/ChangesetService.cs b/src/AutoMerge/Services/ChangesetService.cs index 612c2a7..81474f3 100644 --- a/src/AutoMerge/Services/ChangesetService.cs +++ b/src/AutoMerge/Services/ChangesetService.cs @@ -1,52 +1,95 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Microsoft.TeamFoundation.VersionControl.Client; namespace AutoMerge { - public class ChangesetService - { - private readonly VersionControlServer _versionControlServer; - - public ChangesetService(VersionControlServer versionControlServer) - { - _versionControlServer = versionControlServer; - } - - public ICollection GetUserChangesets(string teamProjectName, string userName, int count) - { - var path = "$/" + teamProjectName; - return _versionControlServer.QueryHistory(path, - VersionSpec.Latest, - 0, - RecursionType.Full, - userName, - null, - null, - count, - false, - true) - .Cast() - .ToList(); - } - - public Changeset GetChangeset(int changesetId) - { - var changeset = _versionControlServer.GetChangeset(changesetId, false, false); - - return changeset; - } - - public Change[] GetChanges(int changesetId) - { - return _versionControlServer.GetChangesForChangeset(changesetId, false, int.MaxValue, null, null, null, true); - } - - public List GetAssociatedBranches(params int[] changesetId) - { - var branches = _versionControlServer.QueryBranchObjectOwnership(changesetId); - - return branches.Select(b => b.RootItem).ToList(); - } - } + public class ChangesetService + { + private readonly VersionControlServer _versionControlServer; + + public ChangesetService(VersionControlServer versionControlServer) + { + _versionControlServer = versionControlServer; + } + + public ICollection GetUserChangesets(string teamProjectName, string userName, int count) + { + var path = "$/" + teamProjectName; + return _versionControlServer.QueryHistory(path, + VersionSpec.Latest, + 0, + RecursionType.Full, + userName, + null, + null, + count, + false, + true) + .Cast() + .ToList(); + } + + public Changeset GetChangeset(int changesetId) + { + var changeset = _versionControlServer.GetChangeset(changesetId, false, false); + + return changeset; + } + + + public Change[] GetChanges(int changesetId) + { + return _versionControlServer.GetChangesForChangeset(changesetId, false, int.MaxValue, null, null, null, true); + } + + public List GetAssociatedBranches(params int[] changesetId) + { + var branches = _versionControlServer.QueryBranchObjectOwnership(changesetId); + + return branches.Select(b => b.RootItem).ToList(); + } + + + public ICollection GetMergeCandidates(string sourceBranch, string targetBranch) + { + var dummy = _versionControlServer.GetMergeCandidates(sourceBranch, targetBranch, RecursionType.Full); + + List result = new List(); + + foreach (MergeCandidate mc in dummy) + { + result.Add(mc.Changeset); + } + + return result; + } + + public IEnumerable ListTfsProjects() + { + var result = _versionControlServer.GetAllTeamProjects(true); + return result; + } + + + public IEnumerable ListBranches(string projectName) + { + var dummy = _versionControlServer.QueryRootBranchObjects(RecursionType.Full); + + var result = new List(); + foreach(var bo in dummy) + { + var ro = bo.Properties.RootItem; + + System.Diagnostics.Debug.WriteLine(ro.Item); + + if (!ro.IsDeleted && ro.Item.Replace(@"$/", "").StartsWith(projectName + @"/")) + result.Add(bo); + } + + return result; + } + + + } } diff --git a/src/AutoMerge/Styles/RecentChangesetResource.xaml b/src/AutoMerge/Styles/RecentChangesetResource.xaml new file mode 100644 index 0000000..a220fde --- /dev/null +++ b/src/AutoMerge/Styles/RecentChangesetResource.xaml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/AutoMerge/UI/AutoMergeListBox.cs b/src/AutoMerge/UI/AutoMergeListBox.cs new file mode 100644 index 0000000..c60df71 --- /dev/null +++ b/src/AutoMerge/UI/AutoMergeListBox.cs @@ -0,0 +1,40 @@ +using System.Collections.ObjectModel; +using System.Linq; +using System.Windows; +using System.Windows.Controls; + +namespace AutoMerge +{ + public class AutoMergeListBox : ListBox + { + public AutoMergeListBox() + { + SelectionChanged += AutoMergeListBox_SelectionChanged; + } + + private void AutoMergeListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (SelectedItemsList != null) + { + foreach (var removedItem in e.RemovedItems.Cast()) + { + SelectedItemsList.Remove(removedItem); + } + + foreach (var addItem in e.AddedItems.Cast()) + { + SelectedItemsList.Add(addItem); + } + } + } + + public ObservableCollection SelectedItemsList + { + get { return (ObservableCollection)GetValue(SelectedItemsListProperty); } + set { SetValue(SelectedItemsListProperty, value); } + } + + public static readonly DependencyProperty SelectedItemsListProperty = + DependencyProperty.Register("SelectedItemsList", typeof(ObservableCollection), typeof(AutoMergeListBox), new PropertyMetadata(null)); + } +}