PageRenderTime 64ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/GitUI/UserControls/RevisionGrid.cs

https://github.com/qgppl/gitextensions
C# | 3198 lines | 2619 code | 491 blank | 88 comment | 524 complexity | aa1cc62956e1d2e0cf91aa051420d9e9 MD5 | raw file
Possible License(s): GPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.DirectoryServices;
  5. using System.Drawing;
  6. using System.Drawing.Drawing2D;
  7. using System.IO;
  8. using System.Linq;
  9. using System.Text.RegularExpressions;
  10. using System.Threading;
  11. using System.Threading.Tasks;
  12. using System.Windows.Forms;
  13. using GitCommands;
  14. using GitCommands.Git;
  15. using GitUI.BuildServerIntegration;
  16. using GitUI.CommandsDialogs;
  17. using GitUI.CommandsDialogs.BrowseDialog;
  18. using GitUI.HelperDialogs;
  19. using GitUI.Hotkey;
  20. using GitUI.RevisionGridClasses;
  21. using GitUI.Script;
  22. using GitUI.UserControls;
  23. using GitUI.UserControls.RevisionGridClasses;
  24. using GitUIPluginInterfaces;
  25. using Gravatar;
  26. using ResourceManager;
  27. namespace GitUI
  28. {
  29. public enum RevisionGridLayout
  30. {
  31. FilledBranchesSmall = 1,
  32. FilledBranchesSmallWithGraph = 2,
  33. Small = 3,
  34. SmallWithGraph = 4,
  35. Card = 5,
  36. CardWithGraph = 6,
  37. LargeCard = 7,
  38. LargeCardWithGraph = 8
  39. }
  40. public enum RevisionGraphDrawStyleEnum
  41. {
  42. Normal,
  43. DrawNonRelativesGray,
  44. HighlightSelected
  45. }
  46. [DefaultEvent("DoubleClick")]
  47. public sealed partial class RevisionGrid : GitModuleControl
  48. {
  49. private readonly TranslationString _droppingFilesBlocked = new TranslationString("For you own protection dropping more than 10 patch files at once is blocked!");
  50. private readonly TranslationString _cannotHighlightSelectedBranch = new TranslationString("Cannot highlight selected branch when revision graph is loading.");
  51. private readonly TranslationString _noRevisionFoundError = new TranslationString("No revision found.");
  52. private const int NodeDimension = 8;
  53. private const int LaneWidth = 13;
  54. private const int LaneLineWidth = 2;
  55. private const int MaxSuperprojectRefs = 4;
  56. private Brush _selectedItemBrush;
  57. private SolidBrush _authoredRevisionsBrush;
  58. private Brush _filledItemBrush; // disposable brush
  59. private readonly FormRevisionFilter _revisionFilter = new FormRevisionFilter();
  60. private RefsFiltringOptions _refsOptions = RefsFiltringOptions.All | RefsFiltringOptions.Boundary;
  61. private bool _initialLoad = true;
  62. private string _initialSelectedRevision;
  63. private string _lastQuickSearchString = string.Empty;
  64. private Label _quickSearchLabel;
  65. private string _quickSearchString;
  66. private RevisionGraph _revisionGraphCommand;
  67. public BuildServerWatcher BuildServerWatcher { get; private set; }
  68. private RevisionGridLayout _layout;
  69. private int _rowHeigth;
  70. public event EventHandler<GitModuleEventArgs> GitModuleChanged;
  71. public event EventHandler<DoubleClickRevisionEventArgs> DoubleClickRevision;
  72. public event EventHandler<EventArgs> ShowFirstParentsToggled;
  73. private readonly RevisionGridMenuCommands _revisionGridMenuCommands;
  74. bool _showCurrentBranchOnlyToolStripMenuItemChecked; // refactoring
  75. bool _showAllBranchesToolStripMenuItemChecked; // refactoring
  76. bool _showFilteredBranchesToolStripMenuItemChecked; // refactoring
  77. private readonly ParentChildNavigationHistory _parentChildNavigationHistory;
  78. private readonly NavigationHistory _navigationHistory = new NavigationHistory();
  79. private AuthorEmailBasedRevisionHighlighting _revisionHighlighting;
  80. private GitRevision _baseCommitToCompare = null;
  81. /// <summary>
  82. /// Refs loaded while the latest processing of git log
  83. /// </summary>
  84. private IEnumerable<IGitRef> LatestRefs = Enumerable.Empty<IGitRef>();
  85. public RevisionGrid()
  86. {
  87. InitLayout();
  88. InitializeComponent();
  89. // Parent-child navigation can expect that SetSelectedRevision is always successfull since it always uses first-parents
  90. _parentChildNavigationHistory = new ParentChildNavigationHistory(revision => SetSelectedRevision(revision));
  91. _revisionHighlighting = new AuthorEmailBasedRevisionHighlighting();
  92. this.Loading.Image = global::GitUI.Properties.Resources.loadingpanel;
  93. Translate();
  94. _revisionGridMenuCommands = new RevisionGridMenuCommands(this);
  95. _revisionGridMenuCommands.CreateOrUpdateMenuCommands();
  96. // fill View context menu from MenuCommands
  97. var viewMenuCommands = _revisionGridMenuCommands.GetViewMenuCommands();
  98. FillMenuFromMenuCommands(viewMenuCommands, viewToolStripMenuItem);
  99. // fill Navigate context menu from MenuCommands
  100. var navigateMenuCommands = _revisionGridMenuCommands.GetNavigateMenuCommands();
  101. FillMenuFromMenuCommands(navigateMenuCommands, navigateToolStripMenuItem);
  102. NormalFont = AppSettings.Font;
  103. Loading.Paint += Loading_Paint;
  104. Revisions.CellPainting += RevisionsCellPainting;
  105. Revisions.CellFormatting += RevisionsCellFormatting;
  106. Revisions.KeyPress += RevisionsKeyPress;
  107. Revisions.KeyDown += RevisionsKeyDown;
  108. Revisions.MouseDown += RevisionsMouseDown;
  109. showMergeCommitsToolStripMenuItem.Checked = AppSettings.ShowMergeCommits;
  110. BranchFilter = String.Empty;
  111. SetShowBranches();
  112. QuickRevisionFilter = "";
  113. FixedRevisionFilter = "";
  114. FixedPathFilter = "";
  115. InMemFilterIgnoreCase = true;
  116. InMemAuthorFilter = "";
  117. InMemCommitterFilter = "";
  118. InMemMessageFilter = "";
  119. AllowGraphWithFilter = false;
  120. _quickSearchString = "";
  121. quickSearchTimer.Tick += QuickSearchTimerTick;
  122. Revisions.Loading += RevisionsLoading;
  123. //Allow to drop patch file on revisiongrid
  124. Revisions.DragEnter += Revisions_DragEnter;
  125. Revisions.DragDrop += Revisions_DragDrop;
  126. Revisions.AllowDrop = true;
  127. Revisions.ColumnHeadersVisible = false;
  128. Revisions.IdColumn.Visible = AppSettings.ShowIds;
  129. IsMessageMultilineDataGridViewColumn.Width = 25;
  130. IsMessageMultilineDataGridViewColumn.DisplayIndex = 2;
  131. IsMessageMultilineDataGridViewColumn.Resizable = DataGridViewTriState.False;
  132. this.HotkeysEnabled = true;
  133. try
  134. {
  135. SetRevisionsLayout((RevisionGridLayout)AppSettings.RevisionGraphLayout);
  136. }
  137. catch
  138. {
  139. SetRevisionsLayout(RevisionGridLayout.SmallWithGraph);
  140. }
  141. compareToBaseToolStripMenuItem.Enabled = false;
  142. }
  143. private void FillMenuFromMenuCommands(IEnumerable<MenuCommand> menuCommands, ToolStripMenuItem targetMenuItem)
  144. {
  145. foreach (var menuCommand in menuCommands)
  146. {
  147. var toolStripItem = (ToolStripItem)MenuCommand.CreateToolStripItem(menuCommand);
  148. var toolStripMenuItem = toolStripItem as ToolStripMenuItem;
  149. if (toolStripMenuItem != null)
  150. {
  151. menuCommand.RegisterMenuItem(toolStripMenuItem);
  152. }
  153. targetMenuItem.DropDownItems.Add(toolStripItem);
  154. }
  155. }
  156. void Loading_Paint(object sender, PaintEventArgs e)
  157. {
  158. // If our loading state has changed since the last paint, update it.
  159. if (Loading != null)
  160. {
  161. if (Loading.Visible != _isLoading)
  162. {
  163. Loading.Visible = _isLoading;
  164. }
  165. }
  166. }
  167. [Browsable(false)]
  168. public Font HeadFont { get; private set; }
  169. [Browsable(false)]
  170. public Font SuperprojectFont { get; private set; }
  171. [Browsable(false)]
  172. public int LastScrollPos { get; private set; }
  173. [Browsable(false)]
  174. public string[] LastSelectedRows { get; private set; }
  175. [Browsable(false)]
  176. public Font RefsFont { get; private set; }
  177. private Font _normalFont;
  178. [Browsable(false)]
  179. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  180. public Font NormalFont
  181. {
  182. get { return _normalFont; }
  183. set
  184. {
  185. _normalFont = value;
  186. MessageDataGridViewColumn.DefaultCellStyle.Font = _normalFont;
  187. DateDataGridViewColumn.DefaultCellStyle.Font = _normalFont;
  188. IdDataGridViewColumn.DefaultCellStyle.Font = new Font("Consolas", _normalFont.SizeInPoints);
  189. IsMessageMultilineDataGridViewColumn.DefaultCellStyle.Font = _normalFont;
  190. RefsFont = IsFilledBranchesLayout() ? _normalFont : new Font(_normalFont, FontStyle.Bold);
  191. HeadFont = new Font(_normalFont, FontStyle.Bold);
  192. SuperprojectFont = new Font(_normalFont, FontStyle.Underline);
  193. }
  194. }
  195. [Category("Filter")]
  196. [DefaultValue("")]
  197. public string QuickRevisionFilter { get; set; }
  198. [Category("Filter")]
  199. [DefaultValue("")]
  200. public string FixedRevisionFilter { get; set; }
  201. [Category("Filter")]
  202. [DefaultValue("")]
  203. public string FixedPathFilter { get; set; }
  204. [Category("Filter")]
  205. [DefaultValue(true)]
  206. public bool InMemFilterIgnoreCase { get; set; }
  207. [Category("Filter")]
  208. [DefaultValue("")]
  209. public string InMemAuthorFilter { get; set; }
  210. [Category("Filter")]
  211. [DefaultValue("")]
  212. public string InMemCommitterFilter { get; set; }
  213. [Category("Filter")]
  214. [DefaultValue("")]
  215. public string InMemMessageFilter { get; set; }
  216. [Category("Filter")]
  217. [DefaultValue("")]
  218. public string BranchFilter { get; set; }
  219. [Category("Filter")]
  220. [DefaultValue(false)]
  221. public bool AllowGraphWithFilter { get; set; }
  222. [Browsable(false)]
  223. public string CurrentCheckout { get; private set; }
  224. [Browsable(false)]
  225. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  226. public string FiltredFileName { get; set; }
  227. [Browsable(false)]
  228. public Task<SuperProjectInfo> SuperprojectCurrentCheckout { get; private set; }
  229. [Browsable(false)]
  230. public int LastRowIndex { get; private set; }
  231. [Description("Indicates whether the user is allowed to select more than one commit at a time.")]
  232. [Category("Behavior")]
  233. [DefaultValue(true)]
  234. public bool MultiSelect
  235. {
  236. get { return Revisions.MultiSelect; }
  237. set { Revisions.MultiSelect = value; }
  238. }
  239. [Description("Show uncommited changes in revision grid if enabled in settings.")]
  240. [Category("Behavior")]
  241. [DefaultValue(false)]
  242. public bool ShowUncommitedChangesIfPossible
  243. {
  244. get;
  245. set;
  246. }
  247. [Description("Show build server information in revision grid if enabled in settings.")]
  248. [Category("Behavior")]
  249. [DefaultValue(false)]
  250. public bool ShowBuildServerInfo
  251. {
  252. get;
  253. set;
  254. }
  255. [Description("Do not open the commit info dialog on double click. This is used if the double click event is handled elseswhere.")]
  256. [Category("Behavior")]
  257. [DefaultValue(false)]
  258. public bool DoubleClickDoesNotOpenCommitInfo
  259. {
  260. get;
  261. set;
  262. }
  263. private IndexWatcher _indexWatcher;
  264. [Browsable(false)]
  265. public IndexWatcher IndexWatcher
  266. {
  267. get
  268. {
  269. if (_indexWatcher == null)
  270. _indexWatcher = new IndexWatcher(UICommandsSource);
  271. return _indexWatcher;
  272. }
  273. }
  274. public void SetInitialRevision(GitRevision initialSelectedRevision)
  275. {
  276. _initialSelectedRevision = initialSelectedRevision != null ? initialSelectedRevision.Guid : null;
  277. }
  278. private bool _isLoading;
  279. private void RevisionsLoading(object sender, DvcsGraph.LoadingEventArgs e)
  280. {
  281. // Since this can happen on a background thread, we'll just set a
  282. // flag and deal with it next time we paint (a bit of a hack, but
  283. // it works)
  284. _isLoading = e.IsLoading;
  285. }
  286. private void ShowQuickSearchString()
  287. {
  288. if (_quickSearchLabel == null)
  289. {
  290. _quickSearchLabel
  291. = new Label
  292. {
  293. Location = new Point(10, 10),
  294. BorderStyle = BorderStyle.FixedSingle,
  295. ForeColor = SystemColors.InfoText,
  296. BackColor = SystemColors.Info
  297. };
  298. Controls.Add(_quickSearchLabel);
  299. }
  300. _quickSearchLabel.Visible = true;
  301. _quickSearchLabel.BringToFront();
  302. _quickSearchLabel.Text = _quickSearchString;
  303. _quickSearchLabel.AutoSize = true;
  304. }
  305. private void HideQuickSearchString()
  306. {
  307. if (_quickSearchLabel != null)
  308. _quickSearchLabel.Visible = false;
  309. }
  310. private void QuickSearchTimerTick(object sender, EventArgs e)
  311. {
  312. quickSearchTimer.Stop();
  313. _quickSearchString = "";
  314. HideQuickSearchString();
  315. }
  316. private void RestartQuickSearchTimer()
  317. {
  318. quickSearchTimer.Stop();
  319. quickSearchTimer.Interval = AppSettings.RevisionGridQuickSearchTimeout;
  320. quickSearchTimer.Start();
  321. }
  322. private void RevisionsKeyPress(object sender, KeyPressEventArgs e)
  323. {
  324. var curIndex = -1;
  325. if (Revisions.SelectedRows.Count > 0)
  326. curIndex = Revisions.SelectedRows[0].Index;
  327. curIndex = curIndex >= 0 ? curIndex : 0;
  328. if (e.KeyChar == 8 && _quickSearchString.Length > 1) //backspace
  329. {
  330. RestartQuickSearchTimer();
  331. _quickSearchString = _quickSearchString.Substring(0, _quickSearchString.Length - 1);
  332. FindNextMatch(curIndex, _quickSearchString, false);
  333. _lastQuickSearchString = _quickSearchString;
  334. e.Handled = true;
  335. ShowQuickSearchString();
  336. }
  337. else if (!char.IsControl(e.KeyChar))
  338. {
  339. RestartQuickSearchTimer();
  340. //The code below is meant to fix the weird keyvalues when pressing keys e.g. ".".
  341. _quickSearchString = string.Concat(_quickSearchString, char.ToLower(e.KeyChar));
  342. FindNextMatch(curIndex, _quickSearchString, false);
  343. _lastQuickSearchString = _quickSearchString;
  344. e.Handled = true;
  345. ShowQuickSearchString();
  346. }
  347. else
  348. {
  349. _quickSearchString = "";
  350. HideQuickSearchString();
  351. e.Handled = false;
  352. }
  353. }
  354. private void RevisionsKeyDown(object sender, KeyEventArgs e)
  355. {
  356. // BrowserBack/BrowserForward keys and additional handling for Alt+Right/Left sent by some keyboards
  357. if ((e.KeyCode == Keys.BrowserBack) || ((e.KeyCode == Keys.Left) && (e.Modifiers.HasFlag(Keys.Alt))))
  358. {
  359. NavigateBackward();
  360. }
  361. else if ((e.KeyCode == Keys.BrowserForward) || ((e.KeyCode == Keys.Right) && (e.Modifiers.HasFlag(Keys.Alt))))
  362. {
  363. NavigateForward();
  364. }
  365. }
  366. private void RevisionsMouseDown(object sender, MouseEventArgs e)
  367. {
  368. if (e.Button == MouseButtons.XButton1)
  369. {
  370. NavigateBackward();
  371. }
  372. else if (e.Button == MouseButtons.XButton2)
  373. {
  374. NavigateForward();
  375. }
  376. }
  377. public void ResetNavigationHistory()
  378. {
  379. var selectedRevisions = GetSelectedRevisions();
  380. if (selectedRevisions.Count == 1)
  381. {
  382. _navigationHistory.Push(selectedRevisions[0].Guid);
  383. }
  384. else
  385. {
  386. _navigationHistory.Clear();
  387. }
  388. }
  389. public void NavigateBackward()
  390. {
  391. if (_navigationHistory.CanNavigateBackward)
  392. {
  393. InternalSetSelectedRevision(_navigationHistory.NavigateBackward());
  394. }
  395. }
  396. public void NavigateForward()
  397. {
  398. if (_navigationHistory.CanNavigateForward)
  399. {
  400. InternalSetSelectedRevision(_navigationHistory.NavigateForward());
  401. }
  402. }
  403. private void FindNextMatch(int startIndex, string searchString, bool reverse)
  404. {
  405. if (Revisions.RowCount == 0)
  406. return;
  407. int? searchResult;
  408. if (reverse)
  409. searchResult = SearchInReverseOrder(startIndex, searchString);
  410. else
  411. searchResult = SearchForward(startIndex, searchString);
  412. if (!searchResult.HasValue)
  413. return;
  414. Revisions.ClearSelection();
  415. Revisions.Rows[searchResult.Value].Selected = true;
  416. Revisions.CurrentCell = Revisions.Rows[searchResult.Value].Cells[1];
  417. }
  418. private int? SearchForward(int startIndex, string searchString)
  419. {
  420. // Check for out of bounds roll over if required
  421. int index;
  422. if (startIndex < 0 || startIndex >= Revisions.RowCount)
  423. startIndex = 0;
  424. for (index = startIndex; index < Revisions.RowCount; ++index)
  425. {
  426. if (GetRevision(index).MatchesSearchString(searchString))
  427. return index;
  428. }
  429. // We didn't find it so start searching from the top
  430. for (index = 0; index < startIndex; ++index)
  431. {
  432. if (GetRevision(index).MatchesSearchString(searchString))
  433. return index;
  434. }
  435. return null;
  436. }
  437. private int? SearchInReverseOrder(int startIndex, string searchString)
  438. {
  439. // Check for out of bounds roll over if required
  440. int index;
  441. if (startIndex < 0 || startIndex >= Revisions.RowCount)
  442. startIndex = Revisions.RowCount - 1;
  443. for (index = startIndex; index >= 0; --index)
  444. {
  445. if (GetRevision(index).MatchesSearchString(searchString))
  446. return index;
  447. }
  448. // We didn't find it so start searching from the bottom
  449. for (index = Revisions.RowCount - 1; index > startIndex; --index)
  450. {
  451. if (GetRevision(index).MatchesSearchString(searchString))
  452. return index;
  453. }
  454. return null;
  455. }
  456. public void DisableContextMenu()
  457. {
  458. Revisions.ContextMenuStrip = null;
  459. }
  460. public void FormatQuickFilter(string filter,
  461. bool[] parameters,
  462. out string revListArgs,
  463. out string inMemMessageFilter,
  464. out string inMemCommitterFilter,
  465. out string inMemAuthorFilter)
  466. {
  467. revListArgs = string.Empty;
  468. inMemMessageFilter = string.Empty;
  469. inMemCommitterFilter = string.Empty;
  470. inMemAuthorFilter = string.Empty;
  471. if (!string.IsNullOrEmpty(filter))
  472. {
  473. // hash filtering only possible in memory
  474. var cmdLineSafe = GitCommandHelpers.VersionInUse.IsRegExStringCmdPassable(filter);
  475. revListArgs = " --regexp-ignore-case ";
  476. if (parameters[0])
  477. if (cmdLineSafe && !MessageFilterCouldBeSHA(filter))
  478. revListArgs += "--grep=\"" + filter + "\" ";
  479. else
  480. inMemMessageFilter = filter;
  481. if (parameters[1] && !filter.IsNullOrWhiteSpace())
  482. if (cmdLineSafe)
  483. revListArgs += "--committer=\"" + filter + "\" ";
  484. else
  485. inMemCommitterFilter = filter;
  486. if (parameters[2] && !filter.IsNullOrWhiteSpace())
  487. if (cmdLineSafe)
  488. revListArgs += "--author=\"" + filter + "\" ";
  489. else
  490. inMemAuthorFilter = filter;
  491. if (parameters[3])
  492. if (cmdLineSafe)
  493. revListArgs += "\"-S" + filter + "\" ";
  494. else
  495. throw new InvalidOperationException("Filter text not valid for \"Diff contains\" filter.");
  496. }
  497. }
  498. public bool SetAndApplyBranchFilter(string filter)
  499. {
  500. if (filter.Equals(_revisionFilter.GetBranchFilter()))
  501. return false;
  502. if (filter.Equals(""))
  503. {
  504. AppSettings.BranchFilterEnabled = false;
  505. AppSettings.ShowCurrentBranchOnly = true;
  506. }
  507. else
  508. {
  509. AppSettings.BranchFilterEnabled = true;
  510. AppSettings.ShowCurrentBranchOnly = false;
  511. _revisionFilter.SetBranchFilter(filter);
  512. }
  513. SetShowBranches();
  514. return true;
  515. }
  516. public void SetLimit(int limit)
  517. {
  518. _revisionFilter.SetLimit(limit);
  519. }
  520. public override void Refresh()
  521. {
  522. if (IsDisposed)
  523. return;
  524. SetRevisionsLayout();
  525. base.Refresh();
  526. Revisions.Refresh();
  527. }
  528. protected override void OnCreateControl()
  529. {
  530. base.OnCreateControl();
  531. _isLoading = true;
  532. Error.Visible = false;
  533. NoCommits.Visible = false;
  534. NoGit.Visible = false;
  535. Revisions.Visible = false;
  536. Loading.Visible = true;
  537. Loading.BringToFront();
  538. BuildServerWatcher = new BuildServerWatcher(this, Revisions);
  539. }
  540. public new void Load()
  541. {
  542. if (!DesignMode)
  543. ReloadHotkeys();
  544. ForceRefreshRevisions();
  545. }
  546. public event EventHandler SelectionChanged;
  547. public void SetSelectedIndex(int index)
  548. {
  549. if (Revisions.Rows[index].Selected)
  550. return;
  551. Revisions.ClearSelection();
  552. Revisions.Rows[index].Selected = true;
  553. Revisions.CurrentCell = Revisions.Rows[index].Cells[1];
  554. Revisions.Select();
  555. }
  556. // Selects row containing revision given its revisionId
  557. // Returns whether the required revision was found and selected
  558. private bool InternalSetSelectedRevision(string revision)
  559. {
  560. int index = FindRevisionIndex(revision);
  561. if( index >= 0 )
  562. {
  563. SetSelectedIndex(index);
  564. return true;
  565. }
  566. else
  567. {
  568. Revisions.ClearSelection();
  569. Revisions.Select();
  570. return false;
  571. }
  572. }
  573. /// <summary>
  574. /// Find specified revision in known to the grid revisions
  575. /// </summary>
  576. /// <param name="revision">Revision to lookup</param>
  577. /// <returns>Index of the found revision or -1 if nothing was found</returns>
  578. private int FindRevisionIndex(string revision)
  579. {
  580. int? revIdx = Revisions.TryGetRevisionIndex(revision);
  581. return revIdx.HasValue ? revIdx.Value : -1;
  582. }
  583. public bool SetSelectedRevision(string revision)
  584. {
  585. var found = InternalSetSelectedRevision(revision);
  586. if (found)
  587. {
  588. _navigationHistory.Push(revision);
  589. }
  590. return found;
  591. }
  592. public GitRevision GetRevision(string guid)
  593. {
  594. return Revisions.GetRevision(guid);
  595. }
  596. public bool SetSelectedRevision(GitRevision revision)
  597. {
  598. return SetSelectedRevision(revision != null ? revision.Guid : null);
  599. }
  600. public void HighlightBranch(string aId)
  601. {
  602. RevisionGraphDrawStyle = RevisionGraphDrawStyleEnum.HighlightSelected;
  603. Revisions.HighlightBranch(aId);
  604. }
  605. private void RevisionsSelectionChanged(object sender, EventArgs e)
  606. {
  607. _parentChildNavigationHistory.RevisionsSelectionChanged();
  608. if (Revisions.SelectedRows.Count > 0)
  609. LastRowIndex = Revisions.SelectedRows[0].Index;
  610. SelectionTimer.Enabled = false;
  611. SelectionTimer.Stop();
  612. SelectionTimer.Enabled = true;
  613. SelectionTimer.Start();
  614. var selectedRevisions = GetSelectedRevisions();
  615. var firstSelectedRevision = selectedRevisions.FirstOrDefault();
  616. if (selectedRevisions.Count == 1 && firstSelectedRevision != null)
  617. _navigationHistory.Push(firstSelectedRevision.Guid);
  618. if (this.Parent != null && !Revisions.UpdatingVisibleRows &&
  619. _revisionHighlighting.ProcessRevisionSelectionChange(Module, selectedRevisions) ==
  620. AuthorEmailBasedRevisionHighlighting.SelectionChangeAction.RefreshUserInterface)
  621. {
  622. Refresh();
  623. }
  624. }
  625. public RevisionGraphDrawStyleEnum RevisionGraphDrawStyle
  626. {
  627. get
  628. {
  629. return Revisions.RevisionGraphDrawStyle;
  630. }
  631. set
  632. {
  633. Revisions.RevisionGraphDrawStyle = value;
  634. }
  635. }
  636. public List<GitRevision> GetSelectedRevisions()
  637. {
  638. return GetSelectedRevisions(null);
  639. }
  640. public List<GitRevision> GetSelectedRevisions(SortDirection? direction)
  641. {
  642. var rows = Revisions
  643. .SelectedRows
  644. .Cast<DataGridViewRow>()
  645. .Where(row => Revisions.RowCount > row.Index);
  646. if (direction.HasValue)
  647. {
  648. int d = direction.Value == SortDirection.Ascending ? 1 : -1;
  649. rows = rows.OrderBy((row) => row.Index, (r1, r2) => d * (r1 - r2));
  650. }
  651. return rows
  652. .Select(row => GetRevision(row.Index))
  653. .ToList();
  654. }
  655. public List<string> GetRevisionChildren(string revision)
  656. {
  657. return Revisions.GetRevisionChildren(revision);
  658. }
  659. public GitRevision GetRevision(int aRow)
  660. {
  661. return Revisions.GetRowData(aRow);
  662. }
  663. public GitRevision GetCurrentRevision()
  664. {
  665. var revision = Module.GetRevision(CurrentCheckout, true);
  666. var refs = Module.GetRefs(true, true);
  667. foreach (var gitRef in refs)
  668. {
  669. if (gitRef.Guid.Equals(revision.Guid))
  670. {
  671. revision.Refs.Add(gitRef);
  672. }
  673. }
  674. return revision;
  675. }
  676. public void RefreshRevisions()
  677. {
  678. if (IndexWatcher.IndexChanged)
  679. ForceRefreshRevisions();
  680. }
  681. private class RevisionGraphInMemFilterOr : RevisionGraphInMemFilter
  682. {
  683. private RevisionGraphInMemFilter fFilter1;
  684. private RevisionGraphInMemFilter fFilter2;
  685. public RevisionGraphInMemFilterOr(RevisionGraphInMemFilter aFilter1,
  686. RevisionGraphInMemFilter aFilter2)
  687. {
  688. fFilter1 = aFilter1;
  689. fFilter2 = aFilter2;
  690. }
  691. public override bool PassThru(GitRevision rev)
  692. {
  693. return fFilter1.PassThru(rev) || fFilter2.PassThru(rev);
  694. }
  695. }
  696. private class RevisionGridInMemFilter : RevisionGraphInMemFilter
  697. {
  698. private readonly string _AuthorFilter;
  699. private readonly Regex _AuthorFilterRegex;
  700. private readonly string _CommitterFilter;
  701. private readonly Regex _CommitterFilterRegex;
  702. private readonly string _MessageFilter;
  703. private readonly Regex _MessageFilterRegex;
  704. private readonly string _ShaFilter;
  705. private readonly Regex _ShaFilterRegex;
  706. public RevisionGridInMemFilter(string authorFilter, string committerFilter, string messageFilter, bool ignoreCase)
  707. {
  708. SetUpVars(authorFilter, ref _AuthorFilter, ref _AuthorFilterRegex, ignoreCase);
  709. SetUpVars(committerFilter, ref _CommitterFilter, ref _CommitterFilterRegex, ignoreCase);
  710. SetUpVars(messageFilter, ref _MessageFilter, ref _MessageFilterRegex, ignoreCase);
  711. if (!string.IsNullOrEmpty(_MessageFilter) && MessageFilterCouldBeSHA(_MessageFilter))
  712. {
  713. SetUpVars(messageFilter, ref _ShaFilter, ref _ShaFilterRegex, false);
  714. }
  715. }
  716. private static void SetUpVars(string filterValue,
  717. ref string filterStr,
  718. ref Regex filterRegEx,
  719. bool ignoreCase)
  720. {
  721. RegexOptions opts = RegexOptions.None;
  722. if (ignoreCase) opts = opts | RegexOptions.IgnoreCase;
  723. filterStr = filterValue != null ? filterValue.Trim() : string.Empty;
  724. try
  725. {
  726. filterRegEx = new Regex(filterStr, opts);
  727. }
  728. catch (ArgumentException)
  729. {
  730. filterRegEx = null;
  731. }
  732. }
  733. private static bool CheckCondition(string filter, Regex regex, string value)
  734. {
  735. return string.IsNullOrEmpty(filter) ||
  736. ((regex != null) && (value != null) && regex.Match(value).Success);
  737. }
  738. public override bool PassThru(GitRevision rev)
  739. {
  740. return CheckCondition(_AuthorFilter, _AuthorFilterRegex, rev.Author) &&
  741. CheckCondition(_CommitterFilter, _CommitterFilterRegex, rev.Committer) &&
  742. (CheckCondition(_MessageFilter, _MessageFilterRegex, rev.Body) ||
  743. CheckCondition(_ShaFilter, _ShaFilterRegex, rev.Guid));
  744. }
  745. public static RevisionGridInMemFilter CreateIfNeeded(string authorFilter,
  746. string committerFilter,
  747. string messageFilter,
  748. bool ignoreCase)
  749. {
  750. if (!(string.IsNullOrEmpty(authorFilter) &&
  751. string.IsNullOrEmpty(committerFilter) &&
  752. string.IsNullOrEmpty(messageFilter) &&
  753. !MessageFilterCouldBeSHA(messageFilter)))
  754. return new RevisionGridInMemFilter(authorFilter,
  755. committerFilter,
  756. messageFilter,
  757. ignoreCase);
  758. else
  759. return null;
  760. }
  761. }
  762. public void ReloadHotkeys()
  763. {
  764. this.Hotkeys = HotkeySettingsManager.LoadHotkeys(HotkeySettingsName);
  765. _revisionGridMenuCommands.CreateOrUpdateMenuCommands();
  766. }
  767. public void ReloadTranslation()
  768. {
  769. Translate();
  770. }
  771. public bool ShowRemoteRef(IGitRef r)
  772. {
  773. if (r.IsTag)
  774. return AppSettings.ShowSuperprojectTags;
  775. if (r.IsHead)
  776. return AppSettings.ShowSuperprojectBranches;
  777. if (r.IsRemote)
  778. return AppSettings.ShowSuperprojectRemoteBranches;
  779. return false;
  780. }
  781. [Browsable(false)]
  782. public Task<bool> UnstagedChanges { get; private set; }
  783. [Browsable(false)]
  784. public Task<bool> StagedChanges { get; private set; }
  785. private string _filtredCurrentCheckout;
  786. public void ForceRefreshRevisions()
  787. {
  788. try
  789. {
  790. RevisionGraphDrawStyle = RevisionGraphDrawStyleEnum.DrawNonRelativesGray;
  791. IsMessageMultilineDataGridViewColumn.Visible = AppSettings.ShowIndicatorForMultilineMessage;
  792. ApplyFilterFromRevisionFilterDialog();
  793. _initialLoad = true;
  794. BuildServerWatcher.CancelBuildStatusFetchOperation();
  795. DisposeRevisionGraphCommand();
  796. var newCurrentCheckout = Module.GetCurrentCheckout();
  797. Task<SuperProjectInfo> newSuperPrjectInfo =
  798. Task.Factory.StartNew(() => GetSuperprojectCheckout(ShowRemoteRef));
  799. newSuperPrjectInfo.ContinueWith((task) => Refresh(),
  800. TaskScheduler.FromCurrentSynchronizationContext());
  801. //Only check for tracked files. This usually makes more sense and it performs a lot
  802. //better then checking for untracked files.
  803. // TODO: Check FiltredFileName
  804. Task<bool> unstagedChanges =
  805. Task.Factory.StartNew(() => Module.GetUnstagedFiles().Any());
  806. Task<bool> stagedChanges =
  807. Task.Factory.StartNew(() => Module.GetStagedFiles().Any());
  808. // If the current checkout changed, don't get the currently selected rows, select the
  809. // new current checkout instead.
  810. if (newCurrentCheckout == CurrentCheckout)
  811. {
  812. LastSelectedRows = Revisions.SelectedIds;
  813. }
  814. else
  815. {
  816. // This is a new checkout, so ensure the variable is cleared out.
  817. LastSelectedRows = null;
  818. }
  819. Revisions.ClearSelection();
  820. CurrentCheckout = newCurrentCheckout;
  821. _filtredCurrentCheckout = null;
  822. _currentCheckoutParents = null;
  823. SuperprojectCurrentCheckout = newSuperPrjectInfo;
  824. UnstagedChanges = unstagedChanges;
  825. StagedChanges = stagedChanges;
  826. Revisions.Clear();
  827. Error.Visible = false;
  828. if (!Module.IsValidGitWorkingDir())
  829. {
  830. Revisions.Visible = false;
  831. NoCommits.Visible = true;
  832. Loading.Visible = false;
  833. NoGit.Visible = true;
  834. string dir = Module.WorkingDir;
  835. if (String.IsNullOrEmpty(dir) || !Directory.Exists(dir) ||
  836. Directory.GetDirectories(dir).Length == 0 &&
  837. Directory.GetFiles(dir).Length == 0)
  838. CloneRepository.Show();
  839. else
  840. CloneRepository.Hide();
  841. NoGit.BringToFront();
  842. return;
  843. }
  844. NoCommits.Visible = false;
  845. NoGit.Visible = false;
  846. Revisions.Visible = true;
  847. Revisions.BringToFront();
  848. Revisions.Enabled = false;
  849. Loading.Visible = true;
  850. Loading.BringToFront();
  851. _isLoading = true;
  852. base.Refresh();
  853. IndexWatcher.Reset();
  854. if (!AppSettings.ShowGitNotes && (_refsOptions & (RefsFiltringOptions.All | RefsFiltringOptions.Boundary)) == (RefsFiltringOptions.All | RefsFiltringOptions.Boundary))
  855. _refsOptions |= RefsFiltringOptions.ShowGitNotes;
  856. if (AppSettings.ShowGitNotes)
  857. _refsOptions &= ~RefsFiltringOptions.ShowGitNotes;
  858. if (!AppSettings.ShowMergeCommits)
  859. _refsOptions |= RefsFiltringOptions.NoMerges;
  860. if (AppSettings.ShowFirstParent)
  861. _refsOptions |= RefsFiltringOptions.FirstParent;
  862. if (AppSettings.ShowSimplifyByDecoration)
  863. _refsOptions |= RefsFiltringOptions.SimplifyByDecoration;
  864. RevisionGridInMemFilter revisionFilterIMF = RevisionGridInMemFilter.CreateIfNeeded(_revisionFilter.GetInMemAuthorFilter(),
  865. _revisionFilter.GetInMemCommitterFilter(),
  866. _revisionFilter.GetInMemMessageFilter(),
  867. _revisionFilter.GetIgnoreCase());
  868. RevisionGridInMemFilter filterBarIMF = RevisionGridInMemFilter.CreateIfNeeded(InMemAuthorFilter,
  869. InMemCommitterFilter,
  870. InMemMessageFilter,
  871. InMemFilterIgnoreCase);
  872. RevisionGraphInMemFilter revGraphIMF;
  873. if (revisionFilterIMF != null && filterBarIMF != null)
  874. revGraphIMF = new RevisionGraphInMemFilterOr(revisionFilterIMF, filterBarIMF);
  875. else if (revisionFilterIMF != null)
  876. revGraphIMF = revisionFilterIMF;
  877. else
  878. revGraphIMF = filterBarIMF;
  879. _revisionGraphCommand = new RevisionGraph(Module) {
  880. BranchFilter = BranchFilter,
  881. RefsOptions = _refsOptions,
  882. RevisionFilter = _revisionFilter.GetRevisionFilter() + QuickRevisionFilter + FixedRevisionFilter,
  883. PathFilter = _revisionFilter.GetPathFilter() + FixedPathFilter,
  884. };
  885. _revisionGraphCommand.Updated += GitGetCommitsCommandUpdated;
  886. _revisionGraphCommand.Exited += GitGetCommitsCommandExited;
  887. _revisionGraphCommand.Error += _revisionGraphCommand_Error;
  888. _revisionGraphCommand.InMemFilter = revGraphIMF;
  889. _revisionGraphCommand.Execute();
  890. LoadRevisions();
  891. SetRevisionsLayout();
  892. ResetNavigationHistory();
  893. }
  894. catch (Exception)
  895. {
  896. Error.Visible = true;
  897. Error.BringToFront();
  898. throw;
  899. }
  900. }
  901. public class SuperProjectInfo
  902. {
  903. public string CurrentBranch;
  904. public string Conflict_Base;
  905. public string Conflict_Remote;
  906. public string Conflict_Local;
  907. public Dictionary<string, List<IGitRef>> Refs;
  908. }
  909. private SuperProjectInfo GetSuperprojectCheckout(Func<IGitRef, bool> showRemoteRef)
  910. {
  911. if (Module.SuperprojectModule == null)
  912. return null;
  913. SuperProjectInfo spi = new SuperProjectInfo();
  914. var currentCheckout = Module.GetSuperprojectCurrentCheckout();
  915. if (currentCheckout.Key == 'U')
  916. {
  917. // return local and remote hashes
  918. var array = Module.SuperprojectModule.GetConflict(Module.SubmodulePath);
  919. spi.Conflict_Base = array.Base.Hash;
  920. spi.Conflict_Local = array.Local.Hash;
  921. spi.Conflict_Remote = array.Remote.Hash;
  922. }
  923. else
  924. {
  925. spi.CurrentBranch = currentCheckout.Value;
  926. }
  927. var refs = Module.SuperprojectModule.GetSubmoduleItemsForEachRef(Module.SubmodulePath, showRemoteRef);
  928. if (refs != null)
  929. {
  930. spi.Refs = refs.Where(a => a.Value != null).GroupBy(a => a.Value.Guid).ToDictionary(gr => gr.Key, gr => gr.Select(a => a.Key).ToList());
  931. }
  932. return spi;
  933. }
  934. private static readonly Regex PotentialShaPattern = new Regex(@"^[a-f0-9]{5,}", RegexOptions.Compiled);
  935. public static bool MessageFilterCouldBeSHA(string filter)
  936. {
  937. bool result = PotentialShaPattern.IsMatch(filter);
  938. return result;
  939. }
  940. private void _revisionGraphCommand_Error(object sender, AsyncErrorEventArgs e)
  941. {
  942. // This has to happen on the UI thread
  943. this.InvokeAsync(o =>
  944. {
  945. Error.Visible = true;
  946. //Error.BringToFront();
  947. NoGit.Visible = false;
  948. NoCommits.Visible = false;
  949. Revisions.Visible = false;
  950. Loading.Visible = false;
  951. }, this);
  952. DisposeRevisionGraphCommand();
  953. this.InvokeAsync(() =>
  954. {
  955. throw e.Exception;
  956. }
  957. );
  958. e.Handled = true;
  959. }
  960. private void GitGetCommitsCommandUpdated(object sender, EventArgs e)
  961. {
  962. var updatedEvent = (RevisionGraph.RevisionGraphUpdatedEventArgs)e;
  963. UpdateGraph(updatedEvent.Revision);
  964. }
  965. internal bool FilterIsApplied(bool inclBranchFilter)
  966. {
  967. return (inclBranchFilter && !string.IsNullOrEmpty(BranchFilter)) ||
  968. !(string.IsNullOrEmpty(QuickRevisionFilter) &&
  969. !_revisionFilter.FilterEnabled() &&
  970. string.IsNullOrEmpty(InMemAuthorFilter) &&
  971. string.IsNullOrEmpty(InMemCommitterFilter) &&
  972. string.IsNullOrEmpty(InMemMessageFilter));
  973. }
  974. private bool ShouldHideGraph(bool inclBranchFilter)
  975. {
  976. return (inclBranchFilter && !string.IsNullOrEmpty(BranchFilter)) ||
  977. !(!_revisionFilter.ShouldHideGraph() &&
  978. string.IsNullOrEmpty(InMemAuthorFilter) &&
  979. string.IsNullOrEmpty(InMemCommitterFilter) &&
  980. string.IsNullOrEmpty(InMemMessageFilter));
  981. }
  982. private void DisposeRevisionGraphCommand()
  983. {
  984. if (_revisionGraphCommand != null)
  985. {
  986. LatestRefs = _revisionGraphCommand.LatestRefs();
  987. //Dispose command, it is not needed anymore
  988. _revisionGraphCommand.Updated -= GitGetCommitsCommandUpdated;
  989. _revisionGraphCommand.Exited -= GitGetCommitsCommandExited;
  990. _revisionGraphCommand.Error -= _revisionGraphCommand_Error;
  991. _revisionGraphCommand.Dispose();
  992. _revisionGraphCommand = null;
  993. }
  994. }
  995. private void GitGetCommitsCommandExited(object sender, EventArgs e)
  996. {
  997. _isLoading = false;
  998. if (_revisionGraphCommand.RevisionCount == 0 &&
  999. !FilterIsApplied(true))
  1000. {
  1001. // This has to happen on the UI thread
  1002. this.InvokeAsync(o =>
  1003. {
  1004. NoGit.Visible = false;
  1005. NoCommits.Visible = true;
  1006. //NoCommits.BringToFront();
  1007. Revisions.Visible = false;
  1008. Loading.Visible = false;
  1009. }, this);
  1010. }
  1011. else
  1012. {
  1013. // This has to happen on the UI thread
  1014. this.InvokeAsync(o =>
  1015. {
  1016. UpdateGraph(null);
  1017. Loading.Visible = false;
  1018. SelectInitialRevision();
  1019. _isLoading = false;
  1020. if (ShowBuildServerInfo)
  1021. BuildServerWatcher.LaunchBuildServerInfoFetchOperation();
  1022. }, this);
  1023. }
  1024. DisposeRevisionGraphCommand();
  1025. }
  1026. private void SelectInitialRevision()
  1027. {
  1028. string filtredCurrentCheckout = _filtredCurrentCheckout;
  1029. string[] lastSelectedRows = LastSelectedRows ?? new string[0];
  1030. //filter out all unavailable commits from LastSelectedRows.
  1031. lastSelectedRows = lastSelectedRows.Where(revision => FindRevisionIndex(revision) >= 0).ToArray();
  1032. if (lastSelectedRows.Any())
  1033. {
  1034. Revisions.SelectedIds = lastSelectedRows;
  1035. LastSelectedRows = null;
  1036. }
  1037. else
  1038. {
  1039. if (!string.IsNullOrEmpty(_initialSelectedRevision))
  1040. {
  1041. int index = SearchRevision(_initialSelectedRevision);
  1042. if (index >= 0)
  1043. SetSelectedIndex(index);
  1044. }
  1045. else
  1046. {
  1047. SetSelectedRevision(filtredCurrentCheckout);
  1048. }
  1049. }
  1050. if (string.IsNullOrEmpty(filtredCurrentCheckout))
  1051. return;
  1052. if (!Revisions.IsRevisionRelative(filtredCurrentCheckout))
  1053. {
  1054. HighlightBranch(filtredCurrentCheckout);
  1055. }
  1056. }
  1057. private string[] GetAllParents(string initRevision)
  1058. {
  1059. var revListParams = "rev-list ";
  1060. if (AppSettings.OrderRevisionByDate)
  1061. revListParams += "--date-order ";
  1062. else
  1063. revListParams += "--topo-order ";
  1064. if (AppSettings.MaxRevisionGraphCommits > 0)
  1065. revListParams += string.Format("--max-count=\"{0}\" ", (int)AppSettings.MaxRevisionGraphCommits);
  1066. return Module.ReadGitOutputLines(revListParams + initRevision).ToArray();
  1067. }
  1068. private int SearchRevision(string initRevision)
  1069. {
  1070. var exactIndex = Revisions.TryGetRevisionIndex(initRevision);
  1071. if (exactIndex.HasValue)
  1072. return exactIndex.Value;
  1073. foreach (var parentHash in GetAllParents(initRevision))
  1074. {
  1075. var parentIndex = Revisions.TryGetRevisionIndex(parentHash);
  1076. if (parentIndex.HasValue)
  1077. return parentIndex.Value;
  1078. }
  1079. return -1;
  1080. }
  1081. private static string GetDateHeaderText()
  1082. {
  1083. return AppSettings.ShowAuthorDate ? Strings.GetAuthorDateText() : Strings.GetCommitDateText();
  1084. }
  1085. private void LoadRevisions()
  1086. {
  1087. if (_revisionGraphCommand == null)
  1088. {
  1089. return;
  1090. }
  1091. Revisions.SuspendLayout();
  1092. Revisions.MessageColumn.HeaderText = Strings.GetMessageText();
  1093. Revisions.AuthorColumn.HeaderText = Strings.GetAuthorText();
  1094. Revisions.DateColumn.HeaderText = GetDateHeaderText();
  1095. Revisions.SelectionChanged -= RevisionsSelectionChanged;
  1096. Revisions.Enabled = true;
  1097. Revisions.Focus();
  1098. Revisions.SelectionChanged += RevisionsSelectionChanged;
  1099. Revisions.ResumeLayout();
  1100. if (!_initialLoad)
  1101. return;
  1102. _initialLoad = false;
  1103. SelectionTimer.Enabled = false;
  1104. SelectionTimer.Stop();
  1105. SelectionTimer.Enabled = true;
  1106. SelectionTimer.Start();
  1107. }
  1108. public struct DrawRefArgs
  1109. {
  1110. public Graphics Graphics;
  1111. public Rectangle CellBounds;
  1112. public bool IsRowSelected;
  1113. public Font RefsFont;
  1114. }
  1115. private void RevisionsCellPainting(object sender, DataGridViewCellPaintingEventArgs e)
  1116. {
  1117. // If our loading state has changed since the last paint, update it.
  1118. if (Loading != null)
  1119. {
  1120. if (Loading.Visible != _isLoading)
  1121. {
  1122. Loading.Visible = _isLoading;
  1123. }
  1124. }
  1125. var columnIndex = e.ColumnIndex;
  1126. int graphColIndex = GraphDataGridViewColumn.Index;
  1127. int messageColIndex = MessageDataGridViewColumn.Index;
  1128. int authorColIndex = AuthorDataGridViewColumn.Index;
  1129. int dateColIndex = DateDataGridViewColumn.Index;
  1130. int idColIndex = IdDataGridViewColumn.Index;
  1131. int isMsgMultilineColIndex = IsMessageMultilineDataGridViewColumn.Index;
  1132. // The graph column is handled by the DvcsGraph
  1133. if (e.ColumnIndex == graphColIndex)
  1134. {
  1135. return;
  1136. }
  1137. if (e.RowIndex < 0 || (e.State & DataGridViewElementStates.Visible) == 0)
  1138. return;
  1139. if (Revisions.RowCount <= e.RowIndex)

Large files files are truncated, but you can click here to view the full file