PageRenderTime 49ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/GitUI/CommandsDialogs/FormPush.cs

https://github.com/qgppl/gitextensions
C# | 986 lines | 828 code | 123 blank | 35 comment | 170 complexity | 0bce434a3f675ffe856e61d64fa6d5ac MD5 | raw file
Possible License(s): GPL-3.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.Objects;
  12. using GitUI.Script;
  13. using GitUI.UserControls;
  14. using GitUIPluginInterfaces;
  15. using ResourceManager;
  16. namespace GitUI.CommandsDialogs
  17. {
  18. public partial class FormPush : GitModuleForm
  19. {
  20. private const string HeadText = "HEAD";
  21. private const string AllRefs = "[ All ]";
  22. private string _currentBranchName;
  23. private GitRemote _currentBranchRemote;
  24. private bool _candidateForRebasingMergeCommit;
  25. private string _selectedBranch;
  26. private GitRemote _selectedRemote;
  27. private string _selectedRemoteBranchName;
  28. private IList<IGitRef> _gitRefs;
  29. private readonly IGitRemoteController _gitRemoteController;
  30. public bool ErrorOccurred { get; private set; }
  31. #region Translation
  32. private readonly TranslationString _branchNewForRemote =
  33. new TranslationString("The branch you are about to push seems to be a new branch for the remote." +
  34. Environment.NewLine + "Are you sure you want to push this branch?");
  35. private readonly TranslationString _pushCaption = new TranslationString("Push");
  36. private readonly TranslationString _pushToCaption = new TranslationString("Push to {0}");
  37. private readonly TranslationString _selectDestinationDirectory =
  38. new TranslationString("Please select a destination directory");
  39. private readonly TranslationString _selectRemote = new TranslationString("Please select a remote repository");
  40. private readonly TranslationString _selectTag =
  41. new TranslationString("You need to select a tag to push or select \"Push all tags\".");
  42. private readonly TranslationString _updateTrackingReference =
  43. new TranslationString("The branch {0} does not have a tracking reference. Do you want to add a tracking reference to {1}?");
  44. private readonly TranslationString _yes = new TranslationString("Yes");
  45. private readonly TranslationString _no = new TranslationString("No");
  46. private readonly TranslationString _pullRepositoryMainInstruction = new TranslationString("Pull latest changes from remote repository");
  47. private readonly TranslationString _pullRepository =
  48. new TranslationString("The push was rejected because the tip of your current branch is behind its remote counterpart. " +
  49. "Merge the remote changes before pushing again.");
  50. private readonly TranslationString _pullRepositoryButtons = new TranslationString("Pull with last pull action ({0})|Pull with rebase|Pull with merge|Force push|Cancel");
  51. private readonly TranslationString _pullActionNone = new TranslationString("none");
  52. private readonly TranslationString _pullActionFetch = new TranslationString("fetch");
  53. private readonly TranslationString _pullActionRebase = new TranslationString("rebase");
  54. private readonly TranslationString _pullActionMerge = new TranslationString("merge");
  55. private readonly TranslationString _pullRepositoryCaption = new TranslationString("Push was rejected from \"{0}\"");
  56. private readonly TranslationString _dontShowAgain = new TranslationString("Remember my decision.");
  57. private readonly TranslationString _useForceWithLeaseInstead =
  58. new TranslationString("Force push may overwrite changes since your last fetch. Do you want to use the safer force with lease instead?");
  59. private readonly TranslationString _forceWithLeaseTooltips =
  60. new TranslationString("Force with lease is a safer way to force push. It ensures you only overwrite work that you have seen in your local repository");
  61. #endregion
  62. private FormPush()
  63. : this(null)
  64. { }
  65. public FormPush(GitUICommands aCommands)
  66. : base(aCommands)
  67. {
  68. InitializeComponent();
  69. Translate();
  70. if (!GitCommandHelpers.VersionInUse.SupportPushForceWithLease)
  71. {
  72. ckForceWithLease.Visible = false;
  73. ForcePushTags.DataBindings.Add("Checked", ForcePushBranches, "Checked",
  74. formattingEnabled: false, updateMode: DataSourceUpdateMode.OnPropertyChanged);
  75. }
  76. else
  77. {
  78. ForcePushTags.DataBindings.Add("Checked", ckForceWithLease, "Checked",
  79. formattingEnabled: false, updateMode: DataSourceUpdateMode.OnPropertyChanged);
  80. toolTip1.SetToolTip(ckForceWithLease, _forceWithLeaseTooltips.Text);
  81. }
  82. //can't be set in OnLoad, because after PushAndShowDialogWhenFailed()
  83. //they are reset to false
  84. if (aCommands != null)
  85. {
  86. _gitRemoteController = new GitRemoteController(Module);
  87. Init();
  88. }
  89. }
  90. private void Init()
  91. {
  92. _gitRefs = Module.GetRefs(false, true);
  93. if (GitCommandHelpers.VersionInUse.SupportPushWithRecursiveSubmodulesCheck)
  94. {
  95. RecursiveSubmodules.Enabled = true;
  96. RecursiveSubmodules.SelectedIndex = AppSettings.RecursiveSubmodules;
  97. if (!GitCommandHelpers.VersionInUse.SupportPushWithRecursiveSubmodulesOnDemand)
  98. RecursiveSubmodules.Items.RemoveAt(2);
  99. }
  100. else
  101. {
  102. RecursiveSubmodules.Enabled = false;
  103. RecursiveSubmodules.SelectedIndex = 0;
  104. }
  105. _currentBranchName = Module.GetSelectedBranch();
  106. // refresh registered git remotes
  107. _gitRemoteController.LoadRemotes();
  108. BindRemotesDropDown(null);
  109. UpdateBranchDropDown();
  110. UpdateRemoteBranchDropDown();
  111. Push.Focus();
  112. if (AppSettings.AlwaysShowAdvOpt)
  113. {
  114. ShowOptions_LinkClicked(null, null);
  115. }
  116. }
  117. public DialogResult PushAndShowDialogWhenFailed(IWin32Window owner)
  118. {
  119. if (!PushChanges(owner))
  120. return ShowDialog(owner);
  121. return DialogResult.OK;
  122. }
  123. public DialogResult PushAndShowDialogWhenFailed()
  124. {
  125. return PushAndShowDialogWhenFailed(null);
  126. }
  127. private void PushClick(object sender, EventArgs e)
  128. {
  129. if (PushChanges(this))
  130. Close();
  131. }
  132. private void BindRemotesDropDown(string selectedRemoteName)
  133. {
  134. _NO_TRANSLATE_Remotes.SelectedIndexChanged -= RemotesUpdated;
  135. _NO_TRANSLATE_Remotes.TextUpdate -= RemotesUpdated;
  136. _NO_TRANSLATE_Remotes.Sorted = false;
  137. _NO_TRANSLATE_Remotes.DataSource = _gitRemoteController.Remotes;
  138. _NO_TRANSLATE_Remotes.DisplayMember = "Name";
  139. _NO_TRANSLATE_Remotes.SelectedIndex = -1;
  140. _NO_TRANSLATE_Remotes.SelectedIndexChanged += RemotesUpdated;
  141. _NO_TRANSLATE_Remotes.TextUpdate += RemotesUpdated;
  142. if (selectedRemoteName.IsNullOrEmpty())
  143. {
  144. selectedRemoteName = Module.GetSetting(string.Format(SettingKeyString.BranchRemote, _currentBranchName));
  145. }
  146. _currentBranchRemote = _gitRemoteController.Remotes.FirstOrDefault(x => x.Name.Equals(selectedRemoteName, StringComparison.OrdinalIgnoreCase));
  147. if (_currentBranchRemote != null)
  148. {
  149. _NO_TRANSLATE_Remotes.SelectedItem = _currentBranchRemote;
  150. }
  151. else if (_gitRemoteController.Remotes.Any())
  152. {
  153. // we couldn't find the default assigned remote for the selected branch
  154. // it is usually gets mapped via FormRemotes -> "default pull behavior" tab
  155. // so pick the default user remote
  156. _NO_TRANSLATE_Remotes.SelectedIndex = 0;
  157. }
  158. else
  159. {
  160. _NO_TRANSLATE_Remotes.SelectedIndex = -1;
  161. }
  162. }
  163. private bool IsBranchKnownToRemote(string remote, string branch)
  164. {
  165. var remoteRefs = _gitRefs.Where(r => r.IsRemote && r.LocalName == branch && r.Remote == remote);
  166. if (remoteRefs.Any())
  167. return true;
  168. var localRefs = _gitRefs.Where(r => r.IsHead && r.Name == branch && r.TrackingRemote == remote);
  169. return localRefs.Any();
  170. }
  171. private bool PushChanges(IWin32Window owner)
  172. {
  173. ErrorOccurred = false;
  174. if (PushToUrl.Checked && string.IsNullOrEmpty(PushDestination.Text))
  175. {
  176. MessageBox.Show(owner, _selectDestinationDirectory.Text);
  177. return false;
  178. }
  179. var selectedRemoteName = _selectedRemote.Name;
  180. if (PushToRemote.Checked && string.IsNullOrEmpty(selectedRemoteName))
  181. {
  182. MessageBox.Show(owner, _selectRemote.Text);
  183. return false;
  184. }
  185. if (TabControlTagBranch.SelectedTab == TagTab && string.IsNullOrEmpty(TagComboBox.Text))
  186. {
  187. MessageBox.Show(owner, _selectTag.Text);
  188. return false;
  189. }
  190. //Extra check if the branch is already known to the remote, give a warning when not.
  191. //This is not possible when the remote is an URL, but this is ok since most users push to
  192. //known remotes anyway.
  193. if (TabControlTagBranch.SelectedTab == BranchTab && PushToRemote.Checked &&
  194. !Module.IsBareRepository())
  195. {
  196. //If the current branch is not the default push, and not known by the remote
  197. //(as far as we know since we are disconnected....)
  198. if (_NO_TRANSLATE_Branch.Text != AllRefs &&
  199. RemoteBranch.Text != _gitRemoteController.GetDefaultPushRemote(_selectedRemote, _NO_TRANSLATE_Branch.Text) &&
  200. !IsBranchKnownToRemote(selectedRemoteName, RemoteBranch.Text))
  201. {
  202. //Ask if this is really what the user wants
  203. if (!AppSettings.DontConfirmPushNewBranch &&
  204. DialogResult.No == MessageBox.Show(owner, _branchNewForRemote.Text, _pushCaption.Text, MessageBoxButtons.YesNo))
  205. {
  206. return false;
  207. }
  208. }
  209. }
  210. if (PushToUrl.Checked)
  211. {
  212. Repositories.AddMostRecentRepository(PushDestination.Text);
  213. }
  214. AppSettings.RecursiveSubmodules = RecursiveSubmodules.SelectedIndex;
  215. var remote = "";
  216. string destination;
  217. if (PushToUrl.Checked)
  218. {
  219. destination = PushDestination.Text;
  220. }
  221. else
  222. {
  223. EnsurePageant(selectedRemoteName);
  224. destination = selectedRemoteName;
  225. remote = selectedRemoteName.Trim();
  226. }
  227. string pushCmd;
  228. if (TabControlTagBranch.SelectedTab == BranchTab)
  229. {
  230. bool track = ReplaceTrackingReference.Checked;
  231. if (!track && !string.IsNullOrWhiteSpace(RemoteBranch.Text))
  232. {
  233. GitRef selectedLocalBranch = _NO_TRANSLATE_Branch.SelectedItem as GitRef;
  234. track = selectedLocalBranch != null && string.IsNullOrEmpty(selectedLocalBranch.TrackingRemote) &&
  235. !_gitRemoteController.Remotes.Any(x => _NO_TRANSLATE_Branch.Text.StartsWith(x.Name, StringComparison.OrdinalIgnoreCase));
  236. var autoSetupMerge = Module.EffectiveConfigFile.GetValue("branch.autoSetupMerge");
  237. if (autoSetupMerge.IsNotNullOrWhitespace() && autoSetupMerge.ToLowerInvariant() == "false")
  238. {
  239. track = false;
  240. }
  241. if (track && !AppSettings.DontConfirmAddTrackingRef)
  242. {
  243. var result = MessageBox.Show(this,
  244. string.Format(_updateTrackingReference.Text, selectedLocalBranch.Name, RemoteBranch.Text),
  245. _pushCaption.Text,
  246. MessageBoxButtons.YesNoCancel);
  247. if (result == DialogResult.Cancel)
  248. {
  249. return false;
  250. }
  251. track = result == DialogResult.Yes;
  252. }
  253. }
  254. if (ForcePushBranches.Checked)
  255. {
  256. if (GitCommandHelpers.VersionInUse.SupportPushForceWithLease)
  257. {
  258. var choice = MessageBox.Show(this,
  259. _useForceWithLeaseInstead.Text,
  260. "", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question,
  261. MessageBoxDefaultButton.Button1);
  262. switch (choice)
  263. {
  264. case DialogResult.Yes:
  265. ForcePushBranches.Checked = false;
  266. ckForceWithLease.Checked = true;
  267. break;
  268. case DialogResult.Cancel:
  269. return false;
  270. }
  271. }
  272. }
  273. if (_NO_TRANSLATE_Branch.Text == AllRefs)
  274. {
  275. pushCmd = Module.PushAllCmd(destination, GetForcePushOption(), track, RecursiveSubmodules.SelectedIndex);
  276. }
  277. else
  278. {
  279. pushCmd = Module.PushCmd(destination, _NO_TRANSLATE_Branch.Text, RemoteBranch.Text,
  280. GetForcePushOption(), track, RecursiveSubmodules.SelectedIndex);
  281. }
  282. }
  283. else if (TabControlTagBranch.SelectedTab == TagTab)
  284. {
  285. string tag = TagComboBox.Text;
  286. bool pushAllTags = false;
  287. if (tag == AllRefs)
  288. {
  289. tag = "";
  290. pushAllTags = true;
  291. }
  292. pushCmd = GitCommandHelpers.PushTagCmd(destination, tag, pushAllTags, GetForcePushOption());
  293. }
  294. else
  295. {
  296. // Push Multiple Branches Tab selected
  297. var pushActions = new List<GitPushAction>();
  298. foreach (DataRow row in _branchTable.Rows)
  299. {
  300. var push = Convert.ToBoolean(row["Push"]);
  301. var force = Convert.ToBoolean(row["Force"]);
  302. var delete = Convert.ToBoolean(row["Delete"]);
  303. if (push || force)
  304. pushActions.Add(new GitPushAction(row["Local"].ToString(), row["Remote"].ToString(), force));
  305. else if (delete)
  306. pushActions.Add(GitPushAction.DeleteRemoteBranch(row["Remote"].ToString()));
  307. }
  308. pushCmd = GitCommandHelpers.PushMultipleCmd(destination, pushActions);
  309. }
  310. ScriptManager.RunEventScripts(this, ScriptEvent.BeforePush);
  311. //controls can be accessed only from UI thread
  312. _selectedBranch = _NO_TRANSLATE_Branch.Text;
  313. _candidateForRebasingMergeCommit = PushToRemote.Checked && (_selectedBranch != AllRefs) && TabControlTagBranch.SelectedTab == BranchTab;
  314. _selectedRemoteBranchName = RemoteBranch.Text;
  315. using (var form = new FormRemoteProcess(Module, pushCmd)
  316. {
  317. Remote = remote,
  318. Text = string.Format(_pushToCaption.Text, destination),
  319. HandleOnExitCallback = HandlePushOnExit
  320. })
  321. {
  322. form.ShowDialog(owner);
  323. ErrorOccurred = form.ErrorOccurred();
  324. if (!Module.InTheMiddleOfAction() && !form.ErrorOccurred())
  325. {
  326. ScriptManager.RunEventScripts(this, ScriptEvent.AfterPush);
  327. if (_createPullRequestCB.Checked)
  328. UICommands.StartCreatePullRequest(owner);
  329. return true;
  330. }
  331. }
  332. return false;
  333. }
  334. private ForcePushOptions GetForcePushOption()
  335. {
  336. if (ForcePushBranches.Checked)
  337. {
  338. return ForcePushOptions.Force;
  339. }
  340. if (ckForceWithLease.Checked)
  341. {
  342. return ForcePushOptions.ForceWithLease;
  343. }
  344. return ForcePushOptions.DoNotForce;
  345. }
  346. private bool IsRebasingMergeCommit()
  347. {
  348. if (AppSettings.FormPullAction == AppSettings.PullAction.Rebase &&
  349. _candidateForRebasingMergeCommit &&
  350. _selectedBranch == _currentBranchName &&
  351. _selectedRemote == _currentBranchRemote)
  352. {
  353. string remoteBranchName = _selectedRemote + "/" + _selectedRemoteBranchName;
  354. return Module.ExistsMergeCommit(remoteBranchName, _selectedBranch);
  355. }
  356. return false;
  357. }
  358. private bool HandlePushOnExit(ref bool isError, FormProcess form)
  359. {
  360. if (!isError)
  361. {
  362. return false;
  363. }
  364. //there is no way to pull to not current branch
  365. if (_selectedBranch != _currentBranchName)
  366. {
  367. return false;
  368. }
  369. //auto pull from URL not supported. See https://github.com/gitextensions/gitextensions/issues/1887
  370. if (!PushToRemote.Checked)
  371. {
  372. return false;
  373. }
  374. //auto pull only if current branch was rejected
  375. Regex isRejected = new Regex(Regex.Escape("! [rejected] ") + ".*" + Regex.Escape(_currentBranchName) + ".*", RegexOptions.Compiled);
  376. if (isRejected.IsMatch(form.GetOutputString()) && !Module.IsBareRepository())
  377. {
  378. bool forcePush = false;
  379. IWin32Window owner = form;
  380. if (AppSettings.AutoPullOnPushRejectedAction == null)
  381. {
  382. bool cancel = false;
  383. string destination = PushToRemote.Checked ? _NO_TRANSLATE_Remotes.Text : PushDestination.Text;
  384. string buttons = _pullRepositoryButtons.Text;
  385. switch (Module.LastPullAction)
  386. {
  387. case AppSettings.PullAction.Fetch:
  388. case AppSettings.PullAction.FetchAll:
  389. buttons = string.Format(buttons, _pullActionFetch.Text);
  390. break;
  391. case AppSettings.PullAction.Merge:
  392. buttons = string.Format(buttons, _pullActionMerge.Text);
  393. break;
  394. case AppSettings.PullAction.Rebase:
  395. buttons = string.Format(buttons, _pullActionRebase.Text);
  396. break;
  397. default:
  398. buttons = string.Format(buttons, _pullActionNone.Text);
  399. break;
  400. }
  401. int idx = PSTaskDialog.cTaskDialog.ShowCommandBox(owner,
  402. String.Format(_pullRepositoryCaption.Text, destination),
  403. _pullRepositoryMainInstruction.Text,
  404. _pullRepository.Text,
  405. "",
  406. "",
  407. _dontShowAgain.Text,
  408. buttons,
  409. true,
  410. 0,
  411. 0);
  412. bool rememberDecision = PSTaskDialog.cTaskDialog.VerificationChecked;
  413. switch (idx)
  414. {
  415. case 0:
  416. if (rememberDecision)
  417. {
  418. AppSettings.AutoPullOnPushRejectedAction = AppSettings.PullAction.Default;
  419. }
  420. break;
  421. case 1:
  422. AppSettings.FormPullAction = AppSettings.PullAction.Rebase;
  423. if (rememberDecision)
  424. {
  425. AppSettings.AutoPullOnPushRejectedAction = AppSettings.FormPullAction;
  426. }
  427. break;
  428. case 2:
  429. AppSettings.FormPullAction = AppSettings.PullAction.Merge;
  430. if (rememberDecision)
  431. {
  432. AppSettings.AutoPullOnPushRejectedAction = AppSettings.FormPullAction;
  433. }
  434. break;
  435. case 3:
  436. forcePush = true;
  437. break;
  438. default:
  439. cancel = true;
  440. if (rememberDecision)
  441. {
  442. AppSettings.AutoPullOnPushRejectedAction = AppSettings.PullAction.None;
  443. }
  444. break;
  445. }
  446. if (cancel)
  447. return false;
  448. }
  449. if (forcePush)
  450. {
  451. if (!form.ProcessArguments.Contains(" -f ") && !form.ProcessArguments.Contains(" --force"))
  452. {
  453. if (GitCommandHelpers.VersionInUse.SupportPushForceWithLease)
  454. {
  455. form.ProcessArguments = form.ProcessArguments.Replace("push", "push --force-with-lease");
  456. }
  457. else
  458. {
  459. form.ProcessArguments = form.ProcessArguments.Replace("push", "push -f");
  460. }
  461. }
  462. form.Retry();
  463. return true;
  464. }
  465. if (AppSettings.AutoPullOnPushRejectedAction == AppSettings.PullAction.None)
  466. {
  467. return false;
  468. }
  469. if (AppSettings.AutoPullOnPushRejectedAction == AppSettings.PullAction.Default)
  470. {
  471. if (Module.LastPullAction == AppSettings.PullAction.None)
  472. {
  473. return false;
  474. }
  475. Module.LastPullActionToFormPullAction();
  476. }
  477. if (AppSettings.FormPullAction == AppSettings.PullAction.Fetch)
  478. {
  479. form.AppendOutput(Environment.NewLine +
  480. "Can not perform auto pull, when merge option is set to fetch.");
  481. return false;
  482. }
  483. if (IsRebasingMergeCommit())
  484. {
  485. form.AppendOutput(Environment.NewLine +
  486. "Can not perform auto pull, when merge option is set to rebase " + Environment.NewLine +
  487. "and one of the commits that are about to be rebased is a merge.");
  488. return false;
  489. }
  490. bool pullCompleted;
  491. UICommands.StartPullDialog(owner, true, _selectedRemoteBranchName, _selectedRemote.Name, out pullCompleted, false);
  492. if (pullCompleted)
  493. {
  494. form.Retry();
  495. return true;
  496. }
  497. }
  498. return false;
  499. }
  500. private void FillPushDestinationDropDown()
  501. {
  502. PushDestination.DataSource = Repositories.RemoteRepositoryHistory.Repositories;
  503. PushDestination.DisplayMember = "Path";
  504. }
  505. private void UpdateBranchDropDown()
  506. {
  507. var curBranch = _NO_TRANSLATE_Branch.Text;
  508. _NO_TRANSLATE_Branch.DisplayMember = "Name";
  509. _NO_TRANSLATE_Branch.Items.Clear();
  510. _NO_TRANSLATE_Branch.Items.Add(AllRefs);
  511. _NO_TRANSLATE_Branch.Items.Add(HeadText);
  512. if (string.IsNullOrEmpty(curBranch))
  513. {
  514. curBranch = _currentBranchName;
  515. if (curBranch.IndexOfAny("() ".ToCharArray()) != -1)
  516. curBranch = HeadText;
  517. }
  518. foreach (var head in _gitRefs)
  519. _NO_TRANSLATE_Branch.Items.Add(head);
  520. _NO_TRANSLATE_Branch.Text = curBranch;
  521. ComboBoxHelper.ResizeComboBoxDropDownWidth(_NO_TRANSLATE_Branch, AppSettings.BranchDropDownMinWidth, AppSettings.BranchDropDownMaxWidth);
  522. }
  523. private void PullClick(object sender, EventArgs e)
  524. {
  525. UICommands.StartPullDialog(this);
  526. }
  527. private void UpdateRemoteBranchDropDown()
  528. {
  529. RemoteBranch.DisplayMember = "Name";
  530. RemoteBranch.Items.Clear();
  531. if (!string.IsNullOrEmpty(_NO_TRANSLATE_Branch.Text))
  532. RemoteBranch.Items.Add(_NO_TRANSLATE_Branch.Text);
  533. foreach (var head in _gitRefs)
  534. if (!RemoteBranch.Items.Contains(head))
  535. RemoteBranch.Items.Add(head);
  536. ComboBoxHelper.ResizeComboBoxDropDownWidth(RemoteBranch, AppSettings.BranchDropDownMinWidth, AppSettings.BranchDropDownMaxWidth);
  537. }
  538. private void BranchSelectedValueChanged(object sender, EventArgs e)
  539. {
  540. if (_NO_TRANSLATE_Branch.Text == AllRefs)
  541. {
  542. RemoteBranch.Text = "";
  543. return;
  544. }
  545. if (_NO_TRANSLATE_Branch.Text != HeadText)
  546. {
  547. if (PushToRemote.Checked)
  548. {
  549. var branch = _NO_TRANSLATE_Branch.SelectedItem as GitRef;
  550. if (branch != null)
  551. {
  552. string defaultRemote = _gitRemoteController.GetDefaultPushRemote(_selectedRemote, branch.Name);
  553. if (!defaultRemote.IsNullOrEmpty())
  554. {
  555. RemoteBranch.Text = defaultRemote;
  556. return;
  557. }
  558. if (branch.TrackingRemote.Equals(_selectedRemote.Name, StringComparison.OrdinalIgnoreCase))
  559. {
  560. RemoteBranch.Text = branch.MergeWith;
  561. if (!string.IsNullOrEmpty(RemoteBranch.Text))
  562. return;
  563. }
  564. }
  565. }
  566. RemoteBranch.Text = _NO_TRANSLATE_Branch.Text;
  567. }
  568. }
  569. private void FormPushLoad(object sender, EventArgs e)
  570. {
  571. _NO_TRANSLATE_Remotes.Select();
  572. Text = string.Concat(_pushCaption.Text, " (", Module.WorkingDir, ")");
  573. var gitHoster = RepoHosts.TryGetGitHosterForModule(Module);
  574. _createPullRequestCB.Enabled = gitHoster != null;
  575. }
  576. private void AddRemoteClick(object sender, EventArgs e)
  577. {
  578. // store the selected remote name
  579. string selectedRemoteName = _selectedRemote.Name;
  580. // launch remote management dialog
  581. UICommands.StartRemotesDialog(this, selectedRemoteName);
  582. // coming back from the dialog, update the remotes list
  583. _gitRemoteController.LoadRemotes();
  584. BindRemotesDropDown(selectedRemoteName);
  585. }
  586. private void PushToUrlCheckedChanged(object sender, EventArgs e)
  587. {
  588. PushDestination.Enabled = PushToUrl.Checked;
  589. folderBrowserButton1.Enabled = PushToUrl.Checked;
  590. _NO_TRANSLATE_Remotes.Enabled = PushToRemote.Checked;
  591. AddRemote.Enabled = PushToRemote.Checked;
  592. if (PushToUrl.Checked)
  593. {
  594. FillPushDestinationDropDown();
  595. BranchSelectedValueChanged(null, null);
  596. }
  597. else
  598. RemotesUpdated(sender, e);
  599. }
  600. private void RemotesUpdated(object sender, EventArgs e)
  601. {
  602. _selectedRemote = _NO_TRANSLATE_Remotes.SelectedItem as GitRemote;
  603. if (_selectedRemote == null)
  604. {
  605. return;
  606. }
  607. if (TabControlTagBranch.SelectedTab == MultipleBranchTab)
  608. {
  609. UpdateMultiBranchView();
  610. }
  611. EnableLoadSshButton();
  612. // update the text box of the Remote Url combobox to show the URL of selected remote
  613. string pushUrl = _selectedRemote.PushUrl;
  614. if (pushUrl.IsNullOrEmpty())
  615. {
  616. pushUrl = _selectedRemote.Url;
  617. }
  618. PushDestination.Text = pushUrl;
  619. if (string.IsNullOrEmpty(_NO_TRANSLATE_Branch.Text))
  620. {
  621. // Doing this makes it pretty easy to accidentally create a branch on the remote.
  622. // But leaving it blank will do the 'default' thing, meaning all branches are pushed.
  623. // Solution: when pushing a branch that doesn't exist on the remote, ask what to do
  624. var currentBranch = new GitRef(Module, null, _currentBranchName, _selectedRemote.Name);
  625. _NO_TRANSLATE_Branch.Items.Add(currentBranch);
  626. _NO_TRANSLATE_Branch.SelectedItem = currentBranch;
  627. }
  628. BranchSelectedValueChanged(null, null);
  629. }
  630. private void EnableLoadSshButton()
  631. {
  632. LoadSSHKey.Visible = _selectedRemote.PuttySshKey.IsNotNullOrWhitespace();
  633. }
  634. private void LoadSshKeyClick(object sender, EventArgs e)
  635. {
  636. StartPageant(_selectedRemote.Name);
  637. }
  638. private void StartPageant(string remote)
  639. {
  640. if (!File.Exists(AppSettings.Pageant))
  641. {
  642. MessageBoxes.PAgentNotFound(this);
  643. }
  644. else
  645. {
  646. Module.StartPageantForRemote(remote);
  647. }
  648. }
  649. private void EnsurePageant(string remote)
  650. {
  651. if (GitCommandHelpers.Plink())
  652. {
  653. StartPageant(remote);
  654. }
  655. }
  656. private void RemotesValidated(object sender, EventArgs e)
  657. {
  658. EnableLoadSshButton();
  659. }
  660. private void FillTagDropDown()
  661. {
  662. // var tags = Module.GetTagHeads(GitModule.GetTagHeadsOption.OrderByCommitDateDescending); // comment out to sort by commit date
  663. var tags = Module.GetTagRefs(GitModule.GetTagRefsSortOrder.ByName)
  664. .Select(tag => tag.Name).ToList();
  665. tags.Insert(0, AllRefs);
  666. TagComboBox.DataSource = tags;
  667. ComboBoxHelper.ResizeComboBoxDropDownWidth(TagComboBox, AppSettings.BranchDropDownMinWidth, AppSettings.BranchDropDownMaxWidth);
  668. }
  669. private void ForcePushBranchesCheckedChanged(object sender, EventArgs e)
  670. {
  671. if (ForcePushBranches.Checked)
  672. {
  673. ckForceWithLease.Checked = false;
  674. }
  675. }
  676. #region Multi-Branch Methods
  677. private DataTable _branchTable;
  678. private void UpdateMultiBranchView()
  679. {
  680. _branchTable = new DataTable();
  681. _branchTable.Columns.Add("Local", typeof(string));
  682. _branchTable.Columns.Add("Remote", typeof(string));
  683. _branchTable.Columns.Add("New", typeof(string));
  684. _branchTable.Columns.Add("Push", typeof(bool));
  685. _branchTable.Columns.Add("Force", typeof(bool));
  686. _branchTable.Columns.Add("Delete", typeof(bool));
  687. _branchTable.ColumnChanged += BranchTable_ColumnChanged;
  688. var bs = new BindingSource { DataSource = _branchTable };
  689. BranchGrid.DataSource = bs;
  690. if (_selectedRemote == null)
  691. {
  692. return;
  693. }
  694. var localHeads = _gitRefs.Where(r => r.IsHead).ToList();
  695. LoadMultiBranchViewData(_selectedRemote.Name, localHeads);
  696. }
  697. private void LoadMultiBranchViewData(string remote, IList<IGitRef> localHeads)
  698. {
  699. Cursor = Cursors.AppStarting;
  700. try
  701. {
  702. IList<IGitRef> remoteHeads;
  703. if (Module.EffectiveSettings.Detailed.GetRemoteBranchesDirectlyFromRemote.ValueOrDefault)
  704. {
  705. EnsurePageant(remote);
  706. var cmdGetBranchesFromRemote = "ls-remote --heads \"" + remote + "\"";
  707. using (var formProcess = new FormRemoteProcess(Module, cmdGetBranchesFromRemote)
  708. {
  709. Remote = remote
  710. })
  711. {
  712. formProcess.ShowDialog(this);
  713. if (formProcess.ErrorOccurred())
  714. {
  715. return;
  716. }
  717. var processOutput = formProcess.GetOutputString();
  718. var cmdOutput = TakeCommandOutput(processOutput);
  719. remoteHeads = Module.GetTreeRefs(cmdOutput);
  720. if (remoteHeads == null)
  721. return;
  722. }
  723. }
  724. else
  725. {
  726. //use remote branches from the git's local database if there were problems with receiving branches from the remote server
  727. remoteHeads = Module.GetRemoteBranches().Where(r => r.Remote == remote).ToList();
  728. }
  729. ProcessHeads(remote, localHeads, remoteHeads);
  730. }
  731. finally
  732. {
  733. Cursor = Cursors.Default;
  734. }
  735. }
  736. private static string TakeCommandOutput(string aProcessOutput)
  737. {
  738. //the command output consists of lines in the format:
  739. //fa77791d780a01a06d1f7d4ccad4ef93ed0ae2fd\trefs/heads/branchName
  740. int firstTabIdx = aProcessOutput.IndexOf('\t');
  741. if (firstTabIdx < 40)
  742. {
  743. return string.Empty;
  744. }
  745. var cmdOutput = aProcessOutput.Substring(firstTabIdx - 40);
  746. return cmdOutput;
  747. }
  748. private void ProcessHeads(string remote, IList<IGitRef> localHeads, IList<IGitRef> remoteHeads)
  749. {
  750. var remoteBranches = remoteHeads.ToHashSet(h => h.LocalName);
  751. // Add all the local branches.
  752. foreach (var head in localHeads)
  753. {
  754. DataRow row = _branchTable.NewRow();
  755. row["Force"] = false;
  756. row["Delete"] = false;
  757. row["Local"] = head.Name;
  758. string remoteName;
  759. if (head.Remote == remote)
  760. remoteName = head.MergeWith ?? head.Name;
  761. else
  762. remoteName = head.Name;
  763. row["Remote"] = remoteName;
  764. bool knownAtRemote = remoteBranches.Contains(remoteName);
  765. row["New"] = knownAtRemote ? _no.Text : _yes.Text;
  766. row["Push"] = knownAtRemote;
  767. _branchTable.Rows.Add(row);
  768. }
  769. // Offer to delete all the left over remote branches.
  770. foreach (var remoteHead in remoteHeads)
  771. {
  772. var head = remoteHead;
  773. if (localHeads.All(h => h.Name != head.LocalName))
  774. {
  775. DataRow row = _branchTable.NewRow();
  776. row["Local"] = null;
  777. row["Remote"] = remoteHead.LocalName;
  778. row["New"] = _no.Text;
  779. row["Push"] = false;
  780. row["Force"] = false;
  781. row["Delete"] = false;
  782. _branchTable.Rows.Add(row);
  783. }
  784. }
  785. BranchGrid.Enabled = true;
  786. }
  787. static void BranchTable_ColumnChanged(object sender, DataColumnChangeEventArgs e)
  788. {
  789. if (e.Column.ColumnName == "Push" && (bool)e.ProposedValue)
  790. {
  791. e.Row["Force"] = false;
  792. e.Row["Delete"] = false;
  793. }
  794. if (e.Column.ColumnName == "Force" && (bool)e.ProposedValue)
  795. {
  796. e.Row["Push"] = false;
  797. e.Row["Delete"] = false;
  798. }
  799. if (e.Column.ColumnName == "Delete" && (bool)e.ProposedValue)
  800. {
  801. e.Row["Push"] = false;
  802. e.Row["Force"] = false;
  803. }
  804. }
  805. private void TabControlTagBranch_Selected(object sender, TabControlEventArgs e)
  806. {
  807. if (TabControlTagBranch.SelectedTab == MultipleBranchTab)
  808. UpdateMultiBranchView();
  809. else if (TabControlTagBranch.SelectedTab == TagTab)
  810. FillTagDropDown();
  811. else
  812. {
  813. UpdateBranchDropDown();
  814. UpdateRemoteBranchDropDown();
  815. }
  816. }
  817. private void BranchGrid_CurrentCellDirtyStateChanged(object sender, EventArgs e)
  818. {
  819. // Push grid checkbox changes immediately into the underlying data table.
  820. if (BranchGrid.CurrentCell is DataGridViewCheckBoxCell)
  821. {
  822. BranchGrid.EndEdit();
  823. ((BindingSource)BranchGrid.DataSource).EndEdit();
  824. }
  825. }
  826. #endregion
  827. private void ShowOptions_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
  828. {
  829. PushOptionsPanel.Visible = true;
  830. ShowOptions.Visible = false;
  831. SetFormSizeToFitAllItems();
  832. }
  833. private void SetFormSizeToFitAllItems()
  834. {
  835. if (Height < MinimumSize.Height + 50)
  836. Height = MinimumSize.Height + 50;
  837. }
  838. private void _NO_TRANSLATE_Branch_SelectedIndexChanged(object sender, EventArgs e)
  839. {
  840. RemoteBranch.Enabled = (_NO_TRANSLATE_Branch.Text != AllRefs);
  841. }
  842. /// <summary>
  843. /// Clean up any resources being used.
  844. /// </summary>
  845. /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
  846. protected override void Dispose(bool disposing)
  847. {
  848. if (disposing)
  849. {
  850. if (components != null)
  851. components.Dispose();
  852. }
  853. base.Dispose(disposing);
  854. }
  855. private void ForceWithLeaseCheckedChanged(object sender, EventArgs e)
  856. {
  857. if (ckForceWithLease.Checked)
  858. {
  859. ForcePushBranches.Checked = false;
  860. }
  861. }
  862. }
  863. }