diff --git a/lib/18.0/Microsoft.TeamFoundation.Client.dll b/lib/18.0/Microsoft.TeamFoundation.Client.dll new file mode 100644 index 0000000..e3ccd35 Binary files /dev/null and b/lib/18.0/Microsoft.TeamFoundation.Client.dll differ diff --git a/lib/18.0/Microsoft.TeamFoundation.Common.dll b/lib/18.0/Microsoft.TeamFoundation.Common.dll new file mode 100644 index 0000000..d3a0d35 Binary files /dev/null and b/lib/18.0/Microsoft.TeamFoundation.Common.dll differ diff --git a/lib/18.0/Microsoft.TeamFoundation.Controls.dll b/lib/18.0/Microsoft.TeamFoundation.Controls.dll new file mode 100644 index 0000000..1c5d7b9 Binary files /dev/null and b/lib/18.0/Microsoft.TeamFoundation.Controls.dll differ diff --git a/lib/18.0/Microsoft.TeamFoundation.Core.WebApi.dll b/lib/18.0/Microsoft.TeamFoundation.Core.WebApi.dll new file mode 100644 index 0000000..229480e Binary files /dev/null and b/lib/18.0/Microsoft.TeamFoundation.Core.WebApi.dll differ diff --git a/lib/18.0/Microsoft.TeamFoundation.VersionControl.Client.dll b/lib/18.0/Microsoft.TeamFoundation.VersionControl.Client.dll new file mode 100644 index 0000000..50793ea Binary files /dev/null and b/lib/18.0/Microsoft.TeamFoundation.VersionControl.Client.dll differ diff --git a/lib/18.0/Microsoft.TeamFoundation.VersionControl.Common.dll b/lib/18.0/Microsoft.TeamFoundation.VersionControl.Common.dll new file mode 100644 index 0000000..b28ef40 Binary files /dev/null and b/lib/18.0/Microsoft.TeamFoundation.VersionControl.Common.dll differ diff --git a/lib/18.0/Microsoft.TeamFoundation.WorkItemTracking.Client.dll b/lib/18.0/Microsoft.TeamFoundation.WorkItemTracking.Client.dll new file mode 100644 index 0000000..1e20c15 Binary files /dev/null and b/lib/18.0/Microsoft.TeamFoundation.WorkItemTracking.Client.dll differ diff --git a/lib/18.0/Microsoft.VisualStudio.Services.Client.Interactive.dll b/lib/18.0/Microsoft.VisualStudio.Services.Client.Interactive.dll new file mode 100644 index 0000000..7e8e968 Binary files /dev/null and b/lib/18.0/Microsoft.VisualStudio.Services.Client.Interactive.dll differ diff --git a/lib/18.0/Microsoft.VisualStudio.Services.Common.dll b/lib/18.0/Microsoft.VisualStudio.Services.Common.dll new file mode 100644 index 0000000..b8c2c75 Binary files /dev/null and b/lib/18.0/Microsoft.VisualStudio.Services.Common.dll differ diff --git a/lib/18.0/Microsoft.VisualStudio.Services.WebApi.dll b/lib/18.0/Microsoft.VisualStudio.Services.WebApi.dll new file mode 100644 index 0000000..01ae575 Binary files /dev/null and b/lib/18.0/Microsoft.VisualStudio.Services.WebApi.dll differ diff --git a/lib/18.0/Newtonsoft.Json.dll b/lib/18.0/Newtonsoft.Json.dll new file mode 100644 index 0000000..62b57c1 Binary files /dev/null and b/lib/18.0/Newtonsoft.Json.dll differ diff --git a/screenshots/standalone-branches-update.png b/screenshots/standalone-branches-update.png new file mode 100644 index 0000000..cbae2fe Binary files /dev/null and b/screenshots/standalone-branches-update.png differ diff --git a/src/.editorconfig b/src/.editorconfig index 2b3e8aa..fae980d 100644 --- a/src/.editorconfig +++ b/src/.editorconfig @@ -5,7 +5,7 @@ root = true end_of_line = crlf insert_final_newline = true indent_style = space -indent_size = 4 +indent_size = 2 charset = utf-8 trim_trailing_whitespace = true diff --git a/src/AutoMerge.Standalone/AddChangesetByIdDialog.Designer.cs b/src/AutoMerge.Standalone/AddChangesetByIdDialog.Designer.cs new file mode 100644 index 0000000..53fa06e --- /dev/null +++ b/src/AutoMerge.Standalone/AddChangesetByIdDialog.Designer.cs @@ -0,0 +1,91 @@ +namespace AutoMerge.Standalone +{ + partial class AddChangesetByIdDialog + { + private System.ComponentModel.IContainer components = null; + + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.txtChangesetId = new System.Windows.Forms.TextBox(); + this.btnOk = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 15); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(96, 17); + this.label1.TabIndex = 0; + this.label1.Text = "Changeset ID:"; + // + // txtChangesetId + // + this.txtChangesetId.Location = new System.Drawing.Point(114, 12); + this.txtChangesetId.Name = "txtChangesetId"; + this.txtChangesetId.Size = new System.Drawing.Size(200, 24); + this.txtChangesetId.TabIndex = 1; + // + // btnOk + // + this.btnOk.Location = new System.Drawing.Point(114, 50); + this.btnOk.Name = "btnOk"; + this.btnOk.Size = new System.Drawing.Size(90, 30); + this.btnOk.TabIndex = 2; + this.btnOk.Text = "OK"; + this.btnOk.UseVisualStyleBackColor = true; + this.btnOk.Click += new System.EventHandler(this.btnOk_Click); + // + // btnCancel + // + this.btnCancel.Location = new System.Drawing.Point(224, 50); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(90, 30); + this.btnCancel.TabIndex = 3; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); + // + // AddChangesetByIdDialog + // + this.AcceptButton = this.btnOk; + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(326, 92); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOk); + this.Controls.Add(this.txtChangesetId); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "AddChangesetByIdDialog"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Add Changeset By ID"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox txtChangesetId; + private System.Windows.Forms.Button btnOk; + private System.Windows.Forms.Button btnCancel; + } +} diff --git a/src/AutoMerge.Standalone/AddChangesetByIdDialog.cs b/src/AutoMerge.Standalone/AddChangesetByIdDialog.cs new file mode 100644 index 0000000..d15973a --- /dev/null +++ b/src/AutoMerge.Standalone/AddChangesetByIdDialog.cs @@ -0,0 +1,35 @@ +using System; +using System.Windows.Forms; + +namespace AutoMerge.Standalone +{ + public partial class AddChangesetByIdDialog : Form + { + public int ChangesetId { get; private set; } + + public AddChangesetByIdDialog() + { + InitializeComponent(); + } + + private void btnOk_Click(object sender, EventArgs e) + { + if (int.TryParse(txtChangesetId.Text, out int id) && id > 0) + { + ChangesetId = id; + DialogResult = DialogResult.OK; + Close(); + } + else + { + MessageBox.Show("Please enter a valid changeset ID.", "Invalid Input", MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + } + + private void btnCancel_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.Cancel; + Close(); + } + } +} diff --git a/src/AutoMerge.Standalone/App.config b/src/AutoMerge.Standalone/App.config new file mode 100644 index 0000000..996fd1e --- /dev/null +++ b/src/AutoMerge.Standalone/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/AutoMerge.Standalone/AutoMerge.Standalone.csproj b/src/AutoMerge.Standalone/AutoMerge.Standalone.csproj new file mode 100644 index 0000000..742bf24 --- /dev/null +++ b/src/AutoMerge.Standalone/AutoMerge.Standalone.csproj @@ -0,0 +1,168 @@ + + + + + Debug + AnyCPU + {8B9A3C2D-5F6E-4A1B-9C3D-2E4F5A6B7C8D} + WinExe + AutoMerge.Standalone + AutoMerge.Standalone + v4.8 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\lib\18.0\Microsoft.TeamFoundation.Core.WebApi.dll + + + ..\..\lib\18.0\Microsoft.VisualStudio.Services.Client.Interactive.dll + + + + + + + + + + + + + + + + + + + ..\..\lib\18.0\Microsoft.TeamFoundation.Client.dll + True + + + ..\..\lib\18.0\Microsoft.TeamFoundation.Common.dll + True + + + ..\..\lib\18.0\Microsoft.TeamFoundation.Controls.dll + True + + + ..\..\lib\18.0\Microsoft.TeamFoundation.VersionControl.Client.dll + True + + + ..\..\lib\18.0\Microsoft.TeamFoundation.VersionControl.Common.dll + True + + + ..\..\lib\18.0\Microsoft.TeamFoundation.WorkItemTracking.Client.dll + True + + + ..\..\lib\18.0\Microsoft.VisualStudio.Services.Common.dll + True + + + ..\..\lib\18.0\Microsoft.VisualStudio.Services.WebApi.dll + True + + + ..\..\lib\18.0\Newtonsoft.Json.dll + True + + + + + + + + + + + + + + + + + + + Form + + + AddChangesetByIdDialog.cs + + + UserControl + + + AutoMergeControl.cs + + + Form + + + MainForm.cs + + + + + + + AutoMergeControl.cs + + + MainForm.cs + Designer + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + {726ED85E-2274-4D95-B822-B2CFE2CE44B9} + AutoMerge + + + + \ No newline at end of file diff --git a/src/AutoMerge.Standalone/AutoMergeControl.Designer.cs b/src/AutoMerge.Standalone/AutoMergeControl.Designer.cs new file mode 100644 index 0000000..91dc59a --- /dev/null +++ b/src/AutoMerge.Standalone/AutoMergeControl.Designer.cs @@ -0,0 +1,327 @@ +namespace AutoMerge.Standalone +{ + partial class AutoMergeControl + { + private System.ComponentModel.IContainer components = null; + + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + private void InitializeComponent() + { + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.grpChangesets = new System.Windows.Forms.GroupBox(); + this.lstChangesets = new System.Windows.Forms.ListView(); + this.colChangesetId = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.colBranch = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.colComment = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.panel2 = new System.Windows.Forms.Panel(); + this.btnAddById = new System.Windows.Forms.Button(); + this.btnRefresh = new System.Windows.Forms.Button(); + this.grpBranches = new System.Windows.Forms.GroupBox(); + this.lstBranches = new System.Windows.Forms.ListView(); + this.colBranchName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.colType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.colValidation = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.panel3 = new System.Windows.Forms.Panel(); + this.cmbMergeMode = new System.Windows.Forms.ComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.btnMerge = new System.Windows.Forms.Button(); + this.statusStripControl = new System.Windows.Forms.StatusStrip(); + this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel(); + this.lblStatus = new System.Windows.Forms.ToolStripStatusLabel(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.grpChangesets.SuspendLayout(); + this.panel2.SuspendLayout(); + this.grpBranches.SuspendLayout(); + this.panel3.SuspendLayout(); + this.statusStripControl.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Margin = new System.Windows.Forms.Padding(2); + this.splitContainer1.Name = "splitContainer1"; + this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.Controls.Add(this.grpChangesets); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.grpBranches); + this.splitContainer1.Size = new System.Drawing.Size(700, 539); + this.splitContainer1.SplitterDistance = 232; + this.splitContainer1.SplitterWidth = 3; + this.splitContainer1.TabIndex = 0; + // + // grpChangesets + // + this.grpChangesets.Controls.Add(this.lstChangesets); + this.grpChangesets.Controls.Add(this.panel2); + this.grpChangesets.Dock = System.Windows.Forms.DockStyle.Fill; + this.grpChangesets.Location = new System.Drawing.Point(0, 0); + this.grpChangesets.Margin = new System.Windows.Forms.Padding(2); + this.grpChangesets.Name = "grpChangesets"; + this.grpChangesets.Padding = new System.Windows.Forms.Padding(2); + this.grpChangesets.Size = new System.Drawing.Size(700, 232); + this.grpChangesets.TabIndex = 0; + this.grpChangesets.TabStop = false; + this.grpChangesets.Text = "Recent Changesets"; + // + // lstChangesets + // + this.lstChangesets.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.colChangesetId, + this.colBranch, + this.colComment}); + this.lstChangesets.Dock = System.Windows.Forms.DockStyle.Fill; + this.lstChangesets.FullRowSelect = true; + this.lstChangesets.HideSelection = false; + this.lstChangesets.Location = new System.Drawing.Point(2, 51); + this.lstChangesets.Margin = new System.Windows.Forms.Padding(2); + this.lstChangesets.MultiSelect = false; + this.lstChangesets.Name = "lstChangesets"; + this.lstChangesets.Size = new System.Drawing.Size(696, 179); + this.lstChangesets.TabIndex = 0; + this.lstChangesets.UseCompatibleStateImageBehavior = false; + this.lstChangesets.View = System.Windows.Forms.View.Details; + this.lstChangesets.SelectedIndexChanged += new System.EventHandler(this.lstChangesets_SelectedIndexChanged); + // + // colChangesetId + // + this.colChangesetId.Text = "ID"; + this.colChangesetId.Width = 80; + // + // colBranch + // + this.colBranch.Text = "Branch"; + this.colBranch.Width = 150; + // + // colComment + // + this.colComment.Text = "Comment"; + this.colComment.Width = 550; + // + // panel2 + // + this.panel2.Controls.Add(this.btnAddById); + this.panel2.Controls.Add(this.btnRefresh); + this.panel2.Dock = System.Windows.Forms.DockStyle.Top; + this.panel2.Location = new System.Drawing.Point(2, 18); + this.panel2.Margin = new System.Windows.Forms.Padding(2); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(696, 33); + this.panel2.TabIndex = 1; + // + // btnAddById + // + this.btnAddById.Location = new System.Drawing.Point(88, 6); + this.btnAddById.Margin = new System.Windows.Forms.Padding(2); + this.btnAddById.Name = "btnAddById"; + this.btnAddById.Size = new System.Drawing.Size(79, 25); + this.btnAddById.TabIndex = 1; + this.btnAddById.Text = "Add By Id"; + this.btnAddById.UseVisualStyleBackColor = true; + this.btnAddById.Click += new System.EventHandler(this.btnAddById_Click); + // + // btnRefresh + // + this.btnRefresh.Location = new System.Drawing.Point(4, 6); + this.btnRefresh.Margin = new System.Windows.Forms.Padding(2); + this.btnRefresh.Name = "btnRefresh"; + this.btnRefresh.Size = new System.Drawing.Size(79, 25); + this.btnRefresh.TabIndex = 0; + this.btnRefresh.Text = "Refresh"; + this.btnRefresh.UseVisualStyleBackColor = true; + this.btnRefresh.Click += new System.EventHandler(this.btnRefresh_Click); + // + // grpBranches + // + this.grpBranches.Controls.Add(this.lstBranches); + this.grpBranches.Controls.Add(this.panel3); + this.grpBranches.Dock = System.Windows.Forms.DockStyle.Fill; + this.grpBranches.Location = new System.Drawing.Point(0, 0); + this.grpBranches.Margin = new System.Windows.Forms.Padding(2); + this.grpBranches.Name = "grpBranches"; + this.grpBranches.Padding = new System.Windows.Forms.Padding(2); + this.grpBranches.Size = new System.Drawing.Size(700, 304); + this.grpBranches.TabIndex = 0; + this.grpBranches.TabStop = false; + this.grpBranches.Text = "Branches"; + // + // lstBranches + // + this.lstBranches.CheckBoxes = true; + this.lstBranches.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.colBranchName, + this.colType, + this.colValidation}); + this.lstBranches.Dock = System.Windows.Forms.DockStyle.Fill; + this.lstBranches.FullRowSelect = true; + this.lstBranches.HideSelection = false; + this.lstBranches.Location = new System.Drawing.Point(2, 51); + this.lstBranches.Margin = new System.Windows.Forms.Padding(2); + this.lstBranches.Name = "lstBranches"; + this.lstBranches.Size = new System.Drawing.Size(696, 251); + this.lstBranches.TabIndex = 0; + this.lstBranches.UseCompatibleStateImageBehavior = false; + this.lstBranches.View = System.Windows.Forms.View.Details; + this.lstBranches.ItemCheck += new System.Windows.Forms.ItemCheckEventHandler(this.lstBranches_ItemCheck); + this.lstBranches.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.lstBranches_ItemChecked); + this.lstBranches.ItemSelectionChanged += new System.Windows.Forms.ListViewItemSelectionChangedEventHandler(this.lstBranches_ItemSelectionChanged); + // + // colBranchName + // + this.colBranchName.Text = "Branch Name"; + this.colBranchName.Width = 300; + // + // colType + // + this.colType.Text = "Type"; + this.colType.Width = 100; + // + // colValidation + // + this.colValidation.Text = "Validation"; + this.colValidation.Width = 380; + // + // panel3 + // + this.panel3.Controls.Add(this.cmbMergeMode); + this.panel3.Controls.Add(this.label2); + this.panel3.Controls.Add(this.btnMerge); + this.panel3.Dock = System.Windows.Forms.DockStyle.Top; + this.panel3.Location = new System.Drawing.Point(2, 18); + this.panel3.Margin = new System.Windows.Forms.Padding(2); + this.panel3.Name = "panel3"; + this.panel3.Size = new System.Drawing.Size(696, 33); + this.panel3.TabIndex = 1; + // + // cmbMergeMode + // + this.cmbMergeMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbMergeMode.FormattingEnabled = true; + this.cmbMergeMode.Items.AddRange(new object[] { + "Merge", + "Merge and Check In"}); + this.cmbMergeMode.Location = new System.Drawing.Point(175, 7); + this.cmbMergeMode.Margin = new System.Windows.Forms.Padding(2); + this.cmbMergeMode.Name = "cmbMergeMode"; + this.cmbMergeMode.Size = new System.Drawing.Size(132, 23); + this.cmbMergeMode.TabIndex = 2; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(91, 10); + this.label2.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(78, 15); + this.label2.TabIndex = 1; + this.label2.Text = "Merge Mode:"; + // + // btnMerge + // + this.btnMerge.Enabled = false; + this.btnMerge.Location = new System.Drawing.Point(4, 6); + this.btnMerge.Margin = new System.Windows.Forms.Padding(2); + this.btnMerge.Name = "btnMerge"; + this.btnMerge.Size = new System.Drawing.Size(79, 25); + this.btnMerge.TabIndex = 0; + this.btnMerge.Text = "Merge"; + this.btnMerge.UseVisualStyleBackColor = true; + this.btnMerge.Click += new System.EventHandler(this.btnMerge_Click); + // + // statusStripControl + // + this.statusStripControl.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripStatusLabel1, + this.lblStatus}); + this.statusStripControl.Location = new System.Drawing.Point(0, 539); + this.statusStripControl.Name = "statusStripControl"; + this.statusStripControl.Padding = new System.Windows.Forms.Padding(1, 0, 12, 0); + this.statusStripControl.Size = new System.Drawing.Size(700, 24); + this.statusStripControl.TabIndex = 1; + this.statusStripControl.Text = "statusStripControl"; + // + // toolStripStatusLabel1 + // + this.toolStripStatusLabel1.Name = "toolStripStatusLabel1"; + this.toolStripStatusLabel1.Padding = new System.Windows.Forms.Padding(0, 0, 10, 0); + this.toolStripStatusLabel1.Size = new System.Drawing.Size(128, 19); + this.toolStripStatusLabel1.Text = "toolStripStatusLabel1"; + // + // lblStatus + // + this.lblStatus.BorderSides = System.Windows.Forms.ToolStripStatusLabelBorderSides.Left; + this.lblStatus.Name = "lblStatus"; + this.lblStatus.Padding = new System.Windows.Forms.Padding(10, 0, 0, 0); + this.lblStatus.Size = new System.Drawing.Size(53, 19); + this.lblStatus.Text = "Ready"; + // + // AutoMergeControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.splitContainer1); + this.Controls.Add(this.statusStripControl); + this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Margin = new System.Windows.Forms.Padding(2); + this.Name = "AutoMergeControl"; + this.Size = new System.Drawing.Size(700, 563); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.grpChangesets.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.grpBranches.ResumeLayout(false); + this.panel3.ResumeLayout(false); + this.panel3.PerformLayout(); + this.statusStripControl.ResumeLayout(false); + this.statusStripControl.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.SplitContainer splitContainer1; + private System.Windows.Forms.GroupBox grpChangesets; + private System.Windows.Forms.ListView lstChangesets; + private System.Windows.Forms.ColumnHeader colChangesetId; + private System.Windows.Forms.ColumnHeader colBranch; + private System.Windows.Forms.ColumnHeader colComment; + private System.Windows.Forms.Panel panel2; + private System.Windows.Forms.Button btnRefresh; + private System.Windows.Forms.GroupBox grpBranches; + private System.Windows.Forms.ListView lstBranches; + private System.Windows.Forms.ColumnHeader colBranchName; + private System.Windows.Forms.ColumnHeader colType; + private System.Windows.Forms.ColumnHeader colValidation; + private System.Windows.Forms.Panel panel3; + private System.Windows.Forms.Button btnMerge; + private System.Windows.Forms.StatusStrip statusStripControl; + private System.Windows.Forms.ToolStripStatusLabel lblStatus; + private System.Windows.Forms.Button btnAddById; + private System.Windows.Forms.ComboBox cmbMergeMode; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel1; + } +} diff --git a/src/AutoMerge.Standalone/AutoMergeControl.cs b/src/AutoMerge.Standalone/AutoMergeControl.cs new file mode 100644 index 0000000..9a0dd5d --- /dev/null +++ b/src/AutoMerge.Standalone/AutoMergeControl.cs @@ -0,0 +1,300 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; +using Microsoft.TeamFoundation.Client; +using Microsoft.TeamFoundation.VersionControl.Client; +using static System.Windows.Forms.VisualStyles.VisualStyleElement.TreeView; + +namespace AutoMerge.Standalone +{ + public partial class AutoMergeControl : UserControl + { + private const string DefaultTeamProjectName = "Intact iQ"; + + private StandaloneServiceProvider _serviceProvider; + private TfsTeamProjectCollection _tfs; + private VersionControlServer _versionControl; + private global::AutoMerge.ChangesetService _changesetService; + private List _changesets; + private int _selectedChangesetId; + + public AutoMergeControl() + { + InitializeComponent(); + _changesets = new List(); + } + + public void Initialize(StandaloneServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + _tfs = serviceProvider.TfsConnection; + _versionControl = _tfs.GetService(); + _changesetService = new global::AutoMerge.ChangesetService(_versionControl); + cmbMergeMode.SelectedIndex = 0; + + InitializeAsync(); + } + + public void ConnectedStatusMessage(string message) + { + toolStripStatusLabel1.Text = message; + } + + private async Task InitializeAsync() + { + try + { + lblStatus.Text = "Loading changesets..."; + await LoadRecentChangesetsAsync(); + lblStatus.Text = "Ready"; + } + catch (Exception ex) + { + lblStatus.Text = $"Error: {ex.Message}"; + MessageBox.Show($"Failed to initialize:\n\n{ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private async Task LoadRecentChangesetsAsync() + { + var changesetProvider = new global::AutoMerge.MyChangesetChangesetProvider( + _changesetService, + DefaultTeamProjectName, + global::AutoMerge.Settings.Instance.ChangesetCount, + true); + + _changesets = await changesetProvider.GetChangesets(_versionControl.AuthorizedUser); + + UpdateChangesetsView(); + } + + private void UpdateChangesetsView() + { + lstChangesets.Items.Clear(); + + foreach (var changeset in _changesets) + { + var item = new ListViewItem(changeset.ChangesetId.ToString()); + + item.SubItems.Add(""); + + item.SubItems.Add(TruncateComment(changeset.Comment) + (string.IsNullOrWhiteSpace(changeset.DisplayBranchName) ? string.Empty : $" [{changeset.DisplayBranchName}]")); + + item.Tag = changeset; + + lstChangesets.Items.Add(item); + } + + if (lstChangesets.Items.Count > 0) + { + lstChangesets.Items[0].Selected = true; + } + } + + private async Task UpdateBranchesViewAsync() + { + try + { + lstBranches.Items.Clear(); + + lblStatus.Text = $"Loading branches for changeset {_selectedChangesetId}..."; + + var selectedChangeset = lstChangesets.SelectedItems.Count > 0 + ? lstChangesets.SelectedItems[0].Tag as global::AutoMerge.ChangesetViewModel + : null; + + if (selectedChangeset != null) + { + var workspaces = _versionControl.QueryWorkspaces(null, _tfs.AuthorizedIdentity.UniqueName, Environment.MachineName); + var workspace = workspaces.Length == 0 + ? null + : global::AutoMerge.WorkspaceHelper.GetWorkspace(_versionControl, workspaces); + + if (workspace != null) + { + var branches = await Task.Run(() => + global::AutoMerge.BranchesViewModel.GetBranches( + _tfs, + selectedChangeset, + workspace, + _changesetService, + new global::AutoMerge.Prism.Events.EventAggregator())); + + foreach (var branch in branches) + { + var branchText = branch.DisplayBranchName; + + var type = "Target"; + + if (branch.IsSourceBranch) + { + continue; // TODO_DS1 Skip source branches for now, as they can't be merged to + //branchText += " (source)"; + //type = "Source"; + } + + var item = new ListViewItem(branchText) { Checked = !branch.IsSourceBranch && branch.Checked }; + + item.SubItems.Add(type); + + item.SubItems.Add(branch.ValidationMessage ?? string.Empty); + + item.Tag = branch; + + if (branch.ValidationResult != BranchValidationResult.Success) + { + item.ForeColor = System.Drawing.Color.Gray; + } + + lstBranches.Items.Add(item); + } + } + else + { + lblStatus.Text = "No workspace found. Create/map a TFVC workspace and refresh."; + } + } + + UpdateMergeButton(); + + if (lblStatus.Text != "No workspace found. Create/map a TFVC workspace and refresh.") + { + lblStatus.Text = "Ready"; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex); + lblStatus.Text = "Error loading branches"; + MessageBox.Show($"Failed to load branches:\n\n{ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private string TruncateComment(string comment) + { + if (string.IsNullOrEmpty(comment)) + return ""; + + var firstLine = comment.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault() ?? ""; + return firstLine.Length > 80 ? firstLine.Substring(0, 77) + "..." : firstLine; + } + + private async void lstChangesets_SelectedIndexChanged(object sender, EventArgs e) + { + if (lstChangesets.SelectedItems.Count > 0) + { + var changeset = lstChangesets.SelectedItems[0].Tag as global::AutoMerge.ChangesetViewModel; + + if (changeset != null) + { + _selectedChangesetId = changeset.ChangesetId; + + await UpdateBranchesViewAsync(); + } + } + } + + private void lstBranches_ItemChecked(object sender, ItemCheckedEventArgs e) + { + UpdateMergeButton(); + } + + private void lstBranches_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) + { + if (e.Item.Tag is MergeInfoViewModel branch) + { + if (branch.ValidationResult != BranchValidationResult.Success) + e.Item.Selected = false; + } + } + + private void lstBranches_ItemCheck(object sender, ItemCheckEventArgs e) + { + if (lstBranches.Items[e.Index].Tag is MergeInfoViewModel branch) + { + if (branch.ValidationResult != BranchValidationResult.Success) + e.NewValue = e.CurrentValue; + } + } + + private void UpdateMergeButton() + { + btnMerge.Enabled = lstBranches.CheckedItems.Count > 0; + } + + private async void btnMerge_Click(object sender, EventArgs e) + { + try + { + btnMerge.Enabled = false; + lblStatus.Text = "Merging..."; + + // TODO_DS1 Placeholder for merge operation + await Task.Delay(1000); + + lblStatus.Text = "Merge completed"; + MessageBox.Show("Merge operation completed successfully.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + catch (Exception ex) + { + lblStatus.Text = "Merge failed"; + MessageBox.Show($"Merge failed:\n\n{ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + finally + { + btnMerge.Enabled = true; + } + } + + private void btnRefresh_Click(object sender, EventArgs e) + { + InitializeAsync(); + } + + private void btnAddById_Click(object sender, EventArgs e) + { + using (var form = new AddChangesetByIdDialog()) + { + if (form.ShowDialog() == DialogResult.OK) + { + var changesetId = form.ChangesetId; + AddChangesetByIdAsync(changesetId); + } + } + } + + private async void AddChangesetByIdAsync(int changesetId) + { + try + { + lblStatus.Text = $"Loading changeset {changesetId}..."; + + var changesetProvider = new global::AutoMerge.ChangesetByIdChangesetProvider(_changesetService, new[] { changesetId }); + var changesets = await changesetProvider.GetChangesets(null); + if (changesets.Count > 0) + { + _changesets.Add(changesets[0]); + } + + UpdateChangesetsView(); + + var items = lstChangesets.Items.Cast() + .Where(i => ((global::AutoMerge.ChangesetViewModel)i.Tag).ChangesetId == changesetId).ToArray(); + if (items.Length > 0) + { + items[0].Selected = true; + items[0].EnsureVisible(); + } + + lblStatus.Text = "Ready"; + } + catch (Exception ex) + { + lblStatus.Text = "Error loading changeset"; + MessageBox.Show($"Failed to load changeset:\n\n{ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + } +} diff --git a/src/AutoMerge.Standalone/AutoMergeControl.resx b/src/AutoMerge.Standalone/AutoMergeControl.resx new file mode 100644 index 0000000..d6dd895 --- /dev/null +++ b/src/AutoMerge.Standalone/AutoMergeControl.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/src/AutoMerge.Standalone/MainForm.Designer.cs b/src/AutoMerge.Standalone/MainForm.Designer.cs new file mode 100644 index 0000000..f4ed8d4 --- /dev/null +++ b/src/AutoMerge.Standalone/MainForm.Designer.cs @@ -0,0 +1,134 @@ +namespace AutoMerge.Standalone +{ + partial class MainForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.panelContent = new System.Windows.Forms.Panel(); + this.autoMergeControl1 = new AutoMerge.Standalone.AutoMergeControl(); + this.panel1 = new System.Windows.Forms.Panel(); + this.btnConnect = new System.Windows.Forms.Button(); + this.txtTfsUrl = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.panelContent.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // panelContent + // + this.panelContent.Controls.Add(this.autoMergeControl1); + this.panelContent.Dock = System.Windows.Forms.DockStyle.Fill; + this.panelContent.Location = new System.Drawing.Point(0, 57); + this.panelContent.Margin = new System.Windows.Forms.Padding(2); + this.panelContent.Name = "panelContent"; + this.panelContent.Size = new System.Drawing.Size(1469, 625); + this.panelContent.TabIndex = 0; + // + // autoMergeControl1 + // + this.autoMergeControl1.Dock = System.Windows.Forms.DockStyle.Fill; + this.autoMergeControl1.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.autoMergeControl1.Location = new System.Drawing.Point(0, 0); + this.autoMergeControl1.Margin = new System.Windows.Forms.Padding(2); + this.autoMergeControl1.Name = "autoMergeControl1"; + this.autoMergeControl1.Size = new System.Drawing.Size(1469, 625); + this.autoMergeControl1.TabIndex = 0; + // + // panel1 + // + this.panel1.Controls.Add(this.btnConnect); + this.panel1.Controls.Add(this.txtTfsUrl); + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Top; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(2); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(1469, 57); + this.panel1.TabIndex = 1; + // + // btnConnect + // + this.btnConnect.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnConnect.Location = new System.Drawing.Point(1381, 17); + this.btnConnect.Margin = new System.Windows.Forms.Padding(2); + this.btnConnect.Name = "btnConnect"; + this.btnConnect.Size = new System.Drawing.Size(77, 25); + this.btnConnect.TabIndex = 2; + this.btnConnect.Text = "Connect"; + this.btnConnect.UseVisualStyleBackColor = true; + this.btnConnect.Click += new System.EventHandler(this.btnConnect_Click); + // + // txtTfsUrl + // + this.txtTfsUrl.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtTfsUrl.Location = new System.Drawing.Point(79, 18); + this.txtTfsUrl.Margin = new System.Windows.Forms.Padding(2); + this.txtTfsUrl.Name = "txtTfsUrl"; + this.txtTfsUrl.Size = new System.Drawing.Size(1294, 23); + this.txtTfsUrl.TabIndex = 1; + this.txtTfsUrl.Text = "https://devops.intactsoftware.com/tfs"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(10, 22); + this.label1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(53, 15); + this.label1.TabIndex = 0; + this.label1.Text = "TFS URL:"; + // + // MainForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1469, 682); + this.Controls.Add(this.panelContent); + this.Controls.Add(this.panel1); + this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Margin = new System.Windows.Forms.Padding(2); + this.Name = "MainForm"; + this.Text = "AutoMerge - Standalone"; + this.Load += new System.EventHandler(this.MainForm_Load); + this.panelContent.ResumeLayout(false); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Panel panelContent; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Button btnConnect; + private System.Windows.Forms.TextBox txtTfsUrl; + private System.Windows.Forms.Label label1; + private AutoMergeControl autoMergeControl1; + } +} diff --git a/src/AutoMerge.Standalone/MainForm.cs b/src/AutoMerge.Standalone/MainForm.cs new file mode 100644 index 0000000..5508e8a --- /dev/null +++ b/src/AutoMerge.Standalone/MainForm.cs @@ -0,0 +1,64 @@ +using System; +using System.Windows.Forms; + +namespace AutoMerge.Standalone +{ + public partial class MainForm : Form + { + private StandaloneServiceProvider _serviceProvider; + //TODO_DS1 private AutoMergeControl _autoMergeControl; + + public MainForm() + { + InitializeComponent(); + } + + private void btnConnect_Click(object sender, EventArgs e) + { + try + { + btnConnect.Enabled = false; + autoMergeControl1.ConnectedStatusMessage("Connecting..."); + //TODO_DS1 toolStripStatusLabel1.Text = "Connecting..."; + Application.DoEvents(); + + // Create service provider with TFS connection + _serviceProvider = new StandaloneServiceProvider(txtTfsUrl.Text); + + //// TODO_DS1 Create the AutoMerge WinForms control + //_autoMergeControl = new AutoMergeControl(); + //_autoMergeControl.Dock = DockStyle.Fill; + + //// TODO_DS1 Add it to the panel + //panelContent.Controls.Clear(); + //panelContent.Controls.Add(_autoMergeControl); + + // Initialize the control + autoMergeControl1.Initialize(_serviceProvider); + + autoMergeControl1.ConnectedStatusMessage($"Connected to {txtTfsUrl.Text}"); + + //TODO_DS1 toolStripStatusLabel1.Text = $"Connected to {txtTfsUrl.Text}"; + txtTfsUrl.Enabled = false; + } + catch (Exception ex) + { + MessageBox.Show( + $"Failed to connect to TFS:\n\n{ex.Message}", + "Connection Error", + MessageBoxButtons.OK, + MessageBoxIcon.Error); + + autoMergeControl1.ConnectedStatusMessage("Connection failed"); + //TODO_DS1 toolStripStatusLabel1.Text = "Connection failed"; + btnConnect.Enabled = true; + } + } + + // TODO_DS1 Change this to be an auto connect, no need for the button and txtbox + private void MainForm_Load(object sender, EventArgs e) + { + btnConnect_Click(null, EventArgs.Empty); + } + } +} diff --git a/src/AutoMerge.Standalone/MainForm.resx b/src/AutoMerge.Standalone/MainForm.resx new file mode 100644 index 0000000..d58980a --- /dev/null +++ b/src/AutoMerge.Standalone/MainForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/AutoMerge.Standalone/Program.cs b/src/AutoMerge.Standalone/Program.cs new file mode 100644 index 0000000..e008ddf --- /dev/null +++ b/src/AutoMerge.Standalone/Program.cs @@ -0,0 +1,19 @@ +using System; +using System.Windows.Forms; + +namespace AutoMerge.Standalone +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainForm()); + } + } +} diff --git a/src/AutoMerge.Standalone/Properties/AssemblyInfo.cs b/src/AutoMerge.Standalone/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..be6645b --- /dev/null +++ b/src/AutoMerge.Standalone/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("AutoMerge.Standalone")] +[assembly: AssemblyDescription("Standalone AutoMerge application for TFS")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("AutoMerge.Standalone")] +[assembly: AssemblyCopyright("Copyright © 2025")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("8b9a3c2d-5f6e-4a1b-9c3d-2e4f5a6b7c8d")] + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/AutoMerge.Standalone/Properties/Resources.Designer.cs b/src/AutoMerge.Standalone/Properties/Resources.Designer.cs new file mode 100644 index 0000000..6a81269 --- /dev/null +++ b/src/AutoMerge.Standalone/Properties/Resources.Designer.cs @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace AutoMerge.Standalone.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Resources : global::System.Resources.ResourceManager { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AutoMerge.Standalone.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/src/AutoMerge.Standalone/Properties/Resources.resx b/src/AutoMerge.Standalone/Properties/Resources.resx new file mode 100644 index 0000000..1510323 --- /dev/null +++ b/src/AutoMerge.Standalone/Properties/Resources.resx @@ -0,0 +1,15 @@ + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/src/AutoMerge.Standalone/Properties/Settings.Designer.cs b/src/AutoMerge.Standalone/Properties/Settings.Designer.cs new file mode 100644 index 0000000..cb80298 --- /dev/null +++ b/src/AutoMerge.Standalone/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace AutoMerge.Standalone.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/src/AutoMerge.Standalone/Properties/Settings.settings b/src/AutoMerge.Standalone/Properties/Settings.settings new file mode 100644 index 0000000..07e66be --- /dev/null +++ b/src/AutoMerge.Standalone/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/AutoMerge.Standalone/README.md b/src/AutoMerge.Standalone/README.md new file mode 100644 index 0000000..6ca93c8 --- /dev/null +++ b/src/AutoMerge.Standalone/README.md @@ -0,0 +1,75 @@ +# AutoMerge - Setup Complete + +## Summary of Changes + +### 1. Fixed AutoMergeToolWindowControl +**Problem**: The tool window wasn't loading changesets or branch data. + +**Solution**: Modified `AutoMergeToolWindowControl.xaml.cs` to properly initialize the ViewModels: +- Added calls to `Initialize()` with `SectionInitializeEventArgs` +- Added calls to `Refresh()` to trigger data loading +- Kept error handling to show detailed error messages if initialization fails + +The ViewModels (`RecentChangesetsViewModel` and `BranchesViewModel`) inherit from `TeamExplorerSectionViewModelBase` which requires proper initialization before they will load data. + +### 2. Created Standalone WinForms Application +**New Project**: `AutoMerge.Standalone` + +A standalone Windows Forms application that can run the AutoMerge functionality outside of Visual Studio. + +**Key Files Created**: +- `AutoMerge.Standalone.csproj` - Project file targeting .NET Framework 4.8 +- `Program.cs` - Application entry point +- `MainForm.cs/Designer.cs` - Main form with TFS connection UI +- `StandaloneServiceProvider.cs` - Service provider for TFS connectivity +- `StandaloneLogger.cs` - Console/Debug logger implementation + +**Features**: +- Connect to any TFS server by entering the URL +- Hosts the AutoMerge WPF control using ElementHost +- Shows connection status in status bar +- Reuses all the existing AutoMerge logic from the VSIX project + +**How to Use**: +1. Build the solution +2. Run `AutoMerge.Standalone.exe` +3. Enter your TFS URL (e.g., `http://your-tfs-server:8080/tfs/DefaultCollection`) +4. Click "Connect" +5. The AutoMerge interface will load with Recent Changesets and Branches sections + +### 3. Dependencies +Both projects now reference the correct VS 2026 TeamFoundation assemblies: +- Microsoft.TeamFoundation.Client (v20.256.x) +- Microsoft.TeamFoundation.Common (v20.256.x) +- Microsoft.TeamFoundation.Controls (v18.5.x) +- Microsoft.TeamFoundation.VersionControl.Client (v20.256.x) +- Microsoft.TeamFoundation.VersionControl.Common (v20.256.x) +- Microsoft.TeamFoundation.WorkItemTracking.Client (v20.256.x) +- Microsoft.VisualStudio.Services.Common +- Microsoft.VisualStudio.Services.WebApi +- Newtonsoft.Json + +All assemblies are set to `Private=True` so they are packaged with the applications. + +## Testing the VSIX + +1. Press **F5** in Visual Studio +2. A new VS instance will launch (experimental) +3. Open a solution connected to TFS +4. Go to **View > Other Windows > AutoMerge Tool Window** OR +5. Use the Team Explorer button +6. The window should now show your recent changesets and branches + +## Testing the Standalone App + +1. Set `AutoMerge.Standalone` as the startup project +2. Press **F5** or **Ctrl+F5** +3. Enter your TFS server URL +4. Click Connect +5. The AutoMerge interface should load + +## Notes + +- The standalone app requires access to a TFS server +- You may need to adjust the `StandaloneServiceProvider` if additional services are needed +- The standalone app uses the same ViewModels and Views as the VSIX, ensuring consistent behavior diff --git a/src/AutoMerge.Standalone/StandaloneLogger.cs b/src/AutoMerge.Standalone/StandaloneLogger.cs new file mode 100644 index 0000000..349b636 --- /dev/null +++ b/src/AutoMerge.Standalone/StandaloneLogger.cs @@ -0,0 +1,16 @@ +using System; + +namespace AutoMerge +{ + /// + /// Standalone logger implementation that writes to console or debug output + /// + public class StandaloneLogger : LoggerBase + { + protected override void WriteMessage(string message) + { + System.Diagnostics.Debug.WriteLine(message); + Console.WriteLine(message); + } + } +} diff --git a/src/AutoMerge.Standalone/StandaloneServiceProvider.cs b/src/AutoMerge.Standalone/StandaloneServiceProvider.cs new file mode 100644 index 0000000..c2da9cf --- /dev/null +++ b/src/AutoMerge.Standalone/StandaloneServiceProvider.cs @@ -0,0 +1,33 @@ +using System; +using Microsoft.TeamFoundation.Client; + +namespace AutoMerge.Standalone +{ + /// + /// Minimal service provider for standalone operation + /// + public class StandaloneServiceProvider : IServiceProvider + { + private readonly TfsTeamProjectCollection _tfsConnection; + + public StandaloneServiceProvider(string tfsUrl) + { + _tfsConnection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tfsUrl)); + _tfsConnection.EnsureAuthenticated(); + } + + public object GetService(Type serviceType) + { + // Return the TFS connection for TFS-related services + if (serviceType == typeof(TfsTeamProjectCollection)) + { + return _tfsConnection; + } + + // Add more service mappings as needed + return null; + } + + public TfsTeamProjectCollection TfsConnection => _tfsConnection; + } +} diff --git a/src/AutoMerge.sln b/src/AutoMerge.sln index de6d2cd..9f4586a 100644 --- a/src/AutoMerge.sln +++ b/src/AutoMerge.sln @@ -1,16 +1,89 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.32112.339 +# Visual Studio Version 18 +VisualStudioVersion = 18.5.11723.231 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoMerge", "AutoMerge\AutoMerge.csproj", "{726ED85E-2274-4D95-B822-B2CFE2CE44B9}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{5101B5A3-BBBF-474C-8BBF-A61EF46643D0}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lib", "lib", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "15.0", "15.0", "{6C80EF52-0D76-4F42-A66C-B18B1477A8CE}" + ProjectSection(SolutionItems) = preProject + ..\lib\15.0\Microsoft.TeamFoundation.Client.dll = ..\lib\15.0\Microsoft.TeamFoundation.Client.dll + ..\lib\15.0\Microsoft.TeamFoundation.Common.dll = ..\lib\15.0\Microsoft.TeamFoundation.Common.dll + ..\lib\15.0\Microsoft.TeamFoundation.Controls.dll = ..\lib\15.0\Microsoft.TeamFoundation.Controls.dll + ..\lib\15.0\Microsoft.TeamFoundation.VersionControl.Client.dll = ..\lib\15.0\Microsoft.TeamFoundation.VersionControl.Client.dll + ..\lib\15.0\Microsoft.TeamFoundation.VersionControl.Common.dll = ..\lib\15.0\Microsoft.TeamFoundation.VersionControl.Common.dll + ..\lib\15.0\Microsoft.TeamFoundation.WorkItemTracking.Client.dll = ..\lib\15.0\Microsoft.TeamFoundation.WorkItemTracking.Client.dll + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "16.0", "16.0", "{067032CB-986B-4AD8-A3E1-5AE11C544A47}" + ProjectSection(SolutionItems) = preProject + ..\lib\16.0\Microsoft.TeamFoundation.Client.dll = ..\lib\16.0\Microsoft.TeamFoundation.Client.dll + ..\lib\16.0\Microsoft.TeamFoundation.Common.dll = ..\lib\16.0\Microsoft.TeamFoundation.Common.dll + ..\lib\16.0\Microsoft.TeamFoundation.Controls.dll = ..\lib\16.0\Microsoft.TeamFoundation.Controls.dll + ..\lib\16.0\Microsoft.TeamFoundation.VersionControl.Client.dll = ..\lib\16.0\Microsoft.TeamFoundation.VersionControl.Client.dll + ..\lib\16.0\Microsoft.TeamFoundation.VersionControl.Common.dll = ..\lib\16.0\Microsoft.TeamFoundation.VersionControl.Common.dll + ..\lib\16.0\Microsoft.TeamFoundation.WorkItemTracking.Client.dll = ..\lib\16.0\Microsoft.TeamFoundation.WorkItemTracking.Client.dll + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "17.0", "17.0", "{5DC7FDC7-5BF6-4C05-A817-72C9C8546090}" + ProjectSection(SolutionItems) = preProject + ..\lib\17.0\Microsoft.TeamFoundation.Client.dll = ..\lib\17.0\Microsoft.TeamFoundation.Client.dll + ..\lib\17.0\Microsoft.TeamFoundation.Common.dll = ..\lib\17.0\Microsoft.TeamFoundation.Common.dll + ..\lib\17.0\Microsoft.TeamFoundation.Controls.dll = ..\lib\17.0\Microsoft.TeamFoundation.Controls.dll + ..\lib\17.0\Microsoft.TeamFoundation.VersionControl.Client.dll = ..\lib\17.0\Microsoft.TeamFoundation.VersionControl.Client.dll + ..\lib\17.0\Microsoft.TeamFoundation.VersionControl.Common.dll = ..\lib\17.0\Microsoft.TeamFoundation.VersionControl.Common.dll + ..\lib\17.0\Microsoft.TeamFoundation.WorkItemTracking.Client.dll = ..\lib\17.0\Microsoft.TeamFoundation.WorkItemTracking.Client.dll + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "18.0", "18.0", "{2286A832-B2F8-45AE-933A-0A855E5AD542}" + ProjectSection(SolutionItems) = preProject + ..\lib\18.0\Microsoft.TeamFoundation.Client.dll = ..\lib\18.0\Microsoft.TeamFoundation.Client.dll + ..\lib\18.0\Microsoft.TeamFoundation.Common.dll = ..\lib\18.0\Microsoft.TeamFoundation.Common.dll + ..\lib\18.0\Microsoft.TeamFoundation.Controls.dll = ..\lib\18.0\Microsoft.TeamFoundation.Controls.dll + ..\lib\18.0\Microsoft.TeamFoundation.Core.WebApi.dll = ..\lib\18.0\Microsoft.TeamFoundation.Core.WebApi.dll + ..\lib\18.0\Microsoft.TeamFoundation.VersionControl.Client.dll = ..\lib\18.0\Microsoft.TeamFoundation.VersionControl.Client.dll + ..\lib\18.0\Microsoft.TeamFoundation.VersionControl.Common.dll = ..\lib\18.0\Microsoft.TeamFoundation.VersionControl.Common.dll + ..\lib\18.0\Microsoft.TeamFoundation.WorkItemTracking.Client.dll = ..\lib\18.0\Microsoft.TeamFoundation.WorkItemTracking.Client.dll + ..\lib\18.0\Microsoft.VisualStudio.Services.Client.Interactive.dll = ..\lib\18.0\Microsoft.VisualStudio.Services.Client.Interactive.dll + ..\lib\18.0\Microsoft.VisualStudio.Services.Common.dll = ..\lib\18.0\Microsoft.VisualStudio.Services.Common.dll + ..\lib\18.0\Microsoft.VisualStudio.Services.WebApi.dll = ..\lib\18.0\Microsoft.VisualStudio.Services.WebApi.dll + ..\lib\18.0\Newtonsoft.Json.dll = ..\lib\18.0\Newtonsoft.Json.dll + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8FE189FF-6AED-444C-B5DE-9F841281B0F9}" + ProjectSection(SolutionItems) = preProject + ..\.gitignore = ..\.gitignore + ..\azure-pipelines.yml = ..\azure-pipelines.yml + ..\build_v15.bat = ..\build_v15.bat + ..\build_v16.bat = ..\build_v16.bat + ..\build_v17.bat = ..\build_v17.bat + ..\LICENSE.txt = ..\LICENSE.txt + ..\README.md = ..\README.md + ..\RELEASE_NOTES.md = ..\RELEASE_NOTES.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{D5AD1EB1-1B80-450D-92E2-C6FECB556576}" + ProjectSection(SolutionItems) = preProject + ..\build\build.fsx = ..\build\build.fsx + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "screenshots", "screenshots", "{A2F06608-443F-4923-8E3B-3BC7E7E40A53}" + ProjectSection(SolutionItems) = preProject + ..\screenshots\automerge_main.png = ..\screenshots\automerge_main.png + ..\screenshots\automerge_teamexplorer.png = ..\screenshots\automerge_teamexplorer.png + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{49184F69-C2BE-4D0C-A9FF-0EEC2C428589}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuGet", "NuGet", "{96B4F09B-685B-4CF3-9B02-FECE8807BE4C}" ProjectSection(SolutionItems) = preProject - ..\build.fsx = ..\build.fsx + ..\tools\NuGet\LICENSE.txt = ..\tools\NuGet\LICENSE.txt + ..\tools\NuGet\NuGet.exe = ..\tools\NuGet\NuGet.exe EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoMerge.Tests", "AutoMerge.Tests\AutoMerge.Tests.csproj", "{4CD4D5BB-FE08-4DAF-A952-93E78022FC12}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoMerge.Standalone", "AutoMerge.Standalone\AutoMerge.Standalone.csproj", "{8B9A3C2D-5F6E-4A1B-9C3D-2E4F5A6B7C8D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -22,12 +95,25 @@ Global {726ED85E-2274-4D95-B822-B2CFE2CE44B9}.Debug|Any CPU.Build.0 = Debug|Any CPU {726ED85E-2274-4D95-B822-B2CFE2CE44B9}.Release|Any CPU.ActiveCfg = Release|Any CPU {726ED85E-2274-4D95-B822-B2CFE2CE44B9}.Release|Any CPU.Build.0 = Release|Any CPU - {4CD4D5BB-FE08-4DAF-A952-93E78022FC12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4CD4D5BB-FE08-4DAF-A952-93E78022FC12}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B9A3C2D-5F6E-4A1B-9C3D-2E4F5A6B7C8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B9A3C2D-5F6E-4A1B-9C3D-2E4F5A6B7C8D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B9A3C2D-5F6E-4A1B-9C3D-2E4F5A6B7C8D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B9A3C2D-5F6E-4A1B-9C3D-2E4F5A6B7C8D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {8FE189FF-6AED-444C-B5DE-9F841281B0F9} + {6C80EF52-0D76-4F42-A66C-B18B1477A8CE} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {067032CB-986B-4AD8-A3E1-5AE11C544A47} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {5DC7FDC7-5BF6-4C05-A817-72C9C8546090} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {2286A832-B2F8-45AE-933A-0A855E5AD542} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {D5AD1EB1-1B80-450D-92E2-C6FECB556576} = {8FE189FF-6AED-444C-B5DE-9F841281B0F9} + {A2F06608-443F-4923-8E3B-3BC7E7E40A53} = {8FE189FF-6AED-444C-B5DE-9F841281B0F9} + {49184F69-C2BE-4D0C-A9FF-0EEC2C428589} = {8FE189FF-6AED-444C-B5DE-9F841281B0F9} + {96B4F09B-685B-4CF3-9B02-FECE8807BE4C} = {49184F69-C2BE-4D0C-A9FF-0EEC2C428589} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D74AF8A3-6127-40F7-B6C1-E948F866D814} EndGlobalSection diff --git a/src/AutoMerge/AutoMerge.csproj b/src/AutoMerge/AutoMerge.csproj index a8d6876..377c502 100644 --- a/src/AutoMerge/AutoMerge.csproj +++ b/src/AutoMerge/AutoMerge.csproj @@ -1,6 +1,12 @@ - + + + true + True + True + True + $(OutDir) $(VisualStudioVersion) 15.0 Program @@ -60,33 +66,54 @@ ..\..\lib\$(VisualStudioVersion)\Microsoft.TeamFoundation.Client.dll - False + True ..\..\lib\$(VisualStudioVersion)\Microsoft.TeamFoundation.Common.dll - False + True ..\..\lib\$(VisualStudioVersion)\Microsoft.TeamFoundation.Controls.dll - False + True ..\..\lib\$(VisualStudioVersion)\Microsoft.TeamFoundation.VersionControl.Client.dll - False + True ..\..\lib\$(VisualStudioVersion)\Microsoft.TeamFoundation.VersionControl.Common.dll - False + True ..\..\lib\$(VisualStudioVersion)\Microsoft.TeamFoundation.WorkItemTracking.Client.dll - False + True + + + ..\..\lib\$(VisualStudioVersion)\Microsoft.VisualStudio.Services.Common.dll + True + + + ..\..\lib\$(VisualStudioVersion)\Microsoft.VisualStudio.Services.WebApi.dll + True + + + ..\..\lib\$(VisualStudioVersion)\Newtonsoft.Json.dll + True + + + - + + + + + + + @@ -187,7 +214,7 @@ - ResXFileCodeGenerator + PublicResXFileCodeGenerator Resources.Designer.cs Designer @@ -220,6 +247,10 @@ true + + + + @@ -249,7 +280,6 @@ true - diff --git a/src/AutoMerge/AutoMergeNavigationItem.cs b/src/AutoMerge/AutoMergeNavigationItem.cs index 590daae..fe887f8 100644 --- a/src/AutoMerge/AutoMergeNavigationItem.cs +++ b/src/AutoMerge/AutoMergeNavigationItem.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel.Composition; using AutoMerge.Base; using AutoMerge.VersionControl; @@ -7,18 +7,18 @@ namespace AutoMerge { - [TeamExplorerNavigationItem(GuidList.AutoMergeNavigationItemId, 210, TargetPageId = GuidList.AutoMergePageId)] - public class AutoMergeNavigationItem : TeamExplorerNavigationItemBase - { + [TeamExplorerNavigationItem(GuidList.AutoMergeNavigationItemId, 210, TargetPageId = GuidList.AutoMergePageId)] + public class AutoMergeNavigationItem : TeamExplorerNavigationItemBase + { - [ImportingConstructor] - public AutoMergeNavigationItem( - [Import(typeof(SVsServiceProvider))] - IServiceProvider serviceProvider) - : base(serviceProvider, GuidList.AutoMergePageId, VersionControlProvider.TeamFoundation) - { - Text = Resources.AutoMergePageName; - Image = Resources.MergeImage; - } - } + [ImportingConstructor] + public AutoMergeNavigationItem( + [Import(typeof(SVsServiceProvider))] + IServiceProvider serviceProvider) + : base(serviceProvider, GuidList.AutoMergePageId, VersionControlProvider.TeamFoundation) + { + Text = Resources.AutoMergePageName; + Image = Resources.MergeImage; + } + } } diff --git a/src/AutoMerge/Base/TeamExplorerSectionViewModelBase.cs b/src/AutoMerge/Base/TeamExplorerSectionViewModelBase.cs index 5bfb91e..eb526a9 100644 --- a/src/AutoMerge/Base/TeamExplorerSectionViewModelBase.cs +++ b/src/AutoMerge/Base/TeamExplorerSectionViewModelBase.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using Microsoft.TeamFoundation.Client; using Microsoft.TeamFoundation.Common.Internal; @@ -8,96 +8,96 @@ namespace AutoMerge.Base { - public abstract class TeamExplorerSectionViewModelBase : TfsTeamExplorerSectionViewModelBase - { - private readonly ILogger _logger; - private static readonly Task _emptyTask = Task.FromResult(0); + public abstract class TeamExplorerSectionViewModelBase : TfsTeamExplorerSectionViewModelBase + { + private readonly ILogger _logger; + private static readonly Task _emptyTask = Task.FromResult(0); - private ITeamFoundationContextManager TfsContextManager { get; set; } - protected ITeamFoundationContext Context { get; private set; } + private ITeamFoundationContextManager TfsContextManager { get; set; } + protected ITeamFoundationContext Context { get; private set; } - protected TeamExplorerSectionViewModelBase(ILogger logger) - { - _logger = logger; - } + protected TeamExplorerSectionViewModelBase(ILogger logger) + { + _logger = logger; + } - protected ILogger Logger { get { return _logger; } } + protected ILogger Logger { get { return _logger; } } - protected virtual Task RefreshAsync() - { - return _emptyTask; - } + protected virtual Task RefreshAsync() + { + return _emptyTask; + } - protected virtual Task InitializeAsync(object sender, SectionInitializeEventArgs e) - { - return _emptyTask; - } + protected virtual Task InitializeAsync(object sender, SectionInitializeEventArgs e) + { + return _emptyTask; + } - public async override void Initialize(object sender, SectionInitializeEventArgs e) - { - ShowBusy(); - - try - { - base.Initialize(sender, e); - if (ServiceProvider != null) - { - TfsContextManager = ServiceProvider.GetService(); - if (TfsContextManager != null) - { - TfsContextManager.ContextChanged -= OnContextChanged; - TfsContextManager.ContextChanged += OnContextChanged; - var context = TfsContextManager.CurrentContext; - Context = context; - } - } - await InitializeAsync(sender, e); - } - catch (Exception ex) - { - ShowError(ex.Message); - } - - HideBusy(); - } + public async override void Initialize(object sender, SectionInitializeEventArgs e) + { + ShowBusy(); - public async override void Refresh() + try + { + base.Initialize(sender, e); + if (ServiceProvider != null) { - ShowBusy(); - - try - { - await RefreshAsync(); - } - catch (Exception ex) - { - ShowError(ex.Message); - _logger.Error(ex.Message, ex); - } - - HideBusy(); + TfsContextManager = ServiceProvider.GetService(); + if (TfsContextManager != null) + { + TfsContextManager.ContextChanged -= OnContextChanged; + TfsContextManager.ContextChanged += OnContextChanged; + var context = TfsContextManager.CurrentContext; + Context = context; + } } + await InitializeAsync(sender, e); + } + catch (Exception ex) + { + ShowError(ex.Message); + } + + HideBusy(); + } - protected void SetMvvmFocus(string id, params object[] args) - { - var focusService = TryResolveService(); - if (focusService == null) - return; - focusService.SetFocus(id, args); - } + public async override void Refresh() + { + ShowBusy(); + + try + { + await RefreshAsync(); + } + catch (Exception ex) + { + ShowError(ex.Message); + _logger.Error(ex.Message, ex); + } + + HideBusy(); + } - public override void Dispose() - { - base.Dispose(); - if (TfsContextManager != null) - { - TfsContextManager.ContextChanged -= OnContextChanged; - } - } + protected void SetMvvmFocus(string id, params object[] args) + { + var focusService = TryResolveService(); + if (focusService == null) + return; + focusService.SetFocus(id, args); + } - protected virtual void OnContextChanged(object sender, ContextChangedEventArgs e) - { + public override void Dispose() + { + base.Dispose(); + if (TfsContextManager != null) + { + TfsContextManager.ContextChanged -= OnContextChanged; + } + } + + protected virtual void OnContextChanged(object sender, ContextChangedEventArgs e) + { - } } + } } diff --git a/src/AutoMerge/Branches/BranchesViewModel.cs b/src/AutoMerge/Branches/BranchesViewModel.cs index ec7d9e8..e9949c4 100644 --- a/src/AutoMerge/Branches/BranchesViewModel.cs +++ b/src/AutoMerge/Branches/BranchesViewModel.cs @@ -21,1361 +21,1414 @@ namespace AutoMerge { - public sealed class BranchesViewModel : TeamExplorerSectionViewModelBase - { - private readonly IEventAggregator _eventAggregator; - private ChangesetService _changesetService; - private Workspace _workspace; + public sealed class BranchesViewModel : TeamExplorerSectionViewModelBase + { + private readonly IEventAggregator _eventAggregator; + private ChangesetService _changesetService; + private Workspace _workspace; - private ChangesetViewModel _changeset; - private bool _merging; + private ChangesetViewModel _changeset; + private bool _merging; - public BranchesViewModel(ILogger logger) - : base(logger) - { - Title = Resources.BrancheSectionName; - IsVisible = true; - IsExpanded = true; - IsBusy = false; + public BranchesViewModel(ILogger logger) + : base(logger) + { + Title = Resources.BrancheSectionName; + IsVisible = true; + IsExpanded = true; + IsBusy = false; - MergeCommand = new DelegateCommand(MergeExecute, m => MergeCanEcexute()); - SelectWorkspaceCommand = new DelegateCommand(SelectWorkspaceExecute); - OpenSourceControlExplorerCommand = new DelegateCommand(OpenSourceControlExplorerExecute, OpenSourceControlExplorerCanExecute); + MergeCommand = new DelegateCommand(MergeExecute, m => MergeCanEcexute()); + SelectWorkspaceCommand = new DelegateCommand(SelectWorkspaceExecute); + OpenSourceControlExplorerCommand = new DelegateCommand(OpenSourceControlExplorerExecute, OpenSourceControlExplorerCanExecute); - _eventAggregator = EventAggregatorFactory.Get(); - _merging = false; - } + _eventAggregator = EventAggregatorFactory.Get(); + _merging = false; + } - public ObservableCollection Branches - { - get - { - return _branches; - } - set - { - _branches = value; - RaisePropertyChanged("Branches"); - } - } - private ObservableCollection _branches; + public ObservableCollection Branches + { + get + { + return _branches; + } + set + { + _branches = value; + RaisePropertyChanged("Branches"); + } + } + private ObservableCollection _branches; - private MergeInfoViewModel _selectedBranch; + private MergeInfoViewModel _selectedBranch; - public MergeInfoViewModel SelectedBranch - { - get - { - return _selectedBranch; - } - set - { - _selectedBranch = value; - RaisePropertyChanged("SelectedBranch"); - } - } + public MergeInfoViewModel SelectedBranch + { + get + { + return _selectedBranch; + } + set + { + _selectedBranch = value; + RaisePropertyChanged("SelectedBranch"); + } + } - public DelegateCommand MergeCommand { get; private set; } + public DelegateCommand MergeCommand { get; private set; } - public MergeOption MergeOption - { - get { return _mergeOption; } - set - { - _mergeOption = value; - RaisePropertyChanged("MergeOption"); - } - } - private MergeOption _mergeOption; + public MergeOption MergeOption + { + get { return _mergeOption; } + set + { + _mergeOption = value; + RaisePropertyChanged("MergeOption"); + } + } + private MergeOption _mergeOption; - public string ErrorMessage - { - get - { - return _errorMessage; - } - set - { - _errorMessage = value; - RaisePropertyChanged("ErrorMessage"); - } - } - private string _errorMessage; + public string ErrorMessage + { + get + { + return _errorMessage; + } + set + { + _errorMessage = value; + RaisePropertyChanged("ErrorMessage"); + } + } + private string _errorMessage; - public Workspace Workspace - { - get - { - return _workspace; - } - set - { - _workspace = value; - RaisePropertyChanged("Workspace"); - } - } + public Workspace Workspace + { + get + { + return _workspace; + } + set + { + _workspace = value; + RaisePropertyChanged("Workspace"); + } + } - private ObservableCollection _workspaces; + private ObservableCollection _workspaces; - public ObservableCollection Workspaces - { - get - { - return _workspaces; - } - set - { - _workspaces = value; - RaisePropertyChanged("Workspaces"); - } - } + public ObservableCollection Workspaces + { + get + { + return _workspaces; + } + set + { + _workspaces = value; + RaisePropertyChanged("Workspaces"); + } + } - private bool _showWorkspaceChooser; - public bool ShowWorkspaceChooser - { - get - { - return _showWorkspaceChooser; - } - set - { - _showWorkspaceChooser = value; - RaisePropertyChanged("ShowWorkspaceChooser"); - } - } + private bool _showWorkspaceChooser; + public bool ShowWorkspaceChooser + { + get + { + return _showWorkspaceChooser; + } + set + { + _showWorkspaceChooser = value; + RaisePropertyChanged("ShowWorkspaceChooser"); + } + } - private MergeMode _mergeMode; - public MergeMode MergeMode - { - get - { - return _mergeMode; - } - set - { - _mergeMode = value; - RaisePropertyChanged("MergeMode"); - } - } + private MergeMode _mergeMode; + public MergeMode MergeMode + { + get + { + return _mergeMode; + } + set + { + _mergeMode = value; + RaisePropertyChanged("MergeMode"); + } + } - private ObservableCollection _mergeModes; + private ObservableCollection _mergeModes; - public ObservableCollection MergeModes - { - get - { - return _mergeModes; - } - set - { - _mergeModes = value; - RaisePropertyChanged("MergeModes"); - } - } + public ObservableCollection MergeModes + { + get + { + return _mergeModes; + } + set + { + _mergeModes = value; + RaisePropertyChanged("MergeModes"); + } + } - public DelegateCommand SelectWorkspaceCommand { get; set; } + public DelegateCommand SelectWorkspaceCommand { get; set; } - public DelegateCommand OpenSourceControlExplorerCommand { get; set; } + public DelegateCommand OpenSourceControlExplorerCommand { get; set; } - private static ObservableCollection GetWorkspaces(VersionControlServer versionControl, TfsTeamProjectCollection tfs) - { - var queryWorkspaces = versionControl.QueryWorkspaces(null, tfs.AuthorizedIdentity.UniqueName, Environment.MachineName); - if (queryWorkspaces.Length > 1) - { - return new ObservableCollection(queryWorkspaces.OrderBy(w => w.Name)); - } - return new ObservableCollection(queryWorkspaces); - } + private static ObservableCollection GetWorkspaces(VersionControlServer versionControl, TfsTeamProjectCollection tfs) + { + var queryWorkspaces = versionControl.QueryWorkspaces(null, tfs.AuthorizedIdentity.UniqueName, Environment.MachineName); + if (queryWorkspaces.Length > 1) + { + return new ObservableCollection(queryWorkspaces.OrderBy(w => w.Name)); + } + return new ObservableCollection(queryWorkspaces); + } - protected async override Task InitializeAsync(object sender, SectionInitializeEventArgs e) - { - Logger.Debug("Start initilize branches section"); + protected async override Task InitializeAsync(object sender, SectionInitializeEventArgs e) + { + Logger.Debug("Start initialize branches section"); - var tfs = Context.TeamProjectCollection; - var versionControl = tfs.GetService(); - SubscribeWorkspaceChanges(versionControl); + // Wait for context to be available if navigating from toolbar/menu + TfsTeamProjectCollection tfs = null; + VersionControlServer versionControl = null; - _changesetService = new ChangesetService(versionControl); + if (Context?.TeamProjectCollection == null) + { + Logger.Debug("Context.TeamProjectCollection is null, attempting to get context from ServiceProvider..."); - _eventAggregator.GetEvent() - .Subscribe(OnSelectedChangeset); - _eventAggregator.GetEvent() - .Subscribe(OnBranchSelectedChanged); + // Try to get context directly from ServiceProvider + var maxRetries = 20; + var retryCount = 0; + while (retryCount < maxRetries) + { + if (Context?.TeamProjectCollection != null) + { + tfs = Context.TeamProjectCollection; + break; + } + + // Also try getting it through VersionControlNavigationHelper + var contextFromHelper = VersionControlNavigationHelper.GetTeamFoundationContext(ServiceProvider); + if (contextFromHelper?.TeamProjectCollection != null) + { + tfs = contextFromHelper.TeamProjectCollection; + Logger.Debug("Got TeamProjectCollection from VersionControlNavigationHelper"); + break; + } + + await Task.Delay(100); + retryCount++; + } - if (e.Context == null) - { - Workspaces = GetWorkspaces(versionControl, tfs); - if (Workspaces.Count > 0) - { - Workspace = WorkspaceHelper.GetWorkspace(versionControl, Workspaces); - ShowWorkspaceChooser = Workspaces.Count > 1; - } - else - { - Workspace = null; - } + if (tfs == null) + { + Logger.Error("TeamProjectCollection is still null after waiting"); + ErrorMessage = "Unable to connect to Team Project. Please ensure you are connected to a TFS/Azure DevOps project."; + Branches = new ObservableCollection(); + return; + } + } + else + { + tfs = Context.TeamProjectCollection; + } + + versionControl = tfs.GetService(); + SubscribeWorkspaceChanges(versionControl); + + _changesetService = new ChangesetService(versionControl); + + _eventAggregator.GetEvent() + .Subscribe(OnSelectedChangeset); + _eventAggregator.GetEvent() + .Subscribe(OnBranchSelectedChanged); + + if (e.Context == null) + { + Workspaces = GetWorkspaces(versionControl, tfs); + if (Workspaces.Count > 0) + { + Workspace = WorkspaceHelper.GetWorkspace(versionControl, Workspaces); + ShowWorkspaceChooser = Workspaces.Count > 1; + } + else + { + Workspace = null; + } - MergeModes = new ObservableCollection + MergeModes = new ObservableCollection { MergeMode.Merge, MergeMode.MergeAndCheckIn }; - MergeMode = Settings.Instance.LastMergeOperation; + MergeMode = Settings.Instance.LastMergeOperation; - await RefreshAsync(); - } - else - { - RestoreContext(e); - } + await RefreshAsync(); + } + else + { + RestoreContext(e); + } - Logger.Debug("End initialize branches section"); - } + Logger.Debug("End initialize branches section"); + } - private void OnBranchSelectedChanged(MergeInfoViewModel obj) - { - MergeCommand.RaiseCanExecuteChanged(); - } + private void OnBranchSelectedChanged(MergeInfoViewModel obj) + { + MergeCommand.RaiseCanExecuteChanged(); + } - /// - /// Refresh the changeset data asynchronously. - /// - protected override async Task RefreshAsync() + /// + /// Refresh the changeset data asynchronously. + /// + protected override async Task RefreshAsync() + { + var changeset = _changeset; + Logger.Debug("Start refresh branches section for changeset {0} ...", + changeset == null ? "null" : changeset.ChangesetId.ToString(CultureInfo.InvariantCulture)); + + string errorMessage = null; + if (Workspaces.Count == 0) + { + errorMessage = "Workspaces not found"; + } + errorMessage = errorMessage ?? CalculateError(changeset); + if (changeset == null || !string.IsNullOrEmpty(errorMessage)) + { + ErrorMessage = errorMessage; + Branches = new ObservableCollection(); + } + else + { + Logger.Info("Getting branches for changeset {0} ...", + changeset.ChangesetId.ToString(CultureInfo.InvariantCulture)); + if (Context?.TeamProjectCollection == null) { - var changeset = _changeset; - Logger.Debug("Start refresh branches section for changeset {0} ...", - changeset == null ? "null" : changeset.ChangesetId.ToString(CultureInfo.InvariantCulture)); - - string errorMessage = null; - if (Workspaces.Count == 0) - { - errorMessage = "Workspaces not found"; - } - errorMessage = errorMessage ?? CalculateError(changeset); - if (changeset == null || !string.IsNullOrEmpty(errorMessage)) - { - ErrorMessage = errorMessage; - Branches = new ObservableCollection(); - } - else - { - Logger.Info("Getting branches for changeset {0} ...", - changeset.ChangesetId.ToString(CultureInfo.InvariantCulture)); - var branches = await Task.Run(() => GetBranches(Context, changeset)); - Logger.Info("Getting branches end for changeset {0}", - changeset.ChangesetId.ToString(CultureInfo.InvariantCulture)); - - // Selected changeset in sequence - if (changeset.ChangesetId == _changeset.ChangesetId) - { - Branches = branches; - ErrorMessage = branches.Count <= 1 ? "Target branches not found" : null; - MergeCommand.RaiseCanExecuteChanged(); - } - } - Logger.Debug("End refresh branches section for changeset {0}", - changeset == null ? "null" : changeset.ChangesetId.ToString(CultureInfo.InvariantCulture)); + Logger.Error("Unable to load branches: TeamProjectCollection context is not available."); + ErrorMessage = "Unable to connect to Team Project. Please ensure you are connected to a TFS/Azure DevOps project."; + Branches = new ObservableCollection(); + return; } - private static string CalculateError(ChangesetViewModel changeset) - { - if (changeset == null) - return "Changeset not selected"; - - if (changeset.Branches.IsNullOrEmpty()) - return "Changeset has not branch"; - - if (changeset.Branches.Count > 1) - return string.Format("Changeset has {0} branches. Merge not possible.", changeset.Branches.Count); + var branches = await Task.Run(() => GetBranches(Context.TeamProjectCollection, changeset, _workspace, _changesetService, _eventAggregator)); + Logger.Info("Getting branches end for changeset {0}", + changeset.ChangesetId.ToString(CultureInfo.InvariantCulture)); - return null; - } - - private void OnSelectedChangeset(ChangesetViewModel changeset) + // Selected changeset in sequence + if (changeset.ChangesetId == _changeset.ChangesetId) { - _changeset = changeset; - Refresh(); + Branches = branches; + ErrorMessage = branches.Count <= 1 ? "Target branches not found" : null; + MergeCommand.RaiseCanExecuteChanged(); } + } + Logger.Debug("End refresh branches section for changeset {0}", + changeset == null ? "null" : changeset.ChangesetId.ToString(CultureInfo.InvariantCulture)); + } - private ObservableCollection GetBranches(ITeamFoundationContext context, ChangesetViewModel changesetViewModel) - { - if (context == null) - return new ObservableCollection(); - var tfs = context.TeamProjectCollection; - var versionControl = tfs.GetService(); + private static string CalculateError(ChangesetViewModel changeset) + { + if (changeset == null) + return "Changeset not selected"; - var result = new ObservableCollection(); + if (changeset.Branches.IsNullOrEmpty()) + return "Changeset has no branch"; - var workspace = _workspace; + if (changeset.Branches.Count > 1) + return string.Format("Changeset has {0} branches. Merge not possible.", changeset.Branches.Count); - var changesetService = _changesetService; + return null; + } - var changes = changesetService.GetChanges(changesetViewModel.ChangesetId); + private void OnSelectedChangeset(ChangesetViewModel changeset) + { + _changeset = changeset; + Refresh(); + } - var sourceTopFolder = CalculateTopFolder(changes); - var mergesRelationships = GetMergesRelationships(sourceTopFolder, versionControl); + public static ObservableCollection GetBranches( + TfsTeamProjectCollection tfs, + ChangesetViewModel changesetViewModel, + Workspace workspace, + ChangesetService changesetService, + IEventAggregator eventAggregator) + { + if (tfs == null || changesetViewModel == null || changesetService == null || workspace == null || eventAggregator == null) + return new ObservableCollection(); - if (mergesRelationships.Count > 0) - { - var sourceBranchIdentifier = changesetViewModel.Branches.Select(b => new ItemIdentifier(b)).First(); + var versionControl = tfs.GetService(); - var sourceBranch = sourceBranchIdentifier.Item; + var result = new ObservableCollection(); - var trackMerges = versionControl.TrackMerges(new[] { changesetViewModel.ChangesetId }, - new ItemIdentifier(sourceTopFolder), - mergesRelationships.ToArray(), - null); + var changes = changesetService.GetChanges(changesetViewModel.ChangesetId); - var changesetVersionSpec = new ChangesetVersionSpec(changesetViewModel.ChangesetId); + var sourceTopFolder = CalculateTopFolder(changes); + var mergesRelationships = GetMergesRelationships(sourceTopFolder, versionControl); - var branchValidator = new BranchValidator(workspace, trackMerges); - var branchFactory = new BranchFactory(sourceBranch, sourceTopFolder, - changesetVersionSpec, branchValidator, - _eventAggregator); + if (mergesRelationships.Count > 0) + { + var sourceBranchIdentifier = changesetViewModel.Branches.Select(b => new ItemIdentifier(b)).First(); - var sourceBranchInfo = versionControl.QueryBranchObjects(sourceBranchIdentifier, RecursionType.None)[0]; - if (sourceBranchInfo.Properties != null && sourceBranchInfo.Properties.ParentBranch != null - && !sourceBranchInfo.Properties.ParentBranch.IsDeleted) - { - var targetBranch = sourceBranchInfo.Properties.ParentBranch; - var targetPath = GetTargetPath(mergesRelationships, targetBranch); - if (targetPath != null) - { - var mergeInfo = branchFactory.CreateTargetBranchInfo(targetBranch, targetPath); - mergeInfo._checked = mergeInfo.ValidationResult == BranchValidationResult.Success; + var sourceBranch = sourceBranchIdentifier.Item; - result.Add(mergeInfo); - } - } + var trackMerges = versionControl.TrackMerges(new[] { changesetViewModel.ChangesetId }, + new ItemIdentifier(sourceTopFolder), + mergesRelationships.ToArray(), + null); - var currentBranchInfo = branchFactory.CreateSourceBranch(); - result.Add(currentBranchInfo); + var changesetVersionSpec = new ChangesetVersionSpec(changesetViewModel.ChangesetId); - if (sourceBranchInfo.ChildBranches != null) - { - var childBranches = sourceBranchInfo.ChildBranches.Where(b => !b.IsDeleted) - .Reverse(); - foreach (var childBranch in childBranches) - { - var targetBranch = childBranch; - var targetPath = GetTargetPath(mergesRelationships, targetBranch); - if (targetPath != null) - { - var mergeInfo = branchFactory.CreateTargetBranchInfo(targetBranch, targetPath); - result.Add(mergeInfo); - } - } - } - - // Feature branch - if (mergesRelationships.Count > 0) - { - var changetIds = - mergesRelationships.Select(r => r.Version).Cast().Select(c => c.ChangesetId) - .Distinct() - .ToArray(); - var branches = _changesetService.GetAssociatedBranches(changetIds); - - foreach (var mergesRelationship in mergesRelationships) - { - var targetBranch = branches.FirstOrDefault(b => IsTargetPath(mergesRelationship, b)); - if (targetBranch != null) - { - var mergeInfo = branchFactory.CreateTargetBranchInfo(targetBranch, mergesRelationship); - result.Add(mergeInfo); - } - } - } - } - - return result; - } + var branchValidator = new BranchValidator(workspace, trackMerges); + var branchFactory = new BranchFactory(sourceBranch, sourceTopFolder, + changesetVersionSpec, branchValidator, + eventAggregator); - private static List GetMergesRelationships(string sourceTopFolder, VersionControlServer versionControl) + var sourceBranchInfo = versionControl.QueryBranchObjects(sourceBranchIdentifier, RecursionType.None)[0]; + if (sourceBranchInfo.Properties != null && sourceBranchInfo.Properties.ParentBranch != null + && !sourceBranchInfo.Properties.ParentBranch.IsDeleted) { - return versionControl.QueryMergeRelationships(sourceTopFolder) - .Where(r => !r.IsDeleted) - .ToList(); + var targetBranch = sourceBranchInfo.Properties.ParentBranch; + var targetPath = GetTargetPath(mergesRelationships, targetBranch); + if (targetPath != null) + { + var mergeInfo = branchFactory.CreateTargetBranchInfo(targetBranch, targetPath); + mergeInfo._checked = mergeInfo.ValidationResult == BranchValidationResult.Success; + + result.Add(mergeInfo); + } } - private TrackMergeInfo GetTrackMergeInfo(VersionControlServer versionControl, - IEnumerable allTrackMerges, - string sourcePath) - { - var result = new TrackMergeInfo - { - FromOriginalToSourceBranches = new List(), - }; - - // INTACT Tweak based on ihcaesar:branch_not_found PR - var trackMerges = allTrackMerges.Where(m => string.Equals(m.TargetItem.Item, sourcePath, StringComparison.OrdinalIgnoreCase)).ToArray(); - //var trackMerges = allTrackMerges.Where(m => m.TargetItem.Item == sourcePath).ToArray(); + var currentBranchInfo = branchFactory.CreateSourceBranch(); + result.Add(currentBranchInfo); - if (!trackMerges.IsNullOrEmpty()) - { - var changesetIds = trackMerges.Select(t => t.SourceChangeset.ChangesetId).ToArray(); - var mergeSourceBranches = _changesetService.GetAssociatedBranches(changesetIds) - .Select(b => b.Item) - .Distinct() - .ToArray(); - - if (mergeSourceBranches.Length == 1) - { - result.FromOriginalToSourceBranches.Add(mergeSourceBranches[0]); - - var sourceFolder = trackMerges.First().SourceItem.Item.ServerItem; - var comment = trackMerges.First().SourceChangeset.Comment; - if (trackMerges.Length > 1) - { - foreach (var merge in trackMerges.Skip(1)) - sourceFolder = FindShareFolder(sourceFolder, merge.SourceItem.Item.ServerItem); - comment = "source changeset has several comments"; - } - - var sourceMergesRelationships = versionControl.QueryMergeRelationships(sourceFolder) - .Where(r => !r.IsDeleted) - .ToArray(); - - var sourceTrackMerges = versionControl.TrackMerges(changesetIds, - new ItemIdentifier(sourceFolder), - sourceMergesRelationships, - null); - - var sourceTrackMergeInfo = GetTrackMergeInfo(versionControl, sourceTrackMerges, sourceFolder); - - if (!sourceTrackMergeInfo.FromOriginalToSourceBranches.IsNullOrEmpty()) - { - result.FromOriginalToSourceBranches.AddRange(sourceTrackMergeInfo.FromOriginalToSourceBranches); - result.OriginalComment = sourceTrackMergeInfo.OriginalComment; - result.OriginaBranch = sourceTrackMergeInfo.OriginaBranch; - } - else - { - result.OriginalComment = comment; - result.OriginaBranch = mergeSourceBranches[0]; - } - } - else - { - result.FromOriginalToSourceBranches.Add("multi"); - result.OriginaBranch = "multi"; - result.OriginalComment = "source changeset has several comments"; - } + if (sourceBranchInfo.ChildBranches != null) + { + var childBranches = sourceBranchInfo.ChildBranches.Where(b => !b.IsDeleted) + .Reverse(); + foreach (var childBranch in childBranches) + { + var targetBranch = childBranch; + var targetPath = GetTargetPath(mergesRelationships, targetBranch); + if (targetPath != null) + { + var mergeInfo = branchFactory.CreateTargetBranchInfo(targetBranch, targetPath); + result.Add(mergeInfo); } - - return result; + } } - private static ItemIdentifier GetTargetPath(ICollection mergesRelationships, ItemIdentifier targetBranch) + // Feature branch + if (mergesRelationships.Count > 0) { - if (mergesRelationships == null || mergesRelationships.Count == 0) - return null; - var targetItem = mergesRelationships.FirstOrDefault(m => IsTargetPath(m, targetBranch)); - if (targetItem != null) - { - mergesRelationships.Remove(targetItem); - return targetItem; + var changetIds = + mergesRelationships.Select(r => r.Version).Cast().Select(c => c.ChangesetId) + .Distinct() + .ToArray(); + var branches = changesetService.GetAssociatedBranches(changetIds); + + foreach (var mergesRelationship in mergesRelationships) + { + var targetBranch = branches.FirstOrDefault(b => IsTargetPath(mergesRelationship, b)); + if (targetBranch != null) + { + var mergeInfo = branchFactory.CreateTargetBranchInfo(targetBranch, mergesRelationship); + result.Add(mergeInfo); } - - return null; + } } + } - private static bool IsTargetPath(ItemIdentifier mergeRelations, ItemIdentifier branch) - { - // INTACT Tweak based on ihcaesar:branch_not_found PR - return mergeRelations.Item.IndexOf(branch.Item + '/', StringComparison.OrdinalIgnoreCase) >= 0 || - string.Compare(mergeRelations.Item, branch.Item, StringComparison.OrdinalIgnoreCase) == 0; + return result; + } - // INTACT Tweak to improve hit rates - //return mergeRelations.Item.Contains(branch.Item + "/"); // ORIGINAL - //return mergeRelations.Item.StartsWith(branch.Item); // INTACT - } + private static List GetMergesRelationships(string sourceTopFolder, VersionControlServer versionControl) + { + return versionControl.QueryMergeRelationships(sourceTopFolder) + .Where(r => !r.IsDeleted) + .ToList(); + } - private static string CalculateTopFolder(IList changes) + private TrackMergeInfo GetTrackMergeInfo(VersionControlServer versionControl, + IEnumerable allTrackMerges, + string sourcePath) + { + var result = new TrackMergeInfo + { + FromOriginalToSourceBranches = new List(), + }; + + // INTACT Tweak based on ihcaesar:branch_not_found PR + var trackMerges = allTrackMerges.Where(m => string.Equals(m.TargetItem.Item, sourcePath, StringComparison.OrdinalIgnoreCase)).ToArray(); + //var trackMerges = allTrackMerges.Where(m => m.TargetItem.Item == sourcePath).ToArray(); + + if (!trackMerges.IsNullOrEmpty()) + { + var changesetIds = trackMerges.Select(t => t.SourceChangeset.ChangesetId).ToArray(); + var mergeSourceBranches = _changesetService.GetAssociatedBranches(changesetIds) + .Select(b => b.Item) + .Distinct() + .ToArray(); + + if (mergeSourceBranches.Length == 1) { - if (changes == null || changes.Count == 0) - throw new ArgumentNullException("changes"); - - string topFolder = null; - if (changes.Count == 1 && - (changes[0].ChangeType.HasFlag(ChangeType.Edit) - && !changes[0].ChangeType.HasFlag(ChangeType.Add) - && !changes[0].ChangeType.HasFlag(ChangeType.Branch))) - { - topFolder = changes[0].Item.ServerItem; - } - else - { - foreach (var change in changes) - { -// if (SkipChange(change.ChangeType, change.Item)) -// continue; - -// if (topFolder != null) -// { -// if (!IsNeedCalculateTopFolder(change.ChangeType, change.Item) change.Item.ServerItem.Contains(topFolder) && change.Item.ServerItem != topFolder) -// continue; -// } - - var changeFolder = ExtractFolder(change.ChangeType, change.Item); - if (changeFolder != topFolder) - topFolder = FindShareFolder(topFolder, changeFolder); - } - } - - if (topFolder != null && topFolder.EndsWith("/")) - { - topFolder = topFolder.Substring(0, topFolder.Length - 1); - } - - return topFolder; + result.FromOriginalToSourceBranches.Add(mergeSourceBranches[0]); + + var sourceFolder = trackMerges.First().SourceItem.Item.ServerItem; + var comment = trackMerges.First().SourceChangeset.Comment; + if (trackMerges.Length > 1) + { + foreach (var merge in trackMerges.Skip(1)) + sourceFolder = FindShareFolder(sourceFolder, merge.SourceItem.Item.ServerItem); + comment = "source changeset has several comments"; + } + + var sourceMergesRelationships = versionControl.QueryMergeRelationships(sourceFolder) + .Where(r => !r.IsDeleted) + .ToArray(); + + var sourceTrackMerges = versionControl.TrackMerges(changesetIds, + new ItemIdentifier(sourceFolder), + sourceMergesRelationships, + null); + + var sourceTrackMergeInfo = GetTrackMergeInfo(versionControl, sourceTrackMerges, sourceFolder); + + if (!sourceTrackMergeInfo.FromOriginalToSourceBranches.IsNullOrEmpty()) + { + result.FromOriginalToSourceBranches.AddRange(sourceTrackMergeInfo.FromOriginalToSourceBranches); + result.OriginalComment = sourceTrackMergeInfo.OriginalComment; + result.OriginaBranch = sourceTrackMergeInfo.OriginaBranch; + } + else + { + result.OriginalComment = comment; + result.OriginaBranch = mergeSourceBranches[0]; + } } - - private static string FindShareFolder(string topFolder, string changeFolder) + else { - // INTACT Tweak based on ihcaesar:branch_not_found PR - //if ((topFolder == null) || topFolder.Contains(changeFolder)) - if ((topFolder == null) || topFolder.IndexOf(changeFolder, StringComparison.OrdinalIgnoreCase) >= 0) - { - return changeFolder; - } - const string rootFolder = "$/"; - var folder = topFolder; - while (folder != rootFolder && !changeFolder.StartsWith(folder, StringComparison.OrdinalIgnoreCase)) - { - folder = ExtractParentFolder(folder); - if (folder != null && changeFolder.StartsWith(folder, StringComparison.OrdinalIgnoreCase)) - break; - } - - return folder == rootFolder ? folder + "/" : folder; + result.FromOriginalToSourceBranches.Add("multi"); + result.OriginaBranch = "multi"; + result.OriginalComment = "source changeset has several comments"; } + } + + return result; + } + + private static ItemIdentifier GetTargetPath(ICollection mergesRelationships, ItemIdentifier targetBranch) + { + if (mergesRelationships == null || mergesRelationships.Count == 0) + return null; + var targetItem = mergesRelationships.FirstOrDefault(m => IsTargetPath(m, targetBranch)); + if (targetItem != null) + { + mergesRelationships.Remove(targetItem); + return targetItem; + } + + return null; + } -// private static bool SkipChange(ChangeType changeType, Item item) -// { -// return changeType.HasFlag(ChangeType.SourceRename) && changeType.HasFlag(ChangeType.Delete); -// } + private static bool IsTargetPath(ItemIdentifier mergeRelations, ItemIdentifier branch) + { + // INTACT Tweak based on ihcaesar:branch_not_found PR + return mergeRelations.Item.IndexOf(branch.Item + '/', StringComparison.OrdinalIgnoreCase) >= 0 || + string.Compare(mergeRelations.Item, branch.Item, StringComparison.OrdinalIgnoreCase) == 0; - private static string ExtractFolder(ChangeType changeType, Item item) + // INTACT Tweak to improve hit rates + //return mergeRelations.Item.Contains(branch.Item + "/"); // ORIGINAL + //return mergeRelations.Item.StartsWith(branch.Item); // INTACT + } + + private static string CalculateTopFolder(IList changes) + { + if (changes == null || changes.Count == 0) + throw new ArgumentNullException("changes"); + + string topFolder = null; + if (changes.Count == 1 && + (changes[0].ChangeType.HasFlag(ChangeType.Edit) + && !changes[0].ChangeType.HasFlag(ChangeType.Add) + && !changes[0].ChangeType.HasFlag(ChangeType.Branch))) + { + topFolder = changes[0].Item.ServerItem; + } + else + { + foreach (var change in changes) { - return ExtractFolder(changeType, item.ServerItem, item.ItemType); + // if (SkipChange(change.ChangeType, change.Item)) + // continue; + + // if (topFolder != null) + // { + // if (!IsNeedCalculateTopFolder(change.ChangeType, change.Item) change.Item.ServerItem.Contains(topFolder) && change.Item.ServerItem != topFolder) + // continue; + // } + + var changeFolder = ExtractFolder(change.ChangeType, change.Item); + if (changeFolder != topFolder) + topFolder = FindShareFolder(topFolder, changeFolder); } + } - private static string ExtractFolder(ChangeType changeType, string path, ItemType itemType) - { - if (IsNeedCalculateTopFolder(changeType, itemType)) - return ExtractParentFolder(path); + if (topFolder != null && topFolder.EndsWith("/")) + { + topFolder = topFolder.Substring(0, topFolder.Length - 1); + } - return itemType == ItemType.Folder - ? path - : ExtractParentFolder(path); - } + return topFolder; + } - private static bool IsNeedCalculateTopFolder(ChangeType changeType, ItemType itemType) - { - return ((changeType.HasFlag(ChangeType.Add) - || changeType.HasFlag(ChangeType.Branch) - || changeType.HasFlag(ChangeType.Rename)) && itemType == ItemType.Folder) - || (itemType == ItemType.File); + private static string FindShareFolder(string topFolder, string changeFolder) + { + // INTACT Tweak based on ihcaesar:branch_not_found PR + //if ((topFolder == null) || topFolder.Contains(changeFolder)) + if ((topFolder == null) || topFolder.IndexOf(changeFolder, StringComparison.OrdinalIgnoreCase) >= 0) + { + return changeFolder; + } + const string rootFolder = "$/"; + var folder = topFolder; + while (folder != rootFolder && !changeFolder.StartsWith(folder, StringComparison.OrdinalIgnoreCase)) + { + folder = ExtractParentFolder(folder); + if (folder != null && changeFolder.StartsWith(folder, StringComparison.OrdinalIgnoreCase)) + break; + } + + return folder == rootFolder ? folder + "/" : folder; + } - } + // private static bool SkipChange(ChangeType changeType, Item item) + // { + // return changeType.HasFlag(ChangeType.SourceRename) && changeType.HasFlag(ChangeType.Delete); + // } - private static string ExtractParentFolder(string serverItem) - { - if (string.IsNullOrWhiteSpace(serverItem)) - throw new ArgumentNullException("serverItem"); + private static string ExtractFolder(ChangeType changeType, Item item) + { + return ExtractFolder(changeType, item.ServerItem, item.ItemType); + } - if (serverItem.EndsWith("/")) - serverItem = serverItem.Substring(0, serverItem.Length - 1); + private static string ExtractFolder(ChangeType changeType, string path, ItemType itemType) + { + if (IsNeedCalculateTopFolder(changeType, itemType)) + return ExtractParentFolder(path); - var lastPosDelimiter = serverItem.LastIndexOf('/'); - if (lastPosDelimiter < 0) - throw new InvalidOperationException(string.Format("Folder delimiter for {0} not found", serverItem)); + return itemType == ItemType.Folder + ? path + : ExtractParentFolder(path); + } - return serverItem.Substring(0, lastPosDelimiter + 1); - } + private static bool IsNeedCalculateTopFolder(ChangeType changeType, ItemType itemType) + { + return ((changeType.HasFlag(ChangeType.Add) + || changeType.HasFlag(ChangeType.Branch) + || changeType.HasFlag(ChangeType.Rename)) && itemType == ItemType.Folder) + || (itemType == ItemType.File); - private async void MergeExecute(MergeMode? mergeMode) - { - Logger.Info("Merging start..."); - if (!mergeMode.HasValue) - return; - MergeMode = mergeMode.Value; - Settings.Instance.LastMergeOperation = mergeMode.Value; - switch (mergeMode) - { - case MergeMode.Merge: - await MergeAndCheckInExecute(false); - break; - case MergeMode.MergeAndCheckIn: - await MergeAndCheckInExecute(true); - break; - } - Logger.Info("Merging end"); - } + } - private async Task MergeAndCheckInExecute(bool checkInIfSuccess) - { - try - { - IsBusy = true; - _merging = true; + private static string ExtractParentFolder(string serverItem) + { + if (string.IsNullOrWhiteSpace(serverItem)) + throw new ArgumentNullException("serverItem"); - MergeCommand.RaiseCanExecuteChanged(); + if (serverItem.EndsWith("/")) + serverItem = serverItem.Substring(0, serverItem.Length - 1); - var result = await Task.Run(() => MergeExecuteInternal(checkInIfSuccess)); - var notifications = new List(); - var notCheckedIn = new List(result.Count); - ClearNotifications(); - foreach (var resultModel in result) - { - var notification = new Notification - { - NotificationType = NotificationType.Information, - Message = string.Empty - }; - var mergePath = string.Format("MERGE {0} -> {1}", - BranchHelper.GetShortBranchName(resultModel.BranchInfo.SourceBranch), - BranchHelper.GetShortBranchName(resultModel.BranchInfo.TargetBranch)); - switch (resultModel.MergeResult) - { - case MergeResult.CheckInEvaluateFail: - notification.NotificationType = NotificationType.Error; - notification.Message = "Check In evaluate failed."; - notCheckedIn.Add(resultModel); - break; - case MergeResult.CheckInFail: - notification.NotificationType = NotificationType.Error; - notification.Message = "Check In failed."; - notCheckedIn.Add(resultModel); - break; - case MergeResult.NothingMerge: - notification.NotificationType = NotificationType.Warning; - notification.Message = "Nothing merged."; - break; - case MergeResult.HasConflicts: - notification.NotificationType = NotificationType.Error; - notification.Message = "Has conflicts."; - notCheckedIn.Add(resultModel); - break; - case MergeResult.CanNotGetLatest: - notification.NotificationType = NotificationType.Error; - notification.Message = "Can not get latest."; - break; - case MergeResult.UnexpectedFileRestored: - notification.NotificationType = NotificationType.Warning; - notification.Message = "Some unexpected files were restored."; - notCheckedIn.Add(resultModel); - break; - case MergeResult.Merged: - notification.NotificationType = NotificationType.Information; - notification.Message = "Files merged but not checked in."; - notCheckedIn.Add(resultModel); - break; - case MergeResult.CheckIn: - var changesetId = resultModel.TagetChangesetId.Value; - notification.NotificationType = NotificationType.Information; - notification.Message = string.Format("[Changeset {0}](Click to view the changeset details) successfully checked in.", changesetId); - notification.Command = new DelegateCommand(() => ViewChangesetDetailsExecute(changesetId)); - break; - } - notification.Message = string.Format("{0}: {1}", mergePath, notification.Message); - if (!string.IsNullOrEmpty(notification.Message)) - notifications.Add(notification); - } - - if (notCheckedIn.Count > 0) - { - OpenPendingChanges(notCheckedIn); - } - else - { - _eventAggregator.GetEvent().Publish(true); - } + var lastPosDelimiter = serverItem.LastIndexOf('/'); + if (lastPosDelimiter < 0) + throw new InvalidOperationException(string.Format("Folder delimiter for {0} not found", serverItem)); - foreach (var notification in notifications) - { - ShowNotification(notification.Message, notification.NotificationType, NotificationFlags.RequiresConfirmation, notification.Command, Guid.NewGuid()); - } - } - catch (Exception ex) - { - Logger.Error("Error while merging", ex); - ClearNotifications(); - ShowError(ex.Message); - } - finally - { - IsBusy = false; - _merging = false; - MergeCommand.RaiseCanExecuteChanged(); - } + return serverItem.Substring(0, lastPosDelimiter + 1); + } + + private async void MergeExecute(MergeMode? mergeMode) + { + Logger.Info("Merging start..."); + if (!mergeMode.HasValue) + return; + MergeMode = mergeMode.Value; + Settings.Instance.LastMergeOperation = mergeMode.Value; + switch (mergeMode) + { + case MergeMode.Merge: + await MergeAndCheckInExecute(false); + break; + case MergeMode.MergeAndCheckIn: + await MergeAndCheckInExecute(true); + break; + } + Logger.Info("Merging end"); + } + + private async Task MergeAndCheckInExecute(bool checkInIfSuccess) + { + try + { + IsBusy = true; + _merging = true; + + MergeCommand.RaiseCanExecuteChanged(); + + var result = await Task.Run(() => MergeExecuteInternal(checkInIfSuccess)); + var notifications = new List(); + var notCheckedIn = new List(result.Count); + ClearNotifications(); + foreach (var resultModel in result) + { + var notification = new Notification + { + NotificationType = NotificationType.Information, + Message = string.Empty + }; + var mergePath = string.Format("MERGE {0} -> {1}", + BranchHelper.GetShortBranchName(resultModel.BranchInfo.SourceBranch), + BranchHelper.GetShortBranchName(resultModel.BranchInfo.TargetBranch)); + switch (resultModel.MergeResult) + { + case MergeResult.CheckInEvaluateFail: + notification.NotificationType = NotificationType.Error; + notification.Message = "Check In evaluate failed."; + notCheckedIn.Add(resultModel); + break; + case MergeResult.CheckInFail: + notification.NotificationType = NotificationType.Error; + notification.Message = "Check In failed."; + notCheckedIn.Add(resultModel); + break; + case MergeResult.NothingMerge: + notification.NotificationType = NotificationType.Warning; + notification.Message = "Nothing merged."; + break; + case MergeResult.HasConflicts: + notification.NotificationType = NotificationType.Error; + notification.Message = "Has conflicts."; + notCheckedIn.Add(resultModel); + break; + case MergeResult.CanNotGetLatest: + notification.NotificationType = NotificationType.Error; + notification.Message = "Can not get latest."; + break; + case MergeResult.UnexpectedFileRestored: + notification.NotificationType = NotificationType.Warning; + notification.Message = "Some unexpected files were restored."; + notCheckedIn.Add(resultModel); + break; + case MergeResult.Merged: + notification.NotificationType = NotificationType.Information; + notification.Message = "Files merged but not checked in."; + notCheckedIn.Add(resultModel); + break; + case MergeResult.CheckIn: + var changesetId = resultModel.TagetChangesetId.Value; + notification.NotificationType = NotificationType.Information; + notification.Message = string.Format("[Changeset {0}](Click to view the changeset details) successfully checked in.", changesetId); + notification.Command = new DelegateCommand(() => ViewChangesetDetailsExecute(changesetId)); + break; + } + notification.Message = string.Format("{0}: {1}", mergePath, notification.Message); + if (!string.IsNullOrEmpty(notification.Message)) + notifications.Add(notification); } - private string ConsolidateDuplicateComments(IEnumerable resultModels) + if (notCheckedIn.Count > 0) { - return String.Join(";", resultModels.Select(rm => rm.Comment).Distinct()); + OpenPendingChanges(notCheckedIn); } - - private void OpenPendingChanges(ICollection resultModels) + else { - var pendingChanges = new List(20); - // all results must have identical workItems - var workItemIds = resultModels.First().WorkItemIds; - - var conflictsPath = new List(); - foreach (var resultModel in resultModels) - { - if (resultModel.PendingChanges != null && resultModel.PendingChanges.Count > 0) - pendingChanges.AddRange(resultModel.PendingChanges); - - if (resultModel.MergeResult == MergeResult.HasConflicts - || resultModel.MergeResult == MergeResult.UnexpectedFileRestored) - conflictsPath.Add(resultModel.BranchInfo.TargetPath); - } - - if (conflictsPath.Count > 0) - InvokeResolveConflictsPage(_workspace, conflictsPath.ToArray()); - OpenPendingChanges(pendingChanges, workItemIds, ConsolidateDuplicateComments(resultModels)); + _eventAggregator.GetEvent().Publish(true); } - private void OpenPendingChanges(List pendingChanges, List workItemIds, string comment) + foreach (var notification in notifications) { - var teamExplorer = ServiceProvider.GetService(); - var pendingChangesPage = (TeamExplorerPageBase)teamExplorer.NavigateToPage(new Guid(TeamExplorerPageIds.PendingChanges), null); - var model = (IPendingCheckin)pendingChangesPage.Model; - model.PendingChanges.Comment = comment; - model.PendingChanges.CheckedPendingChanges = pendingChanges.ToArray(); - - if (Workspaces.Count > 1) - { - var modelType = model.GetType(); - var workspaceProperty = modelType.GetProperty("Workspace"); - workspaceProperty.SetValue(model, Workspace); - } - - if (workItemIds != null && workItemIds.Count > 0) - { - var modelType = model.GetType(); - var method = modelType.GetMethod("AddWorkItemsByIdAsync", - BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); - var workItemsIdsArray = workItemIds.ToArray(); - method.Invoke(model, new object[] { workItemsIdsArray, 1 /* Add */}); - } + ShowNotification(notification.Message, notification.NotificationType, NotificationFlags.RequiresConfirmation, notification.Command, Guid.NewGuid()); } + } + catch (Exception ex) + { + Logger.Error("Error while merging", ex); + ClearNotifications(); + ShowError(ex.Message); + } + finally + { + IsBusy = false; + _merging = false; + MergeCommand.RaiseCanExecuteChanged(); + } + } - private List MergeExecuteInternal(bool checkInIfSuccess) - { - var result = new List(); - var context = Context; - var tfs = context.TeamProjectCollection; - var versionControl = tfs.GetService(); - - var workspace = _workspace; - - var changesetId = _changeset.ChangesetId; - var changesetService = _changesetService; - var changeset = changesetService.GetChangeset(changesetId); - changeset.Changes = changesetService.GetChanges(changesetId); - var mergeOption = _mergeOption; - var workItemStore = tfs.GetService(); - var workItemIds = changeset.AssociatedWorkItems != null - ? changeset.AssociatedWorkItems.Select(w => w.Id).ToList() - : new List(); - - var mergeInfos = _branches; - var targetBranches = mergeInfos.Select(m => m.TargetBranch).ToArray(); - var pendingChanges = GetChangesetPendingChanges(changeset.Changes); - var mergeRelationships = GetMergeRelationships(pendingChanges, targetBranches, versionControl); - - var commentFormater = new CommentFormater(Settings.Instance.CommentFormat); - foreach (var mergeInfo in mergeInfos.Where(b => b.Checked)) - { - var mergeResultModel = new MergeResultModel - { - SourceChangesetId = changesetId, - BranchInfo = mergeInfo, - }; + private string ConsolidateDuplicateComments(IEnumerable resultModels) + { + return String.Join(";", resultModels.Select(rm => rm.Comment).Distinct()); + } - var mergeResult = MergeToBranch(mergeInfo, mergeOption, mergeRelationships, workspace); - var targetPendingChanges = GetPendingChanges(mergeInfo.TargetPath, workspace); - if (mergeResult == MergeResult.UnexpectedFileRestored) - { - workspace.Undo(targetPendingChanges.Select(pendingChange => new ItemSpec(pendingChange)).ToArray(), - true); - mergeResult = MergeByFile(changeset.Changes, mergeInfo.TargetBranch, mergeRelationships, - mergeInfo.ChangesetVersionSpec, mergeOption, workspace); - targetPendingChanges = GetPendingChangesByFile(mergeRelationships, mergeInfo.TargetBranch, workspace); - } - - if (targetPendingChanges.Count == 0) - { - mergeResult = MergeResult.NothingMerge; - } - mergeResultModel.MergeResult = mergeResult; - mergeResultModel.PendingChanges = targetPendingChanges; - mergeResultModel.WorkItemIds = workItemIds; - - var trackMergeInfo = GetTrackMergeInfo(mergeInfo, changeset, versionControl); - var comment = commentFormater.Format(trackMergeInfo, mergeInfo.TargetBranch, mergeOption); - mergeResultModel.Comment = comment; - - result.Add(mergeResultModel); - if (checkInIfSuccess && mergeResultModel.MergeResult == MergeResult.Merged) - { - var checkInResult = CheckIn(mergeResultModel.PendingChanges, comment, workspace, workItemIds, changeset.PolicyOverride, workItemStore); - mergeResultModel.TagetChangesetId = checkInResult.ChangesetId; - mergeResultModel.MergeResult = checkInResult.CheckinResult; - } - } + private void OpenPendingChanges(ICollection resultModels) + { + var pendingChanges = new List(20); + // all results must have identical workItems + var workItemIds = resultModels.First().WorkItemIds; + + var conflictsPath = new List(); + foreach (var resultModel in resultModels) + { + if (resultModel.PendingChanges != null && resultModel.PendingChanges.Count > 0) + pendingChanges.AddRange(resultModel.PendingChanges); + + if (resultModel.MergeResult == MergeResult.HasConflicts + || resultModel.MergeResult == MergeResult.UnexpectedFileRestored) + conflictsPath.Add(resultModel.BranchInfo.TargetPath); + } + + if (conflictsPath.Count > 0) + InvokeResolveConflictsPage(_workspace, conflictsPath.ToArray()); + OpenPendingChanges(pendingChanges, workItemIds, ConsolidateDuplicateComments(resultModels)); + } - return result; - } + private void OpenPendingChanges(List pendingChanges, List workItemIds, string comment) + { + var teamExplorer = ServiceProvider.GetService(); + var pendingChangesPage = (TeamExplorerPageBase)teamExplorer.NavigateToPage(new Guid(TeamExplorerPageIds.PendingChanges), null); + var model = (IPendingCheckin)pendingChangesPage.Model; + model.PendingChanges.Comment = comment; + model.PendingChanges.CheckedPendingChanges = pendingChanges.ToArray(); + + if (Workspaces.Count > 1) + { + var modelType = model.GetType(); + var workspaceProperty = modelType.GetProperty("Workspace"); + workspaceProperty.SetValue(model, Workspace); + } + + if (workItemIds != null && workItemIds.Count > 0) + { + var modelType = model.GetType(); + var method = modelType.GetMethod("AddWorkItemsByIdAsync", + BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); + var workItemsIdsArray = workItemIds.ToArray(); + method.Invoke(model, new object[] { workItemsIdsArray, 1 /* Add */}); + } + } - // Copy from Microsoft.TeamFoundation.VersionControl.Controls.PendingChanges.ChangesetDataProvider.ResetDataFromChangeset, - // Microsoft.TeamFoundation.VersionControl.Controls - private static List GetChangesetPendingChanges(Change[] changes) + private List MergeExecuteInternal(bool checkInIfSuccess) + { + var result = new List(); + var context = Context; + var tfs = context.TeamProjectCollection; + var versionControl = tfs.GetService(); + + var workspace = _workspace; + + var changesetId = _changeset.ChangesetId; + var changesetService = _changesetService; + var changeset = changesetService.GetChangeset(changesetId); + changeset.Changes = changesetService.GetChanges(changesetId); + var mergeOption = _mergeOption; + var workItemStore = tfs.GetService(); + var workItemIds = changeset.AssociatedWorkItems != null + ? changeset.AssociatedWorkItems.Select(w => w.Id).ToList() + : new List(); + + var mergeInfos = _branches; + var targetBranches = mergeInfos.Select(m => m.TargetBranch).ToArray(); + var pendingChanges = GetChangesetPendingChanges(changeset.Changes); + var mergeRelationships = GetMergeRelationships(pendingChanges, targetBranches, versionControl); + + var commentFormater = new CommentFormater(Settings.Instance.CommentFormat); + foreach (var mergeInfo in mergeInfos.Where(b => b.Checked)) + { + var mergeResultModel = new MergeResultModel { - var pendingChanges = new List(changes.Length); - foreach (var change in changes) - { - if (ChangeType.SourceRename != (change.ChangeType & (ChangeType.Add | ChangeType.Branch | ChangeType.Rename | ChangeType.SourceRename))) - { - var pendingChange = new PendingChange(change); - if (change.MergeSources != null) - { - foreach (var mergeSource in change.MergeSources) - { - if (mergeSource.IsRename) - { - pendingChange.UpdateSourceItems(null, mergeSource.ServerItem); - break; - } - } - } - pendingChanges.Add(pendingChange); - } - } + SourceChangesetId = changesetId, + BranchInfo = mergeInfo, + }; - return pendingChanges; + var mergeResult = MergeToBranch(mergeInfo, mergeOption, mergeRelationships, workspace); + var targetPendingChanges = GetPendingChanges(mergeInfo.TargetPath, workspace); + if (mergeResult == MergeResult.UnexpectedFileRestored) + { + workspace.Undo(targetPendingChanges.Select(pendingChange => new ItemSpec(pendingChange)).ToArray(), + true); + mergeResult = MergeByFile(changeset.Changes, mergeInfo.TargetBranch, mergeRelationships, + mergeInfo.ChangesetVersionSpec, mergeOption, workspace); + targetPendingChanges = GetPendingChangesByFile(mergeRelationships, mergeInfo.TargetBranch, workspace); } - private static List GetMergeRelationships(List pendingChanges, string[] targetBranches, VersionControlServer versionControl) + if (targetPendingChanges.Count == 0) { - var mergeRelationships = new List(); - - foreach (var pendingChange in pendingChanges) - { - if (pendingChange.IsAdd || pendingChange.IsBranch) - { - var parentFolder = ExtractFolder(pendingChange.ChangeType, pendingChange.ServerItem, pendingChange.ItemType); - var parentFolderRelationships = versionControl.QueryMergeRelationships(parentFolder); - if (parentFolderRelationships != null) - { - foreach (var parentFolderRelationship in parentFolderRelationships.Where(r => !r.IsDeleted)) - { - mergeRelationships.Add(new MergeRelation - { - Item = pendingChange.ServerItem, - Source = parentFolder, - Target = parentFolderRelationship.Item, - TargetItemType = ItemType.Folder, - GetLatesPath = parentFolderRelationship.Item, - Recursively = pendingChange.ItemType == ItemType.Folder - }); - } - } - } - else if (pendingChange.IsRename) - { - var shareFolder = FindShareFolder(pendingChange.ServerItem, pendingChange.SourceServerItem); - var shareFolderRelationships = versionControl.QueryMergeRelationships(shareFolder); - var sourceRelationships = versionControl.QueryMergeRelationships(pendingChange.SourceServerItem) ?? new ItemIdentifier[0]; - if (shareFolderRelationships != null) - { - foreach (var shareFolderRelationship in shareFolderRelationships.Where(r => !r.IsDeleted)) - { - // INTACT Tweak based on ihcaesar:branch_not_found PR - var targetBranch = - //targetBranches.FirstOrDefault(branch => shareFolderRelationship.Item.Contains(branch)); - targetBranches.FirstOrDefault(branch => shareFolderRelationship.Item.IndexOf(branch, StringComparison.OrdinalIgnoreCase) >= 0); - if (targetBranch != null) - { - // INTACT Tweak based on ihcaesar:branch_not_found PR - var sourceRelationship = sourceRelationships - //.FirstOrDefault(r => r.Item.Contains(targetBranch)); - .FirstOrDefault(r => r.Item.IndexOf(targetBranch, StringComparison.OrdinalIgnoreCase) >= 0); - mergeRelationships.Add(new MergeRelation - { - Item = pendingChange.ServerItem, - Source = shareFolder, - Target = shareFolderRelationship.Item, - TargetItemType = ItemType.Folder, - GetLatesPath = sourceRelationship != null ? sourceRelationship.Item : null, - Recursively = true - }); - } - } - } - } - else - { - var changeRelationShips = versionControl.QueryMergeRelationships(pendingChange.ServerItem); - if (changeRelationShips != null) - { - foreach (var changeRelationShip in changeRelationShips.Where(r => !r.IsDeleted)) - { - mergeRelationships.Add(new MergeRelation - { - Item = pendingChange.ServerItem, - Source = pendingChange.ServerItem, - Target = changeRelationShip.Item, - TargetItemType = pendingChange.ItemType, - GetLatesPath = changeRelationShip.Item - }); - } - } - } - } - return mergeRelationships; + mergeResult = MergeResult.NothingMerge; } + mergeResultModel.MergeResult = mergeResult; + mergeResultModel.PendingChanges = targetPendingChanges; + mergeResultModel.WorkItemIds = workItemIds; + var trackMergeInfo = GetTrackMergeInfo(mergeInfo, changeset, versionControl); + var comment = commentFormater.Format(trackMergeInfo, mergeInfo.TargetBranch, mergeOption); + mergeResultModel.Comment = comment; - private TrackMergeInfo GetTrackMergeInfo(MergeInfoViewModel mergeInfo, Changeset changeset, VersionControlServer versionControl) + result.Add(mergeResultModel); + if (checkInIfSuccess && mergeResultModel.MergeResult == MergeResult.Merged) { - var mergesRelationships = GetMergesRelationships(mergeInfo.SourcePath, versionControl); - var trackMerges = versionControl.TrackMerges(new[] {changeset.ChangesetId}, - new ItemIdentifier(mergeInfo.SourcePath), - mergesRelationships.ToArray(), - null); - - var trackMergeInfo = GetTrackMergeInfo(versionControl, - trackMerges, mergeInfo.SourcePath); - trackMergeInfo.FromOriginalToSourceBranches.Reverse(); - trackMergeInfo.SourceComment = changeset.Comment; - trackMergeInfo.SourceBranch = mergeInfo.SourceBranch; - trackMergeInfo.SourceChangesetId = changeset.ChangesetId; - trackMergeInfo.SourceWorkItemIds = changeset.AssociatedWorkItems != null - ? changeset.AssociatedWorkItems.Select(w => (long) w.Id).ToList() - : new List(0); - trackMergeInfo.SourceWorkItemTitles = changeset.AssociatedWorkItems != null - ? changeset.AssociatedWorkItems.Select(w => w.Title).ToList() - : new List(0); - trackMergeInfo.OriginaBranch = trackMergeInfo.OriginaBranch ?? trackMergeInfo.SourceBranch; - trackMergeInfo.OriginalComment = trackMergeInfo.OriginalComment ?? trackMergeInfo.SourceComment; - return trackMergeInfo; + var checkInResult = CheckIn(mergeResultModel.PendingChanges, comment, workspace, workItemIds, changeset.PolicyOverride, workItemStore); + mergeResultModel.TagetChangesetId = checkInResult.ChangesetId; + mergeResultModel.MergeResult = checkInResult.CheckinResult; } + } - private static CheckInResult CheckIn(IReadOnlyCollection targetPendingChanges, string comment, - Workspace workspace, IReadOnlyCollection workItemIds, PolicyOverrideInfo policyOverride, WorkItemStore workItemStore) - { - var result = new CheckInResult(); + return result; + } - // Another user can update workitem. Need re-read before update. - var workItems = GetWorkItemCheckinInfo(workItemIds, workItemStore); + // Copy from Microsoft.TeamFoundation.VersionControl.Controls.PendingChanges.ChangesetDataProvider.ResetDataFromChangeset, + // Microsoft.TeamFoundation.VersionControl.Controls + private static List GetChangesetPendingChanges(Change[] changes) + { + var pendingChanges = new List(changes.Length); + foreach (var change in changes) + { + if (ChangeType.SourceRename != (change.ChangeType & (ChangeType.Add | ChangeType.Branch | ChangeType.Rename | ChangeType.SourceRename))) + { + var pendingChange = new PendingChange(change); + if (change.MergeSources != null) + { + foreach (var mergeSource in change.MergeSources) + { + if (mergeSource.IsRename) + { + pendingChange.UpdateSourceItems(null, mergeSource.ServerItem); + break; + } + } + } + pendingChanges.Add(pendingChange); + } + } - var evaluateCheckIn = workspace.EvaluateCheckin2(CheckinEvaluationOptions.All, - targetPendingChanges, - comment, - null, - workItems); + return pendingChanges; + } - var skipPolicyValidate = !policyOverride.PolicyFailures.IsNullOrEmpty(); - if (!CanCheckIn(evaluateCheckIn, skipPolicyValidate)) - { - result.CheckinResult = MergeResult.CheckInEvaluateFail; - return result; - } + private static List GetMergeRelationships(List pendingChanges, string[] targetBranches, VersionControlServer versionControl) + { + var mergeRelationships = new List(); - var changesetId = workspace.CheckIn(targetPendingChanges.ToArray(), null, comment, - null, workItems, policyOverride); - if (changesetId > 0) - { - result.ChangesetId = changesetId; - result.CheckinResult = MergeResult.CheckIn; - } - else - { - result.CheckinResult = MergeResult.CheckInFail; + foreach (var pendingChange in pendingChanges) + { + if (pendingChange.IsAdd || pendingChange.IsBranch) + { + var parentFolder = ExtractFolder(pendingChange.ChangeType, pendingChange.ServerItem, pendingChange.ItemType); + var parentFolderRelationships = versionControl.QueryMergeRelationships(parentFolder); + if (parentFolderRelationships != null) + { + foreach (var parentFolderRelationship in parentFolderRelationships.Where(r => !r.IsDeleted)) + { + mergeRelationships.Add(new MergeRelation + { + Item = pendingChange.ServerItem, + Source = parentFolder, + Target = parentFolderRelationship.Item, + TargetItemType = ItemType.Folder, + GetLatesPath = parentFolderRelationship.Item, + Recursively = pendingChange.ItemType == ItemType.Folder + }); } - return result; + } } - - private MergeResult MergeByFile(Change[] changes, string targetBranch, List mergeRelationships, - VersionSpec version, MergeOption mergeOption, Workspace workspace) + else if (pendingChange.IsRename) { - if (!GetLatest(targetBranch, mergeRelationships, workspace)) - { - return MergeResult.CanNotGetLatest; - } - - var mergeOptions = ToTfsMergeOptions(mergeOption); - var hasConflicts = false; - foreach (var change in changes) - { + var shareFolder = FindShareFolder(pendingChange.ServerItem, pendingChange.SourceServerItem); + var shareFolderRelationships = versionControl.QueryMergeRelationships(shareFolder); + var sourceRelationships = versionControl.QueryMergeRelationships(pendingChange.SourceServerItem) ?? new ItemIdentifier[0]; + if (shareFolderRelationships != null) + { + foreach (var shareFolderRelationship in shareFolderRelationships.Where(r => !r.IsDeleted)) + { + // INTACT Tweak based on ihcaesar:branch_not_found PR + var targetBranch = + //targetBranches.FirstOrDefault(branch => shareFolderRelationship.Item.Contains(branch)); + targetBranches.FirstOrDefault(branch => shareFolderRelationship.Item.IndexOf(branch, StringComparison.OrdinalIgnoreCase) >= 0); + if (targetBranch != null) + { // INTACT Tweak based on ihcaesar:branch_not_found PR - var mergeRelation = - mergeRelationships.FirstOrDefault( - //r => r.Item == change.Item.ServerItem && r.Target.StartsWith(targetBranch)); - r => r.Item == change.Item.ServerItem && r.Target.StartsWith(targetBranch, StringComparison.OrdinalIgnoreCase)); - if (mergeRelation != null) - { - var recursionType = CalculateRecursionType(mergeRelation); - var status = workspace.Merge(mergeRelation.Source, mergeRelation.Target, version, version, - LockLevel.None, recursionType, mergeOptions); - if (!hasConflicts && HasConflicts(status)) - { - hasConflicts = true; - } - } - else + var sourceRelationship = sourceRelationships + //.FirstOrDefault(r => r.Item.Contains(targetBranch)); + .FirstOrDefault(r => r.Item.IndexOf(targetBranch, StringComparison.OrdinalIgnoreCase) >= 0); + mergeRelationships.Add(new MergeRelation { - Logger.Info("File {0} not merged to branch {1}", change.Item.ServerItem, targetBranch); - } + Item = pendingChange.ServerItem, + Source = shareFolder, + Target = shareFolderRelationship.Item, + TargetItemType = ItemType.Folder, + GetLatesPath = sourceRelationship != null ? sourceRelationship.Item : null, + Recursively = true + }); + } } - - if (hasConflicts) - { - var conflicts = AutoResolveConflicts(workspace, targetBranch, mergeOption); - if (!conflicts.IsNullOrEmpty()) - { - return IsTryRestoreUnexpectedFile(conflicts) - ? MergeResult.UnexpectedFileRestored - : MergeResult.HasConflicts; - } + } + } + else + { + var changeRelationShips = versionControl.QueryMergeRelationships(pendingChange.ServerItem); + if (changeRelationShips != null) + { + foreach (var changeRelationShip in changeRelationShips.Where(r => !r.IsDeleted)) + { + mergeRelationships.Add(new MergeRelation + { + Item = pendingChange.ServerItem, + Source = pendingChange.ServerItem, + Target = changeRelationShip.Item, + TargetItemType = pendingChange.ItemType, + GetLatesPath = changeRelationShip.Item + }); } - - return MergeResult.Merged; + } } + } + return mergeRelationships; + } - private static MergeResult MergeToBranch(MergeInfoViewModel mergeInfoeViewModel, MergeOption mergeOption, - List mergeRelationships, Workspace workspace) - { - var source = mergeInfoeViewModel.SourcePath; - var target = mergeInfoeViewModel.TargetPath; - var version = mergeInfoeViewModel.ChangesetVersionSpec; - if (!GetLatest(target, mergeRelationships, workspace)) - { - return MergeResult.CanNotGetLatest; - } + private TrackMergeInfo GetTrackMergeInfo(MergeInfoViewModel mergeInfo, Changeset changeset, VersionControlServer versionControl) + { + var mergesRelationships = GetMergesRelationships(mergeInfo.SourcePath, versionControl); + var trackMerges = versionControl.TrackMerges(new[] { changeset.ChangesetId }, + new ItemIdentifier(mergeInfo.SourcePath), + mergesRelationships.ToArray(), + null); + + var trackMergeInfo = GetTrackMergeInfo(versionControl, + trackMerges, mergeInfo.SourcePath); + trackMergeInfo.FromOriginalToSourceBranches.Reverse(); + trackMergeInfo.SourceComment = changeset.Comment; + trackMergeInfo.SourceBranch = mergeInfo.SourceBranch; + trackMergeInfo.SourceChangesetId = changeset.ChangesetId; + trackMergeInfo.SourceWorkItemIds = changeset.AssociatedWorkItems != null + ? changeset.AssociatedWorkItems.Select(w => (long)w.Id).ToList() + : new List(0); + trackMergeInfo.SourceWorkItemTitles = changeset.AssociatedWorkItems != null + ? changeset.AssociatedWorkItems.Select(w => w.Title).ToList() + : new List(0); + trackMergeInfo.OriginaBranch = trackMergeInfo.OriginaBranch ?? trackMergeInfo.SourceBranch; + trackMergeInfo.OriginalComment = trackMergeInfo.OriginalComment ?? trackMergeInfo.SourceComment; + return trackMergeInfo; + } - var mergeOptions = ToTfsMergeOptions(mergeOption); - var status = workspace.Merge(source, target, version, version, LockLevel.None, RecursionType.Full, mergeOptions); - if (HasConflicts(status)) - { - var conflicts = AutoResolveConflicts(workspace, target, mergeOption); - if (!conflicts.IsNullOrEmpty()) - { - return IsTryRestoreUnexpectedFile(conflicts) - ? MergeResult.UnexpectedFileRestored - : MergeResult.HasConflicts; - } - } + private static CheckInResult CheckIn(IReadOnlyCollection targetPendingChanges, string comment, + Workspace workspace, IReadOnlyCollection workItemIds, PolicyOverrideInfo policyOverride, WorkItemStore workItemStore) + { + var result = new CheckInResult(); + + // Another user can update workitem. Need re-read before update. + var workItems = GetWorkItemCheckinInfo(workItemIds, workItemStore); + + var evaluateCheckIn = workspace.EvaluateCheckin2(CheckinEvaluationOptions.All, + targetPendingChanges, + comment, + null, + workItems); + + var skipPolicyValidate = !policyOverride.PolicyFailures.IsNullOrEmpty(); + if (!CanCheckIn(evaluateCheckIn, skipPolicyValidate)) + { + result.CheckinResult = MergeResult.CheckInEvaluateFail; + return result; + } + + var changesetId = workspace.CheckIn(targetPendingChanges.ToArray(), null, comment, + null, workItems, policyOverride); + if (changesetId > 0) + { + result.ChangesetId = changesetId; + result.CheckinResult = MergeResult.CheckIn; + } + else + { + result.CheckinResult = MergeResult.CheckInFail; + } + return result; + } - return MergeResult.Merged; + private MergeResult MergeByFile(Change[] changes, string targetBranch, List mergeRelationships, + VersionSpec version, MergeOption mergeOption, Workspace workspace) + { + if (!GetLatest(targetBranch, mergeRelationships, workspace)) + { + return MergeResult.CanNotGetLatest; + } + + var mergeOptions = ToTfsMergeOptions(mergeOption); + var hasConflicts = false; + foreach (var change in changes) + { + // INTACT Tweak based on ihcaesar:branch_not_found PR + var mergeRelation = + mergeRelationships.FirstOrDefault( + //r => r.Item == change.Item.ServerItem && r.Target.StartsWith(targetBranch)); + r => r.Item == change.Item.ServerItem && r.Target.StartsWith(targetBranch, StringComparison.OrdinalIgnoreCase)); + if (mergeRelation != null) + { + var recursionType = CalculateRecursionType(mergeRelation); + var status = workspace.Merge(mergeRelation.Source, mergeRelation.Target, version, version, + LockLevel.None, recursionType, mergeOptions); + if (!hasConflicts && HasConflicts(status)) + { + hasConflicts = true; + } } - - private static bool IsTryRestoreUnexpectedFile(Conflict[] conflicts) + else { - foreach (var conflict in conflicts) - { - if (conflict.BaseChangeType.HasFlag(ChangeType.Undelete) - && !conflict.TheirChangeType.HasFlag(ChangeType.Undelete)) - { - return true; - } - } - - return false; + Logger.Info("File {0} not merged to branch {1}", change.Item.ServerItem, targetBranch); } + } - private static List GetPendingChanges(string target, Workspace workspace) + if (hasConflicts) + { + var conflicts = AutoResolveConflicts(workspace, targetBranch, mergeOption); + if (!conflicts.IsNullOrEmpty()) { - var allPendingChanges = workspace.GetPendingChangesEnumerable(target, RecursionType.Full); - - // INTACT Tweak based on ihcaesar:branch_not_found PR - var targetPendingChanges = allPendingChanges - //.Where(p => p.IsMerge && p.ServerItem.Contains(target)) - .Where(p => p.IsMerge && p.ServerItem.IndexOf(target, StringComparison.OrdinalIgnoreCase) >= 0) - .ToList(); - - return targetPendingChanges; + return IsTryRestoreUnexpectedFile(conflicts) + ? MergeResult.UnexpectedFileRestored + : MergeResult.HasConflicts; } + } + + return MergeResult.Merged; + } - private static List GetPendingChangesByFile(List mergeRelationships, string targetBranch, Workspace workspace) + private static MergeResult MergeToBranch(MergeInfoViewModel mergeInfoeViewModel, MergeOption mergeOption, + List mergeRelationships, Workspace workspace) + { + var source = mergeInfoeViewModel.SourcePath; + var target = mergeInfoeViewModel.TargetPath; + var version = mergeInfoeViewModel.ChangesetVersionSpec; + + if (!GetLatest(target, mergeRelationships, workspace)) + { + return MergeResult.CanNotGetLatest; + } + + var mergeOptions = ToTfsMergeOptions(mergeOption); + var status = workspace.Merge(source, target, version, version, LockLevel.None, RecursionType.Full, mergeOptions); + if (HasConflicts(status)) + { + var conflicts = AutoResolveConflicts(workspace, target, mergeOption); + if (!conflicts.IsNullOrEmpty()) { - var itemSpecs = new List(); - foreach (var mergeRelationship in mergeRelationships) - { - // INTACT Tweak based on ihcaesar:branch_not_found PR - //if (mergeRelationship.Target.StartsWith(targetBranch)) - if (mergeRelationship.Target.StartsWith(targetBranch, StringComparison.OrdinalIgnoreCase)) - { - var recursionType = CalculateRecursionType(mergeRelationship); - itemSpecs.Add(new ItemSpec(mergeRelationship.Target, recursionType)); - } - } - return workspace.GetPendingChanges(itemSpecs.ToArray()).ToList(); + return IsTryRestoreUnexpectedFile(conflicts) + ? MergeResult.UnexpectedFileRestored + : MergeResult.HasConflicts; } + } + + return MergeResult.Merged; + } - private static RecursionType CalculateRecursionType(MergeRelation mergeRelationship) + private static bool IsTryRestoreUnexpectedFile(Conflict[] conflicts) + { + foreach (var conflict in conflicts) + { + if (conflict.BaseChangeType.HasFlag(ChangeType.Undelete) + && !conflict.TheirChangeType.HasFlag(ChangeType.Undelete)) { - var recursionType = mergeRelationship.Recursively - ? RecursionType.Full - : mergeRelationship.TargetItemType == ItemType.File - ? RecursionType.None - : RecursionType.OneLevel; - return recursionType; + return true; } + } - private static bool GetLatest(string targetPath, List mergeRelationships, Workspace workspace) - { - var getLatestFiles = new List(); - foreach (var mergeRelationship in mergeRelationships.Where(r => r.TargetItemType == ItemType.File && r.GetLatesPath != null)) - { - // INTACT Tweak based on ihcaesar:branch_not_found PR - if (mergeRelationship.GetLatesPath.StartsWith(targetPath, StringComparison.OrdinalIgnoreCase)) - //if (mergeRelationship.GetLatesPath.StartsWith(targetPath)) - getLatestFiles.Add(mergeRelationship.GetLatesPath); - } + return false; + } - var getLatestFilesArray = getLatestFiles.ToArray(); - if (getLatestFilesArray.Length > 0) - { - const RecursionType recursionType = RecursionType.None; - var getLatestResult = workspace.Get(getLatestFilesArray, VersionSpec.Latest, recursionType, GetOptions.None); - if (!getLatestResult.NoActionNeeded) - { - // HACK. - getLatestResult = workspace.Get(getLatestFilesArray, VersionSpec.Latest, recursionType, GetOptions.None); - if (!getLatestResult.NoActionNeeded) - { - return false; - } - } - } + private static List GetPendingChanges(string target, Workspace workspace) + { + var allPendingChanges = workspace.GetPendingChangesEnumerable(target, RecursionType.Full); - return true; - } + // INTACT Tweak based on ihcaesar:branch_not_found PR + var targetPendingChanges = allPendingChanges + //.Where(p => p.IsMerge && p.ServerItem.Contains(target)) + .Where(p => p.IsMerge && p.ServerItem.IndexOf(target, StringComparison.OrdinalIgnoreCase) >= 0) + .ToList(); - private static MergeOptionsEx ToTfsMergeOptions(MergeOption mergeOption) - { - switch (mergeOption) - { - case MergeOption.KeepTarget: - return MergeOptionsEx.AlwaysAcceptMine; - default: - return MergeOptionsEx.None; - } - } + return targetPendingChanges; + } - public bool MergeCanEcexute() + private static List GetPendingChangesByFile(List mergeRelationships, string targetBranch, Workspace workspace) + { + var itemSpecs = new List(); + foreach (var mergeRelationship in mergeRelationships) + { + // INTACT Tweak based on ihcaesar:branch_not_found PR + //if (mergeRelationship.Target.StartsWith(targetBranch)) + if (mergeRelationship.Target.StartsWith(targetBranch, StringComparison.OrdinalIgnoreCase)) { - return !_merging && _branches != null && _branches.Any(b => b.Checked); + var recursionType = CalculateRecursionType(mergeRelationship); + itemSpecs.Add(new ItemSpec(mergeRelationship.Target, recursionType)); } + } + return workspace.GetPendingChanges(itemSpecs.ToArray()).ToList(); + } - private static bool HasConflicts(GetStatus mergeStatus) + private static RecursionType CalculateRecursionType(MergeRelation mergeRelationship) + { + var recursionType = mergeRelationship.Recursively + ? RecursionType.Full + : mergeRelationship.TargetItemType == ItemType.File + ? RecursionType.None + : RecursionType.OneLevel; + return recursionType; + } + + private static bool GetLatest(string targetPath, List mergeRelationships, Workspace workspace) + { + var getLatestFiles = new List(); + foreach (var mergeRelationship in mergeRelationships.Where(r => r.TargetItemType == ItemType.File && r.GetLatesPath != null)) + { + // INTACT Tweak based on ihcaesar:branch_not_found PR + if (mergeRelationship.GetLatesPath.StartsWith(targetPath, StringComparison.OrdinalIgnoreCase)) + //if (mergeRelationship.GetLatesPath.StartsWith(targetPath)) + getLatestFiles.Add(mergeRelationship.GetLatesPath); + } + + var getLatestFilesArray = getLatestFiles.ToArray(); + if (getLatestFilesArray.Length > 0) + { + const RecursionType recursionType = RecursionType.None; + var getLatestResult = workspace.Get(getLatestFilesArray, VersionSpec.Latest, recursionType, GetOptions.None); + if (!getLatestResult.NoActionNeeded) { - return !mergeStatus.NoActionNeeded && mergeStatus.NumConflicts > 0; + // HACK. + getLatestResult = workspace.Get(getLatestFilesArray, VersionSpec.Latest, recursionType, GetOptions.None); + if (!getLatestResult.NoActionNeeded) + { + return false; + } } + } - private static Conflict[] AutoResolveConflicts(Workspace workspace, string targetPath, MergeOption mergeOption) - { - var targetPaths = new[] {targetPath}; - var conflicts = workspace.QueryConflicts(targetPaths, true); - if (conflicts.IsNullOrEmpty()) - return null; + return true; + } - foreach (var conflict in conflicts) - { - TryResolve(workspace, conflict, mergeOption); - } + private static MergeOptionsEx ToTfsMergeOptions(MergeOption mergeOption) + { + switch (mergeOption) + { + case MergeOption.KeepTarget: + return MergeOptionsEx.AlwaysAcceptMine; + default: + return MergeOptionsEx.None; + } + } - conflicts = workspace.QueryConflicts(targetPaths, true); - if (conflicts.IsNullOrEmpty()) - return null; + public bool MergeCanEcexute() + { + return !_merging && _branches != null && _branches.Any(b => b.Checked); + } - workspace.AutoResolveValidConflicts(conflicts, AutoResolveOptions.AllSilent); + private static bool HasConflicts(GetStatus mergeStatus) + { + return !mergeStatus.NoActionNeeded && mergeStatus.NumConflicts > 0; + } - return workspace.QueryConflicts(targetPaths, true); - } + private static Conflict[] AutoResolveConflicts(Workspace workspace, string targetPath, MergeOption mergeOption) + { + var targetPaths = new[] { targetPath }; + var conflicts = workspace.QueryConflicts(targetPaths, true); + if (conflicts.IsNullOrEmpty()) + return null; - private static void TryResolve(Workspace workspace, Conflict conflict, MergeOption mergeOption) - { - if (mergeOption == MergeOption.KeepTarget) - { - conflict.Resolution = Resolution.AcceptYours; - workspace.ResolveConflict(conflict); - } - if (mergeOption == MergeOption.OverwriteTarget) - { - conflict.Resolution = Resolution.AcceptTheirs; - workspace.ResolveConflict(conflict); - } - } + foreach (var conflict in conflicts) + { + TryResolve(workspace, conflict, mergeOption); + } + conflicts = workspace.QueryConflicts(targetPaths, true); + if (conflicts.IsNullOrEmpty()) + return null; - private static WorkItemCheckinInfo[] GetWorkItemCheckinInfo(IReadOnlyCollection workItemIds, WorkItemStore workItemStore) - { + workspace.AutoResolveValidConflicts(conflicts, AutoResolveOptions.AllSilent); - var result = new List(workItemIds.Count); - foreach (var workItemId in workItemIds) - { - var workItem = workItemStore.GetWorkItem(workItemId); - var workItemCheckinInfo = new WorkItemCheckinInfo(workItem, WorkItemCheckinAction.Associate); - result.Add(workItemCheckinInfo); - } + return workspace.QueryConflicts(targetPaths, true); + } - return result.ToArray(); - } + private static void TryResolve(Workspace workspace, Conflict conflict, MergeOption mergeOption) + { + if (mergeOption == MergeOption.KeepTarget) + { + conflict.Resolution = Resolution.AcceptYours; + workspace.ResolveConflict(conflict); + } + if (mergeOption == MergeOption.OverwriteTarget) + { + conflict.Resolution = Resolution.AcceptTheirs; + workspace.ResolveConflict(conflict); + } + } - private static bool CanCheckIn(CheckinEvaluationResult checkinEvaluationResult, bool skipPolicy) - { - var result = checkinEvaluationResult.Conflicts.IsNullOrEmpty() - && checkinEvaluationResult.NoteFailures.IsNullOrEmpty() - && checkinEvaluationResult.PolicyEvaluationException == null; - if (!skipPolicy) - result &= checkinEvaluationResult.PolicyFailures.IsNullOrEmpty(); - return result; - } + private static WorkItemCheckinInfo[] GetWorkItemCheckinInfo(IReadOnlyCollection workItemIds, WorkItemStore workItemStore) + { - private static void InvokeResolveConflictsPage(Workspace workspace, string[] targetPath) - { - var versionControlAssembly = Assembly.Load("Microsoft.VisualStudio.TeamFoundation.VersionControl"); - if (versionControlAssembly == null) - return; - - var rcMgr = versionControlAssembly.GetType("Microsoft.VisualStudio.TeamFoundation.VersionControl.ResolveConflictsManager"); - if (rcMgr == null) - return; - - const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance; - var mi = rcMgr.GetMethod("Initialize", flags); - var instantiatedType = Activator.CreateInstance(rcMgr, flags, null, null, null); - mi.Invoke(instantiatedType, null); - - var resolveConflictsMethod = rcMgr.GetMethod("ResolveConflicts", BindingFlags.NonPublic | BindingFlags.Instance); - resolveConflictsMethod.Invoke(instantiatedType, - new object[] { workspace, targetPath, true, false }); - } + var result = new List(workItemIds.Count); + foreach (var workItemId in workItemIds) + { + var workItem = workItemStore.GetWorkItem(workItemId); + var workItemCheckinInfo = new WorkItemCheckinInfo(workItem, WorkItemCheckinAction.Associate); + result.Add(workItemCheckinInfo); + } - private void SelectWorkspaceExecute(Workspace workspace) - { - Workspace = workspace; - Refresh(); - } + return result.ToArray(); + } - private void SubscribeWorkspaceChanges(VersionControlServer versionControlServer) - { - versionControlServer.CreatedWorkspace += RefreshWorkspaces; - versionControlServer.UpdatedWorkspace += RefreshWorkspaces; - versionControlServer.DeletedWorkspace += RefreshWorkspaces; - } + private static bool CanCheckIn(CheckinEvaluationResult checkinEvaluationResult, bool skipPolicy) + { + var result = checkinEvaluationResult.Conflicts.IsNullOrEmpty() + && checkinEvaluationResult.NoteFailures.IsNullOrEmpty() + && checkinEvaluationResult.PolicyEvaluationException == null; - private void RefreshWorkspaces(object sender, WorkspaceEventArgs e) - { - var tfs = Context.TeamProjectCollection; - var versionControl = tfs.GetService(); + if (!skipPolicy) + result &= checkinEvaluationResult.PolicyFailures.IsNullOrEmpty(); + return result; + } - Workspaces = GetWorkspaces(versionControl, tfs); - if (Workspaces.Count > 0) - { - Workspace = Workspaces[0]; - ShowWorkspaceChooser = Workspaces.Count > 1; - } - else - { - Workspace = null; - } - Refresh(); - } + private static void InvokeResolveConflictsPage(Workspace workspace, string[] targetPath) + { + var versionControlAssembly = Assembly.Load("Microsoft.VisualStudio.TeamFoundation.VersionControl"); + if (versionControlAssembly == null) + return; + + var rcMgr = versionControlAssembly.GetType("Microsoft.VisualStudio.TeamFoundation.VersionControl.ResolveConflictsManager"); + if (rcMgr == null) + return; + + const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance; + var mi = rcMgr.GetMethod("Initialize", flags); + var instantiatedType = Activator.CreateInstance(rcMgr, flags, null, null, null); + mi.Invoke(instantiatedType, null); + + var resolveConflictsMethod = rcMgr.GetMethod("ResolveConflicts", BindingFlags.NonPublic | BindingFlags.Instance); + resolveConflictsMethod.Invoke(instantiatedType, + new object[] { workspace, targetPath, true, false }); + } - private void OpenSourceControlExplorerExecute() - { - // Using HACK. - // Get any service which contain DTE object. + private void SelectWorkspaceExecute(Workspace workspace) + { + Workspace = workspace; + Refresh(); + } - var s = ServiceProvider.GetService() as SourceControl2; - if (s != null) - { - dynamic ext = s.DTE.GetObject("Microsoft.VisualStudio.TeamFoundation.VersionControl.VersionControlExt"); - if (ext != null) - { - var explorer = ext.Explorer; - if (explorer != null) - { - explorer.Navigate(SelectedBranch.TargetPath); - } - } - } - } + private void SubscribeWorkspaceChanges(VersionControlServer versionControlServer) + { + versionControlServer.CreatedWorkspace += RefreshWorkspaces; + versionControlServer.UpdatedWorkspace += RefreshWorkspaces; + versionControlServer.DeletedWorkspace += RefreshWorkspaces; + } - private bool OpenSourceControlExplorerCanExecute() - { - return SelectedBranch != null && !string.IsNullOrEmpty(SelectedBranch.TargetPath); - } + private void RefreshWorkspaces(object sender, WorkspaceEventArgs e) + { + var tfs = Context.TeamProjectCollection; + var versionControl = tfs.GetService(); + + Workspaces = GetWorkspaces(versionControl, tfs); + if (Workspaces.Count > 0) + { + Workspace = Workspaces[0]; + ShowWorkspaceChooser = Workspaces.Count > 1; + } + else + { + Workspace = null; + } + Refresh(); + } - private void ViewChangesetDetailsExecute(int changesetId) + private void OpenSourceControlExplorerExecute() + { + // Using HACK. + // Get any service which contain DTE object. + + var s = ServiceProvider.GetService() as SourceControl2; + if (s != null) + { + dynamic ext = s.DTE.GetObject("Microsoft.VisualStudio.TeamFoundation.VersionControl.VersionControlExt"); + if (ext != null) { - TeamExplorerUtils.Instance.NavigateToPage(TeamExplorerPageIds.ChangesetDetails, ServiceProvider, changesetId); + var explorer = ext.Explorer; + if (explorer != null) + { + explorer.Navigate(SelectedBranch.TargetPath); + } } + } + } - public override void Dispose() - { - base.Dispose(); - if (_eventAggregator != null) - { - _eventAggregator.GetEvent().Unsubscribe(OnSelectedChangeset); - _eventAggregator.GetEvent().Unsubscribe(OnBranchSelectedChanged); - } + private bool OpenSourceControlExplorerCanExecute() + { + return SelectedBranch != null && !string.IsNullOrEmpty(SelectedBranch.TargetPath); + } - var tfs = Context.TeamProjectCollection; - if (tfs != null) - { - var versionControl = tfs.GetService(); - if (versionControl != null) - { - versionControl.CreatedWorkspace -= RefreshWorkspaces; - versionControl.UpdatedWorkspace -= RefreshWorkspaces; - versionControl.DeletedWorkspace -= RefreshWorkspaces; - } - } - } + private void ViewChangesetDetailsExecute(int changesetId) + { + TeamExplorerUtils.Instance.NavigateToPage(TeamExplorerPageIds.ChangesetDetails, ServiceProvider, changesetId); + } - public override void SaveContext(object sender, SectionSaveContextEventArgs e) + public override void Dispose() + { + base.Dispose(); + if (_eventAggregator != null) + { + _eventAggregator.GetEvent().Unsubscribe(OnSelectedChangeset); + _eventAggregator.GetEvent().Unsubscribe(OnBranchSelectedChanged); + } + + var tfs = Context.TeamProjectCollection; + if (tfs != null) + { + var versionControl = tfs.GetService(); + if (versionControl != null) { - var context = new BranchesViewModelContext - { - Branches = Branches, - Changeset = _changeset, - ErrorMessage = ErrorMessage, - MergeMode = MergeMode, - MergeModes = MergeModes, - MergeOption = MergeOption, - SelectedBranch = SelectedBranch, - ShowWorkspaceChooser = ShowWorkspaceChooser, - Workspace = Workspace, - Workspaces = Workspaces - }; - - e.Context = context; + versionControl.CreatedWorkspace -= RefreshWorkspaces; + versionControl.UpdatedWorkspace -= RefreshWorkspaces; + versionControl.DeletedWorkspace -= RefreshWorkspaces; } + } + } - private void RestoreContext(SectionInitializeEventArgs e) - { - var context = (BranchesViewModelContext)e.Context; - _changeset = context.Changeset; - Branches = context.Branches; - ErrorMessage = context.ErrorMessage; - MergeMode = context.MergeMode; - MergeModes = context.MergeModes; - MergeOption = context.MergeOption; - SelectedBranch = context.SelectedBranch; - ShowWorkspaceChooser = context.ShowWorkspaceChooser; - Workspace = context.Workspace; - Workspaces = context.Workspaces; - } + public override void SaveContext(object sender, SectionSaveContextEventArgs e) + { + var context = new BranchesViewModelContext + { + Branches = Branches, + Changeset = _changeset, + ErrorMessage = ErrorMessage, + MergeMode = MergeMode, + MergeModes = MergeModes, + MergeOption = MergeOption, + SelectedBranch = SelectedBranch, + ShowWorkspaceChooser = ShowWorkspaceChooser, + Workspace = Workspace, + Workspaces = Workspaces + }; + + e.Context = context; + } + + private void RestoreContext(SectionInitializeEventArgs e) + { + var context = (BranchesViewModelContext)e.Context; + _changeset = context.Changeset; + Branches = context.Branches; + ErrorMessage = context.ErrorMessage; + MergeMode = context.MergeMode; + MergeModes = context.MergeModes; + MergeOption = context.MergeOption; + SelectedBranch = context.SelectedBranch; + ShowWorkspaceChooser = context.ShowWorkspaceChooser; + Workspace = context.Workspace; + Workspaces = context.Workspaces; } + } } diff --git a/src/AutoMerge/Commands/ShowAutoMergeWindow.cs b/src/AutoMerge/Commands/ShowAutoMergeWindow.cs index b27aa06..132c0ac 100644 --- a/src/AutoMerge/Commands/ShowAutoMergeWindow.cs +++ b/src/AutoMerge/Commands/ShowAutoMergeWindow.cs @@ -1,7 +1,8 @@ +using System; using System.ComponentModel.Design; -using Microsoft.TeamFoundation.Common.Internal; using Microsoft.TeamFoundation.Controls; using Microsoft.VisualStudio.Shell; +using Microsoft.TeamFoundation.Controls.WPF.TeamExplorer; using Task = System.Threading.Tasks.Task; namespace AutoMerge.Commands @@ -24,8 +25,12 @@ private static void Execute(AsyncPackage package) { ThreadHelper.ThrowIfNotOnUIThread(); - var teamExplorer = package.GetService(); - teamExplorer.NavigateToPage(GuidList.AutoMergePageGuid, null); + // Navigate to the AutoMerge page in Team Explorer + var serviceProvider = package as IServiceProvider; + if (serviceProvider != null) + { + TeamExplorerUtils.Instance.NavigateToPage(GuidList.AutoMergePageGuid.ToString(), serviceProvider, null); + } } } } diff --git a/src/AutoMerge/Configuration/Settings.cs b/src/AutoMerge/Configuration/Settings.cs index 3464497..a51d2a6 100644 --- a/src/AutoMerge/Configuration/Settings.cs +++ b/src/AutoMerge/Configuration/Settings.cs @@ -1,186 +1,187 @@ -using System; +using System; using System.Linq; namespace AutoMerge { - internal class Settings + public class Settings + { + private readonly ISettingProvider _settingProvider; + private static readonly Lazy _instance; + + private MergeMode? _lastMergeOperation; + + private const string lastMergeOperationKey = "last_operation"; + private const string mergeModeMerge = "merge"; + private const string mergeModeMergeAndCheckin = "merge_checkin"; + + private const string mergeOperationDefaultKey = "merge_operation_default"; + private const string mergeOperationDefaultLast = "last"; + private const string mergeOperationDefaultMerge = mergeModeMerge; + private const string mergeOperationDefaultMergeCheckin = mergeModeMergeAndCheckin; + private readonly string[] _mergeOperationDefaultValues; + + private const string commentFormatKey = "comment_format"; + private const string commentFormatDefault = "MERGE {FromOriginalToTarget} ({OriginalComment})"; + private const string commentFormatDiscardKey = "comment_format_discard"; + private const string commentFormatDiscardDefault = "DISCARD {" + commentFormatKey + "}"; + private const string branchDelimiterKey = "branch_delimiter"; + private const string branchDelimiterDefault = " -> "; + + private const string changesetCountKey = "changeset_count_show"; + private const int changesetCountDefault = 20; + + static Settings() { - private readonly ISettingProvider _settingProvider; - private static readonly Lazy _instance; - - private MergeMode? _lastMergeOperation; - - private const string lastMergeOperationKey = "last_operation"; - private const string mergeModeMerge = "merge"; - private const string mergeModeMergeAndCheckin = "merge_checkin"; + _instance = new Lazy(() => new Settings()); + } - private const string mergeOperationDefaultKey = "merge_operation_default"; - private const string mergeOperationDefaultLast = "last"; - private const string mergeOperationDefaultMerge = mergeModeMerge; - private const string mergeOperationDefaultMergeCheckin = mergeModeMergeAndCheckin; - private readonly string[] _mergeOperationDefaultValues; + private Settings() + { + _settingProvider = new FileSettingProvider(); + _mergeOperationDefaultValues = new[] + {mergeOperationDefaultLast, mergeOperationDefaultMerge, mergeOperationDefaultMergeCheckin}; + } - private const string commentFormatKey = "comment_format"; - private const string commentFormatDefault = "MERGE {FromOriginalToTarget} ({OriginalComment})"; - private const string commentFormatDiscardKey = "comment_format_discard"; - private const string commentFormatDiscardDefault = "DISCARD {" + commentFormatKey + "}"; - private const string branchDelimiterKey = "branch_delimiter"; - private const string branchDelimiterDefault = " -> "; + public static Settings Instance + { + get { return _instance.Value; } + } - private const string changesetCountKey = "changeset_count_show"; - private const int changesetCountDefault = 20; + public MergeMode LastMergeOperation + { + get + { + return LastMergeOperationGet(); + } + set + { + LastMergeOperationSet(value); + } + } - static Settings() - { - _instance = new Lazy(() => new Settings()); - } + public CommentFormat CommentFormat + { + get { return CommentFormatGet(); } + } - private Settings() - { - _settingProvider = new FileSettingProvider(); - _mergeOperationDefaultValues = new[] - {mergeOperationDefaultLast, mergeOperationDefaultMerge, mergeOperationDefaultMergeCheckin}; - } + public int ChangesetCount + { + get { return ChangesetCountGet(); } + } - public static Settings Instance - { - get { return _instance.Value; } - } + private CommentFormat CommentFormatGet() + { + string commentFormat; + if (!_settingProvider.TryReadValue(commentFormatKey, out commentFormat)) + { + commentFormat = commentFormatDefault; + } + + string commentFormatDiscard; + if (!_settingProvider.TryReadValue(commentFormatDiscardKey, out commentFormatDiscard)) + { + commentFormatDiscard = commentFormatDiscardDefault; + } + + commentFormatDiscard = commentFormatDiscard.Replace("{" + commentFormatKey + "}", commentFormat); + + string branchDelimiter; + if (!_settingProvider.TryReadValue(branchDelimiterKey, out branchDelimiter)) + { + branchDelimiter = branchDelimiterDefault; + } + + return new CommentFormat + { + Format = commentFormat, + BranchDelimiter = branchDelimiter, + DiscardFormat = commentFormatDiscard + }; - public MergeMode LastMergeOperation { - get - { - return LastMergeOperationGet(); - } - set - { - LastMergeOperationSet(value); - } - } + } - public CommentFormat CommentFormat + private MergeMode LastMergeOperationGet() + { + MergeMode result; + var mergeOperationDefaultValue = MergeOperationDefaultGet(); + if (mergeOperationDefaultValue == mergeOperationDefaultLast) + { + if (!_lastMergeOperation.HasValue) { - get { return CommentFormatGet(); } - } + string stringValue; + if (!_settingProvider.TryReadValue(lastMergeOperationKey, out stringValue)) + { + stringValue = mergeModeMergeAndCheckin; + } - public int ChangesetCount - { - get { return ChangesetCountGet(); } + _lastMergeOperation = ToMergeMode(stringValue); } - private CommentFormat CommentFormatGet() - { - string commentFormat; - if (!_settingProvider.TryReadValue(commentFormatKey, out commentFormat)) - { - commentFormat = commentFormatDefault; - } - - string commentFormatDiscard; - if (!_settingProvider.TryReadValue(commentFormatDiscardKey, out commentFormatDiscard)) - { - commentFormatDiscard = commentFormatDiscardDefault; - } - - commentFormatDiscard = commentFormatDiscard.Replace("{" + commentFormatKey + "}", commentFormat); - - string branchDelimiter; - if (!_settingProvider.TryReadValue(branchDelimiterKey, out branchDelimiter)) - { - branchDelimiter = branchDelimiterDefault; - } - - return new CommentFormat - { - Format = commentFormat, - BranchDelimiter = branchDelimiter, - DiscardFormat = commentFormatDiscard - }; + result = _lastMergeOperation.Value; + } + else + { + result = ToMergeMode(mergeOperationDefaultValue); + } - } + return result; + } - private MergeMode LastMergeOperationGet() - { - MergeMode result; - var mergeOperationDefaultValue = MergeOperationDefaultGet(); - if (mergeOperationDefaultValue == mergeOperationDefaultLast) - { - if (!_lastMergeOperation.HasValue) - { - string stringValue; - if (!_settingProvider.TryReadValue(lastMergeOperationKey, out stringValue)) - { - stringValue = mergeModeMergeAndCheckin; - } - - _lastMergeOperation = ToMergeMode(stringValue); - } - - result = _lastMergeOperation.Value; - } - else - { - result = ToMergeMode(mergeOperationDefaultValue); - } - - return result; - } + private void LastMergeOperationSet(MergeMode mergeMode) + { + if (_lastMergeOperation != mergeMode) + { + var stringValue = ToString(mergeMode); + _settingProvider.WriteValue(lastMergeOperationKey, stringValue); + _lastMergeOperation = mergeMode; + } + } - private void LastMergeOperationSet(MergeMode mergeMode) - { - if (_lastMergeOperation != mergeMode) - { - var stringValue = ToString(mergeMode); - _settingProvider.WriteValue(lastMergeOperationKey, stringValue); - _lastMergeOperation = mergeMode; - } - } + private static MergeMode ToMergeMode(string stringValue) + { + if (stringValue == mergeModeMergeAndCheckin) + return MergeMode.MergeAndCheckIn; + return MergeMode.Merge; + } - private static MergeMode ToMergeMode(string stringValue) - { - if (stringValue == mergeModeMergeAndCheckin) - return MergeMode.MergeAndCheckIn; - return MergeMode.Merge; - } + private static string ToString(MergeMode mergeMode) + { + switch (mergeMode) + { + case MergeMode.Merge: + return mergeModeMerge; + case MergeMode.MergeAndCheckIn: + return mergeModeMergeAndCheckin; + default: + return "unknown"; + } + } - private static string ToString(MergeMode mergeMode) - { - switch (mergeMode) - { - case MergeMode.Merge: - return mergeModeMerge; - case MergeMode.MergeAndCheckIn: - return mergeModeMergeAndCheckin; - default: - return "unknown"; - } - } + private string MergeOperationDefaultGet() + { + string mergeOperationDefaultValue; + if (!_settingProvider.TryReadValue(mergeOperationDefaultKey, out mergeOperationDefaultValue)) + { + mergeOperationDefaultValue = mergeOperationDefaultLast; + _settingProvider.WriteValue(mergeOperationDefaultKey, mergeOperationDefaultValue); + } - private string MergeOperationDefaultGet() - { - string mergeOperationDefaultValue; - if (!_settingProvider.TryReadValue(mergeOperationDefaultKey, out mergeOperationDefaultValue)) - { - mergeOperationDefaultValue = mergeOperationDefaultLast; - _settingProvider.WriteValue(mergeOperationDefaultKey, mergeOperationDefaultValue); - } + if (!_mergeOperationDefaultValues.Contains(mergeOperationDefaultValue)) + mergeOperationDefaultValue = "mergeOperationDefaultLast"; - if (!_mergeOperationDefaultValues.Contains(mergeOperationDefaultValue)) - mergeOperationDefaultValue = "mergeOperationDefaultLast"; + return mergeOperationDefaultValue; + } - return mergeOperationDefaultValue; - } + private int ChangesetCountGet() + { + int changesetCount; - private int ChangesetCountGet() - { - int changesetCount; - if (!_settingProvider.TryReadValue(changesetCountKey, out changesetCount)) - { - changesetCount = changesetCountDefault; - } + if (!_settingProvider.TryReadValue(changesetCountKey, out changesetCount)) + { + changesetCount = changesetCountDefault; + } - return changesetCount; - } + return changesetCount; } + } } - diff --git a/src/AutoMerge/RecentChangesets/ChangesetByIdChangesetProvider.cs b/src/AutoMerge/RecentChangesets/ChangesetByIdChangesetProvider.cs index e8d65f2..27e3a1e 100644 --- a/src/AutoMerge/RecentChangesets/ChangesetByIdChangesetProvider.cs +++ b/src/AutoMerge/RecentChangesets/ChangesetByIdChangesetProvider.cs @@ -17,6 +17,15 @@ public ChangesetByIdChangesetProvider(IServiceProvider serviceProvider, IEnumera _changesetIds = changesetIds; } + public ChangesetByIdChangesetProvider(ChangesetService changesetService, IEnumerable changesetIds) + : base(changesetService, null) + { + if (changesetIds == null) + throw new ArgumentNullException("changesetIds"); + + _changesetIds = changesetIds; + } + protected override List GetChangesetsInternal(string userLogin) { var changesets = new List(); @@ -33,4 +42,4 @@ protected override List GetChangesetsInternal(string userLog return changesets; } } -} \ No newline at end of file +} diff --git a/src/AutoMerge/RecentChangesets/ChangesetProviderBase.cs b/src/AutoMerge/RecentChangesets/ChangesetProviderBase.cs index 17627e5..ef88773 100644 --- a/src/AutoMerge/RecentChangesets/ChangesetProviderBase.cs +++ b/src/AutoMerge/RecentChangesets/ChangesetProviderBase.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -6,65 +6,89 @@ namespace AutoMerge { - public abstract class ChangesetProviderBase : IChangesetProvider + public abstract class ChangesetProviderBase : IChangesetProvider + { + private readonly IServiceProvider _serviceProvider; + private readonly Lazy _changesetService; + private readonly ChangesetService _overriddenChangesetService; + private readonly string _projectName; + + protected ChangesetProviderBase(IServiceProvider serviceProvider) { - private readonly IServiceProvider _serviceProvider; - private readonly Lazy _changesetService; + _serviceProvider = serviceProvider; + _changesetService = new Lazy(InitChangesetService); + } - protected ChangesetProviderBase(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - _changesetService = new Lazy(InitChangesetService); - } + protected ChangesetProviderBase(ChangesetService changesetService, string projectName) + { + _overriddenChangesetService = changesetService; + _projectName = projectName; + _changesetService = new Lazy(() => changesetService); + } - public Task> GetChangesets(string userLogin) - { - return Task.Run(() => GetChangesetsInternal(userLogin)); - } + public Task> GetChangesets(string userLogin) + { + return Task.Run(() => GetChangesetsInternal(userLogin)); + } - protected abstract List GetChangesetsInternal(string userLogin); + protected abstract List GetChangesetsInternal(string userLogin); - protected ChangesetViewModel ToChangesetViewModel(Changeset tfsChangeset, ChangesetService changesetService) - { - var changesetViewModel = new ChangesetViewModel - { - ChangesetId = tfsChangeset.ChangesetId, - Comment = tfsChangeset.Comment, - Branches = changesetService.GetAssociatedBranches(tfsChangeset.ChangesetId) - .Select(i => i.Item) - .ToList() - }; - - return changesetViewModel; - } + protected ChangesetViewModel ToChangesetViewModel(Changeset tfsChangeset, ChangesetService changesetService) + { + var changesetViewModel = new ChangesetViewModel + { + ChangesetId = tfsChangeset.ChangesetId, + Comment = tfsChangeset.Comment, + Branches = changesetService.GetAssociatedBranches(tfsChangeset.ChangesetId) + .Select(i => i.Item) + .ToList() + }; - protected ChangesetService GetChangesetService() - { - return _changesetService.Value; - } + return changesetViewModel; + } - private ChangesetService InitChangesetService() - { - var context = VersionControlNavigationHelper.GetTeamFoundationContext(_serviceProvider); - if (context != null && VersionControlNavigationHelper.IsConnectedToTfsCollectionAndProject(context)) - { - var vcs = context.TeamProjectCollection.GetService(); - if (vcs != null) - { - return new ChangesetService(vcs); - } - } - return null; - } + protected ChangesetService GetChangesetService() + { + if (_overriddenChangesetService != null) + { + return _overriddenChangesetService; + } + + return _changesetService.Value; + } + + private ChangesetService InitChangesetService() + { + var context = VersionControlNavigationHelper.GetTeamFoundationContext(_serviceProvider); + + if (context != null && VersionControlNavigationHelper.IsConnectedToTfsCollectionAndProject(context)) + { + var vcs = context.TeamProjectCollection.GetService(); - protected string GetProjectName() + if (vcs != null) { - var context = VersionControlNavigationHelper.GetTeamFoundationContext(_serviceProvider); - if (context != null) - { - return context.TeamProjectName; - } - return null; + return new ChangesetService(vcs); } + } + + return null; + } + + protected string GetProjectName() + { + if (!string.IsNullOrWhiteSpace(_projectName)) + { + return _projectName; + } + + var context = VersionControlNavigationHelper.GetTeamFoundationContext(_serviceProvider); + + if (context != null) + { + return context.TeamProjectName; + } + + return null; } + } } diff --git a/src/AutoMerge/RecentChangesets/MyChangesetChangesetProvider.cs b/src/AutoMerge/RecentChangesets/MyChangesetChangesetProvider.cs index 733d923..f9beb71 100644 --- a/src/AutoMerge/RecentChangesets/MyChangesetChangesetProvider.cs +++ b/src/AutoMerge/RecentChangesets/MyChangesetChangesetProvider.cs @@ -1,38 +1,67 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; namespace AutoMerge { - public class MyChangesetChangesetProvider : ChangesetProviderBase - { - private readonly int _maxChangesetCount; - - public MyChangesetChangesetProvider(IServiceProvider serviceProvider, int maxChangesetCount) - : base(serviceProvider) - { - _maxChangesetCount = maxChangesetCount; - } - - protected override List GetChangesetsInternal(string userLogin) - { - var changesets = new List(); - - if (!string.IsNullOrEmpty(userLogin)) - { - var changesetService = GetChangesetService(); - - if (changesetService != null) - { - var projectName = GetProjectName(); - var tfsChangesets = changesetService.GetUserChangesets(projectName, userLogin, _maxChangesetCount); - changesets = tfsChangesets - .Select(tfsChangeset => ToChangesetViewModel(tfsChangeset, changesetService)) - .ToList(); - } - } - - return changesets; - } - } + public class MyChangesetChangesetProvider : ChangesetProviderBase + { + private readonly int _maxChangesetCount; + + private readonly bool _filterOutMergeChangesets; + + public MyChangesetChangesetProvider(IServiceProvider serviceProvider, int maxChangesetCount) + : base(serviceProvider) + { + _maxChangesetCount = maxChangesetCount; + } + + public MyChangesetChangesetProvider(ChangesetService changesetService, string teamProjectName, int maxChangesetCount) + : base(changesetService, teamProjectName) + { + _maxChangesetCount = maxChangesetCount; + } + + public MyChangesetChangesetProvider(ChangesetService changesetService, string teamProjectName, int maxChangesetCount, bool filterOutMergeChangesets) + : this(changesetService, teamProjectName, maxChangesetCount) + { + _filterOutMergeChangesets = filterOutMergeChangesets; + } + + protected override List GetChangesetsInternal(string userLogin) + { + var changesets = new List(); + + if (!string.IsNullOrEmpty(userLogin)) + { + var changesetService = GetChangesetService(); + + if (changesetService != null) + { + var projectName = GetProjectName(); + + if (_filterOutMergeChangesets) + { + var tfsChangesets = changesetService.GetUserChangesets(projectName, userLogin, _maxChangesetCount * 2); + + changesets = tfsChangesets + .Select(tfsChangeset => ToChangesetViewModel(tfsChangeset, changesetService)) + .Where(cs => !cs.Comment.StartsWith("MERGE") && cs.Branches.Count == 1) + .Take(_maxChangesetCount) + .ToList(); + } + else + { + var tfsChangesets = changesetService.GetUserChangesets(projectName, userLogin, _maxChangesetCount); + + changesets = tfsChangesets + .Select(tfsChangeset => ToChangesetViewModel(tfsChangeset, changesetService)) + .ToList(); + } + } + } + + return changesets; + } + } } diff --git a/src/AutoMerge/RecentChangesets/RecentChangesetsViewModel.cs b/src/AutoMerge/RecentChangesets/RecentChangesetsViewModel.cs index c5c5567..3c0387a 100644 --- a/src/AutoMerge/RecentChangesets/RecentChangesetsViewModel.cs +++ b/src/AutoMerge/RecentChangesets/RecentChangesetsViewModel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using AutoMerge.Events; @@ -14,294 +14,295 @@ namespace AutoMerge { - public sealed class RecentChangesetsViewModel : TeamExplorerSectionViewModelBase + public sealed class RecentChangesetsViewModel : TeamExplorerSectionViewModelBase + { + private readonly string _baseTitle; + private readonly IEventAggregator _eventAggregator; + + public RecentChangesetsViewModel(ILogger logger) + : base(logger) { - private readonly string _baseTitle; - private readonly IEventAggregator _eventAggregator; + Title = Resources.RecentChangesetSectionName; + IsVisible = true; + IsExpanded = true; + IsBusy = false; + Changesets = new ObservableCollection(); + _baseTitle = Title; + + _eventAggregator = EventAggregatorFactory.Get(); + _eventAggregator.GetEvent() + .Subscribe(OnMergeComplete); + + ViewChangesetDetailsCommand = new DelegateCommand(ViewChangesetDetailsExecute, ViewChangesetDetailsCanExecute); + ToggleAddByIdCommand = new DelegateCommand(ToggleAddByIdExecute, ToggleAddByIdCanExecute); + CancelAddChangesetByIdCommand = new DelegateCommand(CancelAddByIdExecute); + AddChangesetByIdCommand = new DelegateCommand(AddChangesetByIdExecute, AddChangesetByIdCanExecute); + } - public RecentChangesetsViewModel(ILogger logger) - : base(logger) - { - Title = Resources.RecentChangesetSectionName; - IsVisible = true; - IsExpanded = true; - IsBusy = false; - Changesets = new ObservableCollection(); - _baseTitle = Title; - - _eventAggregator = EventAggregatorFactory.Get(); - _eventAggregator.GetEvent() - .Subscribe(OnMergeComplete); - - ViewChangesetDetailsCommand = new DelegateCommand(ViewChangesetDetailsExecute, ViewChangesetDetailsCanExecute); - ToggleAddByIdCommand = new DelegateCommand(ToggleAddByIdExecute, ToggleAddByIdCanExecute); - CancelAddChangesetByIdCommand = new DelegateCommand(CancelAddByIdExecute); - AddChangesetByIdCommand = new DelegateCommand(AddChangesetByIdExecute, AddChangesetByIdCanExecute); - } + public ChangesetViewModel SelectedChangeset + { + get + { + return _selectedChangeset; + } + set + { + _selectedChangeset = value; + RaisePropertyChanged("SelectedChangeset"); + _eventAggregator.GetEvent().Publish(value); + } + } + private ChangesetViewModel _selectedChangeset; - public ChangesetViewModel SelectedChangeset - { - get - { - return _selectedChangeset; - } - set - { - _selectedChangeset = value; - RaisePropertyChanged("SelectedChangeset"); - _eventAggregator.GetEvent().Publish(value); - } - } - private ChangesetViewModel _selectedChangeset; + public ObservableCollection Changesets + { + get + { + return _changesets; + } + private set + { + _changesets = value; + RaisePropertyChanged("Changesets"); + } + } + private ObservableCollection _changesets; - public ObservableCollection Changesets - { - get - { - return _changesets; - } - private set - { - _changesets = value; - RaisePropertyChanged("Changesets"); - } - } - private ObservableCollection _changesets; + public bool ShowAddByIdChangeset + { + get + { + return _showAddByIdChangeset; + } + set + { + _showAddByIdChangeset = value; + RaisePropertyChanged("ShowAddByIdChangeset"); + } + } + private bool _showAddByIdChangeset; - public bool ShowAddByIdChangeset - { - get - { - return _showAddByIdChangeset; - } - set - { - _showAddByIdChangeset = value; - RaisePropertyChanged("ShowAddByIdChangeset"); - } - } - private bool _showAddByIdChangeset; + public string ChangesetIdsText + { + get + { + return _changesetIdsText; + } + set + { + _changesetIdsText = value; + RaisePropertyChanged("ChangesetIdsText"); + InvalidateCommands(); + } + } + private string _changesetIdsText; - public string ChangesetIdsText - { - get - { - return _changesetIdsText; - } - set - { - _changesetIdsText = value; - RaisePropertyChanged("ChangesetIdsText"); - InvalidateCommands(); - } - } - private string _changesetIdsText; + public DelegateCommand ViewChangesetDetailsCommand { get; private set; } - public DelegateCommand ViewChangesetDetailsCommand { get; private set; } + public DelegateCommand ToggleAddByIdCommand { get; private set; } - public DelegateCommand ToggleAddByIdCommand { get; private set; } + public DelegateCommand AddChangesetByIdCommand { get; private set; } - public DelegateCommand AddChangesetByIdCommand { get; private set; } + public DelegateCommand CancelAddChangesetByIdCommand { get; private set; } - public DelegateCommand CancelAddChangesetByIdCommand { get; private set; } + private void ViewChangesetDetailsExecute() + { + var changesetId = SelectedChangeset.ChangesetId; + TeamExplorerUtils.Instance.NavigateToPage(TeamExplorerPageIds.ChangesetDetails, ServiceProvider, changesetId); + } - private void ViewChangesetDetailsExecute() - { - var changesetId = SelectedChangeset.ChangesetId; - TeamExplorerUtils.Instance.NavigateToPage(TeamExplorerPageIds.ChangesetDetails, ServiceProvider, changesetId); - } + private bool ViewChangesetDetailsCanExecute() + { + return SelectedChangeset != null; + } - private bool ViewChangesetDetailsCanExecute() - { - return SelectedChangeset != null; - } + private async void OnMergeComplete(bool obj) + { + await RefreshAsync(); + } - private async void OnMergeComplete(bool obj) - { - await RefreshAsync(); - } + protected override async Task InitializeAsync(object sender, SectionInitializeEventArgs e) + { + if (e.Context == null) + { + await RefreshAsync(); + } + else + { + RestoreContext(e); + } + } - protected override async Task InitializeAsync(object sender, SectionInitializeEventArgs e) - { - if (e.Context == null) - { - await RefreshAsync(); - } - else - { - RestoreContext(e); - } - } + protected override async Task RefreshAsync() + { + Changesets.Clear(); - protected override async Task RefreshAsync() - { - Changesets.Clear(); + var changesetProvider = new MyChangesetChangesetProvider(ServiceProvider, Settings.Instance.ChangesetCount); - var changesetProvider = new MyChangesetChangesetProvider(ServiceProvider, Settings.Instance.ChangesetCount); - var userLogin = VersionControlNavigationHelper.GetAuthorizedUser(ServiceProvider); + var userLogin = VersionControlNavigationHelper.GetAuthorizedUser(ServiceProvider); - Logger.Info("Getting changesets ..."); - var changesets = await changesetProvider.GetChangesets(userLogin); - Logger.Info("Getting changesets end"); + Logger.Info("Getting changesets ..."); + var changesets = await changesetProvider.GetChangesets(userLogin); + Logger.Info("Getting changesets end"); - Changesets = new ObservableCollection(changesets); - UpdateTitle(); + Changesets = new ObservableCollection(changesets); + UpdateTitle(); - if (Changesets.Count > 0) - { - if (SelectedChangeset == null || SelectedChangeset.ChangesetId != Changesets[0].ChangesetId) - SelectedChangeset = Changesets[0]; - } - } + if (Changesets.Count > 0) + { + if (SelectedChangeset == null || SelectedChangeset.ChangesetId != Changesets[0].ChangesetId) + SelectedChangeset = Changesets[0]; + } + } - private void UpdateTitle() - { - Title = Changesets.Count > 0 - ? string.Format("{0} ({1})", _baseTitle, Changesets.Count) - : _baseTitle; - } + private void UpdateTitle() + { + Title = Changesets.Count > 0 + ? string.Format("{0} ({1})", _baseTitle, Changesets.Count) + : _baseTitle; + } - private void ToggleAddByIdExecute() - { - try - { - ShowAddByIdChangeset = true; - InvalidateCommands(); - ResetAddById(); - SetMvvmFocus(RecentChangesetFocusableControlNames.ChangesetIdTextBox); - } - catch (Exception ex) - { - ShowException(ex); - throw; - } - } + private void ToggleAddByIdExecute() + { + try + { + ShowAddByIdChangeset = true; + InvalidateCommands(); + ResetAddById(); + SetMvvmFocus(RecentChangesetFocusableControlNames.ChangesetIdTextBox); + } + catch (Exception ex) + { + ShowException(ex); + throw; + } + } - private bool ToggleAddByIdCanExecute() - { - return !ShowAddByIdChangeset; - } + private bool ToggleAddByIdCanExecute() + { + return !ShowAddByIdChangeset; + } - private void CancelAddByIdExecute() - { - try - { - ShowAddByIdChangeset = false; - InvalidateCommands(); - SetMvvmFocus(RecentChangesetFocusableControlNames.AddChangesetByIdLink); - ResetAddById(); - } - catch (Exception ex) - { - ShowException(ex); - } - } + private void CancelAddByIdExecute() + { + try + { + ShowAddByIdChangeset = false; + InvalidateCommands(); + SetMvvmFocus(RecentChangesetFocusableControlNames.AddChangesetByIdLink); + ResetAddById(); + } + catch (Exception ex) + { + ShowException(ex); + } + } - private void ResetAddById() - { - ChangesetIdsText = string.Empty; - } + private void ResetAddById() + { + ChangesetIdsText = string.Empty; + } - private async void AddChangesetByIdExecute() + private async void AddChangesetByIdExecute() + { + ShowBusy(); + try + { + var changesetIds = GeChangesetIdsToAdd(ChangesetIdsText); + if (changesetIds.Count > 0) { - ShowBusy(); - try - { - var changesetIds = GeChangesetIdsToAdd(ChangesetIdsText); - if (changesetIds.Count > 0) - { - var changesetProvider = new ChangesetByIdChangesetProvider(ServiceProvider, changesetIds); - var changesets = await changesetProvider.GetChangesets(null); - - if (changesets.Count > 0) - { - Changesets.Add(changesets[0]); - SelectedChangeset = changesets[0]; - SetMvvmFocus(RecentChangesetFocusableControlNames.ChangesetList); - UpdateTitle(); - } - ShowAddByIdChangeset = false; - } - } - catch (Exception ex) - { - ShowException(ex); - } - HideBusy(); + var changesetProvider = new ChangesetByIdChangesetProvider(ServiceProvider, changesetIds); + var changesets = await changesetProvider.GetChangesets(null); + + if (changesets.Count > 0) + { + Changesets.Add(changesets[0]); + SelectedChangeset = changesets[0]; + SetMvvmFocus(RecentChangesetFocusableControlNames.ChangesetList); + UpdateTitle(); + } + ShowAddByIdChangeset = false; } + } + catch (Exception ex) + { + ShowException(ex); + } + HideBusy(); + } - private bool AddChangesetByIdCanExecute() - { - try - { - return GeChangesetIdsToAdd(ChangesetIdsText).Count > 0; - } - catch (Exception ex) - { - ShowException(ex); - TeamFoundationTrace.TraceException(ex); - } - return false; - } + private bool AddChangesetByIdCanExecute() + { + try + { + return GeChangesetIdsToAdd(ChangesetIdsText).Count > 0; + } + catch (Exception ex) + { + ShowException(ex); + TeamFoundationTrace.TraceException(ex); + } + return false; + } - private static List GeChangesetIdsToAdd(string text) + private static List GeChangesetIdsToAdd(string text) + { + var list = new List(); + var idsStrArray = string.IsNullOrEmpty(text) ? new string[0] : text.Split(new[] { ',', ';' }); + if (idsStrArray.Length > 0) + { + foreach (var idStr in idsStrArray) { - var list = new List(); - var idsStrArray = string.IsNullOrEmpty(text) ? new string[0] : text.Split(new[] { ',', ';' }); - if (idsStrArray.Length > 0) - { - foreach (var idStr in idsStrArray) - { - int result; - if (int.TryParse(idStr.Trim(), out result) && result > 0) - list.Add(result); - } - } - return list; + int result; + if (int.TryParse(idStr.Trim(), out result) && result > 0) + list.Add(result); } + } + return list; + } - private void InvalidateCommands() - { - ViewChangesetDetailsCommand.RaiseCanExecuteChanged(); - ToggleAddByIdCommand.RaiseCanExecuteChanged(); - CancelAddChangesetByIdCommand.RaiseCanExecuteChanged(); - AddChangesetByIdCommand.RaiseCanExecuteChanged(); - } + private void InvalidateCommands() + { + ViewChangesetDetailsCommand.RaiseCanExecuteChanged(); + ToggleAddByIdCommand.RaiseCanExecuteChanged(); + CancelAddChangesetByIdCommand.RaiseCanExecuteChanged(); + AddChangesetByIdCommand.RaiseCanExecuteChanged(); + } - public override void Dispose() - { - base.Dispose(); - _eventAggregator.GetEvent().Unsubscribe(OnMergeComplete); - } + public override void Dispose() + { + base.Dispose(); + _eventAggregator.GetEvent().Unsubscribe(OnMergeComplete); + } - public override void SaveContext(object sender, SectionSaveContextEventArgs e) - { - base.SaveContext(sender, e); - var context = new RecentChangesetsViewModelContext - { - ChangesetIdsText = ChangesetIdsText, - Changesets = Changesets, - SelectedChangeset = SelectedChangeset, - ShowAddByIdChangeset = ShowAddByIdChangeset, - Title = Title - }; - - e.Context = context; - } + public override void SaveContext(object sender, SectionSaveContextEventArgs e) + { + base.SaveContext(sender, e); + var context = new RecentChangesetsViewModelContext + { + ChangesetIdsText = ChangesetIdsText, + Changesets = Changesets, + SelectedChangeset = SelectedChangeset, + ShowAddByIdChangeset = ShowAddByIdChangeset, + Title = Title + }; + + e.Context = context; + } - private void RestoreContext(SectionInitializeEventArgs e) - { - var context = (RecentChangesetsViewModelContext)e.Context; - ChangesetIdsText = context.ChangesetIdsText; - Changesets = context.Changesets; - SelectedChangeset = context.SelectedChangeset; - ShowAddByIdChangeset = context.ShowAddByIdChangeset; - Title = context.Title; - } + private void RestoreContext(SectionInitializeEventArgs e) + { + var context = (RecentChangesetsViewModelContext)e.Context; + ChangesetIdsText = context.ChangesetIdsText; + Changesets = context.Changesets; + SelectedChangeset = context.SelectedChangeset; + ShowAddByIdChangeset = context.ShowAddByIdChangeset; + Title = context.Title; + } - protected override void OnContextChanged(object sender, ContextChangedEventArgs e) - { - Refresh(); - } + protected override void OnContextChanged(object sender, ContextChangedEventArgs e) + { + Refresh(); } + } } diff --git a/src/AutoMerge/Resources.Designer.cs b/src/AutoMerge/Resources.Designer.cs index 9033603..7635965 100644 --- a/src/AutoMerge/Resources.Designer.cs +++ b/src/AutoMerge/Resources.Designer.cs @@ -19,10 +19,10 @@ namespace AutoMerge { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { + public class Resources { private static global::System.Resources.ResourceManager resourceMan; @@ -36,7 +36,7 @@ internal Resources() { /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { + public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AutoMerge.Resources", typeof(Resources).Assembly); @@ -51,7 +51,7 @@ internal Resources() { /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { + public static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } @@ -61,9 +61,9 @@ internal Resources() { } /// - /// Looks up a localized string similar to Intact Auto Merge. + /// Looks up a localized string similar to GenetiQ Auto Merge. /// - internal static string AutoMergePageName { + public static string AutoMergePageName { get { return ResourceManager.GetString("AutoMergePageName", resourceCulture); } @@ -72,7 +72,7 @@ internal static string AutoMergePageName { /// /// Looks up a localized string similar to Target branches. /// - internal static string BrancheSectionName { + public static string BrancheSectionName { get { return ResourceManager.GetString("BrancheSectionName", resourceCulture); } @@ -81,7 +81,7 @@ internal static string BrancheSectionName { /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// - internal static System.Drawing.Bitmap MergeImage { + public static System.Drawing.Bitmap MergeImage { get { object obj = ResourceManager.GetObject("MergeImage", resourceCulture); return ((System.Drawing.Bitmap)(obj)); @@ -91,7 +91,7 @@ internal static System.Drawing.Bitmap MergeImage { /// /// Looks up a localized string similar to My recent changesets. /// - internal static string RecentChangesetSectionName { + public static string RecentChangesetSectionName { get { return ResourceManager.GetString("RecentChangesetSectionName", resourceCulture); } diff --git a/src/AutoMerge/Resources.resx b/src/AutoMerge/Resources.resx index 2fe8727..4a71fee 100644 --- a/src/AutoMerge/Resources.resx +++ b/src/AutoMerge/Resources.resx @@ -118,7 +118,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Intact Auto Merge + GenetiQ Auto Merge My recent changesets diff --git a/src/AutoMerge/Services/ChangesetService.cs b/src/AutoMerge/Services/ChangesetService.cs index 612c2a7..437176e 100644 --- a/src/AutoMerge/Services/ChangesetService.cs +++ b/src/AutoMerge/Services/ChangesetService.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Microsoft.TeamFoundation.VersionControl.Client; @@ -16,6 +16,7 @@ public ChangesetService(VersionControlServer versionControlServer) public ICollection GetUserChangesets(string teamProjectName, string userName, int count) { var path = "$/" + teamProjectName; + return _versionControlServer.QueryHistory(path, VersionSpec.Latest, 0, diff --git a/src/AutoMerge/VSCommandTable.vsct b/src/AutoMerge/VSCommandTable.vsct index 13dc40b..0a193b9 100644 --- a/src/AutoMerge/VSCommandTable.vsct +++ b/src/AutoMerge/VSCommandTable.vsct @@ -37,7 +37,7 @@ specify the "language" attribute for the Strings tag. It is possible however to specify different String sections inside any element definition, one for each supported language. --> - Auto Merge + GenetiQ Auto Merge @@ -46,6 +46,9 @@ + + + @@ -57,14 +60,49 @@ IconIsMoniker AutoMerge.Show - Show Auto Merge + GenetiQ Auto Merge + + + + + + + + + + + + + + + + + + +