Extend the hook scripts with the ability to use information from the project properti...
[TortoiseGit.git] / src / TortoiseProc / SyncDlg.cpp
blob72bb3f2c79fbe6dcddd476669059b4316a80bad5
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2018 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 // SyncDlg.cpp : implementation file
23 #include "stdafx.h"
24 #include "TortoiseProc.h"
25 #include "SyncDlg.h"
26 #include "AppUtils.h"
27 #include "ProgressDlg.h"
28 #include "MessageBox.h"
29 #include "ImportPatchDlg.h"
30 #include "RebaseDlg.h"
31 #include "Hooks.h"
32 #include "SmartHandle.h"
33 #include "ProgressCommands/FetchProgressCommand.h"
34 #include "SyncTabCtrl.h"
35 #include "SysProgressDlg.h"
37 // CSyncDlg dialog
39 IMPLEMENT_DYNAMIC(CSyncDlg, CResizableStandAloneDialog)
41 CSyncDlg::CSyncDlg(CWnd* pParent /*=nullptr*/)
42 : CResizableStandAloneDialog(CSyncDlg::IDD, pParent)
43 , m_iPullRebase(0)
44 , m_CurrentCmd(0)
45 , m_bInited(false)
46 , m_CmdOutCurrentPos(0)
47 , m_bAutoLoadPuttyKey(CAppUtils::IsSSHPutty())
48 , m_bForce(BST_UNCHECKED)
49 , m_bBlock(false)
50 , m_BufStart(0)
51 , m_pThread(nullptr)
52 , m_bAbort(false)
53 , m_bDone(false)
54 , m_bWantToExit(false)
55 , m_GitCmdStatus(-1)
56 , m_startTick(GetTickCount64())
57 , m_seq(0)
59 m_pTooltip = &m_tooltips;
62 CSyncDlg::~CSyncDlg()
66 void CSyncDlg::DoDataExchange(CDataExchange* pDX)
68 CDialog::DoDataExchange(pDX);
69 DDX_Check(pDX, IDC_CHECK_PUTTY_KEY, m_bAutoLoadPuttyKey);
70 DDX_Check(pDX, IDC_CHECK_FORCE,m_bForce);
71 DDX_Control(pDX, IDC_COMBOBOXEX_URL, m_ctrlURL);
72 DDX_Control(pDX, IDC_BUTTON_TABCTRL, m_ctrlDumyButton);
73 DDX_Control(pDX, IDC_BUTTON_PULL, m_ctrlPull);
74 DDX_Control(pDX, IDC_BUTTON_PUSH, m_ctrlPush);
75 DDX_Control(pDX, IDC_STATIC_STATUS, m_ctrlStatus);
76 DDX_Control(pDX, IDC_PROGRESS_SYNC, m_ctrlProgress);
77 DDX_Control(pDX, IDC_ANIMATE_SYNC, m_ctrlAnimate);
78 DDX_Control(pDX, IDC_BUTTON_SUBMODULE,m_ctrlSubmodule);
79 DDX_Control(pDX, IDC_BUTTON_STASH, m_ctrlStash);
80 DDX_Control(pDX, IDC_PROG_LABEL, m_ctrlProgLabel);
81 BRANCH_COMBOX_DDX;
84 BEGIN_MESSAGE_MAP(CSyncDlg, CResizableStandAloneDialog)
85 ON_BN_CLICKED(IDC_BUTTON_PULL, &CSyncDlg::OnBnClickedButtonPull)
86 ON_BN_CLICKED(IDC_BUTTON_PUSH, &CSyncDlg::OnBnClickedButtonPush)
87 ON_BN_CLICKED(IDC_BUTTON_APPLY, &CSyncDlg::OnBnClickedButtonApply)
88 ON_BN_CLICKED(IDC_BUTTON_EMAIL, &CSyncDlg::OnBnClickedButtonEmail)
89 ON_BN_CLICKED(IDC_BUTTON_MANAGE, &CSyncDlg::OnBnClickedButtonManage)
90 BRANCH_COMBOX_EVENT
91 ON_CBN_EDITCHANGE(IDC_COMBOBOXEX_URL, &CSyncDlg::OnCbnEditchangeComboboxex)
92 ON_CBN_EDITCHANGE(IDC_COMBOBOXEX_REMOTE_BRANCH, &CSyncDlg::OnCbnEditchangeComboboxex)
93 ON_MESSAGE(MSG_PROGRESSDLG_UPDATE_UI, OnProgressUpdateUI)
94 ON_MESSAGE(WM_PROG_CMD_FINISH, OnProgCmdFinish)
95 ON_BN_CLICKED(IDC_BUTTON_COMMIT, &CSyncDlg::OnBnClickedButtonCommit)
96 ON_BN_CLICKED(IDC_BUTTON_SUBMODULE, &CSyncDlg::OnBnClickedButtonSubmodule)
97 ON_BN_CLICKED(IDC_BUTTON_STASH, &CSyncDlg::OnBnClickedButtonStash)
98 ON_WM_TIMER()
99 ON_REGISTERED_MESSAGE(TaskBarButtonCreated, OnTaskbarBtnCreated)
100 ON_BN_CLICKED(IDC_CHECK_FORCE, &CSyncDlg::OnBnClickedCheckForce)
101 ON_BN_CLICKED(IDC_LOG, &CSyncDlg::OnBnClickedLog)
102 ON_WM_DESTROY()
103 ON_WM_THEMECHANGED()
104 END_MESSAGE_MAP()
106 void CSyncDlg::EnableControlButton(bool bEnabled)
108 GetDlgItem(IDC_BUTTON_PULL)->EnableWindow(bEnabled);
109 GetDlgItem(IDC_BUTTON_PUSH)->EnableWindow(bEnabled);
110 GetDlgItem(IDC_BUTTON_APPLY)->EnableWindow(bEnabled);
111 GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(bEnabled);
112 GetDlgItem(IDOK)->EnableWindow(bEnabled);
113 GetDlgItem(IDC_BUTTON_SUBMODULE)->EnableWindow(bEnabled);
114 GetDlgItem(IDC_BUTTON_STASH)->EnableWindow(bEnabled);
116 // CSyncDlg message handlers
118 bool CSyncDlg::AskSetTrackedBranch()
120 CString remote, remoteBranch;
121 g_Git.GetRemoteTrackedBranch(m_strLocalBranch, remote, remoteBranch);
122 if (remoteBranch.IsEmpty())
124 remoteBranch = m_strRemoteBranch;
125 if (remoteBranch.IsEmpty())
126 remoteBranch = m_strLocalBranch;
127 CString temp;
128 temp.FormatMessage(IDS_NOTYET_SETTRACKEDBRANCH, (LPCTSTR)m_strLocalBranch, (LPCTSTR)remoteBranch);
129 BOOL dontShowAgain = FALSE;
130 auto ret = CMessageBox::ShowCheck(GetSafeHwnd(), temp, L"TortoiseGit", MB_ICONQUESTION | MB_YESNOCANCEL, nullptr, CString(MAKEINTRESOURCE(IDS_MSGBOX_DONOTSHOW)), &dontShowAgain);
131 if (dontShowAgain)
132 CRegDWORD(L"Software\\TortoiseGit\\AskSetTrackedBranch") = FALSE;
133 if (ret == IDCANCEL)
134 return false;
135 if (ret == IDYES)
137 CString key;
138 key.Format(L"branch.%s.remote", (LPCTSTR)m_strLocalBranch);
139 g_Git.SetConfigValue(key, m_strURL);
140 key.Format(L"branch.%s.merge", (LPCTSTR)m_strLocalBranch);
141 g_Git.SetConfigValue(key, L"refs/heads/" + g_Git.StripRefName(remoteBranch));
144 return true;
147 void CSyncDlg::OnBnClickedButtonPull()
149 bool bShift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
151 int CurrentEntry;
152 CurrentEntry = (int)this->m_ctrlPull.GetCurrentEntry();
153 this->m_regPullButton = CurrentEntry;
155 if (bShift && CurrentEntry > 1)
156 return;
158 this->m_bAbort=false;
159 this->m_GitCmdList.clear();
160 m_ctrlCmdOut.SetWindowText(L"");
161 m_LogText.Empty();
163 this->UpdateData();
164 UpdateCombox();
166 if (g_Git.GetHash(m_oldHash, L"HEAD"))
168 MessageBox(g_Git.GetGitLastErr(L"Could not get HEAD hash."), L"TortoiseGit", MB_ICONERROR);
169 return;
172 m_refList.Clear();
173 m_newHashMap.clear();
174 m_oldHashMap.clear();
176 if( CurrentEntry == 0)
178 CGitHash localBranchHash;
179 if (g_Git.GetHash(localBranchHash, m_strLocalBranch))
181 MessageBox(g_Git.GetGitLastErr(L"Could not get hash of \"" + m_strLocalBranch + L"\"."), L"TortoiseGit", MB_ICONERROR);
182 return;
184 if (localBranchHash != m_oldHash)
186 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_SYNC_PULLWRONGBRANCH, IDS_APPNAME, MB_OK | MB_ICONERROR);
187 return;
191 if(this->m_strURL.IsEmpty())
193 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_GITCONFIG_URLEMPTY, IDS_APPNAME, MB_OK | MB_ICONERROR);
194 return;
197 if (CurrentEntry == 6)
199 SwitchToRun();
200 m_ctrlTabCtrl.ShowTab(IDC_LOG - 1, false);
201 m_ctrlTabCtrl.ShowTab(IDC_REFLIST - 1, false);
202 m_ctrlTabCtrl.ShowTab(IDC_OUT_LOGLIST - 1, false);
203 m_ctrlTabCtrl.ShowTab(IDC_OUT_CHANGELIST - 1, false);
204 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, false);
205 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST - 1, false);
206 m_ctrlTabCtrl.ShowTab(IDC_IN_CONFLICT - 1, false);
207 m_ctrlTabCtrl.ShowTab(IDC_TAGCOMPARELIST - 1, true);
209 CSysProgressDlg sysProgressDlg;
210 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
211 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_LOADING)));
212 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
213 sysProgressDlg.SetShowProgressBar(false);
214 sysProgressDlg.ShowModal(this, true);
215 CString err;
216 auto ret = m_tagCompareList.Fill(m_strURL, err);
217 sysProgressDlg.Stop();
218 if (ret)
219 MessageBox(err, L"TortoiseGit", MB_ICONERROR);
221 BringWindowToTop();
222 SwitchToInput();
223 EnableControlButton();
224 return;
227 if (!IsURL() && !m_strRemoteBranch.IsEmpty() && CurrentEntry == 0 && CRegDWORD(L"Software\\TortoiseGit\\AskSetTrackedBranch", TRUE) == TRUE)
229 if (!AskSetTrackedBranch())
230 return;
233 if (m_bAutoLoadPuttyKey && CurrentEntry != 4) // CurrentEntry (Remote Update) handles this on its own)
235 CAppUtils::LaunchPAgent(this->GetSafeHwnd(), nullptr, &m_strURL);
238 if (g_Git.GetMapHashToFriendName(m_oldHashMap))
239 MessageBox(g_Git.GetGitLastErr(L"Could not get all refs."), L"TortoiseGit", MB_ICONERROR);
241 if (bShift && (CurrentEntry == 0 || CurrentEntry == 1))
243 if (CurrentEntry == 1 || CurrentEntry == 2 || CurrentEntry == 3)
244 CAppUtils::Fetch(GetSafeHwnd(), !IsURL() ? m_strURL : L"");
245 else
246 CAppUtils::Pull(GetSafeHwnd());
248 FillNewRefMap();
249 FetchOutList(true);
251 int hasConflicts = g_Git.HasWorkingTreeConflicts();
252 if (hasConflicts < 0)
254 this->m_ctrlCmdOut.SetSel(-1, -1);
255 this->m_ctrlCmdOut.ReplaceSel(g_Git.GetGitLastErr(L"Checking for conflicts failed.", CGit::GIT_CMD_CHECKCONFLICTS));
257 this->ShowTab(IDC_CMD_LOG);
258 return;
261 if (hasConflicts)
263 this->m_ConflictFileList.Clear();
264 this->m_ConflictFileList.GetStatus(nullptr, true);
265 this->m_ConflictFileList.Show(CTGitPath::LOGACTIONS_UNMERGED,
266 CTGitPath::LOGACTIONS_UNMERGED);
268 this->ShowTab(IDC_IN_CONFLICT);
269 CMessageBox::ShowCheck(GetSafeHwnd(), IDS_NEED_TO_RESOLVE_CONFLICTS_HINT, IDS_APPNAME, MB_ICONINFORMATION, L"MergeConflictsNeedsCommit", IDS_MSGBOX_DONOTSHOWAGAIN);
271 else
272 ShowInCommits(L"HEAD");
274 return;
277 CString force;
278 if(this->m_bForce)
279 force = L" --force";
281 CString cmd;
283 m_iPullRebase = 0;
284 if (CurrentEntry == 0) // check whether we need to override Pull if pull.rebase is set
286 CAutoRepository repo(g_Git.GetGitRepository());
287 if (!repo)
288 MessageBox(CGit::GetLibGit2LastErr(L"Could not open repository."), L"TortoiseGit", MB_OK | MB_ICONERROR);
290 // Check config branch.<name>.rebase and pull.reabse
293 if (!repo)
294 break;
296 if (git_repository_head_detached(repo) == 1)
297 break;
299 CAutoConfig config(true);
300 if (git_repository_config(config.GetPointer(), repo))
301 break;
303 // branch.<name>.rebase overrides pull.rebase
304 if (config.GetBOOL(L"branch." + g_Git.GetCurrentBranch() + L".rebase", m_iPullRebase) == GIT_ENOTFOUND)
306 if (config.GetBOOL(L"pull.rebase", m_iPullRebase) == GIT_ENOTFOUND)
307 break;
308 else
310 CString value;
311 config.GetString(L"pull.rebase", value);
312 if (value == L"preserve")
314 m_iPullRebase = 2;
315 break;
319 else
321 CString value;
322 config.GetString(L"branch." + g_Git.GetCurrentBranch() + L".rebase", value);
323 if (value == L"preserve")
325 m_iPullRebase = 2;
326 break;
329 } while (0);
330 if (m_iPullRebase > 0)
332 CurrentEntry = 1;
333 if (m_strRemoteBranch.IsEmpty())
335 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_PULL_EMPTYBRANCH, IDS_APPNAME, MB_ICONEXCLAMATION);
336 return;
341 SwitchToRun();
343 ShowTab(IDC_CMD_LOG);
345 m_ctrlTabCtrl.ShowTab(IDC_REFLIST - 1, true);
346 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, false);
347 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST - 1, false);
348 m_ctrlTabCtrl.ShowTab(IDC_IN_CONFLICT - 1, false);
349 m_ctrlTabCtrl.ShowTab(IDC_TAGCOMPARELIST - 1, false);
351 ///Pull
352 if(CurrentEntry == 0) //Pull
354 CString remotebranch;
355 remotebranch = m_strRemoteBranch;
357 if(!IsURL())
359 CString pullRemote, pullBranch;
360 g_Git.GetRemoteTrackedBranch(m_strLocalBranch, pullRemote, pullBranch);
361 if(pullBranch == remotebranch && pullRemote == this->m_strURL)
362 remotebranch.Empty();
365 cmd.Format(L"git.exe pull -v --progress%s \"%s\" %s",
366 (LPCTSTR)force,
367 (LPCTSTR)m_strURL,
368 (LPCTSTR)remotebranch);
370 m_CurrentCmd = GIT_COMMAND_PULL;
371 m_GitCmdList.push_back(cmd);
373 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
374 if (!m_pThread)
376 // ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
378 else
380 m_pThread->m_bAutoDelete = TRUE;
381 m_pThread->ResumeThread();
386 ///Fetch
387 if (CurrentEntry == 1 || CurrentEntry == 2 || CurrentEntry == 3)
389 m_oldRemoteHash.Empty();
390 CString remotebranch;
391 if (CurrentEntry == 3)
392 m_strRemoteBranch.Empty();
393 else if (IsURL() || m_strRemoteBranch.IsEmpty())
395 remotebranch=this->m_strRemoteBranch;
398 else
400 remotebranch.Format(L"remotes/%s/%s", (LPCTSTR)m_strURL, (LPCTSTR)m_strRemoteBranch);
401 g_Git.GetHash(m_oldRemoteHash, remotebranch);
402 if (m_oldRemoteHash.IsEmpty())
403 remotebranch=m_strRemoteBranch;
404 else
405 remotebranch = m_strRemoteBranch + L':' + remotebranch;
408 if (CurrentEntry == 1 || CurrentEntry == 3)
409 m_CurrentCmd = GIT_COMMAND_FETCH;
410 else
411 m_CurrentCmd = GIT_COMMAND_FETCHANDREBASE;
413 if (g_Git.UsingLibGit2(CGit::GIT_CMD_FETCH))
415 CString refspec;
416 if (!remotebranch.IsEmpty())
417 refspec.Format(L"refs/heads/%s:refs/remotes/%s/%s", (LPCTSTR)m_strRemoteBranch, (LPCTSTR)m_strURL, (LPCTSTR)m_strRemoteBranch);
419 progressCommand = std::make_unique<FetchProgressCommand>();
420 FetchProgressCommand* fetchProgressCommand = static_cast<FetchProgressCommand*>(progressCommand.get());
421 fetchProgressCommand->SetUrl(m_strURL);
422 fetchProgressCommand->SetRefSpec(refspec);
423 m_GitProgressList.SetCommand(progressCommand.get());
424 m_GitProgressList.Init();
425 ShowTab(IDC_CMD_GIT_PROG);
427 else
429 cmd.Format(L"git.exe fetch --progress -v%s \"%s\" %s",
430 (LPCTSTR)force,
431 (LPCTSTR)m_strURL,
432 (LPCTSTR)remotebranch);
434 m_GitCmdList.push_back(cmd);
436 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
437 if (!m_pThread)
439 // ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
441 else
443 m_pThread->m_bAutoDelete = TRUE;
444 m_pThread->ResumeThread();
449 ///Remote Update
450 if (CurrentEntry == 4)
452 if (m_bAutoLoadPuttyKey)
454 for (size_t i = 0; i < m_remotelist.size(); ++i)
455 CAppUtils::LaunchPAgent(this->GetSafeHwnd(), nullptr, &m_remotelist[i]);
458 m_CurrentCmd = GIT_COMMAND_REMOTE;
459 cmd = L"git.exe remote update";
460 m_GitCmdList.push_back(cmd);
462 InterlockedExchange(&m_bBlock, TRUE);
464 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
465 if (!m_pThread)
467 // ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
468 InterlockedExchange(&m_bBlock, FALSE);
470 else
472 m_pThread->m_bAutoDelete = TRUE;
473 m_pThread->ResumeThread();
477 ///Cleanup stale remote banches
478 if (CurrentEntry == 5)
480 m_CurrentCmd = GIT_COMMAND_REMOTE;
481 cmd.Format(L"git.exe remote prune \"%s\"", (LPCTSTR)m_strURL);
482 m_GitCmdList.push_back(cmd);
484 InterlockedExchange(&m_bBlock, TRUE);
486 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
487 if (!m_pThread)
489 // ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
490 InterlockedExchange(&m_bBlock, FALSE);
492 else
494 m_pThread->m_bAutoDelete = TRUE;
495 m_pThread->ResumeThread();
500 void CSyncDlg::ShowInCommits(const CString& friendname)
502 CGitHash newHash;
504 if (g_Git.GetHash(newHash, friendname))
506 MessageBox(g_Git.GetGitLastErr(L"Could not get " + friendname + L" hash."), L"TortoiseGit", MB_ICONERROR);
507 return;
510 if (newHash == m_oldHash)
512 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST - 1, false);
513 m_InLogList.ShowText(CString(MAKEINTRESOURCE(IDS_UPTODATE)));
514 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, true);
515 ShowTab(IDC_REFLIST);
517 else
519 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST - 1, true);
520 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, true);
522 AddDiffFileList(&m_InChangeFileList, &m_arInChangeList, newHash.ToString(), m_oldHash.ToString());
524 CString range;
525 range.Format(L"%s..%s", (LPCTSTR)m_oldHash.ToString(), (LPCTSTR)newHash.ToString());
526 m_InLogList.FillGitLog(nullptr, &range, CGit::LOG_INFO_STAT | CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
527 ShowTab(IDC_IN_LOGLIST);
531 void CSyncDlg::PullComplete()
533 EnableControlButton(true);
534 SwitchToInput();
535 this->FetchOutList(true);
537 if( this ->m_GitCmdStatus )
539 int hasConflicts = g_Git.HasWorkingTreeConflicts();
540 if (hasConflicts < 0)
542 this->m_ctrlCmdOut.SetSel(-1,-1);
543 this->m_ctrlCmdOut.ReplaceSel(g_Git.GetGitLastErr(L"Checking for conflicts failed.", CGit::GIT_CMD_CHECKCONFLICTS));
545 this->ShowTab(IDC_CMD_LOG);
546 return;
549 if (hasConflicts)
551 this->m_ConflictFileList.Clear();
552 this->m_ConflictFileList.GetStatus(nullptr, true);
553 this->m_ConflictFileList.Show(CTGitPath::LOGACTIONS_UNMERGED,
554 CTGitPath::LOGACTIONS_UNMERGED);
556 this->ShowTab(IDC_IN_CONFLICT);
557 CMessageBox::ShowCheck(GetSafeHwnd(), IDS_NEED_TO_RESOLVE_CONFLICTS_HINT, IDS_APPNAME, MB_ICONINFORMATION, L"MergeConflictsNeedsCommit", IDS_MSGBOX_DONOTSHOWAGAIN);
559 else
560 this->ShowTab(IDC_CMD_LOG);
563 else
564 ShowInCommits(L"HEAD");
567 void CSyncDlg::FetchComplete()
569 EnableControlButton(true);
570 SwitchToInput();
572 if (g_Git.UsingLibGit2(CGit::GIT_CMD_FETCH))
573 ShowTab(IDC_CMD_GIT_PROG);
574 else
575 ShowTab(IDC_REFLIST);
577 if (m_GitCmdStatus || (m_CurrentCmd != GIT_COMMAND_FETCHANDREBASE && m_iPullRebase == 0))
579 FetchOutList(true);
580 return;
583 CString remote;
584 CString remotebranch;
585 CString upstream = L"FETCH_HEAD";
586 m_ctrlURL.GetWindowText(remote);
587 if (!remote.IsEmpty())
589 if (std::find(m_remotelist.cbegin(), m_remotelist.cend(), remote) == m_remotelist.cend())
590 remote.Empty();
592 m_ctrlRemoteBranch.GetWindowText(remotebranch);
593 if (!remote.IsEmpty() && !remotebranch.IsEmpty())
594 upstream = L"remotes/" + remote + L'/' + remotebranch;
596 if (m_iPullRebase > 0)
598 CAppUtils::RebaseAfterFetch(GetSafeHwnd(), upstream, m_iPullRebase ? 2 : 0, m_iPullRebase == 2);
599 FillNewRefMap();
600 FetchOutList(true);
602 ShowInCommits(L"HEAD");
604 return;
607 CGitHash remoteBranchHash;
608 g_Git.GetHash(remoteBranchHash, upstream);
609 if (remoteBranchHash == m_oldRemoteHash && !m_oldRemoteHash.IsEmpty() && CMessageBox::ShowCheck(this->GetSafeHwnd(), IDS_REBASE_BRANCH_UNCHANGED, IDS_APPNAME, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2, L"OpenRebaseRemoteBranchUnchanged", IDS_MSGBOX_DONOTSHOWAGAIN) == IDNO)
611 ShowInCommits(upstream);
612 return;
615 if (g_Git.IsFastForward(L"HEAD", upstream))
617 UINT ret = CMessageBox::ShowCheck(GetSafeHwnd(), IDS_REBASE_BRANCH_FF, IDS_APPNAME, 2, IDI_QUESTION, IDS_MERGEBUTTON, IDS_REBASEBUTTON, IDS_ABORTBUTTON, L"OpenRebaseRemoteBranchFastForwards", IDS_MSGBOX_DONOTSHOWAGAIN);
618 if (ret == 3)
619 return;
620 if (ret == 1)
622 CProgressDlg mergeProgress;
623 mergeProgress.m_GitCmd = L"git.exe merge --ff-only " + upstream;
624 mergeProgress.m_AutoClose = AUTOCLOSE_IF_NO_ERRORS;
625 mergeProgress.m_PostCmdCallback = [](DWORD status, PostCmdList& postCmdList)
627 if (status && g_Git.HasWorkingTreeConflicts())
629 // there are conflict files
630 postCmdList.emplace_back(IDI_RESOLVE, IDS_PROGRS_CMD_RESOLVE, []
632 CString sCmd;
633 sCmd.Format(L"/command:commit /path:\"%s\"", (LPCTSTR)g_Git.m_CurrentDir);
634 CAppUtils::RunTortoiseGitProc(sCmd);
638 mergeProgress.DoModal();
639 FillNewRefMap();
640 FetchOutList(true);
642 ShowInCommits(L"HEAD");
644 return;
648 CAppUtils::RebaseAfterFetch(GetSafeHwnd(), upstream);
649 FillNewRefMap();
650 FetchOutList(true);
652 ShowInCommits(L"HEAD");
655 void CSyncDlg::StashComplete()
657 EnableControlButton(true);
658 INT_PTR entry = m_ctrlStash.GetCurrentEntry();
659 if (entry != 1 && entry != 2)
660 return;
662 SwitchToInput();
663 if (m_GitCmdStatus)
665 int hasConflicts = g_Git.HasWorkingTreeConflicts();
666 if (hasConflicts < 0)
668 m_ctrlCmdOut.SetSel(-1, -1);
669 m_ctrlCmdOut.ReplaceSel(g_Git.GetGitLastErr(L"Checking for conflicts failed.", CGit::GIT_CMD_CHECKCONFLICTS));
671 ShowTab(IDC_CMD_LOG);
672 return;
675 if (hasConflicts)
677 m_ConflictFileList.Clear();
678 m_ConflictFileList.GetStatus(nullptr, true);
679 m_ConflictFileList.Show(CTGitPath::LOGACTIONS_UNMERGED, CTGitPath::LOGACTIONS_UNMERGED);
681 ShowTab(IDC_IN_CONFLICT);
683 else
684 ShowTab(IDC_CMD_LOG);
688 void CSyncDlg::OnBnClickedButtonPush()
690 bool bShift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
691 this->UpdateData();
692 UpdateCombox();
694 if (bShift)
696 if (m_ctrlPush.GetCurrentEntry() == 0)
698 CAppUtils::Push(GetSafeHwnd(), g_Git.FixBranchName(m_strLocalBranch));
699 FillNewRefMap();
700 FetchOutList(true);
702 return;
705 m_ctrlCmdOut.SetWindowText(L"");
706 m_LogText.Empty();
708 if(this->m_strURL.IsEmpty())
710 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_GITCONFIG_URLEMPTY, IDS_APPNAME, MB_OK | MB_ICONERROR);
711 return;
714 if (!IsURL() && m_ctrlPush.GetCurrentEntry() == 0 && CRegDWORD(L"Software\\TortoiseGit\\AskSetTrackedBranch", TRUE) == TRUE)
716 if (!AskSetTrackedBranch())
717 return;
720 this->m_regPushButton=(DWORD)this->m_ctrlPush.GetCurrentEntry();
721 this->SwitchToRun();
722 this->m_bAbort=false;
723 this->m_GitCmdList.clear();
725 ShowTab(IDC_CMD_LOG);
727 CString cmd;
728 CString arg;
730 CString error;
731 DWORD exitcode = 0xFFFFFFFF;
732 CHooks::Instance().SetProjectProperties(g_Git.m_CurrentDir, m_ProjectProperties);
733 if (CHooks::Instance().PrePush(g_Git.m_CurrentDir, exitcode, error))
735 if (exitcode)
737 CString temp;
738 temp.Format(IDS_ERR_HOOKFAILED, (LPCTSTR)error);
739 CMessageBox::Show(GetSafeHwnd(), temp, L"TortoiseGit", MB_OK | MB_ICONERROR);
740 return ;
744 CString refName = g_Git.FixBranchName(m_strLocalBranch);
745 switch (m_ctrlPush.GetCurrentEntry())
747 case 1:
748 arg += L" --tags";
749 break;
750 case 2:
751 refName = L"refs/notes/commits"; //default ref for notes
752 break;
755 if(this->m_bForce)
756 arg += L" --force";
758 cmd.Format(L"git.exe push -v --progress%s \"%s\" %s",
759 (LPCTSTR)arg,
760 (LPCTSTR)m_strURL,
761 (LPCTSTR)refName);
763 if (!m_strRemoteBranch.IsEmpty() && m_ctrlPush.GetCurrentEntry() != 2)
765 cmd += L':' + m_strRemoteBranch;
768 m_GitCmdList.push_back(cmd);
770 m_CurrentCmd = GIT_COMMAND_PUSH;
772 if(this->m_bAutoLoadPuttyKey)
774 CAppUtils::LaunchPAgent(this->GetSafeHwnd(), nullptr, &m_strURL);
777 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
778 if (!m_pThread)
780 // ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
782 else
784 m_pThread->m_bAutoDelete = TRUE;
785 m_pThread->ResumeThread();
789 void CSyncDlg::OnBnClickedButtonApply()
791 CGitHash oldhash;
792 if (g_Git.GetHash(oldhash, L"HEAD"))
794 MessageBox(g_Git.GetGitLastErr(L"Could not get HEAD hash."), L"TortoiseGit", MB_ICONERROR);
795 return;
798 CImportPatchDlg dlg;
799 CString cmd,output;
801 if(dlg.DoModal() == IDOK)
803 int err=0;
804 for (int i = 0; i < dlg.m_PathList.GetCount(); ++i)
806 cmd.Format(L"git.exe am \"%s\"", (LPCTSTR)dlg.m_PathList[i].GetGitPathString());
808 if (g_Git.Run(cmd, &output, CP_UTF8))
810 CMessageBox::Show(GetSafeHwnd(), output, L"TortoiseGit", MB_OK | MB_ICONERROR);
812 err=1;
813 break;
815 this->m_ctrlCmdOut.SetSel(-1,-1);
816 this->m_ctrlCmdOut.ReplaceSel(cmd + L'\n');
817 this->m_ctrlCmdOut.SetSel(-1,-1);
818 this->m_ctrlCmdOut.ReplaceSel(output);
821 CGitHash newhash;
822 if (g_Git.GetHash(newhash, L"HEAD"))
824 MessageBox(g_Git.GetGitLastErr(L"Could not get HEAD hash after applying patches."), L"TortoiseGit", MB_ICONERROR);
825 return;
828 this->m_InLogList.Clear();
829 this->m_InChangeFileList.Clear();
831 if(newhash == oldhash)
833 this->m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST-1,false);
834 this->m_InLogList.ShowText(L"No commits get from patch");
835 this->m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST-1,true);
838 else
840 this->m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST-1,true);
841 this->m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST-1,true);
843 CString range;
844 range.Format(L"%s..%s", (LPCTSTR)m_oldHash.ToString(), (LPCTSTR)newhash.ToString());
845 this->AddDiffFileList(&m_InChangeFileList, &m_arInChangeList, newhash.ToString(), oldhash.ToString());
846 m_InLogList.FillGitLog(nullptr, &range, CGit::LOG_INFO_STAT| CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
848 this->FetchOutList(true);
851 this->m_ctrlTabCtrl.ShowTab(IDC_CMD_LOG-1,true);
853 if(err)
855 this->ShowTab(IDC_CMD_LOG);
857 else
859 this->ShowTab(IDC_IN_LOGLIST);
864 void CSyncDlg::OnBnClickedButtonEmail()
866 CString cmd, out, err;
868 this->m_strLocalBranch = this->m_ctrlLocalBranch.GetString();
869 this->m_ctrlRemoteBranch.GetWindowText(this->m_strRemoteBranch);
870 this->m_ctrlURL.GetWindowText(this->m_strURL);
871 m_strURL=m_strURL.Trim();
872 m_strRemoteBranch=m_strRemoteBranch.Trim();
874 cmd.Format(L"git.exe format-patch -o \"%s\" %s..%s",
875 (LPCTSTR)g_Git.m_CurrentDir,
876 (LPCTSTR)(m_strURL + L'/' + m_strRemoteBranch), (LPCTSTR)g_Git.FixBranchName(m_strLocalBranch));
878 if (g_Git.Run(cmd, &out, &err, CP_UTF8))
880 CMessageBox::Show(GetSafeHwnd(), out + L'\n' + err, L"TortoiseGit", MB_OK | MB_ICONERROR);
881 return ;
884 CAppUtils::SendPatchMail(GetSafeHwnd(), cmd, out);
886 void CSyncDlg::ShowProgressCtrl(bool bShow)
888 int b=bShow?SW_NORMAL:SW_HIDE;
889 this->m_ctrlAnimate.ShowWindow(b);
890 this->m_ctrlProgress.ShowWindow(b);
891 this->m_ctrlProgLabel.ShowWindow(b);
892 this->m_ctrlAnimate.Open(IDR_DOWNLOAD);
893 if(b == SW_NORMAL)
894 this->m_ctrlAnimate.Play(0, UINT_MAX, UINT_MAX);
895 else
896 this->m_ctrlAnimate.Stop();
898 void CSyncDlg::ShowInputCtrl(bool bShow)
900 int b=bShow?SW_NORMAL:SW_HIDE;
901 this->m_ctrlURL.ShowWindow(b);
902 this->m_ctrlLocalBranch.ShowWindow(b);
903 this->m_ctrlRemoteBranch.ShowWindow(b);
904 this->GetDlgItem(IDC_BUTTON_LOCAL_BRANCH)->ShowWindow(b);
905 this->GetDlgItem(IDC_BUTTON_REMOTE_BRANCH)->ShowWindow(b);
906 this->GetDlgItem(IDC_STATIC_LOCAL_BRANCH)->ShowWindow(b);
907 this->GetDlgItem(IDC_STATIC_REMOTE_BRANCH)->ShowWindow(b);
908 this->GetDlgItem(IDC_BUTTON_MANAGE)->ShowWindow(b);
909 this->GetDlgItem(IDC_CHECK_PUTTY_KEY)->ShowWindow(b);
910 this->GetDlgItem(IDC_CHECK_FORCE)->ShowWindow(b);
911 this->GetDlgItem(IDC_STATIC_REMOTE_URL)->ShowWindow(b);
913 BOOL CSyncDlg::OnInitDialog()
915 CResizableStandAloneDialog::OnInitDialog();
916 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
918 // Let the TaskbarButtonCreated message through the UIPI filter. If we don't
919 // do this, Explorer would be unable to send that message to our window if we
920 // were running elevated. It's OK to make the call all the time, since if we're
921 // not elevated, this is a no-op.
922 CHANGEFILTERSTRUCT cfs = { sizeof(CHANGEFILTERSTRUCT) };
923 typedef BOOL STDAPICALLTYPE ChangeWindowMessageFilterExDFN(HWND hWnd, UINT message, DWORD action, PCHANGEFILTERSTRUCT pChangeFilterStruct);
924 CAutoLibrary hUser = AtlLoadSystemLibraryUsingFullPath(L"user32.dll");
925 if (hUser)
927 ChangeWindowMessageFilterExDFN *pfnChangeWindowMessageFilterEx = (ChangeWindowMessageFilterExDFN*)GetProcAddress(hUser, "ChangeWindowMessageFilterEx");
928 if (pfnChangeWindowMessageFilterEx)
930 pfnChangeWindowMessageFilterEx(m_hWnd, TaskBarButtonCreated, MSGFLT_ALLOW, &cfs);
933 m_pTaskbarList.Release();
934 if (FAILED(m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList)))
935 m_pTaskbarList = nullptr;
937 this->GetDlgItem(IDC_CHECK_PUTTY_KEY)->EnableWindow(CAppUtils::IsSSHPutty());
940 this->m_ctrlAnimate.ShowWindow(SW_NORMAL);
941 this->m_ctrlAnimate.Open(IDR_DOWNLOAD);
942 this->m_ctrlAnimate.Play(0,-1,-1);
945 // ------------------ Create Tabctrl -----------
946 CWnd *pwnd=this->GetDlgItem(IDC_BUTTON_TABCTRL);
947 CRect rectDummy;
948 pwnd->GetWindowRect(&rectDummy);
949 this->ScreenToClient(rectDummy);
951 if (!m_ctrlTabCtrl.Create(CMFCTabCtrl::STYLE_FLAT, rectDummy, this, IDC_SYNC_TAB))
953 TRACE0("Failed to create output tab window\n");
954 return FALSE; // fail to create
956 m_ctrlTabCtrl.SetResizeMode(CMFCTabCtrl::RESIZE_NO);
958 // -------------Create Command Log Ctrl ---------
959 DWORD dwStyle;
960 dwStyle= ES_MULTILINE | ES_READONLY | WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_AUTOVSCROLL |WS_VSCROLL ;
962 if( !m_ctrlCmdOut.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_CMD_LOG))
964 TRACE0("Failed to create Log commits window\n");
965 return FALSE; // fail to create
968 // set the font to use in the log message view, configured in the settings dialog
969 CFont m_logFont;
970 CAppUtils::CreateFontForLogs(m_logFont);
971 m_ctrlCmdOut.SetFont(&m_logFont);
972 m_ctrlTabCtrl.InsertTab(&m_ctrlCmdOut, CString(MAKEINTRESOURCE(IDS_LOG)), -1);
973 // make the log message rich edit control send a message when the mouse pointer is over a link
974 m_ctrlCmdOut.SendMessage(EM_SETEVENTMASK, NULL, ENM_LINK | ENM_SCROLL);
976 //---------- Create in coming list ctrl -----------
977 dwStyle =LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;;
979 if( !m_InLogList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_IN_LOGLIST))
981 TRACE0("Failed to create output commits window\n");
982 return FALSE; // fail to create
984 // for some unknown reason, the SetExtendedStyle in OnCreate/PreSubclassWindow is not working here
985 m_InLogList.SetStyle();
987 m_ctrlTabCtrl.InsertTab(&m_InLogList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_INCOMMITS)), -1);
989 m_InLogList.m_ColumnRegKey = L"SyncIn";
990 m_InLogList.InsertGitColumn();
992 //----------- Create In Change file list -----------
993 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
995 if( !m_InChangeFileList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_IN_CHANGELIST))
997 TRACE0("Failed to create output change files window\n");
998 return FALSE; // fail to create
1000 m_ctrlTabCtrl.InsertTab(&m_InChangeFileList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_INCHANGELIST)), -1);
1002 m_InChangeFileList.Init(GITSLC_COLEXT | GITSLC_COLSTATUS |GITSLC_COLADD|GITSLC_COLDEL, L"InSyncDlg",
1003 (CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_COMPARETWOREVISIONS) |
1004 CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_GNUDIFF2REVISIONS)), false, false, GITSLC_COLEXT | GITSLC_COLSTATUS | GITSLC_COLADD | GITSLC_COLDEL);
1007 //---------- Create Conflict List Ctrl -----------------
1008 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
1010 if( !m_ConflictFileList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_IN_CONFLICT))
1012 TRACE0("Failed to create output change files window\n");
1013 return FALSE; // fail to create
1015 m_ctrlTabCtrl.InsertTab(&m_ConflictFileList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_CONFLICTS)), -1);
1017 m_ConflictFileList.Init(GITSLC_COLEXT | GITSLC_COLSTATUS |GITSLC_COLADD|GITSLC_COLDEL, L"ConflictSyncDlg",
1018 (GITSLC_POPEXPLORE | GITSLC_POPOPEN | GITSLC_POPSHOWLOG |
1019 GITSLC_POPCONFLICT|GITSLC_POPRESOLVE),false);
1022 //---------- Create Commit Out List Ctrl---------------
1024 dwStyle =LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;;
1026 if( !m_OutLogList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_OUT_LOGLIST))
1028 TRACE0("Failed to create output commits window\n");
1029 return FALSE; // fail to create
1032 // for some unknown reason, the SetExtendedStyle in OnCreate/PreSubclassWindow is not working here
1033 m_OutLogList.SetStyle();
1035 m_ctrlTabCtrl.InsertTab(&m_OutLogList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_OUTCOMMITS)), -1);
1037 m_OutLogList.m_ColumnRegKey = L"SyncOut";
1038 m_OutLogList.InsertGitColumn();
1040 //------------- Create Change File List Control ----------------
1042 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
1044 if( !m_OutChangeFileList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_OUT_CHANGELIST))
1046 TRACE0("Failed to create output change files window\n");
1047 return FALSE; // fail to create
1049 m_ctrlTabCtrl.InsertTab(&m_OutChangeFileList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_OUTCHANGELIST)), -1);
1051 m_OutChangeFileList.Init(GITSLC_COLEXT | GITSLC_COLSTATUS | GITSLC_COLADD | GITSLC_COLDEL, L"OutSyncDlg",
1052 (CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_COMPARETWOREVISIONS) |
1053 CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_GNUDIFF2REVISIONS)), false, false, GITSLC_COLEXT | GITSLC_COLSTATUS | GITSLC_COLADD | GITSLC_COLDEL);
1055 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | LVS_SINGLESEL | WS_CHILD | WS_VISIBLE;
1056 if (!m_GitProgressList.Create(dwStyle | LVS_OWNERDATA, rectDummy, &m_ctrlTabCtrl, IDC_CMD_GIT_PROG))
1058 TRACE0("Failed to create Git Progress List Window\n");
1059 return FALSE; // fail to create
1061 m_ctrlTabCtrl.InsertTab(&m_GitProgressList, CString(MAKEINTRESOURCE(IDS_LOG)), -1);
1062 m_GitProgressList.m_pAnimate = &m_ctrlAnimate;
1063 m_GitProgressList.m_pPostWnd = this;
1064 m_GitProgressList.m_pProgressLabelCtrl = &m_ctrlProgLabel;
1065 m_GitProgressList.m_pProgControl = &m_ctrlProgress;
1066 m_GitProgressList.m_pTaskbarList = m_pTaskbarList;
1068 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE | LVS_SINGLESEL;
1069 DWORD exStyle = LVS_EX_HEADERDRAGDROP | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP;
1070 if (CRegDWORD(L"Software\\TortoiseGit\\FullRowSelect", TRUE))
1071 exStyle |= LVS_EX_FULLROWSELECT;
1072 if (g_Git.m_IsUseLibGit2)
1074 m_refList.Create(dwStyle, rectDummy, &m_ctrlTabCtrl, IDC_REFLIST);
1075 m_refList.SetExtendedStyle(exStyle);
1076 m_refList.Init();
1077 m_ctrlTabCtrl.InsertTab(&m_refList, CString(MAKEINTRESOURCE(IDS_REFLIST)), -1);
1079 m_tagCompareList.Create(dwStyle, rectDummy, &m_ctrlTabCtrl, IDC_TAGCOMPARELIST);
1080 m_tagCompareList.SetExtendedStyle(exStyle);
1081 m_tagCompareList.Init();
1082 m_ctrlTabCtrl.InsertTab(&m_tagCompareList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_COMPARETAGS)), -1);
1084 m_ProjectProperties.ReadProps();
1086 AdjustControlSize(IDC_CHECK_PUTTY_KEY);
1087 AdjustControlSize(IDC_CHECK_FORCE);
1089 AddAnchor(IDC_SYNC_TAB,TOP_LEFT,BOTTOM_RIGHT);
1091 AddAnchor(IDC_GROUP_INFO,TOP_LEFT,TOP_RIGHT);
1092 AddAnchor(IDC_COMBOBOXEX_URL,TOP_LEFT,TOP_RIGHT);
1093 AddAnchor(IDC_BUTTON_MANAGE,TOP_RIGHT);
1094 AddAnchor(IDC_BUTTON_PULL,BOTTOM_LEFT);
1095 AddAnchor(IDC_BUTTON_PUSH,BOTTOM_LEFT);
1096 AddAnchor(IDC_BUTTON_SUBMODULE,BOTTOM_LEFT);
1097 AddAnchor(IDC_BUTTON_STASH,BOTTOM_LEFT);
1098 AddAnchor(IDC_BUTTON_APPLY,BOTTOM_RIGHT);
1099 AddAnchor(IDC_BUTTON_EMAIL,BOTTOM_RIGHT);
1100 AddAnchor(IDC_PROGRESS_SYNC,TOP_LEFT,TOP_RIGHT);
1101 AddAnchor(IDOK,BOTTOM_RIGHT);
1102 AddAnchor(IDHELP,BOTTOM_RIGHT);
1103 AddAnchor(IDC_STATIC_STATUS, BOTTOM_LEFT, BOTTOM_RIGHT);
1104 AddAnchor(IDC_ANIMATE_SYNC,TOP_LEFT);
1105 AddAnchor(IDC_BUTTON_COMMIT,BOTTOM_LEFT);
1106 AddAnchor(IDC_LOG, BOTTOM_LEFT);
1108 // do not use BRANCH_COMBOX_ADD_ANCHOR here, we want to have different stylings
1109 AddAnchor(IDC_COMBOBOXEX_LOCAL_BRANCH, TOP_LEFT,TOP_CENTER);
1110 AddAnchor(IDC_COMBOBOXEX_REMOTE_BRANCH, TOP_CENTER, TOP_RIGHT);
1111 AddAnchor(IDC_BUTTON_LOCAL_BRANCH, TOP_CENTER);
1112 AddAnchor(IDC_BUTTON_REMOTE_BRANCH, TOP_RIGHT);
1113 AddAnchor(IDC_STATIC_REMOTE_BRANCH, TOP_CENTER);
1114 AddAnchor(IDC_PROG_LABEL, TOP_LEFT);
1116 CString WorkingDir=g_Git.m_CurrentDir;
1117 WorkingDir.Replace(L':', L'_');
1118 m_RegKeyRemoteBranch = L"Software\\TortoiseGit\\History\\SyncBranch\\" + WorkingDir;
1121 this->AddOthersToAnchor();
1123 this->m_ctrlPush.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PUSH)));
1124 this->m_ctrlPush.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PUSHTAGS)));
1125 this->m_ctrlPush.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PUSHNOTES)));
1127 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PULL)));
1128 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_FETCH)));
1129 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_FETCHREBASE)));
1130 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_FETCHALL)));
1131 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_REMOTEUPDATE)));
1132 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_CLEANUPSTALEBRANCHES)));
1133 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_COMPARETAGS)));
1135 this->m_ctrlSubmodule.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_SUBKODULEUPDATE)));
1136 this->m_ctrlSubmodule.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_SUBKODULEINIT)));
1137 this->m_ctrlSubmodule.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_SUBKODULESYNC)));
1139 this->m_ctrlStash.AddEntry(CString(MAKEINTRESOURCE(IDS_MENUSTASHSAVE)));
1140 this->m_ctrlStash.AddEntry(CString(MAKEINTRESOURCE(IDS_MENUSTASHPOP)));
1141 this->m_ctrlStash.AddEntry(CString(MAKEINTRESOURCE(IDS_MENUSTASHAPPLY)));
1143 WorkingDir.Replace(L':', L'_');
1145 CString regkey ;
1146 regkey.Format(L"Software\\TortoiseGit\\TortoiseProc\\Sync\\%s", (LPCTSTR)WorkingDir);
1148 this->m_regPullButton = CRegDWORD(regkey + L"\\Pull", 0);
1149 this->m_regPushButton = CRegDWORD(regkey + L"\\Push", 0);
1150 this->m_regSubmoduleButton = CRegDWORD(regkey + L"\\Submodule");
1151 this->m_regAutoLoadPutty = CRegDWORD(regkey + L"\\AutoLoadPutty", CAppUtils::IsSSHPutty());
1153 this->UpdateData();
1154 this->m_bAutoLoadPuttyKey = m_regAutoLoadPutty;
1155 if(!CAppUtils::IsSSHPutty())
1156 m_bAutoLoadPuttyKey = false;
1157 this->UpdateData(FALSE);
1159 this->m_ctrlPull.SetCurrentEntry(this->m_regPullButton);
1160 this->m_ctrlPush.SetCurrentEntry(this->m_regPushButton);
1161 this->m_ctrlSubmodule.SetCurrentEntry(this->m_regSubmoduleButton);
1163 CString sWindowTitle;
1164 GetWindowText(sWindowTitle);
1165 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
1167 EnableSaveRestore(L"SyncDlg");
1169 m_ctrlURL.SetCaseSensitive(TRUE);
1171 m_ctrlURL.SetCustomAutoSuggest(true, true, true);
1172 m_ctrlURL.SetMaxHistoryItems(0x7FFFFFFF);
1173 this->m_ctrlURL.LoadHistory(L"Software\\TortoiseGit\\History\\SyncURL\\" + WorkingDir, L"url");
1175 m_remotelist.clear();
1176 if(!g_Git.GetRemoteList(m_remotelist))
1178 for (unsigned int i = 0; i < m_remotelist.size(); ++i)
1180 m_ctrlURL.AddString(m_remotelist[i]);
1183 m_ctrlURL.SetCurSel(0);
1184 m_ctrlRemoteBranch.SetCurSel(0);
1186 this->LoadBranchInfo();
1188 this->m_bInited=true;
1189 FetchOutList();
1191 m_ctrlTabCtrl.ShowTab(IDC_CMD_LOG-1,false);
1192 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST-1,false);
1193 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST-1,false);
1194 m_ctrlTabCtrl.ShowTab(IDC_IN_CONFLICT-1,false);
1195 m_ctrlTabCtrl.ShowTab(IDC_CMD_GIT_PROG-1, false);
1196 m_ctrlTabCtrl.ShowTab(IDC_REFLIST-1, false);
1197 m_ctrlTabCtrl.ShowTab(IDC_TAGCOMPARELIST - 1, false);
1199 m_ctrlRemoteBranch.m_bWantReturn = TRUE;
1200 m_ctrlURL.m_bWantReturn = TRUE;
1202 if (m_seq > 0 && (DWORD)CRegDWORD(L"Software\\TortoiseGit\\SyncDialogRandomPos"))
1204 m_seq %= 5;
1205 RECT rect;
1206 GetWindowRect(&rect);
1207 rect.top -= m_seq * 30;
1208 rect.bottom -= m_seq * 30;
1209 if (rect.top < 0)
1211 rect.top += 150;
1212 rect.bottom += 150;
1214 MoveWindow(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
1217 return TRUE; // return TRUE unless you set the focus to a control
1218 // EXCEPTION: OCX Property Pages should return FALSE
1221 void CSyncDlg::OnBnClickedButtonManage()
1223 CAppUtils::LaunchRemoteSetting();
1224 Refresh();
1227 void CSyncDlg::Refresh()
1229 theApp.DoWaitCursor(1);
1231 int lastSelected = m_ctrlURL.GetCurSel();
1232 CString url;
1233 this->m_ctrlURL.GetWindowText(url);
1235 this->m_ctrlURL.Reset();
1236 CString workingDir = g_Git.m_CurrentDir;
1237 workingDir.Replace(L':', L'_');
1238 this->m_ctrlURL.LoadHistory(L"Software\\TortoiseGit\\History\\SyncURL\\" + workingDir, L"url");
1240 bool found = false;
1241 m_remotelist.clear();
1242 if (!g_Git.GetRemoteList(m_remotelist))
1244 for (size_t i = 0; i < m_remotelist.size(); ++i)
1246 m_ctrlURL.AddString(m_remotelist[i]);
1247 if (m_remotelist[i] == url)
1248 found = true;
1251 if (lastSelected >= 0 && !found)
1253 m_ctrlURL.SetCurSel(0);
1254 m_ctrlURL.GetWindowText(url);
1257 CString local;
1258 CString remote;
1259 this->m_ctrlLocalBranch.GetWindowText(local);
1260 this->m_ctrlRemoteBranch.GetWindowText(remote);
1262 this->LoadBranchInfo();
1264 this->m_ctrlLocalBranch.AddString(local);
1265 this->m_ctrlRemoteBranch.AddString(remote);
1266 this->m_ctrlURL.AddString(url);
1268 m_OutLogList.ShowText(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_REFRESHING)));
1269 this->FetchOutList(true);
1270 theApp.DoWaitCursor(-1);
1273 BOOL CSyncDlg::PreTranslateMessage(MSG* pMsg)
1275 if (pMsg->message == WM_KEYDOWN)
1277 switch (pMsg->wParam)
1279 case VK_F5:
1281 if (m_bBlock)
1282 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
1283 Refresh();
1285 break;
1287 /* Avoid TAB control destroy but dialog exist*/
1288 case VK_ESCAPE:
1289 case VK_CANCEL:
1291 TCHAR buff[129];
1292 ::GetClassName(pMsg->hwnd, buff, _countof(buff) - 1);
1294 /* Use MSFTEDIT_CLASS http://msdn.microsoft.com/en-us/library/bb531344.aspx */
1295 if (_wcsnicmp(buff, MSFTEDIT_CLASS, _countof(buff) - 1) == 0 || //Unicode and MFC 2012 and later
1296 _wcsnicmp(buff, RICHEDIT_CLASS, _countof(buff) - 1) == 0 || //ANSI or MFC 2010
1297 _wcsnicmp(buff, L"SysListView32", _countof(buff) - 1) == 0)
1299 this->PostMessage(WM_KEYDOWN,VK_ESCAPE,0);
1300 return TRUE;
1305 return __super::PreTranslateMessage(pMsg);
1307 void CSyncDlg::FetchOutList(bool force)
1309 if (!m_bInited || m_bWantToExit)
1310 return;
1311 m_OutChangeFileList.Clear();
1312 this->m_OutLogList.Clear();
1314 m_ctrlTabCtrl.ShowTab(IDC_OUT_LOGLIST - 1, true);
1315 m_ctrlTabCtrl.ShowTab(IDC_OUT_CHANGELIST - 1, true);
1317 CString remote;
1318 this->m_ctrlURL.GetWindowText(remote);
1319 CString remotebranch;
1320 this->m_ctrlRemoteBranch.GetWindowText(remotebranch);
1321 remotebranch = remote + L'/' + remotebranch;
1322 CGitHash remotebranchHash;
1323 g_Git.GetHash(remotebranchHash, remotebranch);
1325 if(IsURL())
1327 CString str;
1328 str.LoadString(IDS_PROC_SYNC_PUSH_UNKNOWN);
1329 m_OutLogList.ShowText(str);
1330 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,FALSE);
1331 m_OutLocalBranch.Empty();
1332 m_OutRemoteBranch.Empty();
1334 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1335 return ;
1338 else if(remotebranchHash.IsEmpty())
1340 CString str;
1341 str.Format(IDS_PROC_SYNC_PUSH_UNKNOWNBRANCH, (LPCTSTR)remotebranch);
1342 m_OutLogList.ShowText(str);
1343 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,FALSE);
1344 m_OutLocalBranch.Empty();
1345 m_OutRemoteBranch.Empty();
1347 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1348 return ;
1350 else
1352 CString localbranch;
1353 localbranch=this->m_ctrlLocalBranch.GetString();
1355 if(localbranch != m_OutLocalBranch || m_OutRemoteBranch != remotebranch || force)
1357 m_OutLogList.ClearText();
1359 CGitHash base, localBranchHash;
1360 bool isFastForward = g_Git.IsFastForward(remotebranch, localbranch, &base);
1362 if (g_Git.GetHash(localBranchHash, localbranch))
1364 MessageBox(g_Git.GetGitLastErr(L"Could not get hash of \"" + localbranch + L"\"."), L"TortoiseGit", MB_ICONERROR);
1365 return;
1367 if (remotebranchHash == localBranchHash)
1369 CString str;
1370 str.FormatMessage(IDS_PROC_SYNC_COMMITSAHEAD, 0, (LPCTSTR)remotebranch);
1371 m_OutLogList.ShowText(str);
1372 this->m_ctrlStatus.SetWindowText(str);
1373 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,FALSE);
1374 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1376 else if (isFastForward || m_bForce)
1378 CString range;
1379 range.Format(L"%s..%s", (LPCTSTR)g_Git.FixBranchName(remotebranch), (LPCTSTR)g_Git.FixBranchName(localbranch));
1380 //fast forward
1381 m_OutLogList.FillGitLog(nullptr, &range, CGit::LOG_INFO_STAT | CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
1382 CString str;
1383 str.FormatMessage(IDS_PROC_SYNC_COMMITSAHEAD, m_OutLogList.GetItemCount(), (LPCTSTR)remotebranch);
1384 this->m_ctrlStatus.SetWindowText(str);
1386 if (isFastForward)
1387 AddDiffFileList(&m_OutChangeFileList, &m_arOutChangeList, localbranch, remotebranch);
1388 else
1390 AddDiffFileList(&m_OutChangeFileList, &m_arOutChangeList, localbranch, base.ToString());
1393 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,TRUE);
1394 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(TRUE);
1396 else
1398 CString str;
1399 str.FormatMessage(IDS_PROC_SYNC_NOFASTFORWARD, (LPCTSTR)localbranch, (LPCTSTR)remotebranch);
1400 m_OutLogList.ShowText(str);
1401 this->m_ctrlStatus.SetWindowText(str);
1402 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID() - 1, FALSE);
1403 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1406 this->m_OutLocalBranch=localbranch;
1407 this->m_OutRemoteBranch=remotebranch;
1411 bool CSyncDlg::IsURL()
1413 CString str;
1414 this->m_ctrlURL.GetWindowText(str);
1415 return str.Find(L'\\') >= 0 || str.Find(L'/') >= 0;
1418 void CSyncDlg::OnCbnEditchangeComboboxex()
1420 SetTimer(IDT_INPUT, 1000, nullptr);
1421 this->m_OutLogList.ShowText(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_WAINTINPUT)));
1423 //this->FetchOutList();
1426 UINT CSyncDlg::ProgressThread()
1428 m_startTick = GetTickCount64();
1429 m_bDone = false;
1430 STRING_VECTOR list;
1431 CProgressDlg::RunCmdList(this, m_GitCmdList, list, true, nullptr, &this->m_bAbort, &this->m_Databuf);
1432 InterlockedExchange(&m_bBlock, FALSE);
1433 return 0;
1436 LRESULT CSyncDlg::OnProgressUpdateUI(WPARAM wParam,LPARAM lParam)
1438 if (m_bWantToExit)
1439 return 0;
1440 if(wParam == MSG_PROGRESSDLG_START)
1442 m_BufStart = 0;
1443 m_ctrlAnimate.Play(0, UINT_MAX, UINT_MAX);
1444 this->m_ctrlProgress.SetPos(0);
1445 if (m_pTaskbarList)
1447 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
1448 m_pTaskbarList->SetProgressValue(m_hWnd, 0, 100);
1452 if(wParam == MSG_PROGRESSDLG_END || wParam == MSG_PROGRESSDLG_FAILED)
1454 ULONGLONG tickSpent = GetTickCount64() - m_startTick;
1455 CString strEndTime = CLoglistUtils::FormatDateAndTime(CTime::GetCurrentTime(), DATE_SHORTDATE, true, false);
1457 m_BufStart = 0;
1458 m_Databuf.m_critSec.Lock();
1459 m_Databuf.clear();
1460 m_Databuf.m_critSec.Unlock();
1462 m_bDone = true;
1463 m_ctrlAnimate.Stop();
1464 m_ctrlProgress.SetPos(100);
1465 //this->DialogEnableWindow(IDOK,TRUE);
1468 CString text;
1469 m_ctrlCmdOut.GetWindowText(text);
1470 text.Remove('\r');
1471 CAppUtils::StyleURLs(text, &m_ctrlCmdOut);
1474 DWORD exitCode = (DWORD)lParam;
1475 if (exitCode)
1477 if (m_pTaskbarList)
1479 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_ERROR);
1480 m_pTaskbarList->SetProgressValue(m_hWnd, 100, 100);
1482 CString log;
1483 log.Format(IDS_PROC_PROGRESS_GITUNCLEANEXIT, exitCode);
1484 CString err;
1485 err.Format(L"\r\n\r\n%s (%I64u ms @ %s)\r\n", (LPCTSTR)log, tickSpent, (LPCTSTR)strEndTime);
1486 CProgressDlg::InsertColorText(this->m_ctrlCmdOut, err, RGB(255,0,0));
1487 if (CRegDWORD(L"Software\\TortoiseGit\\NoSounds", FALSE) == FALSE)
1488 PlaySound((LPCTSTR)SND_ALIAS_SYSTEMEXCLAMATION, nullptr, SND_ALIAS_ID | SND_ASYNC);
1490 else
1492 if (m_pTaskbarList)
1493 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
1494 CString temp;
1495 temp.LoadString(IDS_SUCCESS);
1496 CString log;
1497 log.Format(L"\r\n%s (%I64u ms @ %s)\r\n", (LPCTSTR)temp, tickSpent, (LPCTSTR)strEndTime);
1498 CProgressDlg::InsertColorText(this->m_ctrlCmdOut, log, RGB(0,0,255));
1500 m_GitCmdStatus = exitCode;
1502 //if(wParam == MSG_PROGRESSDLG_END)
1503 RunPostAction();
1506 if(lParam != 0)
1507 ParserCmdOutput((char)lParam);
1508 else
1510 m_Databuf.m_critSec.Lock();
1511 for (size_t i = m_BufStart; i < m_Databuf.size(); ++i)
1513 char c = m_Databuf[m_BufStart];
1514 ++m_BufStart;
1515 m_Databuf.m_critSec.Unlock();
1516 ParserCmdOutput(c);
1518 m_Databuf.m_critSec.Lock();
1521 if (m_BufStart > 1000)
1523 m_Databuf.erase(m_Databuf.cbegin(), m_Databuf.cbegin() + m_BufStart);
1524 m_BufStart = 0;
1526 m_Databuf.m_critSec.Unlock();
1529 return 0;
1532 static REF_VECTOR HashMapToRefMap(MAP_HASH_NAME& map)
1534 auto rmap = REF_VECTOR();
1535 for (auto mit = map.cbegin(); mit != map.cend(); ++mit)
1537 for (auto rit = mit->second.cbegin(); rit != mit->second.cend(); ++rit)
1539 rmap.emplace_back(TGitRef{ *rit, mit->first });
1542 return rmap;
1545 void CSyncDlg::FillNewRefMap()
1547 m_refList.Clear();
1548 m_newHashMap.clear();
1550 if (!g_Git.m_IsUseLibGit2)
1551 return;
1553 CAutoRepository repo(g_Git.GetGitRepository());
1554 if (!repo)
1556 CMessageBox::Show(m_hWnd, CGit::GetLibGit2LastErr(L"Could not open repository."), L"TortoiseGit", MB_OK | MB_ICONERROR);
1557 return;
1560 if (CGit::GetMapHashToFriendName(repo, m_newHashMap))
1562 MessageBox(CGit::GetLibGit2LastErr(L"Could not get all refs."), L"TortoiseGit", MB_ICONERROR);
1563 return;
1566 auto oldRefMap = HashMapToRefMap(m_oldHashMap);
1567 auto newRefMap = HashMapToRefMap(m_newHashMap);
1568 for (auto oit = oldRefMap.cbegin(); oit != oldRefMap.cend(); ++oit)
1570 bool found = false;
1571 for (auto nit = newRefMap.cbegin(); nit != newRefMap.cend(); ++nit)
1573 // changed ref
1574 if (oit->name == nit->name)
1576 found = true;
1577 m_refList.AddEntry(repo, oit->name, &oit->hash, &nit->hash);
1578 break;
1581 // deleted ref
1582 if (!found)
1583 m_refList.AddEntry(repo, oit->name, &oit->hash, nullptr);
1585 for (auto nit = newRefMap.cbegin(); nit != newRefMap.cend(); ++nit)
1587 bool found = false;
1588 for (auto oit = oldRefMap.cbegin(); oit != oldRefMap.cend(); ++oit)
1590 if (oit->name == nit->name)
1592 found = true;
1593 break;
1596 // new ref
1597 if (!found)
1598 m_refList.AddEntry(repo, nit->name, nullptr, &nit->hash);
1600 m_refList.Show();
1603 void CSyncDlg::RunPostAction()
1605 if (m_bWantToExit)
1606 return;
1608 FillNewRefMap();
1610 if (this->m_CurrentCmd == GIT_COMMAND_PUSH)
1612 DWORD exitcode = 0xFFFFFFFF;
1613 CString error;
1614 CHooks::Instance().SetProjectProperties(g_Git.m_CurrentDir, m_ProjectProperties);
1615 if (CHooks::Instance().PostPush(g_Git.m_CurrentDir, exitcode, error))
1617 if (exitcode)
1619 CString temp;
1620 temp.Format(IDS_ERR_HOOKFAILED, (LPCTSTR)error);
1621 CMessageBox::Show(GetSafeHwnd(), temp, L"TortoiseGit", MB_OK | MB_ICONERROR);
1622 return;
1626 EnableControlButton(true);
1627 SwitchToInput();
1628 this->FetchOutList(true);
1630 else if (this->m_CurrentCmd == GIT_COMMAND_PULL)
1631 PullComplete();
1632 else if (this->m_CurrentCmd == GIT_COMMAND_FETCH || this->m_CurrentCmd == GIT_COMMAND_FETCHANDREBASE)
1633 FetchComplete();
1634 else if (this->m_CurrentCmd == GIT_COMMAND_SUBMODULE)
1636 //this->m_ctrlCmdOut.SetSel(-1,-1);
1637 //this->m_ctrlCmdOut.ReplaceSel(L"Done\r\n");
1638 //this->m_ctrlCmdOut.SetSel(-1,-1);
1639 EnableControlButton(true);
1640 SwitchToInput();
1642 else if (this->m_CurrentCmd == GIT_COMMAND_STASH)
1643 StashComplete();
1644 else if (this->m_CurrentCmd == GIT_COMMAND_REMOTE)
1646 this->FetchOutList(true);
1647 EnableControlButton(true);
1648 SwitchToInput();
1649 ShowTab(IDC_REFLIST);
1652 void CSyncDlg::ParserCmdOutput(char ch)
1654 if (m_bAbort)
1655 return;
1656 CProgressDlg::ParserCmdOutput(m_ctrlCmdOut,m_ctrlProgress,m_hWnd,m_pTaskbarList,m_LogText,ch);
1658 void CSyncDlg::OnBnClickedButtonCommit()
1660 CString cmd = L"/command:commit";
1661 cmd += L" /path:\"";
1662 cmd += g_Git.m_CurrentDir;
1663 cmd += L'"';
1665 CAppUtils::RunTortoiseGitProc(cmd);
1668 void CSyncDlg::OnOK()
1670 UpdateCombox();
1671 this->UpdateData();
1672 m_ctrlURL.SaveHistory();
1673 SaveHistory();
1674 m_regAutoLoadPutty = this->m_bAutoLoadPuttyKey;
1675 m_tooltips.Pop();
1676 __super::OnOK();
1679 void CSyncDlg::OnCancel()
1681 m_bAbort = true;
1682 m_GitProgressList.Cancel();
1683 if (m_bDone && !m_GitProgressList.IsRunning())
1685 CResizableStandAloneDialog::OnCancel();
1686 return;
1688 if (m_GitProgressList.IsRunning())
1689 WaitForSingleObject(m_GitProgressList.m_pThread->m_hThread, 10000);
1691 if (g_Git.m_CurrentGitPi.hProcess)
1693 DWORD dwConfirmKillProcess = CRegDWORD(L"Software\\TortoiseGit\\ConfirmKillProcess");
1694 if (dwConfirmKillProcess && CMessageBox::Show(m_hWnd, IDS_PROC_CONFIRMKILLPROCESS, IDS_APPNAME, MB_YESNO | MB_ICONQUESTION) != IDYES)
1695 return;
1696 if (::GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0))
1697 ::WaitForSingleObject(g_Git.m_CurrentGitPi.hProcess, 10000);
1699 CProgressDlg::KillProcessTree(g_Git.m_CurrentGitPi.dwProcessId);
1702 ::WaitForSingleObject(g_Git.m_CurrentGitPi.hProcess ,10000);
1703 if (m_pThread)
1705 if (::WaitForSingleObject(m_pThread->m_hThread, 5000) == WAIT_TIMEOUT)
1706 g_Git.KillRelatedThreads(m_pThread);
1708 m_tooltips.Pop();
1709 CResizableStandAloneDialog::OnCancel();
1712 void CSyncDlg::OnBnClickedButtonSubmodule()
1714 bool bShift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
1715 this->UpdateData();
1716 UpdateCombox();
1718 if (bShift)
1720 switch (m_ctrlSubmodule.GetCurrentEntry())
1722 case 0:
1723 case 1: // fall-through
1724 CAppUtils::RunTortoiseGitProc(L"/command:subupdate /bkpath:\"" + g_Git.m_CurrentDir + L"\"");
1725 break;
1726 case 2:
1727 CAppUtils::RunTortoiseGitProc(L"/command:subsync /bkpath:\"" + g_Git.m_CurrentDir + L"\"");
1728 break;
1730 return;
1733 m_ctrlCmdOut.SetWindowText(L"");
1734 m_LogText.Empty();
1736 this->m_regSubmoduleButton = (DWORD)this->m_ctrlSubmodule.GetCurrentEntry();
1738 this->SwitchToRun();
1740 this->m_bAbort=false;
1741 this->m_GitCmdList.clear();
1743 ShowTab(IDC_CMD_LOG);
1745 CString cmd;
1747 switch (m_ctrlSubmodule.GetCurrentEntry())
1749 case 0:
1750 cmd = L"git.exe submodule update --init";
1751 break;
1752 case 1:
1753 cmd = L"git.exe submodule init";
1754 break;
1755 case 2:
1756 cmd = L"git.exe submodule sync";
1757 break;
1760 m_GitCmdList.push_back(cmd);
1762 m_CurrentCmd = GIT_COMMAND_SUBMODULE;
1764 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
1765 if (!m_pThread)
1767 // ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
1769 else
1771 m_pThread->m_bAutoDelete = TRUE;
1772 m_pThread->ResumeThread();
1776 void CSyncDlg::OnBnClickedButtonStash()
1778 bool bShift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
1779 UpdateData();
1780 UpdateCombox();
1782 if (bShift)
1784 if (m_ctrlStash.GetCurrentEntry() == 0)
1785 CAppUtils::RunTortoiseGitProc(L"/command:stashsave");
1786 return;
1789 m_ctrlCmdOut.SetWindowText(L"");
1790 m_LogText.Empty();
1792 SwitchToRun();
1794 m_bAbort = false;
1795 m_GitCmdList.clear();
1797 ShowTab(IDC_CMD_LOG);
1799 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, false);
1800 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST -1, false);
1801 m_ctrlTabCtrl.ShowTab(IDC_IN_CONFLICT -1, false);
1803 CString cmd;
1804 switch (m_ctrlStash.GetCurrentEntry())
1806 case 0:
1807 cmd = L"git.exe stash push";
1808 if (!CAppUtils::IsGitVersionNewerOrEqual(GetSafeHwnd(), 2, 14))
1809 cmd = L"git.exe stash save";
1810 break;
1811 case 1:
1812 cmd = L"git.exe stash pop";
1813 break;
1814 case 2:
1815 cmd = L"git.exe stash apply";
1816 break;
1819 m_GitCmdList.push_back(cmd);
1820 m_CurrentCmd = GIT_COMMAND_STASH;
1822 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
1823 if (!m_pThread)
1825 //ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
1827 else
1829 m_pThread->m_bAutoDelete = TRUE;
1830 m_pThread->ResumeThread();
1834 void CSyncDlg::OnTimer(UINT_PTR nIDEvent)
1836 if( nIDEvent == IDT_INPUT)
1838 KillTimer(IDT_INPUT);
1839 this->FetchOutList(true);
1840 m_ctrlTabCtrl.ShowTab(IDC_TAGCOMPARELIST - 1, false);
1844 LRESULT CSyncDlg::OnTaskbarBtnCreated(WPARAM wParam, LPARAM lParam)
1846 m_pTaskbarList.Release();
1847 m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList);
1848 m_GitProgressList.m_pTaskbarList = m_pTaskbarList;
1849 return __super::OnTaskbarButtonCreated(wParam, lParam);
1852 void CSyncDlg::OnBnClickedCheckForce()
1854 UpdateData();
1857 void CSyncDlg::OnBnClickedLog()
1859 CString cmd = L"/command:log";
1860 cmd += L" /path:\"";
1861 cmd += g_Git.m_CurrentDir;
1862 cmd += L'"';
1864 CAppUtils::RunTortoiseGitProc(cmd);
1867 LRESULT CSyncDlg::OnProgCmdFinish(WPARAM /*wParam*/, LPARAM /*lParam*/)
1869 RefreshCursor();
1870 RunPostAction();
1871 return 0;
1874 void CSyncDlg::OnDestroy()
1876 m_bWantToExit = true;
1877 __super::OnDestroy();
1880 LRESULT CSyncDlg::OnThemeChanged()
1882 CMFCVisualManager::GetInstance()->DestroyInstance();
1883 return 0;
1886 void CSyncDlg::OnEnLinkLog(NMHDR *pNMHDR, LRESULT *pResult)
1888 // similar code in ProgressDlg.cpp and LogDlg.cpp
1889 ENLINK *pEnLink = reinterpret_cast<ENLINK *>(pNMHDR);
1890 if ((pEnLink->msg == WM_LBUTTONUP) || (pEnLink->msg == WM_SETCURSOR))
1892 CString msg;
1893 m_ctrlCmdOut.GetWindowText(msg);
1894 msg.Replace(L"\r\n", L"\n");
1895 CString url = msg.Mid(pEnLink->chrg.cpMin, pEnLink->chrg.cpMax - pEnLink->chrg.cpMin);
1896 // check if it's an email address
1897 auto atpos = url.Find(L'@');
1898 if ((atpos > 0) && (url.ReverseFind(L'.') > atpos) && !::PathIsURL(url))
1899 url = L"mailto:" + url;
1900 if (::PathIsURL(url))
1902 if (pEnLink->msg == WM_LBUTTONUP)
1903 ShellExecute(GetSafeHwnd(), L"open", url, nullptr, nullptr, SW_SHOWDEFAULT);
1904 else
1906 static RECT prevRect = { 0 };
1907 CWnd* pMsgView = &m_ctrlCmdOut;
1908 if (pMsgView)
1910 RECT rc;
1911 POINTL pt;
1912 pMsgView->SendMessage(EM_POSFROMCHAR, (WPARAM)&pt, pEnLink->chrg.cpMin);
1913 rc.left = pt.x;
1914 rc.top = pt.y;
1915 pMsgView->SendMessage(EM_POSFROMCHAR, (WPARAM)&pt, pEnLink->chrg.cpMax);
1916 rc.right = pt.x;
1917 rc.bottom = pt.y + 12;
1918 if ((prevRect.left != rc.left) || (prevRect.top != rc.top))
1920 m_tooltips.DelTool(pMsgView, 1);
1921 m_tooltips.AddTool(pMsgView, url, &rc, 1);
1922 prevRect = rc;
1928 *pResult = 0;
1931 void CSyncDlg::OnEnscrollLog()
1933 m_tooltips.DelTool(&m_ctrlCmdOut, 1);