├── .gitignore
├── LICENSE.txt
├── README.md
├── RELEASE_NOTES.md
├── azure-pipelines.yml
├── build
└── build.fsx
├── build_v15.bat
├── build_v16.bat
├── build_v17.bat
├── lib
├── 15.0
│ ├── Microsoft.TeamFoundation.Client.dll
│ ├── Microsoft.TeamFoundation.Common.dll
│ ├── Microsoft.TeamFoundation.Controls.dll
│ ├── Microsoft.TeamFoundation.VersionControl.Client.dll
│ ├── Microsoft.TeamFoundation.VersionControl.Common.dll
│ └── Microsoft.TeamFoundation.WorkItemTracking.Client.dll
├── 16.0
│ ├── Microsoft.TeamFoundation.Client.dll
│ ├── Microsoft.TeamFoundation.Common.dll
│ ├── Microsoft.TeamFoundation.Controls.dll
│ ├── Microsoft.TeamFoundation.VersionControl.Client.dll
│ ├── Microsoft.TeamFoundation.VersionControl.Common.dll
│ └── Microsoft.TeamFoundation.WorkItemTracking.Client.dll
└── 17.0
│ ├── Microsoft.TeamFoundation.Client.dll
│ ├── Microsoft.TeamFoundation.Common.dll
│ ├── Microsoft.TeamFoundation.Controls.dll
│ ├── Microsoft.TeamFoundation.VersionControl.Client.dll
│ ├── Microsoft.TeamFoundation.VersionControl.Common.dll
│ └── Microsoft.TeamFoundation.WorkItemTracking.Client.dll
├── screenshots
├── automerge_main.png
└── automerge_teamexplorer.png
├── src
├── .editorconfig
├── AutoMerge.Tests
│ ├── AutoMerge.Tests.csproj
│ └── JsonParserTests.cs
├── AutoMerge.sln
└── AutoMerge
│ ├── AutoMerge.csproj
│ ├── AutoMergeNavigationItem.cs
│ ├── AutoMergePackage.cs
│ ├── AutoMergePage.cs
│ ├── Base
│ ├── TeamExplorerBaseNavigationLink.cs
│ ├── TeamExplorerNavigationItemBase.cs
│ ├── TeamExplorerPageBase.cs
│ ├── TeamExplorerSectionBase.cs
│ └── TeamExplorerSectionViewModelBase.cs
│ ├── Behaviours
│ ├── GridViewColumnResize.cs
│ └── ScrollDeligateBehavior.cs
│ ├── Branches
│ ├── BranchFactory.cs
│ ├── BranchValidationResult.cs
│ ├── BranchValidator.cs
│ ├── BranchesSection.cs
│ ├── BranchesView.xaml
│ ├── BranchesView.xaml.cs
│ ├── BranchesViewModel.cs
│ ├── BranchesViewModelContext.cs
│ ├── CheckInResult.cs
│ ├── EscapeMnemonicConverter.cs
│ ├── FileMergeInfo.cs
│ ├── MergeInfoViewModel.cs
│ ├── MergeMode.cs
│ ├── MergeModeToStringConverter.cs
│ ├── MergeOption.cs
│ ├── MergeOptionToCheckedConverter.cs
│ ├── MergeOptionToStringConverter.cs
│ ├── MergeRelation.cs
│ ├── MergeResult.cs
│ ├── MergeResultModel.cs
│ ├── Notification.cs
│ └── TrackMergeInfo.cs
│ ├── Commands
│ └── ShowAutoMergeWindow.cs
│ ├── CommentFormater.cs
│ ├── Configuration
│ ├── CommentFormat.cs
│ ├── FileSettingProvider.cs
│ ├── ISettingProvider.cs
│ ├── JsonParser.cs
│ └── Settings.cs
│ ├── Controls
│ ├── SplitButton.xaml
│ └── SplitButton.xaml.cs
│ ├── Events
│ ├── BranchSelectedChangedEvent.cs
│ ├── EventAggregatorFactory.cs
│ ├── MergeCompleteEvent.cs
│ └── SelectChangesetEvent.cs
│ ├── GlobalSuppressions.cs
│ ├── Guids.cs
│ ├── Helpers
│ ├── BranchHelper.cs
│ ├── EnumerableExtensions.cs
│ ├── VersionControlNavigationHelper.cs
│ └── WorkspaceHelper.cs
│ ├── Key.snk
│ ├── Prism
│ ├── BindableBase.cs
│ ├── Command
│ │ ├── DelegateCommand.cs
│ │ ├── DelegateCommandBase .cs
│ │ ├── IActiveAware.cs
│ │ └── WeakEventHandlerManager.cs
│ ├── Events
│ │ ├── BackgroundEventSubscription.cs
│ │ ├── DelegateReference.cs
│ │ ├── DispatcherEventSubscription.cs
│ │ ├── EventAggregator.cs
│ │ ├── EventBase.cs
│ │ ├── EventSubscription.cs
│ │ ├── IDelegateReference.cs
│ │ ├── IEventAggregator.cs
│ │ ├── IEventSubscription.cs
│ │ ├── PubSubEvent.cs
│ │ ├── SubscriptionToken.cs
│ │ └── ThreadOption.cs
│ └── PropertySupport.cs
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── RecentChangesets
│ ├── ChangesetByIdChangesetProvider.cs
│ ├── ChangesetCommentConverter.cs
│ ├── ChangesetProviderBase.cs
│ ├── ChangesetViewModel.cs
│ ├── IChangesetProvider.cs
│ ├── MyChangesetChangesetProvider.cs
│ ├── RecentChangesetFocusableControlNames.cs
│ ├── RecentChangesetsSection.cs
│ ├── RecentChangesetsView.xaml
│ ├── RecentChangesetsView.xaml.cs
│ ├── RecentChangesetsViewModel.cs
│ └── RecentChangesetsViewModelContext.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Resources
│ ├── Package.ico
│ ├── merge.png
│ └── preview.png
│ ├── Services
│ ├── ChangesetService.cs
│ ├── ILogger.cs
│ ├── LoggerBase.cs
│ └── VsLogger.cs
│ ├── Styles
│ └── CommonStyles.xaml
│ ├── VSCommandTable.vsct
│ ├── VSPackage.resx
│ ├── VersionControl
│ └── VersionControlProvider.cs
│ ├── source.extension.vsixmanifest
│ ├── source.vs15.0.extension.vsixmanifest
│ ├── source.vs16.0.extension.vsixmanifest
│ └── source.vs17.0.extension.vsixmanifest
└── tools
└── NuGet
├── LICENSE.txt
└── NuGet.exe
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | .fake
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.sln.docstates
10 |
11 | # Build results
12 |
13 | [Dd]ebug/
14 | [Rr]elease/
15 | build/
16 | [Bb]in/
17 | [Oo]bj/
18 |
19 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
20 | !packages/*/build/
21 |
22 | # MSTest test Results
23 | [Tt]est[Rr]esult*/
24 | [Bb]uild[Ll]og.*
25 |
26 | *_i.c
27 | *_p.c
28 | *.ilk
29 | *.meta
30 | *.obj
31 | *.pch
32 | *.pdb
33 | *.pgc
34 | *.pgd
35 | *.rsp
36 | *.sbr
37 | *.tlb
38 | *.tli
39 | *.tlh
40 | *.tmp
41 | *.tmp_proj
42 | *.log
43 | *.vspscc
44 | *.vssscc
45 | .builds
46 | *.pidb
47 | *.log
48 | *.scc
49 |
50 | # Visual Studio profiler
51 | *.psess
52 | *.vsp
53 | *.vspx
54 |
55 | # Guidance Automation Toolkit
56 | *.gpState
57 |
58 | # ReSharper is a .NET coding add-in
59 | _ReSharper*/
60 | *.[Rr]e[Ss]harper
61 |
62 | # TeamCity is a build add-in
63 | _TeamCity*
64 |
65 | # DotCover is a Code Coverage Tool
66 | *.dotCover
67 |
68 | # NCrunch
69 | *.ncrunch*
70 | .*crunch*.local.xml
71 |
72 | # Click-Once directory
73 | publish/
74 |
75 | # NuGet Packages Directory
76 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
77 | packages/
78 | [Ll]ib/
79 |
80 | # Backup & report files from converting an old project file to a newer
81 | # Visual Studio version. Backup files are not needed, because we have git ;-)
82 | _UpgradeReport_Files/
83 | Backup*/
84 | UpgradeLog*.XML
85 | UpgradeLog*.htm
86 |
87 | # =========================
88 | # Windows detritus
89 | # =========================
90 |
91 | # Windows image file caches
92 | Thumbs.db
93 | ehthumbs.db
94 |
95 | # Folder config file
96 | Desktop.ini
97 |
98 | # Recycle Bin used on file shares
99 | $RECYCLE.BIN/
100 |
101 | # Mac desktop service store files
102 | .DS_Store
103 |
104 | # Tools
105 | [tT]ools/
106 |
107 | # ide temp
108 | *.sln.ide/
109 |
110 | .vs
111 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2014 Kulikov Denis
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Auto Merge
2 | **Easy way to merge changeset**
3 |
4 | [](https://cduke.visualstudio.com/AutoMerge/_build/latest?definitionId=12&branchName=master)
5 |
6 | TFS changeset simple merge.
7 | Usefull when need merge bugfix changeset to several branches.
8 |
9 | ## Features
10 | * Validate target folder (mapped, user has rights, already merged)
11 | * Get latest source before merge
12 | * Associate changeset with same workitem
13 | * Add comment
14 | * Easy way discard/force merge
15 | * Check-In after merge or only fill pending changes page
16 | * Double click open changeset details
17 |
18 | ## Usage
19 | On team explorer page select Auto Merge. Extension show last changesets and list of branches. You select branches and press merge. That's all.
20 |
21 | Team explorer home page
22 |
23 | 
24 |
25 | Auto Merge page
26 |
27 | 
28 |
29 | ## Issues
30 | [https://github.com/CDuke/AutoMerge/issues](https://github.com/CDuke/AutoMerge/issues)
31 |
--------------------------------------------------------------------------------
/RELEASE_NOTES.md:
--------------------------------------------------------------------------------
1 | #### 0.2.6.10 (2021-03-19)
2 | * (fix) When you have multiple branches with same prefix, e.g. ABC and ABCDE. When merging to ABC method choses ABCDE instead, and as a result there are multiple branches with the same name in target branch selector
3 |
4 | #### 0.2.6.9 (2017-05-09)
5 | * (fix) If the comment doesn't contain any target branch information the comment will be the same for each target branch. Consolidate duplicate comments when displaying in the Pending Changes view (thanks psaut)
6 |
7 | #### 0.2.6.8 (2017-03-02)
8 | * (enhancement) Order workspaces by name in "Target branches" list (thanks MrLuje)
9 | * (fix) Fix typo at "Resolve" word
10 |
11 | #### 0.2.6.7 (2016-09-02)
12 | * (enhancement) Added the ability to specify the number of changesets shown for merging. Name - changeset_count_show
13 |
14 | #### 0.2.6.6 (2016-03-21)
15 | * (enhancement) Add "{SourceWorkItemTitles}" keyword for comment_format (#22)
16 |
17 | #### 0.2.6.5 (2015-11-26)
18 | * (fix) Null reference when config does not exists
19 |
20 | #### 0.2.6.4 (2015-11-23)
21 | * (fix) Crash when comment template has newline (#18)
22 |
23 | #### 0.2.6.3 (2015-05-18)
24 | * (fix) The extension requires a version of .NET Framework that is not installed. (#15)
25 |
26 | #### 0.2.6.2 (2015-03-30)
27 | * (fix) SQL Server error 2627
28 |
29 | #### 0.2.6.1 (2015-01-19)
30 | * (fix) Showing all changes regardless of the project
31 |
32 | #### 0.2.6 (2015-01-18)
33 | * (enhancement) Change behavior "Merge & Check In". Now it check in if no conflicts, otherwise show resolve conflicts window.
34 | * (enhancement) If found to be restored unexpected file, merging occurs by file.
35 | * (enhancement) After check in show changeset id.
36 | * (fix) Fix refreshing changeset when project changed.
37 |
38 | #### 0.2.5.1 (2014-12-03)
39 | * (enhancement) Allow templating discard merge comment (#11)
40 |
41 | #### 0.2.5 (2014-11-29)
42 | * (enhancement) Allow templating merge comment (#6)
43 |
44 | #### 0.2.4 (2014-09-24)
45 | * (enhancement) Allow set default mode for "Merge" button
46 |
47 | #### 0.2.3 (2014-09-22)
48 | * (enhancement) Double click on branch open it in source control explorer (#7)
49 | * (fix) Multi load branches info, because not unsubscribe events
50 |
51 | #### 0.2.2 (2014-06-23)
52 | * (fix) Button "Merge" not disabled while merging (#3)
53 | * (enhancement) Rename button "Merge" -> "Merge & Check In"
54 | * (enhancement) Save last merge operation and next time it will be default (#4)
55 |
56 | #### 0.2.1 (2014-06-13)
57 | * (fix) In some cases occurs "Cannot add instance of type 'ScrollDeligateBehavior' to a collection of type 'BehaviorCollection'" (#2)
58 | * (enhancement) Add support multi workspace (#1)
59 |
60 | #### 0.2 (2014-05-20)
61 | * Initial Release
62 |
--------------------------------------------------------------------------------
/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | # .NET Desktop
2 | # Build and run tests for .NET Desktop or Windows classic desktop solutions.
3 | # Add steps that publish symbols, save build artifacts, and more:
4 | # https://docs.microsoft.com/azure/devops/pipelines/apps/windows/dot-net
5 |
6 | trigger:
7 | - master
8 |
9 | pool:
10 | vmImage: 'windows-latest'
11 |
12 | variables:
13 | solution: '**/*.sln'
14 | buildPlatform: 'Any CPU'
15 | buildConfiguration: 'Release'
16 | msbuildArgs: '/p:VisualStudioVersion=17.0 -restore'
17 |
18 | steps:
19 |
20 | - task: VSBuild@1
21 | displayName: Build
22 | inputs:
23 | solution: '$(solution)'
24 | platform: '$(buildPlatform)'
25 | configuration: '$(buildConfiguration)'
26 | msbuildArgs: '$(msbuildArgs)'
27 |
28 | - task: CopyFiles@2
29 | inputs:
30 | SourceFolder: '$(Build.SourcesDirectory)'
31 | Contents: '**/bin/**/*.vsix'
32 | TargetFolder: '$(Build.ArtifactStagingDirectory)'
33 |
34 | - task: PublishBuildArtifacts@1
35 | inputs:
36 | pathToPublish: $(Build.ArtifactStagingDirectory)
37 | artifactName: VSIX
38 |
39 |
40 |
--------------------------------------------------------------------------------
/build/build.fsx:
--------------------------------------------------------------------------------
1 | #r @"../tools/FAKE/tools/FakeLib.dll"
2 | open Fake
3 | open System.Xml
4 |
5 | // Properties
6 | let vsVersion = environVarOrDefault "VisualStudioVersion" "12.0"
7 | let buildDirBase = "./bin/"
8 | let buildDir = buildDirBase + vsVersion
9 |
10 |
11 | // files
12 | let slnReferences = !!"./src/AutoMerge.sln"
13 |
14 | // Targets
15 | Target "Clean" (fun _ ->
16 | CleanDir buildDir
17 | )
18 |
19 | Target "RestorePackages" (fun _ ->
20 | !! "./**/packages.config"
21 | |> Seq.iter (fun id -> (RestorePackage (fun p -> {p with OutputPath = "./lib"}) id))
22 | )
23 |
24 | Target "BuildApp" (fun _ ->
25 | slnReferences
26 | |> MSBuildRelease buildDir "Build"
27 | |> Log "AppBuild-Output: "
28 | )
29 |
30 | Target "All" DoNothing
31 |
32 |
33 | // Dependencies
34 | "Clean"
35 | ==> "RestorePackages"
36 | ==> "BuildApp"
37 | ==> "All"
38 |
39 | // start build
40 | RunTargetOrDefault "All"
--------------------------------------------------------------------------------
/build_v15.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | cls
3 | IF NOT EXIST "tools/FAKE" (
4 | "tools/nuget/nuget.exe" "install" "FAKE" "-OutputDirectory" "tools" "-ExcludeVersion"
5 | )
6 |
7 | "tools/FAKE/tools/Fake.exe" "build/build.fsx" "VisualStudioVersion=15.0"
8 | pause
9 | exit /b %errorlevel%
--------------------------------------------------------------------------------
/build_v16.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | cls
3 | IF NOT EXIST "tools/FAKE" (
4 | "tools/nuget/nuget.exe" "install" "FAKE" "-OutputDirectory" "tools" "-ExcludeVersion"
5 | )
6 |
7 | "tools/FAKE/tools/Fake.exe" "build/build.fsx" "VisualStudioVersion=16.0"
8 | pause
9 | exit /b %errorlevel%
--------------------------------------------------------------------------------
/build_v17.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | cls
3 | IF NOT EXIST "tools/FAKE" (
4 | "tools/nuget/nuget.exe" "install" "FAKE" "-OutputDirectory" "tools" "-ExcludeVersion"
5 | )
6 |
7 | "tools/FAKE/tools/Fake.exe" "build/build.fsx" "VisualStudioVersion=17.0"
8 | pause
9 | exit /b %errorlevel%
--------------------------------------------------------------------------------
/lib/15.0/Microsoft.TeamFoundation.Client.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/lib/15.0/Microsoft.TeamFoundation.Client.dll
--------------------------------------------------------------------------------
/lib/15.0/Microsoft.TeamFoundation.Common.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/lib/15.0/Microsoft.TeamFoundation.Common.dll
--------------------------------------------------------------------------------
/lib/15.0/Microsoft.TeamFoundation.Controls.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/lib/15.0/Microsoft.TeamFoundation.Controls.dll
--------------------------------------------------------------------------------
/lib/15.0/Microsoft.TeamFoundation.VersionControl.Client.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/lib/15.0/Microsoft.TeamFoundation.VersionControl.Client.dll
--------------------------------------------------------------------------------
/lib/15.0/Microsoft.TeamFoundation.VersionControl.Common.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/lib/15.0/Microsoft.TeamFoundation.VersionControl.Common.dll
--------------------------------------------------------------------------------
/lib/15.0/Microsoft.TeamFoundation.WorkItemTracking.Client.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/lib/15.0/Microsoft.TeamFoundation.WorkItemTracking.Client.dll
--------------------------------------------------------------------------------
/lib/16.0/Microsoft.TeamFoundation.Client.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/lib/16.0/Microsoft.TeamFoundation.Client.dll
--------------------------------------------------------------------------------
/lib/16.0/Microsoft.TeamFoundation.Common.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/lib/16.0/Microsoft.TeamFoundation.Common.dll
--------------------------------------------------------------------------------
/lib/16.0/Microsoft.TeamFoundation.Controls.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/lib/16.0/Microsoft.TeamFoundation.Controls.dll
--------------------------------------------------------------------------------
/lib/16.0/Microsoft.TeamFoundation.VersionControl.Client.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/lib/16.0/Microsoft.TeamFoundation.VersionControl.Client.dll
--------------------------------------------------------------------------------
/lib/16.0/Microsoft.TeamFoundation.VersionControl.Common.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/lib/16.0/Microsoft.TeamFoundation.VersionControl.Common.dll
--------------------------------------------------------------------------------
/lib/16.0/Microsoft.TeamFoundation.WorkItemTracking.Client.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/lib/16.0/Microsoft.TeamFoundation.WorkItemTracking.Client.dll
--------------------------------------------------------------------------------
/lib/17.0/Microsoft.TeamFoundation.Client.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/lib/17.0/Microsoft.TeamFoundation.Client.dll
--------------------------------------------------------------------------------
/lib/17.0/Microsoft.TeamFoundation.Common.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/lib/17.0/Microsoft.TeamFoundation.Common.dll
--------------------------------------------------------------------------------
/lib/17.0/Microsoft.TeamFoundation.Controls.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/lib/17.0/Microsoft.TeamFoundation.Controls.dll
--------------------------------------------------------------------------------
/lib/17.0/Microsoft.TeamFoundation.VersionControl.Client.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/lib/17.0/Microsoft.TeamFoundation.VersionControl.Client.dll
--------------------------------------------------------------------------------
/lib/17.0/Microsoft.TeamFoundation.VersionControl.Common.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/lib/17.0/Microsoft.TeamFoundation.VersionControl.Common.dll
--------------------------------------------------------------------------------
/lib/17.0/Microsoft.TeamFoundation.WorkItemTracking.Client.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/lib/17.0/Microsoft.TeamFoundation.WorkItemTracking.Client.dll
--------------------------------------------------------------------------------
/screenshots/automerge_main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/screenshots/automerge_main.png
--------------------------------------------------------------------------------
/screenshots/automerge_teamexplorer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/screenshots/automerge_teamexplorer.png
--------------------------------------------------------------------------------
/src/.editorconfig:
--------------------------------------------------------------------------------
1 | # top-most EditorConfig file
2 | root = true
3 |
4 | [*]
5 | end_of_line = crlf
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 4
9 | charset = utf-8
10 | trim_trailing_whitespace = true
11 |
12 | [*.xaml]
13 | indent_size = 2
14 |
15 | [*.json]
16 | indent_size = 2
17 |
--------------------------------------------------------------------------------
/src/AutoMerge.Tests/AutoMerge.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48
5 | net472
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/AutoMerge.Tests/JsonParserTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Xunit;
3 |
4 | namespace AutoMerge.Tests
5 | {
6 | public class JsonParserTests
7 | {
8 | [Fact]
9 | public void WhenValueHasNewLine_ShouldCorrectParse()
10 | {
11 | var value = string.Format("{0}Patch Back", Environment.NewLine);
12 | var json = string.Format("{{\"comment_format\": \"{0}\"}}", value);
13 |
14 | var values = JsonParser.ParseJson(json);
15 |
16 | Assert.NotEmpty(values);
17 | Assert.Equal(value, values["comment_format"]);
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/AutoMerge.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29905.134
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoMerge", "AutoMerge\AutoMerge.csproj", "{726ED85E-2274-4D95-B822-B2CFE2CE44B9}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{5101B5A3-BBBF-474C-8BBF-A61EF46643D0}"
9 | ProjectSection(SolutionItems) = preProject
10 | ..\build.fsx = ..\build.fsx
11 | EndProjectSection
12 | EndProject
13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoMerge.Tests", "AutoMerge.Tests\AutoMerge.Tests.csproj", "{4CD4D5BB-FE08-4DAF-A952-93E78022FC12}"
14 | EndProject
15 | Global
16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
17 | Debug|Any CPU = Debug|Any CPU
18 | Release|Any CPU = Release|Any CPU
19 | EndGlobalSection
20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
21 | {726ED85E-2274-4D95-B822-B2CFE2CE44B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
22 | {726ED85E-2274-4D95-B822-B2CFE2CE44B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
23 | {726ED85E-2274-4D95-B822-B2CFE2CE44B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
24 | {726ED85E-2274-4D95-B822-B2CFE2CE44B9}.Release|Any CPU.Build.0 = Release|Any CPU
25 | {4CD4D5BB-FE08-4DAF-A952-93E78022FC12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26 | {4CD4D5BB-FE08-4DAF-A952-93E78022FC12}.Debug|Any CPU.Build.0 = Debug|Any CPU
27 | {4CD4D5BB-FE08-4DAF-A952-93E78022FC12}.Release|Any CPU.ActiveCfg = Release|Any CPU
28 | {4CD4D5BB-FE08-4DAF-A952-93E78022FC12}.Release|Any CPU.Build.0 = Release|Any CPU
29 | EndGlobalSection
30 | GlobalSection(SolutionProperties) = preSolution
31 | HideSolutionNode = FALSE
32 | EndGlobalSection
33 | EndGlobal
34 |
--------------------------------------------------------------------------------
/src/AutoMerge/AutoMergeNavigationItem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.Composition;
3 | using AutoMerge.Base;
4 | using AutoMerge.VersionControl;
5 | using Microsoft.TeamFoundation.Controls;
6 | using Microsoft.VisualStudio.Shell;
7 |
8 | namespace AutoMerge
9 | {
10 | [TeamExplorerNavigationItem(GuidList.AutoMergeNavigationItemId, 210, TargetPageId = GuidList.AutoMergePageId)]
11 | public class AutoMergeNavigationItem : TeamExplorerNavigationItemBase
12 | {
13 |
14 | [ImportingConstructor]
15 | public AutoMergeNavigationItem(
16 | [Import(typeof(SVsServiceProvider))]
17 | IServiceProvider serviceProvider)
18 | : base(serviceProvider, GuidList.AutoMergePageId, VersionControlProvider.TeamFoundation)
19 | {
20 | Text = Resources.AutoMergePageName;
21 | Image = Resources.MergeImage;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/AutoMerge/AutoMergePackage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 | using System.Threading;
4 | using AutoMerge.Commands;
5 | using Microsoft.VisualStudio.Shell;
6 |
7 | namespace AutoMerge
8 | {
9 | ///
10 | /// This is the class that implements the package exposed by this assembly.
11 | ///
12 | /// The minimum requirement for a class to be considered a valid package for Visual Studio
13 | /// is to implement the IVsPackage interface and register itself with the shell.
14 | /// This package uses the helper classes defined inside the Managed Package Framework (MPF)
15 | /// to do it: it derives from the Package class that provides the implementation of the
16 | /// IVsPackage interface and uses the registration attributes defined in the framework to
17 | /// register itself and its components with the shell.
18 | ///
19 | // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is
20 | // a package.
21 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
22 | // This attribute is used to register the information needed to show this package
23 | // in the Help/About dialog of Visual Studio.
24 | [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
25 | [Guid(GuidList.guidAutoMergePkgString)]
26 | [ProvideMenuResource("Menus.ctmenu", 1)]
27 | [ProvideBindingPath]
28 | public sealed class AutoMergePackage : AsyncPackage
29 | {
30 | protected override async System.Threading.Tasks.Task InitializeAsync(CancellationToken cancellationToken, IProgress progress)
31 | {
32 | await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
33 |
34 | await ShowAutoMergeWindow.InitializeAsync(this);
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/AutoMerge/AutoMergePage.cs:
--------------------------------------------------------------------------------
1 |
2 | using AutoMerge.Base;
3 | using Microsoft.TeamFoundation.Controls;
4 |
5 | namespace AutoMerge
6 | {
7 | [TeamExplorerPage(GuidList.AutoMergePageId)]
8 | public class AutoMergePage : TeamExplorerPageBase
9 | {
10 |
11 | ///
12 | /// Constructor.
13 | ///
14 | public AutoMergePage()
15 | {
16 | Title = Resources.AutoMergePageName;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/AutoMerge/Base/TeamExplorerBaseNavigationLink.cs:
--------------------------------------------------------------------------------
1 | using TfsTeamExplorerNavigationLinkBase = Microsoft.TeamFoundation.Controls.WPF.TeamExplorer.TeamExplorerNavigationLinkBase;
2 |
3 | namespace AutoMerge.Base
4 | {
5 | ///
6 | /// Team Explorer base navigation link class.
7 | ///
8 | public abstract class TeamExplorerBaseNavigationLink : TfsTeamExplorerNavigationLinkBase
9 | {
10 |
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/AutoMerge/Base/TeamExplorerNavigationItemBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AutoMerge.VersionControl;
3 | using Microsoft.TeamFoundation.Controls.WPF.TeamExplorer;
4 |
5 | using TfsTeamExplorerNavigationItemBase = Microsoft.TeamFoundation.Controls.WPF.TeamExplorer.TeamExplorerNavigationItemBase;
6 |
7 | namespace AutoMerge.Base
8 | {
9 | public abstract class TeamExplorerNavigationItemBase : TfsTeamExplorerNavigationItemBase
10 | {
11 | private readonly VersionControlProvider _versionControlProvider;
12 | private Guid _pageId;
13 |
14 | protected IServiceProvider ServiceProvider { get; private set; }
15 |
16 | protected TeamExplorerNavigationItemBase(IServiceProvider serviceProvider,
17 | string pageId, VersionControlProvider versionControlProvider)
18 | {
19 | _versionControlProvider = versionControlProvider;
20 | ServiceProvider = serviceProvider;
21 | _pageId = new Guid(pageId);
22 | }
23 |
24 | public override void Execute()
25 | {
26 | TeamExplorerUtils.Instance.NavigateToPage(_pageId.ToString(), ServiceProvider, null);
27 | }
28 |
29 | public override void Invalidate()
30 | {
31 | base.Invalidate();
32 | IsVisible = CalculateVisible();
33 | }
34 |
35 | private bool CalculateVisible()
36 | {
37 | return VersionControlNavigationHelper.IsProviderActive(ServiceProvider, _versionControlProvider)
38 | && VersionControlNavigationHelper.IsConnectedToTfsCollectionAndProject(ServiceProvider);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/AutoMerge/Base/TeamExplorerPageBase.cs:
--------------------------------------------------------------------------------
1 | using TfsTeamExplorerPageBase = Microsoft.TeamFoundation.Controls.WPF.TeamExplorer.TeamExplorerPageBase;
2 |
3 | namespace AutoMerge.Base
4 | {
5 | ///
6 | /// Team Explorer page base class.
7 | ///
8 | public abstract class TeamExplorerPageBase : TfsTeamExplorerPageBase
9 | {
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/AutoMerge/Base/TeamExplorerSectionBase.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.TeamFoundation.Controls;
2 | using Microsoft.TeamFoundation.MVVM;
3 | using TfsTeamExplorerSectionBase = Microsoft.TeamFoundation.Controls.WPF.TeamExplorer.TeamExplorerSectionBase;
4 |
5 | namespace AutoMerge.Base
6 | {
7 | public abstract class TeamExplorerSectionBase : TfsTeamExplorerSectionBase
8 | {
9 | public override void Initialize(object sender, SectionInitializeEventArgs e)
10 | {
11 | base.Initialize(sender, e);
12 | var viewModelBase = ViewModel as ViewModelBase;
13 | if (View != null && viewModelBase != null)
14 | {
15 | viewModelBase.RegisterService(View);
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/AutoMerge/Base/TeamExplorerSectionViewModelBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Microsoft.TeamFoundation.Client;
4 | using Microsoft.TeamFoundation.Common.Internal;
5 | using Microsoft.TeamFoundation.Controls;
6 | using Microsoft.TeamFoundation.Controls.MVVM;
7 | using TfsTeamExplorerSectionViewModelBase = Microsoft.TeamFoundation.Controls.WPF.TeamExplorer.TeamExplorerSectionViewModelBase;
8 |
9 | namespace AutoMerge.Base
10 | {
11 | public abstract class TeamExplorerSectionViewModelBase : TfsTeamExplorerSectionViewModelBase
12 | {
13 | private readonly ILogger _logger;
14 | private static readonly Task _emptyTask = Task.FromResult(0);
15 |
16 | private ITeamFoundationContextManager TfsContextManager { get; set; }
17 | protected ITeamFoundationContext Context { get; private set; }
18 |
19 | protected TeamExplorerSectionViewModelBase(ILogger logger)
20 | {
21 | _logger = logger;
22 | }
23 |
24 | protected ILogger Logger { get { return _logger; } }
25 |
26 | protected virtual Task RefreshAsync()
27 | {
28 | return _emptyTask;
29 | }
30 |
31 | protected virtual Task InitializeAsync(object sender, SectionInitializeEventArgs e)
32 | {
33 | return _emptyTask;
34 | }
35 |
36 | public async override void Initialize(object sender, SectionInitializeEventArgs e)
37 | {
38 | ShowBusy();
39 |
40 | try
41 | {
42 | base.Initialize(sender, e);
43 | if (ServiceProvider != null)
44 | {
45 | TfsContextManager = ServiceProvider.GetService();
46 | if (TfsContextManager != null)
47 | {
48 | TfsContextManager.ContextChanged -= OnContextChanged;
49 | TfsContextManager.ContextChanged += OnContextChanged;
50 | var context = TfsContextManager.CurrentContext;
51 | Context = context;
52 | }
53 | }
54 | await InitializeAsync(sender, e);
55 | }
56 | catch (Exception ex)
57 | {
58 | ShowError(ex.Message);
59 | }
60 |
61 | HideBusy();
62 | }
63 |
64 | public async override void Refresh()
65 | {
66 | ShowBusy();
67 |
68 | try
69 | {
70 | await RefreshAsync();
71 | }
72 | catch (Exception ex)
73 | {
74 | ShowError(ex.Message);
75 | _logger.Error(ex.Message, ex);
76 | }
77 |
78 | HideBusy();
79 | }
80 |
81 | protected void SetMvvmFocus(string id, params object[] args)
82 | {
83 | var focusService = TryResolveService();
84 | if (focusService == null)
85 | return;
86 | focusService.SetFocus(id, args);
87 | }
88 |
89 | public override void Dispose()
90 | {
91 | base.Dispose();
92 | if (TfsContextManager != null)
93 | {
94 | TfsContextManager.ContextChanged -= OnContextChanged;
95 | }
96 | }
97 |
98 | protected virtual void OnContextChanged(object sender, ContextChangedEventArgs e)
99 | {
100 |
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/AutoMerge/Behaviours/GridViewColumnResize.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading;
5 | using System.Windows;
6 | using System.Windows.Controls;
7 |
8 | namespace AutoMerge.Behaviours
9 | {
10 | ///
11 | /// Static class used to attach to wpf control
12 | ///
13 | public static class GridViewColumnResize
14 | {
15 | #region DependencyProperties
16 |
17 | public static readonly DependencyProperty WidthProperty =
18 | DependencyProperty.RegisterAttached("Width", typeof(string), typeof(GridViewColumnResize),
19 | new PropertyMetadata(OnSetWidthCallback));
20 |
21 | public static readonly DependencyProperty GridViewColumnResizeBehaviorProperty =
22 | DependencyProperty.RegisterAttached("GridViewColumnResizeBehavior",
23 | typeof(GridViewColumnResizeBehavior), typeof(GridViewColumnResize),
24 | null);
25 |
26 | public static readonly DependencyProperty EnabledProperty =
27 | DependencyProperty.RegisterAttached("Enabled", typeof(bool), typeof(GridViewColumnResize),
28 | new PropertyMetadata(OnSetEnabledCallback));
29 |
30 | public static readonly DependencyProperty ListViewResizeBehaviorProperty =
31 | DependencyProperty.RegisterAttached("ListViewResizeBehaviorProperty",
32 | typeof(ListViewResizeBehavior), typeof(GridViewColumnResize), null);
33 |
34 | #endregion
35 |
36 | public static string GetWidth(DependencyObject obj)
37 | {
38 | return (string)obj.GetValue(WidthProperty);
39 | }
40 |
41 | public static void SetWidth(DependencyObject obj, string value)
42 | {
43 | obj.SetValue(WidthProperty, value);
44 | }
45 |
46 | public static bool GetEnabled(DependencyObject obj)
47 | {
48 | return (bool)obj.GetValue(EnabledProperty);
49 | }
50 |
51 | public static void SetEnabled(DependencyObject obj, bool value)
52 | {
53 | obj.SetValue(EnabledProperty, value);
54 | }
55 |
56 | #region CallBack
57 |
58 | private static void OnSetWidthCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
59 | {
60 | var element = dependencyObject as GridViewColumn;
61 | if (element != null)
62 | {
63 | GridViewColumnResizeBehavior behavior = GetOrCreateBehavior(element);
64 | behavior.Width = e.NewValue as string;
65 | }
66 | else
67 | {
68 | Console.Error.WriteLine("Error: Expected type GridViewColumn but found " +
69 | dependencyObject.GetType().Name);
70 | }
71 | }
72 |
73 | private static void OnSetEnabledCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
74 | {
75 | var element = dependencyObject as ListView;
76 | if (element != null)
77 | {
78 | ListViewResizeBehavior behavior = GetOrCreateBehavior(element);
79 | behavior.Enabled = (bool)e.NewValue;
80 | }
81 | else
82 | {
83 | Console.Error.WriteLine("Error: Expected type ListView but found " + dependencyObject.GetType().Name);
84 | }
85 | }
86 |
87 |
88 | private static ListViewResizeBehavior GetOrCreateBehavior(ListView element)
89 | {
90 | var behavior = element.GetValue(GridViewColumnResizeBehaviorProperty) as ListViewResizeBehavior;
91 | if (behavior == null)
92 | {
93 | behavior = new ListViewResizeBehavior(element);
94 | element.SetValue(ListViewResizeBehaviorProperty, behavior);
95 | }
96 |
97 | return behavior;
98 | }
99 |
100 | private static GridViewColumnResizeBehavior GetOrCreateBehavior(GridViewColumn element)
101 | {
102 | var behavior = element.GetValue(GridViewColumnResizeBehaviorProperty) as GridViewColumnResizeBehavior;
103 | if (behavior == null)
104 | {
105 | behavior = new GridViewColumnResizeBehavior(element);
106 | element.SetValue(GridViewColumnResizeBehaviorProperty, behavior);
107 | }
108 |
109 | return behavior;
110 | }
111 |
112 | #endregion
113 |
114 | #region Nested type: GridViewColumnResizeBehavior
115 |
116 | ///
117 | /// GridViewColumn class that gets attached to the GridViewColumn control
118 | ///
119 | public class GridViewColumnResizeBehavior
120 | {
121 | private readonly GridViewColumn _element;
122 |
123 | public GridViewColumnResizeBehavior(GridViewColumn element)
124 | {
125 | _element = element;
126 | }
127 |
128 | public string Width { get; set; }
129 |
130 | public bool IsStatic
131 | {
132 | get { return StaticWidth >= 0; }
133 | }
134 |
135 | public double StaticWidth
136 | {
137 | get
138 | {
139 | double result;
140 | return double.TryParse(Width, out result) ? result : -1;
141 | }
142 | }
143 |
144 | public double Percentage
145 | {
146 | get
147 | {
148 | if (!IsStatic)
149 | {
150 | return Mulitplier * 100;
151 | }
152 | return 0;
153 | }
154 | }
155 |
156 | public double Mulitplier
157 | {
158 | get
159 | {
160 | if (Width == "*" || Width == "1*") return 1;
161 | if (Width.EndsWith("*"))
162 | {
163 | double perc;
164 | if (double.TryParse(Width.Substring(0, Width.Length - 1), out perc))
165 | {
166 | return perc;
167 | }
168 | }
169 | return 1;
170 | }
171 | }
172 |
173 | public void SetWidth(double allowedSpace, double totalPercentage)
174 | {
175 | if (IsStatic)
176 | {
177 | _element.Width = StaticWidth;
178 | }
179 | else
180 | {
181 | double width = allowedSpace * (Percentage / totalPercentage);
182 | _element.Width = width;
183 | }
184 | }
185 | }
186 |
187 | #endregion
188 |
189 | #region Nested type: ListViewResizeBehavior
190 |
191 | ///
192 | /// ListViewResizeBehavior class that gets attached to the ListView control
193 | ///
194 | public sealed class ListViewResizeBehavior : IDisposable
195 | {
196 | private bool _disposed;
197 | private const int Margin = 25;
198 | private const long RefreshTime = Timeout.Infinite;
199 | private const long Delay = 500;
200 |
201 | private readonly ListView _element;
202 | private readonly Timer _timer;
203 |
204 | public ListViewResizeBehavior(ListView element)
205 | {
206 | if (element == null) throw new ArgumentNullException("element");
207 | _element = element;
208 | element.Loaded += OnLoaded;
209 |
210 | // Action for resizing and re-enable the size lookup
211 | // This stops the columns from constantly resizing to improve performance
212 | Action resizeAndEnableSize = () =>
213 | {
214 | Resize();
215 | _element.SizeChanged += OnSizeChanged;
216 | };
217 | _timer = new Timer(x => Application.Current.Dispatcher.BeginInvoke(resizeAndEnableSize), null, Delay,
218 | RefreshTime);
219 | }
220 |
221 | public bool Enabled { get; set; }
222 |
223 |
224 | private void OnLoaded(object sender, RoutedEventArgs e)
225 | {
226 | _element.SizeChanged += OnSizeChanged;
227 | }
228 |
229 | private void OnSizeChanged(object sender, SizeChangedEventArgs e)
230 | {
231 | if (e.WidthChanged)
232 | {
233 | _element.SizeChanged -= OnSizeChanged;
234 | _timer.Change(Delay, RefreshTime);
235 | }
236 | }
237 |
238 | private void Resize()
239 | {
240 | if (Enabled)
241 | {
242 | double totalWidth = _element.ActualWidth;
243 | var gv = _element.View as GridView;
244 | if (gv != null)
245 | {
246 | double allowedSpace = totalWidth - GetAllocatedSpace(gv);
247 | allowedSpace = allowedSpace - Margin;
248 | double totalPercentage = GridViewColumnResizeBehaviors(gv).Sum(x => x.Percentage);
249 | foreach (GridViewColumnResizeBehavior behavior in GridViewColumnResizeBehaviors(gv))
250 | {
251 | behavior.SetWidth(allowedSpace, totalPercentage);
252 | }
253 | }
254 | }
255 | }
256 |
257 | private static IEnumerable GridViewColumnResizeBehaviors(GridView gv)
258 | {
259 | foreach (GridViewColumn t in gv.Columns)
260 | {
261 | var gridViewColumnResizeBehavior =
262 | t.GetValue(GridViewColumnResizeBehaviorProperty) as GridViewColumnResizeBehavior;
263 | if (gridViewColumnResizeBehavior != null)
264 | {
265 | yield return gridViewColumnResizeBehavior;
266 | }
267 | }
268 | }
269 |
270 | private static double GetAllocatedSpace(GridView gv)
271 | {
272 | double totalWidth = 0;
273 | foreach (GridViewColumn t in gv.Columns)
274 | {
275 | var gridViewColumnResizeBehavior =
276 | t.GetValue(GridViewColumnResizeBehaviorProperty) as GridViewColumnResizeBehavior;
277 | if (gridViewColumnResizeBehavior != null)
278 | {
279 | if (gridViewColumnResizeBehavior.IsStatic)
280 | {
281 | totalWidth += gridViewColumnResizeBehavior.StaticWidth;
282 | }
283 | }
284 | else
285 | {
286 | totalWidth += t.ActualWidth;
287 | }
288 | }
289 | return totalWidth;
290 | }
291 |
292 | public void Dispose()
293 | {
294 | if (!_disposed)
295 | {
296 | _timer.Dispose();
297 | }
298 |
299 | _disposed = true;
300 | }
301 | }
302 |
303 | #endregion
304 | }
305 | }
--------------------------------------------------------------------------------
/src/AutoMerge/Behaviours/ScrollDeligateBehavior.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Input;
3 | using System.Windows.Interactivity;
4 |
5 |
6 | namespace AutoMerge.Behaviours
7 | {
8 | public class ScrollDeligateBehavior : Behavior
9 | {
10 | protected override void OnAttached()
11 | {
12 | base.OnAttached();
13 | AssociatedObject.PreviewMouseWheel += ScrollDeligatePreviewMouseWheel;
14 | }
15 |
16 | protected override void OnDetaching()
17 | {
18 | AssociatedObject.PreviewMouseWheel -= ScrollDeligatePreviewMouseWheel;
19 | base.OnDetaching();
20 | }
21 |
22 | private void ScrollDeligatePreviewMouseWheel(object sender, MouseWheelEventArgs e)
23 | {
24 | if (!e.Handled)
25 | {
26 | var sourceElement = sender as FrameworkElement;
27 | if (sourceElement != null)
28 | {
29 | var uIElement = sourceElement.Parent as UIElement;
30 | if (uIElement != null)
31 | {
32 | e.Handled = true;
33 | uIElement.RaiseEvent(new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)
34 | {
35 | RoutedEvent = UIElement.MouseWheelEvent,
36 | Source = this
37 | });
38 | }
39 | }
40 | }
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/BranchFactory.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using AutoMerge.Prism.Events;
3 | using Microsoft.TeamFoundation.VersionControl.Client;
4 |
5 | namespace AutoMerge
6 | {
7 | public class BranchFactory
8 | {
9 | private readonly string _sourceBranch;
10 | private readonly string _sourceFolder;
11 | private readonly ChangesetVersionSpec _changesetVersion;
12 | private readonly BranchValidator _branchValidator;
13 | private readonly IEventAggregator _eventAggregator;
14 |
15 | public BranchFactory(string sourceBranch,
16 | string sourceFolder,
17 | ChangesetVersionSpec changesetVersion,
18 | BranchValidator branchValidator,
19 | IEventAggregator eventAggregator)
20 | {
21 | _sourceBranch = sourceBranch;
22 | _sourceFolder = sourceFolder;
23 | _changesetVersion = changesetVersion;
24 | _branchValidator = branchValidator;
25 | _eventAggregator = eventAggregator;
26 | }
27 |
28 | public MergeInfoViewModel CreateTargetBranchInfo(ItemIdentifier targetBranch, ItemIdentifier targetPath)
29 | {
30 | return CreateBranch(targetBranch.Item, targetPath.Item);
31 | }
32 |
33 | public MergeInfoViewModel CreateSourceBranch()
34 | {
35 | return CreateBranch(_sourceBranch, _sourceFolder);
36 | }
37 |
38 | private MergeInfoViewModel CreateBranch(string targetBranch, string targetPath)
39 | {
40 | var mergeInfo = new MergeInfoViewModel(_eventAggregator)
41 | {
42 | SourceBranch = _sourceBranch,
43 | TargetBranch = targetBranch,
44 | SourcePath = _sourceFolder,
45 | TargetPath = targetPath,
46 | ChangesetVersionSpec = _changesetVersion,
47 | ValidationResult = BranchValidationResult.Success,
48 | };
49 |
50 | if (_sourceBranch != targetBranch)
51 | {
52 | mergeInfo = _branchValidator.Validate(mergeInfo);
53 | }
54 |
55 | return mergeInfo;
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/BranchValidationResult.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMerge
2 | {
3 | public enum BranchValidationResult
4 | {
5 | Undefined,
6 |
7 | Success,
8 |
9 | BranchNotMapped,
10 |
11 | ItemHasLocalChanges,
12 |
13 | AlreadyMerged,
14 |
15 | NoAccess
16 | }
17 | }
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/BranchValidator.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using Microsoft.TeamFoundation.VersionControl.Client;
4 |
5 | namespace AutoMerge
6 | {
7 | public class BranchValidator
8 | {
9 | private readonly Workspace _workspace;
10 | private readonly IEnumerable _trackMerges;
11 |
12 | public BranchValidator(Workspace workspace, IEnumerable trackMerges)
13 | {
14 | _workspace = workspace;
15 | _trackMerges = trackMerges;
16 | }
17 |
18 | public MergeInfoViewModel Validate(MergeInfoViewModel branchInfo)
19 | {
20 | branchInfo.ValidationResult = ValidateItem(_workspace, branchInfo, _trackMerges);
21 | branchInfo.ValidationMessage = ToMessage(branchInfo.ValidationResult);
22 |
23 | return branchInfo;
24 | }
25 |
26 | private static BranchValidationResult ValidateItem(Workspace workspace, MergeInfoViewModel mergeInfoViewModel, IEnumerable trackMerges)
27 | {
28 | var result = BranchValidationResult.Success;
29 |
30 | if (result == BranchValidationResult.Success)
31 | {
32 | var isMerged = IsMerged(mergeInfoViewModel.SourcePath, mergeInfoViewModel.TargetPath, trackMerges);
33 | if (isMerged)
34 | result = BranchValidationResult.AlreadyMerged;
35 | }
36 |
37 | if (result == BranchValidationResult.Success)
38 | {
39 | var userHasAccess = UserHasAccess(workspace.VersionControlServer, mergeInfoViewModel.TargetPath);
40 | if (!userHasAccess)
41 | result = BranchValidationResult.NoAccess;
42 | }
43 |
44 | if (result == BranchValidationResult.Success)
45 | {
46 | var isMapped = IsMapped(workspace, mergeInfoViewModel.TargetPath);
47 | if (!isMapped)
48 | result = BranchValidationResult.BranchNotMapped;
49 | }
50 |
51 | if (result == BranchValidationResult.Success)
52 | {
53 | var hasLocalChanges = HasLocalChanges(workspace, mergeInfoViewModel.TargetPath);
54 | if (hasLocalChanges)
55 | result = BranchValidationResult.ItemHasLocalChanges;
56 | }
57 | return result;
58 | }
59 |
60 | private static bool IsMerged(string sourcePath, string targetPath, IEnumerable trackMerges)
61 | {
62 | if (trackMerges == null)
63 | return false;
64 |
65 | return trackMerges.Any(m => (m.TargetItem.Item == sourcePath && m.SourceItem.Item.ServerItem == targetPath)
66 | || (m.TargetItem.Item == targetPath && m.SourceItem.Item.ServerItem == sourcePath));
67 | }
68 |
69 | private static bool UserHasAccess(VersionControlServer versionControlServer, string targetPath)
70 | {
71 | var permissions = versionControlServer.GetEffectivePermissions(versionControlServer.AuthorizedUser, targetPath);
72 |
73 | if (permissions == null || permissions.Length < 4)
74 | return false;
75 |
76 | return permissions.Contains("Read")
77 | && permissions.Contains("PendChange")
78 | && permissions.Contains("Checkin")
79 | && permissions.Contains("Merge");
80 | }
81 |
82 | private static bool IsMapped(Workspace workspace, string targetItem)
83 | {
84 | return workspace.IsServerPathMapped(targetItem);
85 | }
86 |
87 | private static bool HasLocalChanges(Workspace workspace, string targetPath)
88 | {
89 | return workspace.GetPendingChangesEnumerable(targetPath, RecursionType.Full).Any();
90 | }
91 |
92 | private static string ToMessage(BranchValidationResult validationResult)
93 | {
94 | switch (validationResult)
95 | {
96 | case BranchValidationResult.Success:
97 | return null;
98 | case BranchValidationResult.AlreadyMerged:
99 | return "Already merged";
100 | case BranchValidationResult.BranchNotMapped:
101 | return "Branch not mapped";
102 | case BranchValidationResult.ItemHasLocalChanges:
103 | return "Folder has local changes. Check-in or undo it";
104 | case BranchValidationResult.NoAccess:
105 | return "You have not rights for edit";
106 | default:
107 | return "Unknown error";
108 | }
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/BranchesSection.cs:
--------------------------------------------------------------------------------
1 | using AutoMerge.Base;
2 | using Microsoft.TeamFoundation.Controls;
3 |
4 | namespace AutoMerge
5 | {
6 | [TeamExplorerSection(GuidList.BranchesSectionId, GuidList.AutoMergePageId, 20)]
7 | public class BranchesSection : TeamExplorerSectionBase
8 | {
9 | protected override object CreateView(SectionInitializeEventArgs e)
10 | {
11 | return new BranchesView();
12 | }
13 |
14 | protected override ITeamExplorerSection CreateViewModel(SectionInitializeEventArgs e)
15 | {
16 | var viewModel = base.CreateViewModel(e) ?? new BranchesViewModel(new VsLogger(ServiceProvider));
17 |
18 | return viewModel;
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/BranchesView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 |
3 | namespace AutoMerge
4 | {
5 | ///
6 | /// Interaction logic for BranchesView.xaml
7 | ///
8 | public partial class BranchesView : UserControl
9 | {
10 | public BranchesView()
11 | {
12 | InitializeComponent();
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/BranchesViewModelContext.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.ObjectModel;
2 | using Microsoft.TeamFoundation.VersionControl.Client;
3 |
4 | namespace AutoMerge
5 | {
6 | public class BranchesViewModelContext
7 | {
8 | public ObservableCollection Branches { get; set; }
9 |
10 | public MergeInfoViewModel SelectedBranch { get; set; }
11 |
12 | public MergeOption MergeOption { get; set; }
13 |
14 | public string ErrorMessage { get; set; }
15 |
16 | public Workspace Workspace { get; set; }
17 |
18 | public ObservableCollection Workspaces { get; set; }
19 |
20 | public bool ShowWorkspaceChooser { get; set; }
21 |
22 | public MergeMode MergeMode { get; set; }
23 |
24 | public ObservableCollection MergeModes { get; set; }
25 |
26 | public ChangesetViewModel Changeset { get; set; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/CheckInResult.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMerge
2 | {
3 | public class CheckInResult
4 | {
5 | public MergeResult CheckinResult { get; set; }
6 |
7 | public int? ChangesetId { get; set; }
8 | }
9 | }
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/EscapeMnemonicConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Data;
3 |
4 | namespace AutoMerge
5 | {
6 | ///
7 | /// Escape the label mnemonics such as underscore
8 | ///
9 | public class EscapeMnemonicConverter : IValueConverter
10 | {
11 | public object Convert(object value, Type targetType, object parameter,
12 | System.Globalization.CultureInfo culture)
13 | {
14 | var label = (value is string) ? (string)value : String.Empty;
15 | return label.Replace("_", "__");
16 | }
17 |
18 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
19 | {
20 | return string.Empty;
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/FileMergeInfo.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.TeamFoundation.VersionControl.Client;
2 |
3 | namespace AutoMerge
4 | {
5 | public class FileMergeInfo
6 | {
7 | public string SourceFile { get; set; }
8 |
9 | public string TargetFile { get; set; }
10 |
11 | public ChangesetVersionSpec ChangesetVersionSpec { get; set; }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/MergeInfoViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AutoMerge.Events;
3 | using AutoMerge.Prism.Events;
4 | using Microsoft.TeamFoundation.VersionControl.Client;
5 |
6 | namespace AutoMerge
7 | {
8 | public class MergeInfoViewModel
9 | {
10 | private readonly IEventAggregator _eventAggregator;
11 | internal bool _checked;
12 |
13 | public MergeInfoViewModel(IEventAggregator eventAggregator)
14 | {
15 | _eventAggregator = eventAggregator;
16 | }
17 |
18 | public bool Checked
19 | {
20 | get
21 | {
22 | return _checked;
23 | }
24 | set
25 | {
26 | _checked = value;
27 | _eventAggregator.GetEvent().Publish(this);
28 | }
29 | }
30 |
31 | public string SourcePath { get; set; }
32 |
33 | public string TargetPath { get; set; }
34 |
35 | public string SourceBranch { get; set; }
36 |
37 | public string TargetBranch { get; set; }
38 |
39 | public ChangesetVersionSpec ChangesetVersionSpec { get; set; }
40 |
41 |
42 | public string DisplayBranchName
43 | {
44 | get
45 | {
46 | return BranchHelper.GetShortBranchName(TargetBranch);
47 | }
48 | }
49 |
50 | public BranchValidationResult ValidationResult { get; set; }
51 |
52 | public string ValidationMessage { get; set; }
53 |
54 | public bool IsSourceBranch
55 | {
56 | get
57 | {
58 | return string.Equals(SourceBranch, TargetBranch, StringComparison.OrdinalIgnoreCase);
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/MergeMode.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMerge
2 | {
3 | public enum MergeMode
4 | {
5 | Merge,
6 |
7 | MergeAndCheckIn
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/MergeModeToStringConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows.Data;
4 |
5 | namespace AutoMerge
6 | {
7 | public class MergeModeToStringConverter : IValueConverter
8 | {
9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
10 | {
11 | if (value == null)
12 | return null;
13 | var mergeMode = (MergeMode)value;
14 | switch (mergeMode)
15 | {
16 | case MergeMode.Merge:
17 | return "Merge";
18 | case MergeMode.MergeAndCheckIn:
19 | return "Merge & Check In";
20 | default:
21 | return "Unknown mode";
22 | }
23 | }
24 |
25 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
26 | {
27 | throw new NotImplementedException();
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/MergeOption.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMerge
2 | {
3 | public enum MergeOption
4 | {
5 | ManualResolveConflict,
6 | KeepTarget,
7 | OverwriteTarget
8 | }
9 | }
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/MergeOptionToCheckedConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows.Data;
4 |
5 | namespace AutoMerge
6 | {
7 | public class MergeOptionToCheckedConverter : IValueConverter
8 | {
9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
10 | {
11 | return (MergeOption) value == (MergeOption) parameter;
12 | }
13 |
14 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
15 | {
16 | return parameter;
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/MergeOptionToStringConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows.Data;
4 |
5 | namespace AutoMerge
6 | {
7 | public class MergeOptionToStringConverter : IValueConverter
8 | {
9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
10 | {
11 | if (value != null)
12 | {
13 | var mergeOption = (MergeOption)value;
14 | switch (mergeOption)
15 | {
16 | case MergeOption.KeepTarget:
17 | return "Keep target (discard)";
18 | case MergeOption.ManualResolveConflict:
19 | return "Manual";
20 | case MergeOption.OverwriteTarget:
21 | return "Overwrite target";
22 | }
23 | }
24 |
25 | return "Unknown mode";
26 | }
27 |
28 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
29 | {
30 | if (value != null)
31 | {
32 | var mergeOptionDisplay = (string) value;
33 | if (mergeOptionDisplay == "Keep target (discard)")
34 | return MergeOption.KeepTarget;
35 |
36 | if (mergeOptionDisplay == "Manual")
37 | return MergeOption.ManualResolveConflict;
38 |
39 | if (mergeOptionDisplay == "Overwrite target")
40 | return MergeOption.OverwriteTarget;
41 |
42 | }
43 |
44 | return MergeOption.ManualResolveConflict;
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/MergeRelation.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.TeamFoundation.VersionControl.Client;
2 |
3 | namespace AutoMerge
4 | {
5 | public class MergeRelation
6 | {
7 | public string Item { get; set; }
8 |
9 | public string Source { get; set; }
10 |
11 | public string Target { get; set; }
12 |
13 | public ItemType TargetItemType { get; set; }
14 |
15 | public string GetLatesPath { get; set; }
16 |
17 | public bool Recursively { get; set; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/MergeResult.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMerge
2 | {
3 | public enum MergeResult
4 | {
5 | CheckIn,
6 | Merged,
7 | NothingMerge,
8 | CheckInFail,
9 | CheckInEvaluateFail,
10 | HasConflicts,
11 | CanNotGetLatest,
12 | HasLocalChanges,
13 | UnexpectedFileRestored,
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/MergeResultModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Microsoft.TeamFoundation.VersionControl.Client;
3 |
4 | namespace AutoMerge
5 | {
6 | public class MergeResultModel
7 | {
8 | public MergeResult MergeResult { get; set; }
9 |
10 | public List PendingChanges { get; set; }
11 |
12 | public MergeInfoViewModel BranchInfo { get; set; }
13 |
14 | public string Comment { get; set; }
15 |
16 | public List WorkItemIds { get; set; }
17 |
18 | public int SourceChangesetId { get; set; }
19 |
20 | public int? TagetChangesetId { get; set; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/Notification.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Input;
2 | using Microsoft.TeamFoundation.Controls;
3 |
4 | namespace AutoMerge
5 | {
6 | internal class Notification
7 | {
8 | public string Message { get; set; }
9 |
10 | public NotificationType NotificationType { get; set; }
11 |
12 | public ICommand Command { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/AutoMerge/Branches/TrackMergeInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace AutoMerge
4 | {
5 | public class TrackMergeInfo
6 | {
7 | public string OriginaBranch { get; set; }
8 |
9 | public string OriginalComment { get; set; }
10 |
11 | public List FromOriginalToSourceBranches { get; set; }
12 |
13 | public string SourceBranch { get; set; }
14 |
15 | public string SourceComment { get; set; }
16 |
17 | public long SourceChangesetId { get; set; }
18 |
19 | public List SourceWorkItemIds { get; set; }
20 |
21 | public List SourceWorkItemTitles { get; set; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/AutoMerge/Commands/ShowAutoMergeWindow.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.Design;
2 | using Microsoft.TeamFoundation.Common.Internal;
3 | using Microsoft.TeamFoundation.Controls;
4 | using Microsoft.VisualStudio.Shell;
5 | using Task = System.Threading.Tasks.Task;
6 |
7 | namespace AutoMerge.Commands
8 | {
9 | internal sealed class ShowAutoMergeWindow
10 | {
11 | public static async Task InitializeAsync(AsyncPackage package)
12 | {
13 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
14 |
15 | var commandService = await package.GetServiceAsync((typeof(IMenuCommandService))) as OleMenuCommandService;
16 |
17 | // must match the button GUID and ID specified in the .vsct file
18 | var cmdId = new CommandID(GuidList.ShowAutoMergeCmdSet, 0x0100);
19 | var cmd = new MenuCommand((s, e) => Execute(package), cmdId);
20 | commandService.AddCommand(cmd);
21 | }
22 |
23 | private static void Execute(AsyncPackage package)
24 | {
25 | ThreadHelper.ThrowIfNotOnUIThread();
26 |
27 | var teamExplorer = package.GetService();
28 | teamExplorer.NavigateToPage(GuidList.AutoMergePageGuid, null);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/AutoMerge/CommentFormater.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Globalization;
3 | using System.Linq;
4 |
5 | namespace AutoMerge
6 | {
7 | public class CommentFormater
8 | {
9 | private readonly CommentFormat _format;
10 |
11 | public CommentFormater(CommentFormat format)
12 | {
13 | _format = format;
14 | }
15 |
16 | public string Format(TrackMergeInfo trackMergeInfo, string targetBranch, MergeOption mergeOption)
17 | {
18 | var comment = mergeOption == MergeOption.KeepTarget ? _format.DiscardFormat : _format.Format;
19 | comment = comment
20 | .Replace("{OriginalBranch}", BranchHelper.GetShortBranchName(trackMergeInfo.OriginaBranch))
21 | .Replace("{OriginalBranchFull}", trackMergeInfo.OriginaBranch)
22 | .Replace("{SourceBranch}", BranchHelper.GetShortBranchName(trackMergeInfo.SourceBranch))
23 | .Replace("{SourceBranchFull}", trackMergeInfo.SourceBranch)
24 | .Replace("{TargetBranch}", BranchHelper.GetShortBranchName(targetBranch))
25 | .Replace("{TargetBranchFull}", targetBranch)
26 | .Replace("{FromOriginalToTarget}", FromOriginalToTarget(trackMergeInfo, targetBranch))
27 | .Replace("{FromOriginalToTargetFull}", FromOriginalToTargetFull(trackMergeInfo, targetBranch))
28 | .Replace("{OriginalComment}", trackMergeInfo.OriginalComment)
29 | .Replace("{SourceComment}", trackMergeInfo.SourceComment)
30 | .Replace("{SourceChangesetId}", trackMergeInfo.SourceChangesetId.ToString(CultureInfo.InvariantCulture))
31 | .Replace("{SourceWorkItemIds}", GetWorkItemIds(trackMergeInfo.SourceWorkItemIds))
32 | .Replace("{SourceWorkItemTitles}", GetWorkItemTitles(trackMergeInfo.SourceWorkItemTitles));
33 |
34 | return comment;
35 | }
36 |
37 | private static string GetWorkItemIds(List sourceWorkItemIds)
38 | {
39 | return string.Join(", ", sourceWorkItemIds.Select(id => id.ToString(CultureInfo.InvariantCulture)));
40 | }
41 |
42 | private static string GetWorkItemTitles(List sourceWorkItemTitles)
43 | {
44 | return string.Join("; ", sourceWorkItemTitles);
45 | }
46 |
47 | private string FromOriginalToTarget(TrackMergeInfo trackMergeInfo, string targetBranch)
48 | {
49 | var mergePath = trackMergeInfo.FromOriginalToSourceBranches.Concat(new[] { trackMergeInfo.SourceBranch, targetBranch })
50 | .Select(fullBranchName => BranchHelper.GetShortBranchName(fullBranchName));
51 | var mergePathString = string.Join(_format.BranchDelimiter, mergePath);
52 | return mergePathString;
53 | }
54 |
55 | private string FromOriginalToTargetFull(TrackMergeInfo trackMergeInfo, string targetBranch)
56 | {
57 | var mergePath = trackMergeInfo.FromOriginalToSourceBranches.Concat(new[] { trackMergeInfo.SourceBranch, targetBranch });
58 | var mergePathString = string.Join(_format.BranchDelimiter, mergePath);
59 | return mergePathString;
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/AutoMerge/Configuration/CommentFormat.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMerge
2 | {
3 | public class CommentFormat
4 | {
5 | public string Format { get; set; }
6 |
7 | public string DiscardFormat { get; set; }
8 |
9 | public string BranchDelimiter { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/AutoMerge/Configuration/FileSettingProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace AutoMerge
5 | {
6 | internal class FileSettingProvider : ISettingProvider
7 | {
8 | public T ReadValue(string key)
9 | {
10 | if (key == null)
11 | throw new ArgumentNullException("key");
12 |
13 | T value;
14 | if (!TryReadValue(key, out value))
15 | throw new InvalidOperationException(string.Format("Setting {0} not found", key));
16 |
17 | return value;
18 | }
19 |
20 | public bool TryReadValue(string key, out T value)
21 | {
22 | if (key == null)
23 | throw new ArgumentNullException("key");
24 |
25 | value = default(T);
26 | var path = GetSettingFilePath();
27 | var settingJson = File.ReadAllText(path);
28 | var settings = JsonParser.ParseJson(settingJson);
29 |
30 | string stringValue;
31 | if (!settings.TryGetValue(key, out stringValue))
32 | return false;
33 |
34 | value = (T) Convert.ChangeType(stringValue, typeof(T));
35 | return true;
36 | }
37 |
38 | public void WriteValue(string key, T value)
39 | {
40 | if (key == null)
41 | throw new ArgumentNullException("key");
42 |
43 | var path = GetSettingFilePath();
44 | var settingJson = File.ReadAllText(path);
45 | var settings = JsonParser.ParseJson(settingJson);
46 |
47 | settings[key] = value.ToString();
48 |
49 | settingJson = JsonParser.ToJson(settings);
50 | File.WriteAllText(path, settingJson);
51 | }
52 |
53 | private static string GetSettingFilePath()
54 | {
55 | var roamingPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
56 | var autoMergeFolder = Path.Combine(roamingPath, "Visual Studio Auto Merge");
57 | if (!Directory.Exists(autoMergeFolder))
58 | {
59 | Directory.CreateDirectory(autoMergeFolder);
60 | }
61 | var settingFilePath = Path.Combine(autoMergeFolder, "automerge.conf");
62 | if (!File.Exists(settingFilePath))
63 | {
64 | using (File.Create(settingFilePath))
65 | {
66 | }
67 | }
68 | return settingFilePath;
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/AutoMerge/Configuration/ISettingProvider.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMerge
2 | {
3 | internal interface ISettingProvider
4 | {
5 | T ReadValue(string key);
6 | bool TryReadValue(string key, out T value);
7 | void WriteValue(string key, T value);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/AutoMerge/Configuration/JsonParser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Web.Script.Serialization;
5 |
6 | namespace AutoMerge
7 | {
8 | public static class JsonParser
9 | {
10 | public static Dictionary ParseJson(string jsonText)
11 | {
12 | var jss = new JavaScriptSerializer();
13 | return jss.Deserialize>(jsonText) ?? new Dictionary();
14 | }
15 |
16 | public static string ToJson(Dictionary dict)
17 | {
18 | var entries = dict.Select(d =>
19 | string.Format("\"{0}\": \"{1}\"", d.Key, d.Value));
20 | return "{" + Environment.NewLine + " " + string.Join(",\r\n ", entries) + Environment.NewLine + "}";
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/AutoMerge/Configuration/Settings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 |
4 | namespace AutoMerge
5 | {
6 | internal class Settings
7 | {
8 | private readonly ISettingProvider _settingProvider;
9 | private static readonly Lazy _instance;
10 |
11 | private MergeMode? _lastMergeOperation;
12 |
13 | private const string lastMergeOperationKey = "last_operation";
14 | private const string mergeModeMerge = "merge";
15 | private const string mergeModeMergeAndCheckin = "merge_checkin";
16 |
17 | private const string mergeOperationDefaultKey = "merge_operation_default";
18 | private const string mergeOperationDefaultLast = "last";
19 | private const string mergeOperationDefaultMerge = mergeModeMerge;
20 | private const string mergeOperationDefaultMergeCheckin = mergeModeMergeAndCheckin;
21 | private readonly string[] _mergeOperationDefaultValues;
22 |
23 | private const string commentFormatKey = "comment_format";
24 | private const string commentFormatDefault = "MERGE {FromOriginalToTarget} ({OriginalComment})";
25 | private const string commentFormatDiscardKey = "comment_format_discard";
26 | private const string commentFormatDiscardDefault = "DISCARD {" + commentFormatKey + "}";
27 | private const string branchDelimiterKey = "branch_delimiter";
28 | private const string branchDelimiterDefault = " -> ";
29 |
30 | private const string changesetCountKey = "changeset_count_show";
31 | private const int changesetCountDefault = 20;
32 |
33 | static Settings()
34 | {
35 | _instance = new Lazy(() => new Settings());
36 | }
37 |
38 | private Settings()
39 | {
40 | _settingProvider = new FileSettingProvider();
41 | _mergeOperationDefaultValues = new[]
42 | {mergeOperationDefaultLast, mergeOperationDefaultMerge, mergeOperationDefaultMergeCheckin};
43 | }
44 |
45 | public static Settings Instance
46 | {
47 | get { return _instance.Value; }
48 | }
49 |
50 | public MergeMode LastMergeOperation {
51 | get
52 | {
53 | return LastMergeOperationGet();
54 | }
55 | set
56 | {
57 | LastMergeOperationSet(value);
58 | }
59 | }
60 |
61 | public CommentFormat CommentFormat
62 | {
63 | get { return CommentFormatGet(); }
64 | }
65 |
66 | public int ChangesetCount
67 | {
68 | get { return ChangesetCountGet(); }
69 | }
70 |
71 | private CommentFormat CommentFormatGet()
72 | {
73 | string commentFormat;
74 | if (!_settingProvider.TryReadValue(commentFormatKey, out commentFormat))
75 | {
76 | commentFormat = commentFormatDefault;
77 | }
78 |
79 | string commentFormatDiscard;
80 | if (!_settingProvider.TryReadValue(commentFormatDiscardKey, out commentFormatDiscard))
81 | {
82 | commentFormatDiscard = commentFormatDiscardDefault;
83 | }
84 |
85 | commentFormatDiscard = commentFormatDiscard.Replace("{" + commentFormatKey + "}", commentFormat);
86 |
87 | string branchDelimiter;
88 | if (!_settingProvider.TryReadValue(branchDelimiterKey, out branchDelimiter))
89 | {
90 | branchDelimiter = branchDelimiterDefault;
91 | }
92 |
93 | return new CommentFormat
94 | {
95 | Format = commentFormat,
96 | BranchDelimiter = branchDelimiter,
97 | DiscardFormat = commentFormatDiscard
98 | };
99 |
100 | }
101 |
102 | private MergeMode LastMergeOperationGet()
103 | {
104 | MergeMode result;
105 | var mergeOperationDefaultValue = MergeOperationDefaultGet();
106 | if (mergeOperationDefaultValue == mergeOperationDefaultLast)
107 | {
108 | if (!_lastMergeOperation.HasValue)
109 | {
110 | string stringValue;
111 | if (!_settingProvider.TryReadValue(lastMergeOperationKey, out stringValue))
112 | {
113 | stringValue = mergeModeMergeAndCheckin;
114 | }
115 |
116 | _lastMergeOperation = ToMergeMode(stringValue);
117 | }
118 |
119 | result = _lastMergeOperation.Value;
120 | }
121 | else
122 | {
123 | result = ToMergeMode(mergeOperationDefaultValue);
124 | }
125 |
126 | return result;
127 | }
128 |
129 | private void LastMergeOperationSet(MergeMode mergeMode)
130 | {
131 | if (_lastMergeOperation != mergeMode)
132 | {
133 | var stringValue = ToString(mergeMode);
134 | _settingProvider.WriteValue(lastMergeOperationKey, stringValue);
135 | _lastMergeOperation = mergeMode;
136 | }
137 | }
138 |
139 | private static MergeMode ToMergeMode(string stringValue)
140 | {
141 | if (stringValue == mergeModeMergeAndCheckin)
142 | return MergeMode.MergeAndCheckIn;
143 | return MergeMode.Merge;
144 | }
145 |
146 | private static string ToString(MergeMode mergeMode)
147 | {
148 | switch (mergeMode)
149 | {
150 | case MergeMode.Merge:
151 | return mergeModeMerge;
152 | case MergeMode.MergeAndCheckIn:
153 | return mergeModeMergeAndCheckin;
154 | default:
155 | return "unknown";
156 | }
157 | }
158 |
159 | private string MergeOperationDefaultGet()
160 | {
161 | string mergeOperationDefaultValue;
162 | if (!_settingProvider.TryReadValue(mergeOperationDefaultKey, out mergeOperationDefaultValue))
163 | {
164 | mergeOperationDefaultValue = mergeOperationDefaultLast;
165 | _settingProvider.WriteValue(mergeOperationDefaultKey, mergeOperationDefaultValue);
166 | }
167 |
168 | if (!_mergeOperationDefaultValues.Contains(mergeOperationDefaultValue))
169 | mergeOperationDefaultValue = "mergeOperationDefaultLast";
170 |
171 | return mergeOperationDefaultValue;
172 | }
173 |
174 | private int ChangesetCountGet()
175 | {
176 | int changesetCount;
177 | if (!_settingProvider.TryReadValue(changesetCountKey, out changesetCount))
178 | {
179 | changesetCount = changesetCountDefault;
180 | }
181 |
182 | return changesetCount;
183 | }
184 | }
185 | }
186 |
187 |
--------------------------------------------------------------------------------
/src/AutoMerge/Controls/SplitButton.xaml:
--------------------------------------------------------------------------------
1 |
140 |
--------------------------------------------------------------------------------
/src/AutoMerge/Controls/SplitButton.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 | using System.Windows.Controls;
4 | using System.Windows.Controls.Primitives;
5 | using System.Windows.Input;
6 | using Microsoft.TeamFoundation.Controls.WPF.TeamExplorer;
7 |
8 | namespace AutoMerge.Controls
9 | {
10 | ///
11 | /// Interaction logic for SplitButton.xaml
12 | ///
13 | public partial class SplitButton : Button
14 | {
15 | public static readonly DependencyProperty DropDownMenuCommandProperty;
16 | public static readonly DependencyProperty DropDownMenuProperty;
17 | public static readonly DependencyProperty DropDownMenuCommandParameterProperty;
18 | public static readonly DependencyProperty ShowArrowProperty;
19 | private bool _dropDownOpen;
20 |
21 | public bool ShowArrow
22 | {
23 | get
24 | {
25 | return (bool)GetValue(ShowArrowProperty);
26 | }
27 | set
28 | {
29 | SetValue(ShowArrowProperty, value);
30 | }
31 | }
32 |
33 | public ContextMenu DropDownMenu
34 | {
35 | get
36 | {
37 | return (ContextMenu)GetValue(DropDownMenuProperty);
38 | }
39 | set
40 | {
41 | SetValue(DropDownMenuProperty, value);
42 | }
43 | }
44 |
45 | public ICommand DropDownMenuCommand
46 | {
47 | get
48 | {
49 | return (ICommand)GetValue(DropDownMenuCommandProperty);
50 | }
51 | set
52 | {
53 | SetValue(DropDownMenuCommandProperty, value);
54 | }
55 | }
56 |
57 | public object DropDownMenuCommandParameter
58 | {
59 | get
60 | {
61 | return GetValue(DropDownMenuCommandParameterProperty);
62 | }
63 | set
64 | {
65 | SetValue(DropDownMenuCommandParameterProperty, value);
66 | }
67 | }
68 |
69 | static SplitButton()
70 | {
71 | DropDownMenuCommandProperty
72 | = DependencyProperty.Register("DropDownMenuCommand", typeof(ICommand), typeof(SplitButton), new FrameworkPropertyMetadata(null, OnDropDownMenuCommandChanged));
73 |
74 | DropDownMenuProperty
75 | = DependencyProperty.Register("DropDownMenu", typeof(ContextMenu), typeof(SplitButton), new PropertyMetadata(OnDropDownMenuChanged));
76 |
77 | DropDownMenuCommandParameterProperty
78 | = DependencyProperty.Register("DropDownMenuCommandParameter", typeof(object), typeof(SplitButton));
79 |
80 | ShowArrowProperty
81 | = DependencyProperty.Register("ShowArrow", typeof(bool), typeof(SplitButton), new PropertyMetadata(true));
82 | }
83 |
84 | public SplitButton()
85 | {
86 | InitializeComponent();
87 | KeyDown += SplitButton_KeyDown;
88 | }
89 |
90 | private static void OnDropDownMenuChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
91 | {
92 | var splitButton = d as SplitButton;
93 | if (splitButton == null)
94 | return;
95 |
96 | var contextMenu1 = e.OldValue as ContextMenu;
97 | if (contextMenu1 != null)
98 | {
99 | contextMenu1.Opened -= splitButton.OnContextMenuOpened;
100 | contextMenu1.Closed -= splitButton.OnContextMenuClosed;
101 | }
102 |
103 | var contextMenu2 = e.NewValue as ContextMenu;
104 | if (contextMenu2 == null)
105 | return;
106 | contextMenu2.Opened += splitButton.OnContextMenuOpened;
107 | contextMenu2.Closed += splitButton.OnContextMenuClosed;
108 | }
109 |
110 | private static void OnDropDownMenuCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
111 | {
112 | var splitButton = d as SplitButton;
113 | if (splitButton == null)
114 | return;
115 | splitButton.OnDropDownMenuCommandChanged((ICommand)e.OldValue, (ICommand)e.NewValue);
116 | }
117 |
118 | private void OnDropDownMenuCommandChanged(ICommand oldCommand, ICommand newCommand)
119 | {
120 | if (oldCommand != null)
121 | oldCommand.CanExecuteChanged -= OnCanExecuteChanged;
122 | if (newCommand != null)
123 | newCommand.CanExecuteChanged += OnCanExecuteChanged;
124 | UpdateCanExecute();
125 | }
126 |
127 | private void OnCanExecuteChanged(object sender, EventArgs e)
128 | {
129 | UpdateCanExecute();
130 | }
131 |
132 | private void UpdateCanExecute()
133 | {
134 | IsEnabled = DropDownMenuCommand != null && DropDownMenuCommand.CanExecute(DropDownMenuCommandParameter);
135 | }
136 |
137 | private void SplitButton_KeyDown(object sender, KeyEventArgs e)
138 | {
139 | if (!e.Handled && e.Key == Key.Down)
140 | {
141 | e.Handled = true;
142 | ToggleDropDown();
143 | }
144 | if ((e.Handled || e.Key != Key.Right) && e.Key != Key.Left)
145 | return;
146 | e.Handled = true;
147 | if (!IsFocused || e.Key != Key.Right)
148 | return;
149 | var uiElement = Template.FindName("SplitButtonDropDownButton", this) as UIElement;
150 | if (uiElement == null)
151 | return;
152 | uiElement.Focus();
153 | }
154 |
155 | private void SplitButtonDropDownButton_KeyDown(object sender, KeyEventArgs e)
156 | {
157 | if (e.Handled)
158 | return;
159 | if (e.Key == Key.Return || e.Key == Key.Space || e.Key == Key.Down)
160 | {
161 | e.Handled = true;
162 | ToggleDropDown();
163 | }
164 | else if (e.Key == Key.Left)
165 | {
166 | e.Handled = true;
167 | Focus();
168 | }
169 | else
170 | {
171 | if (e.Key != Key.Right)
172 | return;
173 | e.Handled = true;
174 | }
175 | }
176 |
177 | private void SplitButtonDropDownButton_Click(object sender, RoutedEventArgs e)
178 | {
179 | if (e.Handled)
180 | return;
181 | e.Handled = true;
182 | ToggleDropDown();
183 | }
184 |
185 | private void ToggleDropDown()
186 | {
187 | var dropDownMenuCommand = DropDownMenuCommand;
188 | if (dropDownMenuCommand != null)
189 | {
190 | if (!dropDownMenuCommand.CanExecute(null))
191 | return;
192 | var point = PointToScreen(new Point(0.0, ActualHeight));
193 | if (DropDownMenuCommandParameter != null)
194 | dropDownMenuCommand.Execute(new DropDownLink.DropDownMenuCommandParameters
195 | {
196 | Point = point,
197 | Parameter = DropDownMenuCommandParameter
198 | });
199 | else
200 | dropDownMenuCommand.Execute(point);
201 | }
202 | else
203 | {
204 | if (DropDownMenu == null)
205 | return;
206 | if (_dropDownOpen)
207 | {
208 | DropDownMenu.IsOpen = false;
209 | }
210 | else
211 | {
212 | DropDownMenu.FontFamily = FontFamily;
213 | DropDownMenu.FontSize = FontSize;
214 | DropDownMenu.PlacementTarget = this;
215 | DropDownMenu.Placement = PlacementMode.Bottom;
216 | DropDownMenu.IsOpen = true;
217 | }
218 | }
219 | }
220 |
221 | private void OnContextMenuOpened(object sender, RoutedEventArgs e)
222 | {
223 | _dropDownOpen = true;
224 | }
225 |
226 | private void OnContextMenuClosed(object sender, RoutedEventArgs e)
227 | {
228 | _dropDownOpen = false;
229 | }
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/src/AutoMerge/Events/BranchSelectedChangedEvent.cs:
--------------------------------------------------------------------------------
1 | using AutoMerge.Prism.Events;
2 |
3 | namespace AutoMerge.Events
4 | {
5 | public class BranchSelectedChangedEvent : PubSubEvent
6 | {
7 |
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/AutoMerge/Events/EventAggregatorFactory.cs:
--------------------------------------------------------------------------------
1 | using AutoMerge.Prism.Events;
2 |
3 | namespace AutoMerge.Events
4 | {
5 | internal static class EventAggregatorFactory
6 | {
7 | private static readonly IEventAggregator _eventAggregator = new EventAggregator();
8 |
9 | public static IEventAggregator Get()
10 | {
11 | return _eventAggregator;
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/AutoMerge/Events/MergeCompleteEvent.cs:
--------------------------------------------------------------------------------
1 | using AutoMerge.Prism.Events;
2 |
3 | namespace AutoMerge.Events
4 | {
5 | public class MergeCompleteEvent : PubSubEvent
6 | {
7 |
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/AutoMerge/Events/SelectChangesetEvent.cs:
--------------------------------------------------------------------------------
1 | using AutoMerge.Prism.Events;
2 |
3 | namespace AutoMerge.Events
4 | {
5 | public class SelectChangesetEvent : PubSubEvent
6 | {
7 |
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/AutoMerge/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
1 | // This file is used by Code Analysis to maintain SuppressMessage
2 | // attributes that are applied to this project. Project-level
3 | // suppressions either have no target or are given a specific target
4 | // and scoped to a namespace, type, member, etc.
5 | //
6 | // To add a suppression to this file, right-click the message in the
7 | // Error List, point to "Suppress Message(s)", and click "In Project
8 | // Suppression File". You do not need to add suppressions to this
9 | // file manually.
10 |
11 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")]
12 |
--------------------------------------------------------------------------------
/src/AutoMerge/Guids.cs:
--------------------------------------------------------------------------------
1 | // Guids.cs
2 | // MUST match guids.h
3 | using System;
4 |
5 | namespace AutoMerge
6 | {
7 | public static class GuidList
8 | {
9 | public const string guidAutoMergePkgString = "f05bac3e-6794-4a9e-9ee7-1b8a200778ee";
10 | public const string guidAutoMergeCmdSetString = "550e8690-9fae-46d1-8ff7-d6d0edf9449c";
11 |
12 | public static readonly Guid ShowAutoMergeCmdSet = new Guid(guidAutoMergeCmdSetString);
13 |
14 | public const string AutoMergeNavigationItemId = "02A9D8B3-287B-4C55-83E7-7BFDB435546D";
15 | public const string AutoMergePageId = "3B582638-5F12-4715-8719-5E5777AB4581";
16 | public static readonly Guid AutoMergePageGuid = new Guid(AutoMergePageId);
17 | public const string RecentChangesetsSectionId = "8DA59790-3996-465E-A13F-27D64B3C2A9D";
18 |
19 | public const string BranchesSectionId = "36BF6F52-F4AC-44A0-9985-817B2A65B3B0";
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/src/AutoMerge/Helpers/BranchHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace AutoMerge
4 | {
5 | internal static class BranchHelper
6 | {
7 | public static string GetShortBranchName(string branchFullName)
8 | {
9 | var pos = branchFullName.LastIndexOf('/');
10 | var name = branchFullName.Substring(pos + 1);
11 | return name;
12 | }
13 |
14 | public static string GetDisplayBranchName(List branches)
15 | {
16 | if (branches == null || branches.Count == 0)
17 | return string.Empty;
18 |
19 | if (branches.Count == 1)
20 | {
21 | return GetShortBranchName(branches[0]);
22 | }
23 | return "multi";
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/src/AutoMerge/Helpers/EnumerableExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace AutoMerge
5 | {
6 | internal static class EnumerableExtensions
7 | {
8 | public static bool IsNullOrEmpty(this IEnumerable items)
9 | {
10 | return items == null || !items.Any();
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/AutoMerge/Helpers/VersionControlNavigationHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AutoMerge.VersionControl;
3 | using Microsoft.TeamFoundation.Client;
4 | using Microsoft.TeamFoundation.Common.Internal;
5 | using Microsoft.TeamFoundation.VersionControl.Client;
6 | using Microsoft.VisualStudio.Shell;
7 | using Microsoft.VisualStudio.Shell.Interop;
8 |
9 | namespace AutoMerge
10 | {
11 | public class VersionControlNavigationHelper
12 | {
13 | private static readonly Guid TfsProviderGuid = new Guid("4CA58AB2-18FA-4F8D-95D4-32DDF27D184C");
14 | private static readonly Guid GitProviderGuid = new Guid("11b8e6d7-c08b-4385-b321-321078cdd1f8");
15 |
16 | static VersionControlNavigationHelper()
17 | {
18 | }
19 |
20 | public static bool IsProviderActive(IServiceProvider serviceProvider, VersionControlProvider provider)
21 | {
22 | if (serviceProvider != null)
23 | {
24 | var service = serviceProvider.GetService();
25 | if (service != null)
26 | {
27 | // ISSUE: variable of a compiler-generated type
28 | var providerInterface = service as IVsGetScciProviderInterface;
29 | if (providerInterface != null)
30 | {
31 | Guid pguidSCCProvider;
32 | // ISSUE: reference to a compiler-generated method
33 | providerInterface.GetSourceControlProviderID(out pguidSCCProvider);
34 | if (pguidSCCProvider == GetProviderGuid(provider))
35 | return true;
36 | if (Guid.Empty.Equals(pguidSCCProvider))
37 | return provider == VersionControlProvider.TeamFoundation;
38 | return false;
39 | }
40 | }
41 | }
42 | return true;
43 | }
44 |
45 | public static UIContext GetProviderUIContext(VersionControlProvider provider)
46 | {
47 | return UIContext.FromUIContextGuid(GetProviderGuid(provider));
48 | }
49 |
50 | public static bool IsConnectedToTfsCollectionAndProject(IServiceProvider provider)
51 | {
52 | var context = GetTeamFoundationContext(provider);
53 | if (context != null)
54 | {
55 | return context.HasCollection && context.HasTeamProject;
56 | }
57 |
58 | return false;
59 | }
60 |
61 | public static bool IsConnectedToTfsCollectionAndProject(ITeamFoundationContext context)
62 | {
63 | if (context != null)
64 | {
65 | return context.HasCollection && context.HasTeamProject;
66 | }
67 |
68 | return false;
69 | }
70 |
71 | private static Guid GetProviderGuid(VersionControlProvider provider)
72 | {
73 | switch (provider)
74 | {
75 | case VersionControlProvider.TeamFoundation:
76 | return TfsProviderGuid;
77 | case VersionControlProvider.Git:
78 | return GitProviderGuid;
79 | default:
80 | return Guid.Empty;
81 | }
82 | }
83 |
84 | public static ITeamFoundationContext GetTeamFoundationContext(IServiceProvider serviceProvider)
85 | {
86 | if (serviceProvider != null)
87 | {
88 | var tfContextManager = serviceProvider.GetService();
89 | if (tfContextManager != null)
90 | {
91 | var context = tfContextManager.CurrentContext;
92 | return context;
93 | }
94 | }
95 |
96 | return null;
97 | }
98 |
99 | public static string GetAuthorizedUser(IServiceProvider serviceProvider)
100 | {
101 | var context = GetTeamFoundationContext(serviceProvider);
102 | if (context != null && IsConnectedToTfsCollectionAndProject(context))
103 | {
104 | var vcs = context.TeamProjectCollection.GetService();
105 | return vcs.AuthorizedUser;
106 | }
107 |
108 | return string.Empty;
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/AutoMerge/Helpers/WorkspaceHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Microsoft.TeamFoundation.Client;
5 | using Microsoft.TeamFoundation.VersionControl.Client;
6 | using Microsoft.TeamFoundation.VersionControl.Common;
7 | using Microsoft.Win32;
8 |
9 | namespace AutoMerge
10 | {
11 | public static class WorkspaceHelper
12 | {
13 | private const string VCRegistryRootDefault = "TeamFoundation\\SourceControl";
14 | private const string GeneralLastWorkspaceString = "LastWorkspace";
15 |
16 | public static Workspace GetWorkspace(VersionControlServer versionControlServer,
17 | IEnumerable userWorkspaces)
18 | {
19 | var workspace = GetLastPendingChangesWorkspace(versionControlServer);
20 | if (workspace != null)
21 | {
22 | return FindWorkspace(userWorkspaces, workspace);
23 | }
24 | workspace = GetLastSourceControlExplorerWorkspace(versionControlServer);
25 | if (workspace != null)
26 | {
27 | return FindWorkspace(userWorkspaces, workspace);
28 | }
29 |
30 | return userWorkspaces.FirstOrDefault();
31 | }
32 |
33 | private static Workspace FindWorkspace(IEnumerable userWorkspaces, Workspace workspace)
34 | {
35 | return userWorkspaces.FirstOrDefault(w => w.QualifiedName == workspace.QualifiedName);
36 | }
37 |
38 | private static Workspace GetLastSourceControlExplorerWorkspace(VersionControlServer versionControlServer)
39 | {
40 | return GetLastWorkspaceForServer(versionControlServer, "Explorer");
41 | }
42 |
43 | private static Workspace GetLastPendingChangesWorkspace(VersionControlServer versionControlServer)
44 | {
45 | return GetLastWorkspaceForServer(versionControlServer, "PendingCheckins");
46 | }
47 |
48 | private static Workspace GetLastWorkspaceForServer(VersionControlServer versionControlServer, string viewName)
49 | {
50 | var workspace = (Workspace)null;
51 | try
52 | {
53 | var workspaceSpecForServer = GetLastWorkspaceSpecForServer(versionControlServer, viewName);
54 | if (!string.IsNullOrEmpty(workspaceSpecForServer))
55 | {
56 |
57 | string workspaceName;
58 | string workspaceOwner;
59 | WorkspaceSpec.Parse(workspaceSpecForServer, versionControlServer.AuthorizedUser, out workspaceName, out workspaceOwner);
60 | var localWorkspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(versionControlServer, workspaceName, workspaceOwner);
61 | if (localWorkspaceInfo != null)
62 | workspace = localWorkspaceInfo.GetWorkspace(versionControlServer.TeamProjectCollection);
63 | }
64 | }
65 | catch (Exception)
66 | {
67 | }
68 | return workspace;
69 | }
70 |
71 | private static string GetLastWorkspaceSpecForServer(VersionControlServer vcServer, string viewName)
72 | {
73 | var str1 = (string)null;
74 | try
75 | {
76 | if (vcServer != null)
77 | {
78 | if (!string.IsNullOrEmpty(viewName))
79 | {
80 | using (var featureServerKey = GetFeatureServerKey(viewName, vcServer.ServerGuid.ToString()))
81 | {
82 | if (featureServerKey != null)
83 | {
84 | var str2 = featureServerKey.GetValue(GeneralLastWorkspaceString) as string;
85 | if (!string.IsNullOrEmpty(str2))
86 | str1 = str2;
87 | }
88 | }
89 | }
90 | }
91 | }
92 | catch (Exception)
93 | {
94 | }
95 | return str1;
96 | }
97 |
98 | private static RegistryKey GetFeatureServerKey(string featurePath, string serverGuid)
99 | {
100 | using (var registryKey = UIHost.UserRegistryRoot)
101 | {
102 | if (registryKey != null)
103 | {
104 | var subKey = string.Join("\\", VCRegistryRootDefault, featurePath, serverGuid);
105 | return registryKey.OpenSubKey(subKey, true);
106 | }
107 | }
108 | return null;
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/AutoMerge/Key.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CDuke/AutoMerge/9550b96365ede7440bbda52efea1a73f49391c3f/src/AutoMerge/Key.snk
--------------------------------------------------------------------------------
/src/AutoMerge/Prism/BindableBase.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
2 |
3 | using System;
4 | using System.ComponentModel;
5 | using System.Linq.Expressions;
6 | using System.Runtime.CompilerServices;
7 |
8 | namespace AutoMerge.Prism
9 | {
10 | ///
11 | /// Implementation of to simplify models.
12 | ///
13 | public abstract class BindableBase : INotifyPropertyChanged
14 | {
15 | ///
16 | /// Occurs when a property value changes.
17 | ///
18 | public event PropertyChangedEventHandler PropertyChanged;
19 |
20 | ///
21 | /// Checks if a property already matches a desired value. Sets the property and
22 | /// notifies listeners only when necessary.
23 | ///
24 | /// Type of the property.
25 | /// Reference to a property with both getter and setter.
26 | /// Desired value for the property.
27 | /// Name of the property used to notify listeners. This
28 | /// value is optional and can be provided automatically when invoked from compilers that
29 | /// support CallerMemberName.
30 | /// True if the value was changed, false if the existing value matched the
31 | /// desired value.
32 | protected virtual bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null)
33 | {
34 | if (object.Equals(storage, value)) return false;
35 |
36 | storage = value;
37 | this.OnPropertyChanged(propertyName);
38 |
39 | return true;
40 | }
41 |
42 | ///
43 | /// Notifies listeners that a property value has changed.
44 | ///
45 | /// Name of the property used to notify listeners. This
46 | /// value is optional and can be provided automatically when invoked from compilers
47 | /// that support .
48 | protected void OnPropertyChanged(string propertyName)
49 | {
50 | var eventHandler = this.PropertyChanged;
51 | if (eventHandler != null)
52 | {
53 | eventHandler(this, new PropertyChangedEventArgs(propertyName));
54 | }
55 | }
56 |
57 | ///
58 | /// Raises this object's PropertyChanged event.
59 | ///
60 | /// The type of the property that has a new value
61 | /// A Lambda expression representing the property that has a new value.
62 | protected void OnPropertyChanged(Expression> propertyExpression)
63 | {
64 | var propertyName = PropertySupport.ExtractPropertyName(propertyExpression);
65 | this.OnPropertyChanged(propertyName);
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/AutoMerge/Prism/Command/DelegateCommandBase .cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using System.Windows.Input;
5 |
6 | namespace AutoMerge.Prism.Command
7 | {
8 | ///
9 | /// An whose delegates can be attached for and .
10 | ///
11 | public abstract class DelegateCommandBase : ICommand, IActiveAware
12 | {
13 | private bool _isActive;
14 | private List _canExecuteChangedHandlers;
15 |
16 | protected readonly Func