PageRenderTime 62ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/GitUI/UserControls/RevisionGrid.cs

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

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