Menu

[38707f]: / FormRevisionHistory.cs  Maximize  Restore  History

Download this file

364 lines (325 with data), 14.9 kB

  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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
using System;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using GitForce.Main.Right.Panels;
namespace GitForce
{
/// <summary>
/// Class dialog showing a log of a single file.
/// </summary>
public partial class FormRevisionHistory : Form
{
/// <summary>
/// The file name whose log this form shows
/// </summary>
private readonly string file;
/// <summary>
/// The current SHA string to initialize the list
/// </summary>
public string Sha { private get; set; }
/// <summary>
/// 2 last recently selected SHA submits
/// </summary>
private readonly string[] lruSha = new string[2];
/// <summary>
/// Temp file counter number
/// </summary>
private int tmpFileCounter = 1;
/// <summary>
/// Form constructor. Takes the git file name whose history is to be shown.
/// </summary>
public FormRevisionHistory(string targetFile)
{
InitializeComponent();
ClassWinGeometry.Restore(this);
// Apply the same font we use for description of changes
textDescription.Font = Properties.Settings.Default.commitFont;
file = targetFile;
Sha = String.Empty;
// Show complete path to the file being examined using the OS specific path separator
Text = @"Revision History for " + App.Repos.Current.Path.Replace('\\', Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar + targetFile.Replace('\\', Path.DirectorySeparatorChar);
// If the path specifies a folder (for example, user clicked on the root repo name on the view pane), add "..."
if (Text[Text.Length - 1] == Path.DirectorySeparatorChar)
Text = Text + "...";
}
/// <summary>
/// Access a virtual member
/// </summary>
public override sealed string Text
{
get { return base.Text; }
set { base.Text = value; }
}
/// <summary>
/// Form is closing.
/// </summary>
private void FormRevisionHistoryFormClosing(object sender, FormClosingEventArgs e)
{
ClassWinGeometry.Save(this);
}
/// <summary>
/// The form is loading. Get the file log information and fill it in.
/// </summary>
private void FormRevisionHistoryLoad(object sender, EventArgs e)
{
// Get the list of revisions by running a git command
StringBuilder cmd = new StringBuilder("log ");
cmd.Append(" --pretty=format:"); // Start formatting section
cmd.Append("%h%x09"); // Abbreviated commit hash
cmd.Append("%ct%x09"); // Committing time, UNIX-style
cmd.Append("%an%x09"); // Author name
cmd.Append("%s"); // Subject
// Limit the number of commits to show
if (Properties.Settings.Default.commitsRetrieveAll == false)
cmd.Append(" -" + Properties.Settings.Default.commitsRetrieveLast);
// Get the log of a single file only
cmd.Append(" -- \"" + file + "\"");
ExecResult result = App.Repos.Current.Run(cmd.ToString());
if (result.Success())
PanelRevlist.UpdateList(listRev, result.stdout, true, string.Empty);
if (listRev.Items.Count > 0)
{
// Activate the given SHA item or the first one if none given
int index = listRev.Items.IndexOfKey(Sha);
if (index < 0)
index = 0;
listRev.SelectedIndices.Add(index);
listRev.Items[index].EnsureVisible();
}
}
/// <summary>
/// User clicked on a log item. Fetch its full description.
/// </summary>
private void ListRevSelectedIndexChanged(object sender, EventArgs e)
{
// Set the menu enables according to the number of items selected
viewMenuItem.Enabled = syncMenuItem.Enabled = diffVsClientFileMenuItem.Enabled = listRev.SelectedIndices.Count == 1;
diffRevisionsMenuItem.Enabled = listRev.SelectedIndices.Count == 2;
// Set up for 2 SHA checkins: the one in the [0] spot being the most recently selected
if (listRev.SelectedIndices.Count == 1)
lruSha[0] = lruSha[1] = listRev.SelectedItems[0].Name;
if (listRev.SelectedIndices.Count > 1)
{
if (listRev.SelectedItems[0].Name == lruSha[0])
lruSha[1] = listRev.SelectedItems[1].Name;
else
lruSha[1] = listRev.SelectedItems[0].Name;
}
// Fill in the description of a selected checkin if a single one is selected
if (listRev.SelectedIndices.Count == 1)
{
string sha = lruSha[1];
string cmd = string.Format("show -s {0}", sha);
ExecResult result = App.Repos.Current.Run(cmd);
textDescription.Text = result.Success() ? result.stdout : result.stderr;
}
}
/// <summary>
/// Close the dialog.
/// </summary>
private void ExitMenuItemClick(object sender, EventArgs e)
{
Close();
}
/// <summary>
/// Diff selected against the HEAD revision
/// </summary>
private void DiffVsClientFileMenuItemClick(object sender, EventArgs e)
{
string cmd = "difftool " + ClassDiff.GetDiffCmd() + " " + lruSha[0] + "..HEAD -- " + file;
RunDiff(cmd);
}
/// <summary>
/// Diff 2 selected revisions
/// </summary>
private void DiffRevisionsMenuItemClick(object sender, EventArgs e)
{
string cmd = "difftool " + ClassDiff.GetDiffCmd() + " " + lruSha[0] + ".." + lruSha[1] + " -- " + file;
RunDiff(cmd);
}
/// <summary>
/// Runs a diff tool in the context of the current repo for a selected file.
/// This is a separate function that runs a git command since we want to start a
/// diff process and do not block.
/// </summary>
private void RunDiff(string cmd)
{
try
{
Process proc = new Process
{
StartInfo =
{
FileName = Properties.Settings.Default.GitPath,
Arguments = cmd,
WorkingDirectory = App.Repos.Current.Path,
UseShellExecute = false,
CreateNoWindow = true
}
};
proc.Start();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error executing diff", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// Sync file to the selected revision
/// </summary>
private void SyncMenuItemClick(object sender, EventArgs e)
{
if (MessageBox.Show("This will sync file to a previous version. Continue?", "Revision Sync",
MessageBoxButtons.YesNo, MessageBoxIcon.Question)== DialogResult.Yes)
{
string cmd = string.Format("checkout {1} -- \"{0}\"", file, lruSha[0]);
ExecResult result = App.Repos.Current.RunCmd(cmd);
if (result.Success())
{
App.PrintStatusMessage("File checked out at a previous revision " + lruSha[0] + ": " + file, MessageType.General);
App.DoRefresh();
}
else
{
App.PrintStatusMessage("Sync error: " + result.stderr, MessageType.Error);
MessageBox.Show(result.stderr, "Sync error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
/// <summary>
/// View file menu item is opening.
/// Build a list of editors to view the selected file.
/// </summary>
private void ViewMenuItemDropDownOpening(object sender, EventArgs e)
{
viewMenuItem.DropDownItems.Clear();
ToolStripMenuItem mEditAssoc = new ToolStripMenuItem("Associated Editor", null, MenuViewEditClick);
viewMenuItem.DropDownItems.Add(mEditAssoc);
string values = Properties.Settings.Default.EditViewPrograms;
string[] progs = values.Split(("\0").ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
foreach (string s in progs)
viewMenuItem.DropDownItems.Add(new ToolStripMenuItem(Path.GetFileName(s), null, MenuViewEditClick) { Tag = s });
}
/// <summary>
/// Handler for the view file menus.
/// The tag of the sender specifies the operation on a selected file: null will open a file
/// using the default association, while any other tag specifies a program to run.
/// </summary>
private void MenuViewEditClick(object sender, EventArgs e)
{
// Create a temp file on the selected git file version
string temp = GetTempFile(file, listRev.SelectedItems[0].Name);
if (!string.IsNullOrEmpty(temp))
ClassUtils.FileOpenFromMenu(sender, temp);
}
/// <summary>
/// Control is double-clicked. Open the selected item for viewing.
/// Depending on the saved options, we either do nothing ("0"), open a file
/// using a default Explorer file association ("1"), or open a file using a
/// specified application ("2")
/// </summary>
private void ListRevDoubleClick(object sender, EventArgs e)
{
if (listRev.SelectedIndices.Count == 1)
{
// Create a temp file and open the file
string temp = GetTempFile(file, listRev.SelectedItems[0].Name);
if (!string.IsNullOrEmpty(temp))
ClassUtils.FileDoubleClick(temp);
}
}
/// <summary>
/// Right-mouse button opens a popup with the context menu
/// </summary>
private void ListRevMouseUp(object sender, MouseEventArgs e)
{
// Clear the context menu first so it's only shown when we enter the condition below
contextMenu.Items.Clear();
if (e.Button == MouseButtons.Right && listRev.SelectedIndices.Count > 0)
{
// Build the context menu to be shown
contextMenu.Items.AddRange(GetContextMenu(contextMenu));
}
}
/// <summary>
/// Builds and returns a context menu for revision history list
/// </summary>
public ToolStripItemCollection GetContextMenu(ToolStrip owner)
{
// Build the "View Using" submenus
// The default option is to open the file using the OS-associated editor,
// after which all the user-specified programs are listed
ToolStripMenuItem mViewAssoc = new ToolStripMenuItem("Associated Editor", null, MenuViewEditClick) { };
ToolStripMenuItem mView = new ToolStripMenuItem("View Using");
mView.DropDownItems.Add(mViewAssoc);
string values = Properties.Settings.Default.EditViewPrograms;
string[] progs = values.Split(("\0").ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
if (progs.Any())
{
mView.DropDownItems.Add(new ToolStripMenuItem(Path.GetFileName(progs[0]), null, MenuViewEditClick) { Tag = progs[0] });
foreach (string s in progs.Skip(1))
mView.DropDownItems.Add(new ToolStripMenuItem(Path.GetFileName(s), null, MenuViewEditClick) { Tag = s });
}
ToolStripMenuItem mDescribe = new ToolStripMenuItem("Describe Changelist...", null, MenuDescribeClick);
ToolStripMenuItem mCopy = new ToolStripMenuItem("Copy SHA", null, MenuCopyShaClick);
ToolStripItemCollection menu = new ToolStripItemCollection(owner, new ToolStripItem[] {
mView, mDescribe, mCopy
});
if (listRev.SelectedIndices.Count != 1)
mView.Enabled = mDescribe.Enabled = mCopy.Enabled = false;
return menu;
}
/// <summary>
/// Copy the selected SHA number into the clipboard
/// </summary>
private void MenuCopyShaClick(object sender, EventArgs e)
{
if (listRev.SelectedIndices.Count == 1)
{
string sha = listRev.SelectedItems[0].Name;
Clipboard.SetText(sha);
}
}
/// <summary>
/// Describe (view) selected changelist
/// </summary>
private void MenuDescribeClick(object sender, EventArgs e)
{
FormShowChangelist.DriveChangelistFromListViewEx(ref listRev);
}
/// <summary>
/// Create a git file of a specific version. Use a temp file since the file
/// content needs to be created from the git history.
/// </summary>
private string GetTempFile(string file, string sha)
{
// git show commands needs '/' as file path separator
string gitpath = file.Replace(Path.DirectorySeparatorChar, '/');
string cmd = string.Format("show {1}:\"{0}\"", gitpath, sha);
ExecResult result = App.Repos.Current.Run(cmd);
if (result.Success() == false)
return string.Empty;
string response = result.stdout;
// Create a temp file based on a version of our file and write its content to it
string rev = listRev.Items.Find(sha, false)[0].Text.Trim();
file = Path.GetFileName(file);
file = string.Format("ReadOnly-{0}-Rev-{1}-{2}", tmpFileCounter, rev, file);
tmpFileCounter++;
string tempFile = Path.Combine(Path.GetTempPath(), file);
try
{
File.WriteAllText(tempFile, response);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "System error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
// Add the temp file to the global list of temp files to be removed at the app exit time
ClassGlobals.TempFiles.Add(tempFile);
return tempFile;
}
}
}
Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.