-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathFormShowChangelist.cs
260 lines (222 loc) · 10.5 KB
/
FormShowChangelist.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace GitForce
{
/// <summary>
/// Shows a changelist or stash with the ability to "walk" the chain.
/// Returns "DialogResult.No" to load a previous change
/// Returns "DialogResult.Yes" to load a next change
/// Otherwise returns "DialogResult.Cancel"
/// </summary>
public partial class FormShowChangelist : Form
{
private readonly string cr; // Just a convinient shortcut
/// <summary>
/// Constructor, set the same font as for commit text box.
/// </summary>
public FormShowChangelist()
{
InitializeComponent();
ClassWinGeometry.Restore(this);
cr = Environment.NewLine;
textChangelist.Font = Properties.Settings.Default.commitFont;
comboShow.SelectedIndex = Properties.Settings.Default.ShowFormatIndex;
}
/// <summary>
/// On form closing, save the dialog location and size
/// </summary>
private void FormShowChangelistFormClosing(object sender, FormClosingEventArgs e)
{
ClassWinGeometry.Save(this);
}
/// <summary>
/// Describe (view) selected changelist and drive up/down back propagation through the host list view
/// </summary>
static public void DriveChangelistFromListViewEx(ref ListViewEx listRev)
{
// Get the SHA associated with the selected item on the log list
ListView li = listRev;
if (li.SelectedIndices.Count != 1)
return;
bool multiSel = li.MultiSelect;
li.MultiSelect = false;
int index = li.SelectedIndices[0];
FormShowChangelist form = new FormShowChangelist();
DialogResult dlg;
do
{
li.Items[index].Selected = true;
string sha = li.Items[index].Tag.ToString();
form.LoadChangelist(sha);
dlg = form.ShowDialog();
// Using the "Yes" value to load a next commit
if (dlg == DialogResult.Yes && index > 0)
index--;
// Using the "No" value to load a previous commit
if (dlg == DialogResult.No && index < li.Items.Count - 1)
index++;
} while (dlg != DialogResult.Cancel);
li.MultiSelect = multiSel;
}
/// <summary>
/// Given a Sha string, loads that commit into the form.
/// </summary>
public void LoadChangelist(string sha)
{
Tag = sha; // Store the SHA of a current commit in the Tag field of this form
// Issuing "show" command can take _very_ long time with a commit full of files
// Run a much faster 'whatchanged' command first to get the list of files
string cmd = "whatchanged " + sha + " -n 1 --format=medium";
ExecResult result = App.Repos.Current.Run(cmd);
string[] response = new[] { string.Empty };
if (result.Success())
response = result.stdout.Split(new[] { cr }, StringSplitOptions.None);
// Go over the resulting list and add to our text box
textChangelist.Text = ""; // Clear the rich text box
// Get the list of lines that describe individual files vs the rest (checkin comment)
List<string> files = response.Where(s => s.StartsWith(":")).ToList();
List<string> comment = response.Where(s => !s.StartsWith(":")).ToList();
// ---------------- Print the comment section ----------------
foreach (string s in comment)
textChangelist.AppendText(s + cr);
// ---------------- Print the files section ----------------
textChangelist.AppendText(cr + "Files:" + cr + cr, Color.Red);
foreach (string s in files)
{
// Parse the file name indicator following this template:
// :100644 000000 ed81075... 0000000... D file0
// [attributes] [prev] [next] [file]
// 0 1 2 3 4 (preamble[])
// Starting with git 2.10, new output is added for copy-edit and rename-edit (https://git-scm.com/docs/git-diff-tree)
// :100644 000000 ed81075... 0000000... C68 file1 file2 (copy-edit with a similarity "score")
// ^tab ^tab
string[] parts = s.Split(new[] { '\t' }, StringSplitOptions.RemoveEmptyEntries);
string[] preamble = parts[0].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if ((parts.Length == 2 || parts.Length == 3) && preamble.Length == 5)
{
// In the tag of the link, we will send file name and related SHA keys (stripped of ".")
string tag = string.Format("{0}#{1}#{2}", parts[1], preamble[2].Replace(".", ""), preamble[3].Replace(".", ""));
textChangelist.AppendText(preamble[4] + "\t");
if (parts.Length == 3) // Two files are listed
{
textChangelist.AppendText(parts[1] + " -> ");
textChangelist.InsertLink(parts[2], tag);
}
else
textChangelist.InsertLink(parts[1], tag);
}
else
textChangelist.AppendText(s);
textChangelist.AppendText(cr);
}
// Now optionally run the detailed show command, but if the number of files is large,
// ask the user to confirm for any more than, say, 30 files
if (comboShow.SelectedIndex==0)
return;
if (files.Count > 30)
{
string q =
"The number of files changed in this commit is very large and it may take considerable time" +
" to display their detailed difference. Do you still want to proceed?\n\n(To skip this message in the future, select " +
"(none) in the details option)";
if (MessageBox.Show(q, "Detailed difference of " + files.Count + " files", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.No)
return;
}
cmd = "show -t " + sha + " --format=" + comboShow.SelectedItem;
result = App.Repos.Current.Run(cmd);
if (result.Success())
response = result.stdout.Split(new[] { cr }, StringSplitOptions.None);
textChangelist.AppendText(cr + "Details" + cr + cr, Color.Red);
// Write out the complete response text containing all files' differences
foreach (string s in response)
textChangelist.SelectedText = s + cr;
textChangelist.Select(0, 0);
}
/// <summary>
/// User left-clicked on a file link within the rich text box
/// </summary>
private void TextChangelistLinkClicked(object sender, LinkClickedEventArgs e)
{
// The LinkText as sent contains the text + link separated by '#' character.
// Use the link portion only to send to our handlers.
string[] chunk = e.LinkText.Split('#');
contextMenuFile.Tag = chunk[1];
if (chunk[2] != "0000000") // SHA of the previous revision of a file
{
menuItemDiffPrev.Enabled = true;
menuItemDiffPrev.Tag = Tag + "^.." + Tag;
}
else
menuItemDiffPrev.Enabled = false;
if (chunk[3] != "0000000") // SHA of the next revision of a file
{
menuItemDiffNext.Enabled = true;
menuItemDiffNext.Tag = chunk[3];
}
else
menuItemDiffNext.Enabled = false;
// If the next revision exists, compare against a possible HEAD
menuItemDiffHead.Enabled = menuItemDiffNext.Enabled;
menuItemDiffHead.Tag = Tag + "..HEAD";
contextMenuFile.Show(Cursor.Position);
}
/// <summary>
/// User changed the show mode (format)
/// 'This' Tag contains the SHA of a current commit
/// </summary>
private void ComboShowSelectedIndexChanged(object sender, EventArgs e)
{
if (Properties.Settings.Default.ShowFormatIndex != comboShow.SelectedIndex)
{
Properties.Settings.Default.ShowFormatIndex = comboShow.SelectedIndex;
LoadChangelist(Tag.ToString());
}
}
/// <summary>
/// Diff selected file versus one of several options.
/// The root menu class' Tag contains the name of the file to diff
/// The individual menu item class Tag contains the diff options
/// </summary>
private void MenuItemDiffClick(object sender, EventArgs e)
{
string tag = (sender as ToolStripMenuItem).Tag.ToString();
string diffOpt = tag;
string fileName = contextMenuFile.Tag.ToString();
ClassStatus status = App.Repos.Current.Status;
status.Repo.GitDiff(diffOpt, new List<string> {fileName});
}
/// <summary>
/// Show the revision history dialog for a selected file.
/// This dialog is _not_ modal, so user can view multiple files.
/// </summary>
private void MenuItemRevClick(object sender, EventArgs e)
{
string file = contextMenuFile.Tag as string;
var formRevisionHistory = new FormRevisionHistory(file) {Sha = Tag.ToString()};
formRevisionHistory.Show();
}
#region Handle selected text copy
/// <summary>
/// Copy selected text onto the clipboard
/// </summary>
private void MenuItemCopyClick(object sender, EventArgs e)
{
textChangelist.Copy();
}
/// <summary>
/// Handle key down event so we can do the copy (Ctrl + C)
/// </summary>
private void TextChangelistKeyDown(object sender, KeyEventArgs e)
{
if ((ModifierKeys & Keys.Control) == Keys.Control && e.KeyCode == Keys.C)
{
MenuItemCopyClick(sender, null);
e.SuppressKeyPress = true;
}
}
#endregion
}
}