PageRenderTime 63ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/GitUI/CommandsDialogs/FormPush.cs

https://github.com/vbjay/gitextensions
C# | 844 lines | 701 code | 119 blank | 24 comment | 158 complexity | a7fd7730f157bd3ef76ccdd5bd5cb103 MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Data;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text.RegularExpressions;
  7. using System.Windows.Forms;
  8. using GitCommands;
  9. using GitCommands.Config;
  10. using GitCommands.Repository;
  11. using GitUI.Script;
  12. using ResourceManager;
  13. namespace GitUI.CommandsDialogs
  14. {
  15. public partial class FormPush : GitModuleForm
  16. {
  17. private const string HeadText = "HEAD";
  18. private const string AllRefs = "[ All ]";
  19. private string _currentBranch;
  20. private string _currentBranchRemote;
  21. private bool _candidateForRebasingMergeCommit;
  22. private string _selectedBranch;
  23. private string _selectedBranchRemote;
  24. private string _selectedRemoteBranchName;
  25. private readonly AsyncLoader _remoteBranchesLoader = new AsyncLoader();
  26. public bool ErrorOccurred { get; private set; }
  27. #region Translation
  28. private readonly TranslationString _branchNewForRemote =
  29. new TranslationString("The branch you are about to push seems to be a new branch for the remote." +
  30. Environment.NewLine + "Are you sure you want to push this branch?");
  31. private readonly TranslationString _pushCaption = new TranslationString("Push");
  32. private readonly TranslationString _pushToCaption = new TranslationString("Push to {0}");
  33. private readonly TranslationString _selectDestinationDirectory =
  34. new TranslationString("Please select a destination directory");
  35. private readonly TranslationString _selectRemote = new TranslationString("Please select a remote repository");
  36. private readonly TranslationString _selectTag =
  37. new TranslationString("You need to select a tag to push or select \"Push all tags\".");
  38. private readonly TranslationString _updateTrackingReference =
  39. new TranslationString("The branch {0} does not have a tracking reference. Do you want to add a tracking reference to {1}?");
  40. private readonly TranslationString _yes = new TranslationString("Yes");
  41. private readonly TranslationString _no = new TranslationString("No");
  42. private readonly TranslationString _pullRepositoryMainInstruction = new TranslationString("Pull latest changes from remote repository");
  43. private readonly TranslationString _pullRepository =
  44. new TranslationString("The push was rejected because the tip of your current branch is behind its remote counterpart. " +
  45. "Merge the remote changes before pushing again.");
  46. private readonly TranslationString _pullRepositoryButtons = new TranslationString("Pull with last pull action ({0})|Pull with rebase|Pull with merge|Force push|Cancel");
  47. private readonly TranslationString _pullActionNone = new TranslationString("none");
  48. private readonly TranslationString _pullActionFetch = new TranslationString("fetch");
  49. private readonly TranslationString _pullActionRebase = new TranslationString("rebase");
  50. private readonly TranslationString _pullActionMerge = new TranslationString("merge");
  51. private readonly TranslationString _pullRepositoryCaption = new TranslationString("Push was rejected from \"{0}\"");
  52. private readonly TranslationString _dontShowAgain = new TranslationString("Remember my decision.");
  53. #endregion
  54. private FormPush()
  55. : this(null)
  56. { }
  57. public FormPush(GitUICommands aCommands)
  58. : base(aCommands)
  59. {
  60. InitializeComponent();
  61. Translate();
  62. //can't be set in OnLoad, because after PushAndShowDialogWhenFailed()
  63. //they are reset to false
  64. if (aCommands != null)
  65. Init();
  66. }
  67. private void Init()
  68. {
  69. if (GitCommandHelpers.VersionInUse.SupportPushWithRecursiveSubmodulesCheck)
  70. {
  71. RecursiveSubmodules.Enabled = true;
  72. RecursiveSubmodules.SelectedIndex = AppSettings.RecursiveSubmodules;
  73. if (!GitCommandHelpers.VersionInUse.SupportPushWithRecursiveSubmodulesOnDemand)
  74. RecursiveSubmodules.Items.RemoveAt(2);
  75. }
  76. else
  77. {
  78. RecursiveSubmodules.Enabled = false;
  79. RecursiveSubmodules.SelectedIndex = 0;
  80. }
  81. _currentBranch = Module.GetSelectedBranch();
  82. _NO_TRANSLATE_Remotes.DataSource = Module.GetRemotes();
  83. UpdateBranchDropDown();
  84. UpdateRemoteBranchDropDown();
  85. Push.Focus();
  86. _currentBranchRemote = Module.GetSetting(string.Format("branch.{0}.remote", _currentBranch));
  87. if (_currentBranchRemote.IsNullOrEmpty() && _NO_TRANSLATE_Remotes.Items.Count >= 2)
  88. {
  89. IList<string> remotes = (IList<string>)_NO_TRANSLATE_Remotes.DataSource;
  90. int i = remotes.IndexOf("origin");
  91. _NO_TRANSLATE_Remotes.SelectedIndex = i >= 0 ? i : 0;
  92. }
  93. else
  94. _NO_TRANSLATE_Remotes.Text = _currentBranchRemote;
  95. RemotesUpdated(null, null);
  96. if (AppSettings.AlwaysShowAdvOpt)
  97. ShowOptions_LinkClicked(null, null);
  98. }
  99. public DialogResult PushAndShowDialogWhenFailed(IWin32Window owner)
  100. {
  101. if (!PushChanges(owner))
  102. return ShowDialog(owner);
  103. return DialogResult.OK;
  104. }
  105. public DialogResult PushAndShowDialogWhenFailed()
  106. {
  107. return PushAndShowDialogWhenFailed(null);
  108. }
  109. private void PushClick(object sender, EventArgs e)
  110. {
  111. if (PushChanges(this))
  112. Close();
  113. }
  114. private string GetDefaultPushRemote(String remote, String branch)
  115. {
  116. Func<string, string, bool> IsSettingForBranch = (aSetting, aBranch) =>
  117. {
  118. var head = new GitRef(Module, string.Empty, aSetting);
  119. return head.IsHead && head.Name.Equals(aBranch);
  120. };
  121. var pushSettings = Module.GetSettings(string.Format("remote.{0}.push", remote));
  122. var remoteHead = pushSettings.
  123. Select(s => s.Split(':')).
  124. Where(t => t.Length == 2).
  125. Where(t => IsSettingForBranch(t[0], branch)).
  126. Select(t => new GitRef(Module, string.Empty, t[1])).
  127. Where(h => h.IsHead).
  128. FirstOrDefault();
  129. return remoteHead == null ? null : remoteHead.Name;
  130. }
  131. private bool IsBranchKnownToRemote(string remote, string branch)
  132. {
  133. var refs = Module.GetRefs(true, true);
  134. var remoteRefs = refs.Where(r => r.IsRemote && r.LocalName == branch && r.Remote == remote);
  135. if (remoteRefs.Any())
  136. return true;
  137. var localRefs = refs.Where(r => r.IsHead && r.Name == branch && r.TrackingRemote == remote);
  138. return localRefs.Any();
  139. }
  140. private bool PushChanges(IWin32Window owner)
  141. {
  142. ErrorOccurred = false;
  143. if (PushToUrl.Checked && string.IsNullOrEmpty(PushDestination.Text))
  144. {
  145. MessageBox.Show(owner, _selectDestinationDirectory.Text);
  146. return false;
  147. }
  148. if (PushToRemote.Checked && string.IsNullOrEmpty(_NO_TRANSLATE_Remotes.Text))
  149. {
  150. MessageBox.Show(owner, _selectRemote.Text);
  151. return false;
  152. }
  153. if (TabControlTagBranch.SelectedTab == TagTab && string.IsNullOrEmpty(TagComboBox.Text))
  154. {
  155. MessageBox.Show(owner, _selectTag.Text);
  156. return false;
  157. }
  158. //Extra check if the branch is already known to the remote, give a warning when not.
  159. //This is not possible when the remote is an URL, but this is ok since most users push to
  160. //known remotes anyway.
  161. if (TabControlTagBranch.SelectedTab == BranchTab && PushToRemote.Checked &&
  162. !Module.IsBareRepository())
  163. {
  164. //If the current branch is not the default push, and not known by the remote
  165. //(as far as we know since we are disconnected....)
  166. if (_NO_TRANSLATE_Branch.Text != AllRefs &&
  167. RemoteBranch.Text != GetDefaultPushRemote(_NO_TRANSLATE_Remotes.Text, _NO_TRANSLATE_Branch.Text) &&
  168. !IsBranchKnownToRemote(_NO_TRANSLATE_Remotes.Text, RemoteBranch.Text))
  169. {
  170. //Ask if this is really what the user wants
  171. if (!AppSettings.DontConfirmPushNewBranch &&
  172. MessageBox.Show(owner, _branchNewForRemote.Text, _pushCaption.Text, MessageBoxButtons.YesNo) ==
  173. DialogResult.No)
  174. {
  175. return false;
  176. }
  177. }
  178. }
  179. if (PushToUrl.Checked)
  180. Repositories.AddMostRecentRepository(PushDestination.Text);
  181. AppSettings.RecursiveSubmodules = RecursiveSubmodules.SelectedIndex;
  182. var remote = "";
  183. string destination;
  184. if (PushToUrl.Checked)
  185. {
  186. destination = PushDestination.Text;
  187. }
  188. else
  189. {
  190. if (GitCommandHelpers.Plink())
  191. {
  192. if (!File.Exists(AppSettings.Pageant))
  193. MessageBoxes.PAgentNotFound(owner);
  194. else
  195. Module.StartPageantForRemote(_NO_TRANSLATE_Remotes.Text);
  196. }
  197. destination = _NO_TRANSLATE_Remotes.Text;
  198. remote = _NO_TRANSLATE_Remotes.Text.Trim();
  199. }
  200. string pushCmd;
  201. if (TabControlTagBranch.SelectedTab == BranchTab)
  202. {
  203. bool track = ReplaceTrackingReference.Checked;
  204. if (!track && !string.IsNullOrWhiteSpace(RemoteBranch.Text))
  205. {
  206. GitRef selectedLocalBranch = _NO_TRANSLATE_Branch.SelectedItem as GitRef;
  207. track = selectedLocalBranch != null && string.IsNullOrEmpty(selectedLocalBranch.TrackingRemote);
  208. string[] remotes = _NO_TRANSLATE_Remotes.DataSource as string[];
  209. if (remotes != null)
  210. foreach (string remoteBranch in remotes)
  211. if (!string.IsNullOrEmpty(remoteBranch) && _NO_TRANSLATE_Branch.Text.StartsWith(remoteBranch))
  212. track = false;
  213. if (track && !AppSettings.DontConfirmAddTrackingRef)
  214. {
  215. DialogResult result = MessageBox.Show(String.Format(_updateTrackingReference.Text, selectedLocalBranch.Name, RemoteBranch.Text), _pushCaption.Text, MessageBoxButtons.YesNoCancel);
  216. if (result == DialogResult.Cancel)
  217. return false;
  218. track = result == DialogResult.Yes;
  219. }
  220. }
  221. // Try to make source rev into a fully qualified branch name. If that
  222. // doesn't exist, then it must be something other than a branch, so
  223. // fall back to using the name just as it was passed in.
  224. string srcRev = "";
  225. bool pushAllBranches = false;
  226. if (_NO_TRANSLATE_Branch.Text == AllRefs)
  227. pushAllBranches = true;
  228. else
  229. {
  230. srcRev = GitCommandHelpers.GetFullBranchName(_NO_TRANSLATE_Branch.Text);
  231. if (String.IsNullOrEmpty(Module.RevParse(srcRev)))
  232. srcRev = _NO_TRANSLATE_Branch.Text;
  233. }
  234. pushCmd = GitCommandHelpers.PushCmd(destination, srcRev, RemoteBranch.Text,
  235. pushAllBranches, ForcePushBranches.Checked, track, RecursiveSubmodules.SelectedIndex);
  236. }
  237. else if (TabControlTagBranch.SelectedTab == TagTab)
  238. {
  239. string tag = TagComboBox.Text;
  240. bool pushAllTags = false;
  241. if (tag == AllRefs)
  242. {
  243. tag = "";
  244. pushAllTags = true;
  245. }
  246. pushCmd = GitCommandHelpers.PushTagCmd(destination, tag, pushAllTags,
  247. ForcePushBranches.Checked);
  248. }
  249. else
  250. {
  251. // Push Multiple Branches Tab selected
  252. var pushActions = new List<GitPushAction>();
  253. foreach (DataRow row in _branchTable.Rows)
  254. {
  255. var push = Convert.ToBoolean(row["Push"]);
  256. var force = Convert.ToBoolean(row["Force"]);
  257. var delete = Convert.ToBoolean(row["Delete"]);
  258. if (push || force)
  259. pushActions.Add(new GitPushAction(row["Local"].ToString(), row["Remote"].ToString(), force));
  260. else if (delete)
  261. pushActions.Add(GitPushAction.DeleteRemoteBranch(row["Remote"].ToString()));
  262. }
  263. pushCmd = GitCommandHelpers.PushMultipleCmd(destination, pushActions);
  264. }
  265. ScriptManager.RunEventScripts(this, ScriptEvent.BeforePush);
  266. //controls can be accessed only from UI thread
  267. _selectedBranch = _NO_TRANSLATE_Branch.Text;
  268. _candidateForRebasingMergeCommit = PushToRemote.Checked && (_selectedBranch != AllRefs) && TabControlTagBranch.SelectedTab == BranchTab;
  269. _selectedBranchRemote = _NO_TRANSLATE_Remotes.Text;
  270. _selectedRemoteBranchName = RemoteBranch.Text;
  271. using (var form = new FormRemoteProcess(Module, pushCmd)
  272. {
  273. Remote = remote,
  274. Text = string.Format(_pushToCaption.Text, destination),
  275. HandleOnExitCallback = HandlePushOnExit
  276. })
  277. {
  278. form.ShowDialog(owner);
  279. ErrorOccurred = form.ErrorOccurred();
  280. if (!Module.InTheMiddleOfAction() && !form.ErrorOccurred())
  281. {
  282. ScriptManager.RunEventScripts(this, ScriptEvent.AfterPush);
  283. if (_createPullRequestCB.Checked)
  284. UICommands.StartCreatePullRequest(owner);
  285. return true;
  286. }
  287. }
  288. return false;
  289. }
  290. private bool IsRebasingMergeCommit()
  291. {
  292. if (AppSettings.FormPullAction == AppSettings.PullAction.Rebase && _candidateForRebasingMergeCommit)
  293. {
  294. if (_selectedBranch == _currentBranch && _selectedBranchRemote == _currentBranchRemote)
  295. {
  296. string remoteBranchName = _selectedBranchRemote + "/" + _selectedRemoteBranchName;
  297. return Module.ExistsMergeCommit(remoteBranchName, _selectedBranch);
  298. }
  299. else
  300. return false;
  301. }
  302. else
  303. return false;
  304. }
  305. private bool HandlePushOnExit(ref bool isError, FormProcess form)
  306. {
  307. if (!isError)
  308. return false;
  309. //there is no way to pull to not current branch
  310. if (_selectedBranch != _currentBranch)
  311. return false;
  312. //auto pull from URL not supported. See https://github.com/gitextensions/gitextensions/issues/1887
  313. if (!PushToRemote.Checked)
  314. return false;
  315. //auto pull only if current branch was rejected
  316. Regex IsRejected = new Regex(Regex.Escape("! [rejected] ") + ".*" + Regex.Escape(_currentBranch) + ".*", RegexOptions.Compiled);
  317. if (IsRejected.IsMatch(form.GetOutputString()) && !Module.IsBareRepository())
  318. {
  319. bool forcePush = false;
  320. IWin32Window owner = form;
  321. if (AppSettings.AutoPullOnPushRejectedAction == null)
  322. {
  323. bool cancel = false;
  324. string destination = PushToRemote.Checked ? _NO_TRANSLATE_Remotes.Text : PushDestination.Text;
  325. string buttons = _pullRepositoryButtons.Text;
  326. switch (Module.LastPullAction)
  327. {
  328. case AppSettings.PullAction.Fetch:
  329. case AppSettings.PullAction.FetchAll:
  330. buttons = string.Format(buttons, _pullActionFetch.Text);
  331. break;
  332. case AppSettings.PullAction.Merge:
  333. buttons = string.Format(buttons, _pullActionMerge.Text);
  334. break;
  335. case AppSettings.PullAction.Rebase:
  336. buttons = string.Format(buttons, _pullActionRebase.Text);
  337. break;
  338. default:
  339. buttons = string.Format(buttons, _pullActionNone.Text);
  340. break;
  341. }
  342. int idx = PSTaskDialog.cTaskDialog.ShowCommandBox(owner,
  343. String.Format(_pullRepositoryCaption.Text, destination),
  344. _pullRepositoryMainInstruction.Text,
  345. _pullRepository.Text,
  346. "",
  347. "",
  348. _dontShowAgain.Text,
  349. buttons,
  350. true,
  351. 0,
  352. 0);
  353. bool rememberDecision = PSTaskDialog.cTaskDialog.VerificationChecked;
  354. switch (idx)
  355. {
  356. case 0:
  357. if (rememberDecision)
  358. {
  359. AppSettings.AutoPullOnPushRejectedAction = AppSettings.PullAction.Default;
  360. }
  361. if (Module.LastPullAction == AppSettings.PullAction.None)
  362. {
  363. return false;
  364. }
  365. Module.LastPullActionToFormPullAction();
  366. break;
  367. case 1:
  368. AppSettings.FormPullAction = AppSettings.PullAction.Rebase;
  369. if (rememberDecision)
  370. {
  371. AppSettings.AutoPullOnPushRejectedAction = AppSettings.FormPullAction;
  372. }
  373. break;
  374. case 2:
  375. AppSettings.FormPullAction = AppSettings.PullAction.Merge;
  376. if (rememberDecision)
  377. {
  378. AppSettings.AutoPullOnPushRejectedAction = AppSettings.FormPullAction;
  379. }
  380. break;
  381. case 3:
  382. forcePush = true;
  383. break;
  384. default:
  385. cancel = true;
  386. if (rememberDecision)
  387. {
  388. AppSettings.AutoPullOnPushRejectedAction = AppSettings.PullAction.None;
  389. }
  390. break;
  391. }
  392. if (cancel)
  393. return false;
  394. }
  395. if (forcePush)
  396. {
  397. if (!form.ProcessArguments.Contains(" -f "))
  398. form.ProcessArguments = form.ProcessArguments.Replace("push", "push -f");
  399. form.Retry();
  400. return true;
  401. }
  402. if (AppSettings.AutoPullOnPushRejectedAction == AppSettings.PullAction.None)
  403. return false;
  404. if (AppSettings.FormPullAction == AppSettings.PullAction.Fetch)
  405. {
  406. form.AppendOutputLine(Environment.NewLine +
  407. "Can not perform auto pull, when merge option is set to fetch.");
  408. return false;
  409. }
  410. if (IsRebasingMergeCommit())
  411. {
  412. form.AppendOutputLine(Environment.NewLine +
  413. "Can not perform auto pull, when merge option is set to rebase " + Environment.NewLine +
  414. "and one of the commits that are about to be rebased is a merge.");
  415. return false;
  416. }
  417. bool pullCompleted;
  418. UICommands.StartPullDialog(owner, true, null, _selectedBranchRemote, out pullCompleted, false);
  419. if (pullCompleted)
  420. {
  421. form.Retry();
  422. return true;
  423. }
  424. }
  425. return false;
  426. }
  427. private void FillPushDestinationDropDown()
  428. {
  429. PushDestination.DataSource = Repositories.RemoteRepositoryHistory.Repositories;
  430. PushDestination.DisplayMember = "Path";
  431. }
  432. private void UpdateBranchDropDown()
  433. {
  434. var curBranch = _NO_TRANSLATE_Branch.Text;
  435. _NO_TRANSLATE_Branch.DisplayMember = "Name";
  436. _NO_TRANSLATE_Branch.Items.Clear();
  437. _NO_TRANSLATE_Branch.Items.Add(AllRefs);
  438. _NO_TRANSLATE_Branch.Items.Add(HeadText);
  439. if (string.IsNullOrEmpty(curBranch))
  440. {
  441. curBranch = _currentBranch;
  442. if (curBranch.IndexOfAny("() ".ToCharArray()) != -1)
  443. curBranch = HeadText;
  444. }
  445. foreach (var head in Module.GetRefs(false, true))
  446. _NO_TRANSLATE_Branch.Items.Add(head);
  447. _NO_TRANSLATE_Branch.Text = curBranch;
  448. }
  449. private void PullClick(object sender, EventArgs e)
  450. {
  451. UICommands.StartPullDialog(this);
  452. }
  453. private void UpdateRemoteBranchDropDown()
  454. {
  455. RemoteBranch.DisplayMember = "Name";
  456. RemoteBranch.Items.Clear();
  457. if (!string.IsNullOrEmpty(_NO_TRANSLATE_Branch.Text))
  458. RemoteBranch.Items.Add(_NO_TRANSLATE_Branch.Text);
  459. foreach (var head in Module.GetRefs(false, true))
  460. if (!RemoteBranch.Items.Contains(head))
  461. RemoteBranch.Items.Add(head);
  462. }
  463. private void BranchSelectedValueChanged(object sender, EventArgs e)
  464. {
  465. if (_NO_TRANSLATE_Branch.Text == AllRefs)
  466. {
  467. RemoteBranch.Text = "";
  468. return;
  469. }
  470. if (_NO_TRANSLATE_Branch.Text != HeadText)
  471. {
  472. if (PushToRemote.Checked)
  473. {
  474. var branch = _NO_TRANSLATE_Branch.SelectedItem as GitRef;
  475. if (branch != null)
  476. {
  477. string defaultRemote = GetDefaultPushRemote(_NO_TRANSLATE_Remotes.Text.Trim(), branch.Name);
  478. if (!defaultRemote.IsNullOrEmpty())
  479. {
  480. RemoteBranch.Text = defaultRemote;
  481. return;
  482. }
  483. if (branch.TrackingRemote.Equals(_NO_TRANSLATE_Remotes.Text.Trim()))
  484. {
  485. RemoteBranch.Text = branch.MergeWith;
  486. if (!string.IsNullOrEmpty(RemoteBranch.Text))
  487. return;
  488. }
  489. }
  490. }
  491. RemoteBranch.Text = _NO_TRANSLATE_Branch.Text;
  492. }
  493. }
  494. private void FormPushLoad(object sender, EventArgs e)
  495. {
  496. _NO_TRANSLATE_Remotes.Select();
  497. Text = string.Concat(_pushCaption.Text, " (", Module.WorkingDir, ")");
  498. var gitHoster = RepoHosts.TryGetGitHosterForModule(Module);
  499. _createPullRequestCB.Enabled = gitHoster != null;
  500. }
  501. private void AddRemoteClick(object sender, EventArgs e)
  502. {
  503. UICommands.StartRemotesDialog(this, _NO_TRANSLATE_Remotes.Text);
  504. string origText = _NO_TRANSLATE_Remotes.Text;
  505. _NO_TRANSLATE_Remotes.DataSource = Module.GetRemotes();
  506. if (_NO_TRANSLATE_Remotes.Items.Contains(origText)) // else first item gets selected
  507. {
  508. _NO_TRANSLATE_Remotes.Text = origText;
  509. }
  510. }
  511. private void PushToUrlCheckedChanged(object sender, EventArgs e)
  512. {
  513. PushDestination.Enabled = PushToUrl.Checked;
  514. folderBrowserButton1.Enabled = PushToUrl.Checked;
  515. _NO_TRANSLATE_Remotes.Enabled = PushToRemote.Checked;
  516. AddRemote.Enabled = PushToRemote.Checked;
  517. if (PushToUrl.Checked)
  518. {
  519. FillPushDestinationDropDown();
  520. BranchSelectedValueChanged(null, null);
  521. }
  522. else
  523. RemotesUpdated(sender, e);
  524. }
  525. private void RemotesUpdated(object sender, EventArgs e)
  526. {
  527. if (TabControlTagBranch.SelectedTab == MultipleBranchTab)
  528. UpdateMultiBranchView();
  529. EnableLoadSshButton();
  530. // update the text box of the Remote Url combobox to show the URL of selected remote
  531. {
  532. string pushUrl = Module.GetPathSetting(string.Format(SettingKeyString.RemotePushUrl, _NO_TRANSLATE_Remotes.Text));
  533. if (pushUrl.IsNullOrEmpty())
  534. {
  535. pushUrl = Module.GetPathSetting(string.Format(SettingKeyString.RemoteUrl, _NO_TRANSLATE_Remotes.Text));
  536. }
  537. PushDestination.Text = pushUrl;
  538. }
  539. if (string.IsNullOrEmpty(_NO_TRANSLATE_Branch.Text))
  540. {
  541. // Doing this makes it pretty easy to accidentally create a branch on the remote.
  542. // But leaving it blank will do the 'default' thing, meaning all branches are pushed.
  543. // Solution: when pushing a branch that doesn't exist on the remote, ask what to do
  544. var currentBranch = new GitRef(Module, null, _currentBranch, _NO_TRANSLATE_Remotes.Text);
  545. _NO_TRANSLATE_Branch.Items.Add(currentBranch);
  546. _NO_TRANSLATE_Branch.SelectedItem = currentBranch;
  547. }
  548. BranchSelectedValueChanged(null, null);
  549. }
  550. private void EnableLoadSshButton()
  551. {
  552. LoadSSHKey.Visible = !string.IsNullOrEmpty(Module.GetPuttyKeyFileForRemote(_NO_TRANSLATE_Remotes.Text));
  553. }
  554. private void LoadSshKeyClick(object sender, EventArgs e)
  555. {
  556. if (!File.Exists(AppSettings.Pageant))
  557. MessageBoxes.PAgentNotFound(this);
  558. else
  559. Module.StartPageantForRemote(_NO_TRANSLATE_Remotes.Text);
  560. }
  561. private void RemotesValidated(object sender, EventArgs e)
  562. {
  563. EnableLoadSshButton();
  564. }
  565. private void FillTagDropDown()
  566. {
  567. /// var tags = Module.GetTagHeads(GitModule.GetTagHeadsOption.OrderByCommitDateDescending); // comment out to sort by commit date
  568. var tags = Module.GetTagRefs(GitModule.GetTagRefsSortOrder.ByName)
  569. .Select(tag => tag.Name).ToList();
  570. tags.Insert(0, AllRefs);
  571. TagComboBox.DataSource = tags;
  572. }
  573. private void ForcePushBranchesCheckedChanged(object sender, EventArgs e)
  574. {
  575. ForcePushTags.Checked = ForcePushBranches.Checked;
  576. }
  577. private void ForcePushTagsCheckedChanged(object sender, EventArgs e)
  578. {
  579. ForcePushBranches.Checked = ForcePushTags.Checked;
  580. }
  581. #region Multi-Branch Methods
  582. private DataTable _branchTable;
  583. private void UpdateMultiBranchView()
  584. {
  585. _branchTable = new DataTable();
  586. _branchTable.Columns.Add("Local", typeof(string));
  587. _branchTable.Columns.Add("Remote", typeof(string));
  588. _branchTable.Columns.Add("New", typeof(string));
  589. _branchTable.Columns.Add("Push", typeof(bool));
  590. _branchTable.Columns.Add("Force", typeof(bool));
  591. _branchTable.Columns.Add("Delete", typeof(bool));
  592. _branchTable.ColumnChanged += BranchTable_ColumnChanged;
  593. var bs = new BindingSource { DataSource = _branchTable };
  594. BranchGrid.DataSource = bs;
  595. string remote = _NO_TRANSLATE_Remotes.Text.Trim();
  596. if (remote == "")
  597. return;
  598. var localHeads = Module.GetRefs(false, true);
  599. LoadMultiBranchViewData(remote, localHeads);
  600. }
  601. private void LoadMultiBranchViewData(string remote, IList<GitRef> localHeads)
  602. {
  603. _remoteBranchesLoader.Cancel();
  604. Cursor = Cursors.AppStarting;
  605. _remoteBranchesLoader.Load(() => { return Module.GetRemoteRefs(remote, false, true); },
  606. (remoteHeads) =>
  607. {
  608. Cursor = Cursors.Default;
  609. if (remoteHeads.HostKeyFail)
  610. {
  611. string remoteUrl;
  612. remoteUrl = Module.GetPathSetting(string.Format(SettingKeyString.RemoteUrl, remote));
  613. if (string.IsNullOrEmpty(remoteUrl))
  614. remoteUrl = remote;
  615. if (FormRemoteProcess.AskForCacheHostkey(this, Module, remoteUrl))
  616. {
  617. LoadMultiBranchViewData(remote, localHeads);
  618. }
  619. }
  620. else if (remoteHeads.AuthenticationFail)
  621. {
  622. string loadedKey;
  623. if (FormPuttyError.AskForKey(this, out loadedKey))
  624. {
  625. LoadMultiBranchViewData(remote, localHeads);
  626. }
  627. }
  628. else
  629. {
  630. // Add all the local branches.
  631. foreach (var head in localHeads)
  632. {
  633. DataRow row = _branchTable.NewRow();
  634. row["Force"] = false;
  635. row["Delete"] = false;
  636. row["Local"] = head.Name;
  637. string remoteName;
  638. if (head.Remote == remote)
  639. remoteName = head.MergeWith ?? head.Name;
  640. else
  641. remoteName = head.Name;
  642. row["Remote"] = remoteName;
  643. bool newAtRemote = remoteHeads.Result.Any(h => h.Name == remoteName);
  644. row["New"] = newAtRemote ? _no.Text : _yes.Text;
  645. row["Push"] = newAtRemote;
  646. _branchTable.Rows.Add(row);
  647. }
  648. // Offer to delete all the left over remote branches.
  649. foreach (var remoteHead in remoteHeads.Result)
  650. {
  651. GitRef head = remoteHead;
  652. if (localHeads.All(h => h.Name != head.Name))
  653. {
  654. DataRow row = _branchTable.NewRow();
  655. row["Local"] = null;
  656. row["Remote"] = remoteHead.Name;
  657. row["New"] = _no.Text;
  658. row["Push"] = false;
  659. row["Force"] = false;
  660. row["Delete"] = false;
  661. _branchTable.Rows.Add(row);
  662. }
  663. }
  664. }
  665. BranchGrid.Enabled = true;
  666. });
  667. }
  668. static void BranchTable_ColumnChanged(object sender, DataColumnChangeEventArgs e)
  669. {
  670. if (e.Column.ColumnName == "Push" && (bool)e.ProposedValue)
  671. {
  672. e.Row["Force"] = false;
  673. e.Row["Delete"] = false;
  674. }
  675. if (e.Column.ColumnName == "Force" && (bool)e.ProposedValue)
  676. {
  677. e.Row["Push"] = false;
  678. e.Row["Delete"] = false;
  679. }
  680. if (e.Column.ColumnName == "Delete" && (bool)e.ProposedValue)
  681. {
  682. e.Row["Push"] = false;
  683. e.Row["Force"] = false;
  684. }
  685. }
  686. private void TabControlTagBranch_Selected(object sender, TabControlEventArgs e)
  687. {
  688. _remoteBranchesLoader.Cancel();
  689. if (TabControlTagBranch.SelectedTab == MultipleBranchTab)
  690. UpdateMultiBranchView();
  691. else if (TabControlTagBranch.SelectedTab == TagTab)
  692. FillTagDropDown();
  693. else
  694. {
  695. UpdateBranchDropDown();
  696. UpdateRemoteBranchDropDown();
  697. }
  698. }
  699. private void BranchGrid_CurrentCellDirtyStateChanged(object sender, EventArgs e)
  700. {
  701. // Push grid checkbox changes immediately into the underlying data table.
  702. if (BranchGrid.CurrentCell is DataGridViewCheckBoxCell)
  703. {
  704. BranchGrid.EndEdit();
  705. ((BindingSource)BranchGrid.DataSource).EndEdit();
  706. }
  707. }
  708. #endregion
  709. private void ShowOptions_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
  710. {
  711. PushOptionsPanel.Visible = true;
  712. ShowOptions.Visible = false;
  713. SetFormSizeToFitAllItems();
  714. }
  715. private void SetFormSizeToFitAllItems()
  716. {
  717. if (Height < MinimumSize.Height + 50)
  718. Height = MinimumSize.Height + 50;
  719. }
  720. private void _NO_TRANSLATE_Branch_SelectedIndexChanged(object sender, EventArgs e)
  721. {
  722. RemoteBranch.Enabled = (_NO_TRANSLATE_Branch.Text != AllRefs);
  723. }
  724. }
  725. }