Merge branch 'statdlg'
[TortoiseGit.git] / src / TortoiseProc / SyncDlg.cpp
blob240b254f2726a63fb8b8d49424b7214cd71df012
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2017 - 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"
36 // CSyncDlg dialog
38 IMPLEMENT_DYNAMIC(CSyncDlg, CResizableStandAloneDialog)
40 CSyncDlg::CSyncDlg(CWnd* pParent /*=nullptr*/)
41 : CResizableStandAloneDialog(CSyncDlg::IDD, pParent)
42 , m_iPullRebase(0)
43 , m_CurrentCmd(0)
44 , m_bInited(false)
45 , m_CmdOutCurrentPos(0)
46 , m_bAutoLoadPuttyKey(CAppUtils::IsSSHPutty())
47 , m_bForce(BST_UNCHECKED)
48 , m_bBlock(false)
49 , m_BufStart(0)
50 , m_pThread(nullptr)
51 , m_bAbort(false)
52 , m_bDone(false)
53 , m_bWantToExit(false)
54 , m_GitCmdStatus(-1)
55 , m_startTick(GetTickCount64())
56 , m_seq(0)
58 m_pTooltip = &m_tooltips;
61 CSyncDlg::~CSyncDlg()
65 void CSyncDlg::DoDataExchange(CDataExchange* pDX)
67 CDialog::DoDataExchange(pDX);
68 DDX_Check(pDX, IDC_CHECK_PUTTY_KEY, m_bAutoLoadPuttyKey);
69 DDX_Check(pDX, IDC_CHECK_FORCE,m_bForce);
70 DDX_Control(pDX, IDC_COMBOBOXEX_URL, m_ctrlURL);
71 DDX_Control(pDX, IDC_BUTTON_TABCTRL, m_ctrlDumyButton);
72 DDX_Control(pDX, IDC_BUTTON_PULL, m_ctrlPull);
73 DDX_Control(pDX, IDC_BUTTON_PUSH, m_ctrlPush);
74 DDX_Control(pDX, IDC_STATIC_STATUS, m_ctrlStatus);
75 DDX_Control(pDX, IDC_PROGRESS_SYNC, m_ctrlProgress);
76 DDX_Control(pDX, IDC_ANIMATE_SYNC, m_ctrlAnimate);
77 DDX_Control(pDX, IDC_BUTTON_SUBMODULE,m_ctrlSubmodule);
78 DDX_Control(pDX, IDC_BUTTON_STASH, m_ctrlStash);
79 DDX_Control(pDX, IDC_PROG_LABEL, m_ctrlProgLabel);
80 BRANCH_COMBOX_DDX;
83 BEGIN_MESSAGE_MAP(CSyncDlg, CResizableStandAloneDialog)
84 ON_BN_CLICKED(IDC_BUTTON_PULL, &CSyncDlg::OnBnClickedButtonPull)
85 ON_BN_CLICKED(IDC_BUTTON_PUSH, &CSyncDlg::OnBnClickedButtonPush)
86 ON_BN_CLICKED(IDC_BUTTON_APPLY, &CSyncDlg::OnBnClickedButtonApply)
87 ON_BN_CLICKED(IDC_BUTTON_EMAIL, &CSyncDlg::OnBnClickedButtonEmail)
88 ON_BN_CLICKED(IDC_BUTTON_MANAGE, &CSyncDlg::OnBnClickedButtonManage)
89 BRANCH_COMBOX_EVENT
90 ON_CBN_EDITCHANGE(IDC_COMBOBOXEX_URL, &CSyncDlg::OnCbnEditchangeComboboxex)
91 ON_CBN_EDITCHANGE(IDC_COMBOBOXEX_REMOTE_BRANCH, &CSyncDlg::OnCbnEditchangeComboboxex)
92 ON_MESSAGE(MSG_PROGRESSDLG_UPDATE_UI, OnProgressUpdateUI)
93 ON_MESSAGE(WM_PROG_CMD_FINISH, OnProgCmdFinish)
94 ON_BN_CLICKED(IDC_BUTTON_COMMIT, &CSyncDlg::OnBnClickedButtonCommit)
95 ON_BN_CLICKED(IDC_BUTTON_SUBMODULE, &CSyncDlg::OnBnClickedButtonSubmodule)
96 ON_BN_CLICKED(IDC_BUTTON_STASH, &CSyncDlg::OnBnClickedButtonStash)
97 ON_WM_TIMER()
98 ON_REGISTERED_MESSAGE(TaskBarButtonCreated, OnTaskbarBtnCreated)
99 ON_BN_CLICKED(IDC_CHECK_FORCE, &CSyncDlg::OnBnClickedCheckForce)
100 ON_BN_CLICKED(IDC_LOG, &CSyncDlg::OnBnClickedLog)
101 ON_WM_DESTROY()
102 ON_WM_THEMECHANGED()
103 END_MESSAGE_MAP()
105 void CSyncDlg::EnableControlButton(bool bEnabled)
107 GetDlgItem(IDC_BUTTON_PULL)->EnableWindow(bEnabled);
108 GetDlgItem(IDC_BUTTON_PUSH)->EnableWindow(bEnabled);
109 GetDlgItem(IDC_BUTTON_APPLY)->EnableWindow(bEnabled);
110 GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(bEnabled);
111 GetDlgItem(IDOK)->EnableWindow(bEnabled);
112 GetDlgItem(IDC_BUTTON_SUBMODULE)->EnableWindow(bEnabled);
113 GetDlgItem(IDC_BUTTON_STASH)->EnableWindow(bEnabled);
115 // CSyncDlg message handlers
117 bool CSyncDlg::AskSetTrackedBranch()
119 CString remote, remoteBranch;
120 g_Git.GetRemoteTrackedBranch(m_strLocalBranch, remote, remoteBranch);
121 if (remoteBranch.IsEmpty())
123 remoteBranch = m_strRemoteBranch;
124 if (remoteBranch.IsEmpty())
125 remoteBranch = m_strLocalBranch;
126 CString temp;
127 temp.Format(IDS_NOTYET_SETTRACKEDBRANCH, (LPCTSTR)m_strLocalBranch, (LPCTSTR)remoteBranch);
128 BOOL dontShowAgain = FALSE;
129 auto ret = CMessageBox::ShowCheck(GetSafeHwnd(), temp, L"TortoiseGit", MB_ICONQUESTION | MB_YESNOCANCEL, nullptr, CString(MAKEINTRESOURCE(IDS_MSGBOX_DONOTSHOW)), &dontShowAgain);
130 if (dontShowAgain)
131 CRegDWORD(L"Software\\TortoiseGit\\AskSetTrackedBranch") = FALSE;
132 if (ret == IDCANCEL)
133 return false;
134 if (ret == IDYES)
136 CString key;
137 key.Format(L"branch.%s.remote", (LPCTSTR)m_strLocalBranch);
138 g_Git.SetConfigValue(key, m_strURL);
139 key.Format(L"branch.%s.merge", (LPCTSTR)m_strLocalBranch);
140 g_Git.SetConfigValue(key, L"refs/heads/" + g_Git.StripRefName(remoteBranch));
143 return true;
146 void CSyncDlg::OnBnClickedButtonPull()
148 int CurrentEntry;
149 CurrentEntry = (int)this->m_ctrlPull.GetCurrentEntry();
150 this->m_regPullButton = CurrentEntry;
152 this->m_bAbort=false;
153 this->m_GitCmdList.clear();
154 m_ctrlCmdOut.SetWindowText(L"");
155 m_LogText.Empty();
157 this->UpdateData();
158 UpdateCombox();
160 if (g_Git.GetHash(m_oldHash, L"HEAD"))
162 MessageBox(g_Git.GetGitLastErr(L"Could not get HEAD hash."), L"TortoiseGit", MB_ICONERROR);
163 return;
166 m_refList.Clear();
167 m_newHashMap.clear();
168 m_oldHashMap.clear();
170 if( CurrentEntry == 0)
172 CGitHash localBranchHash;
173 if (g_Git.GetHash(localBranchHash, m_strLocalBranch))
175 MessageBox(g_Git.GetGitLastErr(L"Could not get hash of \"" + m_strLocalBranch + L"\"."), L"TortoiseGit", MB_ICONERROR);
176 return;
178 if (localBranchHash != m_oldHash)
180 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_SYNC_PULLWRONGBRANCH, IDS_APPNAME, MB_OK | MB_ICONERROR);
181 return;
185 if(this->m_strURL.IsEmpty())
187 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_GITCONFIG_URLEMPTY, IDS_APPNAME, MB_OK | MB_ICONERROR);
188 return;
191 if (!IsURL() && !m_strRemoteBranch.IsEmpty() && CurrentEntry == 0 && CRegDWORD(L"Software\\TortoiseGit\\AskSetTrackedBranch", TRUE) == TRUE)
193 if (!AskSetTrackedBranch())
194 return;
197 if (m_bAutoLoadPuttyKey && CurrentEntry != 4) // CurrentEntry (Remote Update) handles this on its own)
199 CAppUtils::LaunchPAgent(nullptr, &m_strURL);
202 if (g_Git.GetMapHashToFriendName(m_oldHashMap))
203 MessageBox(g_Git.GetGitLastErr(L"Could not get all refs."), L"TortoiseGit", MB_ICONERROR);
205 CString force;
206 if(this->m_bForce)
207 force = L" --force";
209 CString cmd;
211 m_iPullRebase = 0;
212 if (CurrentEntry == 0 && CRegDWORD(L"Software\\TortoiseGit\\PullRebaseBehaviorLike1816", FALSE) == FALSE) // check whether we need to override Pull if pull.rebase is set
214 CAutoRepository repo(g_Git.GetGitRepository());
215 if (!repo)
216 MessageBox(CGit::GetLibGit2LastErr(L"Could not open repository."), L"TortoiseGit", MB_OK | MB_ICONERROR);
218 // Check config branch.<name>.rebase and pull.reabse
221 if (!repo)
222 break;
224 if (git_repository_head_detached(repo) == 1)
225 break;
227 CAutoConfig config(true);
228 if (git_repository_config(config.GetPointer(), repo))
229 break;
231 // branch.<name>.rebase overrides pull.rebase
232 if (config.GetBOOL(L"branch." + g_Git.GetCurrentBranch() + L".rebase", m_iPullRebase) == GIT_ENOTFOUND)
234 if (config.GetBOOL(L"pull.rebase", m_iPullRebase) == GIT_ENOTFOUND)
235 break;
236 else
238 CString value;
239 config.GetString(L"pull.rebase", value);
240 if (value == L"preserve")
242 m_iPullRebase = 2;
243 break;
247 else
249 CString value;
250 config.GetString(L"branch." + g_Git.GetCurrentBranch() + L".rebase", value);
251 if (value == L"preserve")
253 m_iPullRebase = 2;
254 break;
257 } while (0);
258 if (m_iPullRebase > 0)
260 CurrentEntry = 1;
261 if (m_strRemoteBranch.IsEmpty())
263 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_PULL_EMPTYBRANCH, IDS_APPNAME, MB_ICONEXCLAMATION);
264 return;
269 SwitchToRun();
271 ShowTab(IDC_CMD_LOG);
273 m_ctrlTabCtrl.ShowTab(IDC_REFLIST - 1, true);
274 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, false);
275 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST - 1, false);
276 m_ctrlTabCtrl.ShowTab(IDC_IN_CONFLICT - 1, false);
278 ///Pull
279 if(CurrentEntry == 0) //Pull
281 CString remotebranch;
282 remotebranch = m_strRemoteBranch;
284 if(!IsURL())
286 CString pullRemote, pullBranch;
287 g_Git.GetRemoteTrackedBranch(m_strLocalBranch, pullRemote, pullBranch);
288 if(pullBranch == remotebranch && pullRemote == this->m_strURL)
289 remotebranch.Empty();
292 cmd.Format(L"git.exe pull -v --progress%s \"%s\" %s",
293 (LPCTSTR)force,
294 (LPCTSTR)m_strURL,
295 (LPCTSTR)remotebranch);
297 m_CurrentCmd = GIT_COMMAND_PULL;
298 m_GitCmdList.push_back(cmd);
300 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
301 if (!m_pThread)
303 // ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
305 else
307 m_pThread->m_bAutoDelete = TRUE;
308 m_pThread->ResumeThread();
313 ///Fetch
314 if (CurrentEntry == 1 || CurrentEntry == 2 || CurrentEntry == 3)
316 m_oldRemoteHash.Empty();
317 CString remotebranch;
318 if (CurrentEntry == 3)
319 m_strRemoteBranch.Empty();
320 else if (IsURL() || m_strRemoteBranch.IsEmpty())
322 remotebranch=this->m_strRemoteBranch;
325 else
327 remotebranch.Format(L"remotes/%s/%s", (LPCTSTR)m_strURL, (LPCTSTR)m_strRemoteBranch);
328 g_Git.GetHash(m_oldRemoteHash, remotebranch);
329 if (m_oldRemoteHash.IsEmpty())
330 remotebranch=m_strRemoteBranch;
331 else
332 remotebranch = m_strRemoteBranch + L':' + remotebranch;
335 if (CurrentEntry == 1 || CurrentEntry == 3)
336 m_CurrentCmd = GIT_COMMAND_FETCH;
337 else
338 m_CurrentCmd = GIT_COMMAND_FETCHANDREBASE;
340 if (g_Git.UsingLibGit2(CGit::GIT_CMD_FETCH))
342 CString refspec;
343 if (!remotebranch.IsEmpty())
344 refspec.Format(L"refs/heads/%s:refs/remotes/%s/%s", (LPCTSTR)m_strRemoteBranch, (LPCTSTR)m_strURL, (LPCTSTR)m_strRemoteBranch);
346 progressCommand = std::make_unique<FetchProgressCommand>();
347 FetchProgressCommand* fetchProgressCommand = static_cast<FetchProgressCommand*>(progressCommand.get());
348 fetchProgressCommand->SetUrl(m_strURL);
349 fetchProgressCommand->SetRefSpec(refspec);
350 m_GitProgressList.SetCommand(progressCommand.get());
351 m_GitProgressList.Init();
352 ShowTab(IDC_CMD_GIT_PROG);
354 else
356 cmd.Format(L"git.exe fetch --progress -v%s \"%s\" %s",
357 (LPCTSTR)force,
358 (LPCTSTR)m_strURL,
359 (LPCTSTR)remotebranch);
361 m_GitCmdList.push_back(cmd);
363 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
364 if (!m_pThread)
366 // ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
368 else
370 m_pThread->m_bAutoDelete = TRUE;
371 m_pThread->ResumeThread();
376 ///Remote Update
377 if (CurrentEntry == 4)
379 if (m_bAutoLoadPuttyKey)
381 STRING_VECTOR list;
382 if (!g_Git.GetRemoteList(list))
384 for (size_t i = 0; i < list.size(); ++i)
385 CAppUtils::LaunchPAgent(nullptr, &list[i]);
389 m_CurrentCmd = GIT_COMMAND_REMOTE;
390 cmd = L"git.exe remote update";
391 m_GitCmdList.push_back(cmd);
393 InterlockedExchange(&m_bBlock, TRUE);
395 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
396 if (!m_pThread)
398 // ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
399 InterlockedExchange(&m_bBlock, FALSE);
401 else
403 m_pThread->m_bAutoDelete = TRUE;
404 m_pThread->ResumeThread();
408 ///Cleanup stale remote banches
409 if (CurrentEntry == 5)
411 m_CurrentCmd = GIT_COMMAND_REMOTE;
412 cmd.Format(L"git.exe remote prune \"%s\"", (LPCTSTR)m_strURL);
413 m_GitCmdList.push_back(cmd);
415 InterlockedExchange(&m_bBlock, TRUE);
417 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
418 if (!m_pThread)
420 // ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
421 InterlockedExchange(&m_bBlock, FALSE);
423 else
425 m_pThread->m_bAutoDelete = TRUE;
426 m_pThread->ResumeThread();
431 void CSyncDlg::ShowInCommits(const CString& friendname)
433 CGitHash newHash;
435 if (g_Git.GetHash(newHash, friendname))
437 MessageBox(g_Git.GetGitLastErr(L"Could not get " + friendname + L" hash."), L"TortoiseGit", MB_ICONERROR);
438 return;
441 if (newHash == m_oldHash)
443 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST - 1, false);
444 m_InLogList.ShowText(CString(MAKEINTRESOURCE(IDS_UPTODATE)));
445 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, true);
446 ShowTab(IDC_REFLIST);
448 else
450 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST - 1, true);
451 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, true);
453 AddDiffFileList(&m_InChangeFileList, &m_arInChangeList, newHash.ToString(), m_oldHash.ToString());
455 CString range;
456 range.Format(L"%s..%s", (LPCTSTR)m_oldHash.ToString(), (LPCTSTR)newHash.ToString());
457 m_InLogList.FillGitLog(nullptr, &range, CGit::LOG_INFO_STAT | CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
458 ShowTab(IDC_IN_LOGLIST);
462 void CSyncDlg::PullComplete()
464 EnableControlButton(true);
465 SwitchToInput();
466 this->FetchOutList(true);
468 if( this ->m_GitCmdStatus )
470 int hasConflicts = g_Git.HasWorkingTreeConflicts();
471 if (hasConflicts < 0)
473 this->m_ctrlCmdOut.SetSel(-1,-1);
474 this->m_ctrlCmdOut.ReplaceSel(g_Git.GetGitLastErr(L"Checking for conflicts failed.", CGit::GIT_CMD_CHECKCONFLICTS));
476 this->ShowTab(IDC_CMD_LOG);
477 return;
480 if (hasConflicts)
482 this->m_ConflictFileList.Clear();
483 CTGitPathList list;
484 CTGitPath path;
485 list.AddPath(path);
487 this->m_ConflictFileList.GetStatus(&list,true);
488 this->m_ConflictFileList.Show(CTGitPath::LOGACTIONS_UNMERGED,
489 CTGitPath::LOGACTIONS_UNMERGED);
491 this->ShowTab(IDC_IN_CONFLICT);
493 else
494 this->ShowTab(IDC_CMD_LOG);
497 else
498 ShowInCommits(L"HEAD");
501 void CSyncDlg::FetchComplete()
503 EnableControlButton(true);
504 SwitchToInput();
506 if (g_Git.UsingLibGit2(CGit::GIT_CMD_FETCH))
507 ShowTab(IDC_CMD_GIT_PROG);
508 else
509 ShowTab(IDC_REFLIST);
511 if (m_GitCmdStatus || (m_CurrentCmd != GIT_COMMAND_FETCHANDREBASE && m_iPullRebase == 0))
513 FetchOutList(true);
514 return;
517 CString remote;
518 CString remotebranch;
519 CString upstream = L"FETCH_HEAD";
520 m_ctrlURL.GetWindowText(remote);
521 if (!remote.IsEmpty())
523 STRING_VECTOR remotes;
524 g_Git.GetRemoteList(remotes);
525 if (std::find(remotes.cbegin(), remotes.cend(), remote) == remotes.cend())
526 remote.Empty();
528 m_ctrlRemoteBranch.GetWindowText(remotebranch);
529 if (!remote.IsEmpty() && !remotebranch.IsEmpty())
530 upstream = L"remotes/" + remote + L'/' + remotebranch;
532 if (m_iPullRebase > 0)
534 CAppUtils::RebaseAfterFetch(upstream, m_iPullRebase ? 2 : 0, m_iPullRebase == 2);
535 FillNewRefMap();
536 FetchOutList(true);
538 ShowInCommits(L"HEAD");
540 return;
543 CGitHash remoteBranchHash;
544 g_Git.GetHash(remoteBranchHash, upstream);
545 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)
547 ShowInCommits(upstream);
548 return;
551 if (g_Git.IsFastForward(L"HEAD", upstream))
553 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);
554 if (ret == 3)
555 return;
556 if (ret == 1)
558 CProgressDlg mergeProgress;
559 mergeProgress.m_GitCmd = L"git.exe merge --ff-only " + upstream;
560 mergeProgress.m_AutoClose = AUTOCLOSE_IF_NO_ERRORS;
561 mergeProgress.m_PostCmdCallback = [](DWORD status, PostCmdList& postCmdList)
563 if (status && g_Git.HasWorkingTreeConflicts())
565 // there are conflict files
566 postCmdList.emplace_back(IDI_RESOLVE, IDS_PROGRS_CMD_RESOLVE, []
568 CString sCmd;
569 sCmd.Format(L"/command:commit /path:\"%s\"", (LPCTSTR)g_Git.m_CurrentDir);
570 CAppUtils::RunTortoiseGitProc(sCmd);
574 mergeProgress.DoModal();
575 FillNewRefMap();
576 FetchOutList(true);
578 ShowInCommits(L"HEAD");
580 return;
584 CAppUtils::RebaseAfterFetch(upstream);
585 FillNewRefMap();
586 FetchOutList(true);
588 ShowInCommits(L"HEAD");
591 void CSyncDlg::StashComplete()
593 EnableControlButton(true);
594 INT_PTR entry = m_ctrlStash.GetCurrentEntry();
595 if (entry != 1 && entry != 2)
596 return;
598 SwitchToInput();
599 if (m_GitCmdStatus)
601 int hasConflicts = g_Git.HasWorkingTreeConflicts();
602 if (hasConflicts < 0)
604 m_ctrlCmdOut.SetSel(-1, -1);
605 m_ctrlCmdOut.ReplaceSel(g_Git.GetGitLastErr(L"Checking for conflicts failed.", CGit::GIT_CMD_CHECKCONFLICTS));
607 ShowTab(IDC_CMD_LOG);
608 return;
611 if (hasConflicts)
613 m_ConflictFileList.Clear();
614 CTGitPathList list;
615 CTGitPath path;
616 list.AddPath(path);
618 m_ConflictFileList.GetStatus(&list,true);
619 m_ConflictFileList.Show(CTGitPath::LOGACTIONS_UNMERGED, CTGitPath::LOGACTIONS_UNMERGED);
621 ShowTab(IDC_IN_CONFLICT);
623 else
624 ShowTab(IDC_CMD_LOG);
628 void CSyncDlg::OnBnClickedButtonPush()
630 this->UpdateData();
631 UpdateCombox();
632 m_ctrlCmdOut.SetWindowText(L"");
633 m_LogText.Empty();
635 if(this->m_strURL.IsEmpty())
637 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_GITCONFIG_URLEMPTY, IDS_APPNAME, MB_OK | MB_ICONERROR);
638 return;
641 if (!IsURL() && m_ctrlPush.GetCurrentEntry() == 0 && CRegDWORD(L"Software\\TortoiseGit\\AskSetTrackedBranch", TRUE) == TRUE)
643 if (!AskSetTrackedBranch())
644 return;
647 this->m_regPushButton=(DWORD)this->m_ctrlPush.GetCurrentEntry();
648 this->SwitchToRun();
649 this->m_bAbort=false;
650 this->m_GitCmdList.clear();
652 ShowTab(IDC_CMD_LOG);
654 CString cmd;
655 CString arg;
657 CString error;
658 DWORD exitcode = 0xFFFFFFFF;
659 if (CHooks::Instance().PrePush(g_Git.m_CurrentDir, exitcode, error))
661 if (exitcode)
663 CString temp;
664 temp.Format(IDS_ERR_HOOKFAILED, (LPCTSTR)error);
665 CMessageBox::Show(GetSafeHwnd(), temp, L"TortoiseGit", MB_OK | MB_ICONERROR);
666 return ;
670 CString refName = g_Git.FixBranchName(m_strLocalBranch);
671 switch (m_ctrlPush.GetCurrentEntry())
673 case 1:
674 arg += L" --tags";
675 break;
676 case 2:
677 refName = L"refs/notes/commits"; //default ref for notes
678 break;
681 if(this->m_bForce)
682 arg += L" --force";
684 cmd.Format(L"git.exe push -v --progress%s \"%s\" %s",
685 (LPCTSTR)arg,
686 (LPCTSTR)m_strURL,
687 (LPCTSTR)refName);
689 if (!m_strRemoteBranch.IsEmpty() && m_ctrlPush.GetCurrentEntry() != 2)
691 cmd += L':' + m_strRemoteBranch;
694 m_GitCmdList.push_back(cmd);
696 m_CurrentCmd = GIT_COMMAND_PUSH;
698 if(this->m_bAutoLoadPuttyKey)
700 CAppUtils::LaunchPAgent(nullptr, &m_strURL);
703 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
704 if (!m_pThread)
706 // ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
708 else
710 m_pThread->m_bAutoDelete = TRUE;
711 m_pThread->ResumeThread();
715 void CSyncDlg::OnBnClickedButtonApply()
717 CGitHash oldhash;
718 if (g_Git.GetHash(oldhash, L"HEAD"))
720 MessageBox(g_Git.GetGitLastErr(L"Could not get HEAD hash."), L"TortoiseGit", MB_ICONERROR);
721 return;
724 CImportPatchDlg dlg;
725 CString cmd,output;
727 if(dlg.DoModal() == IDOK)
729 int err=0;
730 for (int i = 0; i < dlg.m_PathList.GetCount(); ++i)
732 cmd.Format(L"git.exe am \"%s\"", (LPCTSTR)dlg.m_PathList[i].GetGitPathString());
734 if (g_Git.Run(cmd, &output, CP_UTF8))
736 CMessageBox::Show(GetSafeHwnd(), output, L"TortoiseGit", MB_OK | MB_ICONERROR);
738 err=1;
739 break;
741 this->m_ctrlCmdOut.SetSel(-1,-1);
742 this->m_ctrlCmdOut.ReplaceSel(cmd + L'\n');
743 this->m_ctrlCmdOut.SetSel(-1,-1);
744 this->m_ctrlCmdOut.ReplaceSel(output);
747 CGitHash newhash;
748 if (g_Git.GetHash(newhash, L"HEAD"))
750 MessageBox(g_Git.GetGitLastErr(L"Could not get HEAD hash after applying patches."), L"TortoiseGit", MB_ICONERROR);
751 return;
754 this->m_InLogList.Clear();
755 this->m_InChangeFileList.Clear();
757 if(newhash == oldhash)
759 this->m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST-1,false);
760 this->m_InLogList.ShowText(L"No commits get from patch");
761 this->m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST-1,true);
764 else
766 this->m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST-1,true);
767 this->m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST-1,true);
769 CString range;
770 range.Format(L"%s..%s", (LPCTSTR)m_oldHash.ToString(), (LPCTSTR)newhash.ToString());
771 this->AddDiffFileList(&m_InChangeFileList, &m_arInChangeList, newhash.ToString(), oldhash.ToString());
772 m_InLogList.FillGitLog(nullptr, &range, CGit::LOG_INFO_STAT| CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
774 this->FetchOutList(true);
777 this->m_ctrlTabCtrl.ShowTab(IDC_CMD_LOG-1,true);
779 if(err)
781 this->ShowTab(IDC_CMD_LOG);
783 else
785 this->ShowTab(IDC_IN_LOGLIST);
790 void CSyncDlg::OnBnClickedButtonEmail()
792 CString cmd, out, err;
794 this->m_strLocalBranch = this->m_ctrlLocalBranch.GetString();
795 this->m_ctrlRemoteBranch.GetWindowText(this->m_strRemoteBranch);
796 this->m_ctrlURL.GetWindowText(this->m_strURL);
797 m_strURL=m_strURL.Trim();
798 m_strRemoteBranch=m_strRemoteBranch.Trim();
800 cmd.Format(L"git.exe format-patch -o \"%s\" %s..%s",
801 (LPCTSTR)g_Git.m_CurrentDir,
802 (LPCTSTR)(m_strURL + L'/' + m_strRemoteBranch), (LPCTSTR)g_Git.FixBranchName(m_strLocalBranch));
804 if (g_Git.Run(cmd, &out, &err, CP_UTF8))
806 CMessageBox::Show(GetSafeHwnd(), out + L'\n' + err, L"TortoiseGit", MB_OK | MB_ICONERROR);
807 return ;
810 CAppUtils::SendPatchMail(cmd,out);
812 void CSyncDlg::ShowProgressCtrl(bool bShow)
814 int b=bShow?SW_NORMAL:SW_HIDE;
815 this->m_ctrlAnimate.ShowWindow(b);
816 this->m_ctrlProgress.ShowWindow(b);
817 this->m_ctrlProgLabel.ShowWindow(b);
818 this->m_ctrlAnimate.Open(IDR_DOWNLOAD);
819 if(b == SW_NORMAL)
820 this->m_ctrlAnimate.Play(0, UINT_MAX, UINT_MAX);
821 else
822 this->m_ctrlAnimate.Stop();
824 void CSyncDlg::ShowInputCtrl(bool bShow)
826 int b=bShow?SW_NORMAL:SW_HIDE;
827 this->m_ctrlURL.ShowWindow(b);
828 this->m_ctrlLocalBranch.ShowWindow(b);
829 this->m_ctrlRemoteBranch.ShowWindow(b);
830 this->GetDlgItem(IDC_BUTTON_LOCAL_BRANCH)->ShowWindow(b);
831 this->GetDlgItem(IDC_BUTTON_REMOTE_BRANCH)->ShowWindow(b);
832 this->GetDlgItem(IDC_STATIC_LOCAL_BRANCH)->ShowWindow(b);
833 this->GetDlgItem(IDC_STATIC_REMOTE_BRANCH)->ShowWindow(b);
834 this->GetDlgItem(IDC_BUTTON_MANAGE)->ShowWindow(b);
835 this->GetDlgItem(IDC_CHECK_PUTTY_KEY)->ShowWindow(b);
836 this->GetDlgItem(IDC_CHECK_FORCE)->ShowWindow(b);
837 this->GetDlgItem(IDC_STATIC_REMOTE_URL)->ShowWindow(b);
839 BOOL CSyncDlg::OnInitDialog()
841 CResizableStandAloneDialog::OnInitDialog();
842 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
844 // Let the TaskbarButtonCreated message through the UIPI filter. If we don't
845 // do this, Explorer would be unable to send that message to our window if we
846 // were running elevated. It's OK to make the call all the time, since if we're
847 // not elevated, this is a no-op.
848 CHANGEFILTERSTRUCT cfs = { sizeof(CHANGEFILTERSTRUCT) };
849 typedef BOOL STDAPICALLTYPE ChangeWindowMessageFilterExDFN(HWND hWnd, UINT message, DWORD action, PCHANGEFILTERSTRUCT pChangeFilterStruct);
850 CAutoLibrary hUser = AtlLoadSystemLibraryUsingFullPath(L"user32.dll");
851 if (hUser)
853 ChangeWindowMessageFilterExDFN *pfnChangeWindowMessageFilterEx = (ChangeWindowMessageFilterExDFN*)GetProcAddress(hUser, "ChangeWindowMessageFilterEx");
854 if (pfnChangeWindowMessageFilterEx)
856 pfnChangeWindowMessageFilterEx(m_hWnd, TaskBarButtonCreated, MSGFLT_ALLOW, &cfs);
859 m_pTaskbarList.Release();
860 if (FAILED(m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList)))
861 m_pTaskbarList = nullptr;
863 this->GetDlgItem(IDC_CHECK_PUTTY_KEY)->EnableWindow(CAppUtils::IsSSHPutty());
866 this->m_ctrlAnimate.ShowWindow(SW_NORMAL);
867 this->m_ctrlAnimate.Open(IDR_DOWNLOAD);
868 this->m_ctrlAnimate.Play(0,-1,-1);
871 // ------------------ Create Tabctrl -----------
872 CWnd *pwnd=this->GetDlgItem(IDC_BUTTON_TABCTRL);
873 CRect rectDummy;
874 pwnd->GetWindowRect(&rectDummy);
875 this->ScreenToClient(rectDummy);
877 if (!m_ctrlTabCtrl.Create(CMFCTabCtrl::STYLE_FLAT, rectDummy, this, IDC_SYNC_TAB))
879 TRACE0("Failed to create output tab window\n");
880 return FALSE; // fail to create
882 m_ctrlTabCtrl.SetResizeMode(CMFCTabCtrl::RESIZE_NO);
884 // -------------Create Command Log Ctrl ---------
885 DWORD dwStyle;
886 dwStyle= ES_MULTILINE | ES_READONLY | WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_AUTOVSCROLL |WS_VSCROLL ;
888 if( !m_ctrlCmdOut.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_CMD_LOG))
890 TRACE0("Failed to create Log commits window\n");
891 return FALSE; // fail to create
894 // set the font to use in the log message view, configured in the settings dialog
895 CFont m_logFont;
896 CAppUtils::CreateFontForLogs(m_logFont);
897 m_ctrlCmdOut.SetFont(&m_logFont);
898 m_ctrlTabCtrl.InsertTab(&m_ctrlCmdOut, CString(MAKEINTRESOURCE(IDS_LOG)), -1);
899 // make the log message rich edit control send a message when the mouse pointer is over a link
900 m_ctrlCmdOut.SendMessage(EM_SETEVENTMASK, NULL, ENM_LINK | ENM_SCROLL);
902 //---------- Create in coming list ctrl -----------
903 dwStyle =LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;;
905 if( !m_InLogList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_IN_LOGLIST))
907 TRACE0("Failed to create output commits window\n");
908 return FALSE; // fail to create
910 // for some unknown reason, the SetExtendedStyle in OnCreate/PreSubclassWindow is not working here
911 m_InLogList.SetStyle();
913 m_ctrlTabCtrl.InsertTab(&m_InLogList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_INCOMMITS)), -1);
915 m_InLogList.m_ColumnRegKey = L"SyncIn";
916 m_InLogList.InsertGitColumn();
918 //----------- Create In Change file list -----------
919 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
921 if( !m_InChangeFileList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_IN_CHANGELIST))
923 TRACE0("Failed to create output change files window\n");
924 return FALSE; // fail to create
926 m_ctrlTabCtrl.InsertTab(&m_InChangeFileList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_INCHANGELIST)), -1);
928 m_InChangeFileList.Init(GITSLC_COLEXT | GITSLC_COLSTATUS |GITSLC_COLADD|GITSLC_COLDEL, L"InSyncDlg",
929 (CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_COMPARETWOREVISIONS) |
930 CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_GNUDIFF2REVISIONS)), false, false, GITSLC_COLEXT | GITSLC_COLSTATUS | GITSLC_COLADD | GITSLC_COLDEL);
933 //---------- Create Conflict List Ctrl -----------------
934 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
936 if( !m_ConflictFileList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_IN_CONFLICT))
938 TRACE0("Failed to create output change files window\n");
939 return FALSE; // fail to create
941 m_ctrlTabCtrl.InsertTab(&m_ConflictFileList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_CONFLICTS)), -1);
943 m_ConflictFileList.Init(GITSLC_COLEXT | GITSLC_COLSTATUS |GITSLC_COLADD|GITSLC_COLDEL, L"ConflictSyncDlg",
944 (GITSLC_POPEXPLORE | GITSLC_POPOPEN | GITSLC_POPSHOWLOG |
945 GITSLC_POPCONFLICT|GITSLC_POPRESOLVE),false);
948 //---------- Create Commit Out List Ctrl---------------
950 dwStyle =LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;;
952 if( !m_OutLogList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_OUT_LOGLIST))
954 TRACE0("Failed to create output commits window\n");
955 return FALSE; // fail to create
958 // for some unknown reason, the SetExtendedStyle in OnCreate/PreSubclassWindow is not working here
959 m_OutLogList.SetStyle();
961 m_ctrlTabCtrl.InsertTab(&m_OutLogList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_OUTCOMMITS)), -1);
963 m_OutLogList.m_ColumnRegKey = L"SyncOut";
964 m_OutLogList.InsertGitColumn();
966 //------------- Create Change File List Control ----------------
968 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
970 if( !m_OutChangeFileList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_OUT_CHANGELIST))
972 TRACE0("Failed to create output change files window\n");
973 return FALSE; // fail to create
975 m_ctrlTabCtrl.InsertTab(&m_OutChangeFileList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_OUTCHANGELIST)), -1);
977 m_OutChangeFileList.Init(GITSLC_COLEXT | GITSLC_COLSTATUS | GITSLC_COLADD | GITSLC_COLDEL, L"OutSyncDlg",
978 (CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_COMPARETWOREVISIONS) |
979 CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_GNUDIFF2REVISIONS)), false, false, GITSLC_COLEXT | GITSLC_COLSTATUS | GITSLC_COLADD | GITSLC_COLDEL);
981 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | LVS_SINGLESEL | WS_CHILD | WS_VISIBLE;
982 if (!m_GitProgressList.Create(dwStyle | LVS_OWNERDATA, rectDummy, &m_ctrlTabCtrl, IDC_CMD_GIT_PROG))
984 TRACE0("Failed to create Git Progress List Window\n");
985 return FALSE; // fail to create
987 m_ctrlTabCtrl.InsertTab(&m_GitProgressList, CString(MAKEINTRESOURCE(IDS_LOG)), -1);
988 m_GitProgressList.m_pAnimate = &m_ctrlAnimate;
989 m_GitProgressList.m_pPostWnd = this;
990 m_GitProgressList.m_pProgressLabelCtrl = &m_ctrlProgLabel;
991 m_GitProgressList.m_pProgControl = &m_ctrlProgress;
992 m_GitProgressList.m_pTaskbarList = m_pTaskbarList;
994 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE | LVS_SINGLESEL;
995 DWORD exStyle = LVS_EX_HEADERDRAGDROP | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP;
996 if (CRegDWORD(L"Software\\TortoiseGit\\FullRowSelect", TRUE))
997 exStyle |= LVS_EX_FULLROWSELECT;
998 if (g_Git.m_IsUseLibGit2)
1000 m_refList.Create(dwStyle, rectDummy, &m_ctrlTabCtrl, IDC_REFLIST);
1001 m_refList.SetExtendedStyle(exStyle);
1002 m_refList.Init();
1003 m_ctrlTabCtrl.InsertTab(&m_refList, CString(MAKEINTRESOURCE(IDS_REFLIST)), -1);
1006 AdjustControlSize(IDC_CHECK_PUTTY_KEY);
1007 AdjustControlSize(IDC_CHECK_FORCE);
1009 AddAnchor(IDC_SYNC_TAB,TOP_LEFT,BOTTOM_RIGHT);
1011 AddAnchor(IDC_GROUP_INFO,TOP_LEFT,TOP_RIGHT);
1012 AddAnchor(IDC_COMBOBOXEX_URL,TOP_LEFT,TOP_RIGHT);
1013 AddAnchor(IDC_BUTTON_MANAGE,TOP_RIGHT);
1014 AddAnchor(IDC_BUTTON_PULL,BOTTOM_LEFT);
1015 AddAnchor(IDC_BUTTON_PUSH,BOTTOM_LEFT);
1016 AddAnchor(IDC_BUTTON_SUBMODULE,BOTTOM_LEFT);
1017 AddAnchor(IDC_BUTTON_STASH,BOTTOM_LEFT);
1018 AddAnchor(IDC_BUTTON_APPLY,BOTTOM_RIGHT);
1019 AddAnchor(IDC_BUTTON_EMAIL,BOTTOM_RIGHT);
1020 AddAnchor(IDC_PROGRESS_SYNC,TOP_LEFT,TOP_RIGHT);
1021 AddAnchor(IDOK,BOTTOM_RIGHT);
1022 AddAnchor(IDHELP,BOTTOM_RIGHT);
1023 AddAnchor(IDC_STATIC_STATUS, BOTTOM_LEFT, BOTTOM_RIGHT);
1024 AddAnchor(IDC_ANIMATE_SYNC,TOP_LEFT);
1025 AddAnchor(IDC_BUTTON_COMMIT,BOTTOM_LEFT);
1026 AddAnchor(IDC_LOG, BOTTOM_LEFT);
1028 // do not use BRANCH_COMBOX_ADD_ANCHOR here, we want to have different stylings
1029 AddAnchor(IDC_COMBOBOXEX_LOCAL_BRANCH, TOP_LEFT,TOP_CENTER);
1030 AddAnchor(IDC_COMBOBOXEX_REMOTE_BRANCH, TOP_CENTER, TOP_RIGHT);
1031 AddAnchor(IDC_BUTTON_LOCAL_BRANCH, TOP_CENTER);
1032 AddAnchor(IDC_BUTTON_REMOTE_BRANCH, TOP_RIGHT);
1033 AddAnchor(IDC_STATIC_REMOTE_BRANCH, TOP_CENTER);
1034 AddAnchor(IDC_PROG_LABEL, TOP_LEFT);
1036 CString WorkingDir=g_Git.m_CurrentDir;
1037 WorkingDir.Replace(L':', L'_');
1038 m_RegKeyRemoteBranch = L"Software\\TortoiseGit\\History\\SyncBranch\\" + WorkingDir;
1041 this->AddOthersToAnchor();
1043 this->m_ctrlPush.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PUSH)));
1044 this->m_ctrlPush.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PUSHTAGS)));
1045 this->m_ctrlPush.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PUSHNOTES)));
1047 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PULL)));
1048 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_FETCH)));
1049 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_FETCHREBASE)));
1050 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_FETCHALL)));
1051 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_REMOTEUPDATE)));
1052 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_CLEANUPSTALEBRANCHES)));
1054 this->m_ctrlSubmodule.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_SUBKODULEUPDATE)));
1055 this->m_ctrlSubmodule.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_SUBKODULEINIT)));
1056 this->m_ctrlSubmodule.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_SUBKODULESYNC)));
1058 this->m_ctrlStash.AddEntry(CString(MAKEINTRESOURCE(IDS_MENUSTASHSAVE)));
1059 this->m_ctrlStash.AddEntry(CString(MAKEINTRESOURCE(IDS_MENUSTASHPOP)));
1060 this->m_ctrlStash.AddEntry(CString(MAKEINTRESOURCE(IDS_MENUSTASHAPPLY)));
1062 WorkingDir.Replace(L':', L'_');
1064 CString regkey ;
1065 regkey.Format(L"Software\\TortoiseGit\\TortoiseProc\\Sync\\%s", (LPCTSTR)WorkingDir);
1067 this->m_regPullButton = CRegDWORD(regkey + L"\\Pull", 0);
1068 this->m_regPushButton = CRegDWORD(regkey + L"\\Push", 0);
1069 this->m_regSubmoduleButton = CRegDWORD(regkey + L"\\Submodule");
1070 this->m_regAutoLoadPutty = CRegDWORD(regkey + L"\\AutoLoadPutty", CAppUtils::IsSSHPutty());
1072 this->UpdateData();
1073 this->m_bAutoLoadPuttyKey = m_regAutoLoadPutty;
1074 if(!CAppUtils::IsSSHPutty())
1075 m_bAutoLoadPuttyKey = false;
1076 this->UpdateData(FALSE);
1078 this->m_ctrlPull.SetCurrentEntry(this->m_regPullButton);
1079 this->m_ctrlPush.SetCurrentEntry(this->m_regPushButton);
1080 this->m_ctrlSubmodule.SetCurrentEntry(this->m_regSubmoduleButton);
1082 CString sWindowTitle;
1083 GetWindowText(sWindowTitle);
1084 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
1086 EnableSaveRestore(L"SyncDlg");
1088 m_ctrlURL.SetCaseSensitive(TRUE);
1089 m_ctrlURL.SetURLHistory(true);
1090 m_ctrlURL.SetMaxHistoryItems(0x7FFFFFFF);
1091 this->m_ctrlURL.LoadHistory(L"Software\\TortoiseGit\\History\\SyncURL\\" + WorkingDir, L"url");
1093 STRING_VECTOR list;
1095 if(!g_Git.GetRemoteList(list))
1097 for (unsigned int i = 0; i < list.size(); ++i)
1099 m_ctrlURL.AddString(list[i]);
1102 m_ctrlURL.SetCurSel(0);
1103 m_ctrlRemoteBranch.SetCurSel(0);
1105 this->LoadBranchInfo();
1107 this->m_bInited=true;
1108 FetchOutList();
1110 m_ctrlTabCtrl.ShowTab(IDC_CMD_LOG-1,false);
1111 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST-1,false);
1112 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST-1,false);
1113 m_ctrlTabCtrl.ShowTab(IDC_IN_CONFLICT-1,false);
1114 m_ctrlTabCtrl.ShowTab(IDC_CMD_GIT_PROG-1, false);
1115 m_ctrlTabCtrl.ShowTab(IDC_REFLIST-1, false);
1117 m_ctrlRemoteBranch.m_bWantReturn = TRUE;
1118 m_ctrlURL.m_bWantReturn = TRUE;
1120 if (m_seq > 0 && (DWORD)CRegDWORD(L"Software\\TortoiseGit\\SyncDialogRandomPos"))
1122 m_seq %= 5;
1123 RECT rect;
1124 GetWindowRect(&rect);
1125 rect.top -= m_seq * 30;
1126 rect.bottom -= m_seq * 30;
1127 if (rect.top < 0)
1129 rect.top += 150;
1130 rect.bottom += 150;
1132 MoveWindow(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
1135 return TRUE; // return TRUE unless you set the focus to a control
1136 // EXCEPTION: OCX Property Pages should return FALSE
1139 void CSyncDlg::OnBnClickedButtonManage()
1141 CAppUtils::LaunchRemoteSetting();
1142 Refresh();
1145 void CSyncDlg::Refresh()
1147 theApp.DoWaitCursor(1);
1149 int lastSelected = m_ctrlURL.GetCurSel();
1150 CString url;
1151 this->m_ctrlURL.GetWindowText(url);
1153 this->m_ctrlURL.Reset();
1154 CString workingDir = g_Git.m_CurrentDir;
1155 workingDir.Replace(L':', L'_');
1156 this->m_ctrlURL.LoadHistory(L"Software\\TortoiseGit\\History\\SyncURL\\" + workingDir, L"url");
1158 STRING_VECTOR list;
1159 bool found = false;
1160 if (!g_Git.GetRemoteList(list))
1162 for (size_t i = 0; i < list.size(); ++i)
1164 m_ctrlURL.AddString(list[i]);
1165 if (list[i] == url)
1166 found = true;
1169 if (lastSelected >= 0 && !found)
1171 m_ctrlURL.SetCurSel(0);
1172 m_ctrlURL.GetWindowText(url);
1175 CString local;
1176 CString remote;
1177 this->m_ctrlLocalBranch.GetWindowText(local);
1178 this->m_ctrlRemoteBranch.GetWindowText(remote);
1180 this->LoadBranchInfo();
1182 this->m_ctrlLocalBranch.AddString(local);
1183 this->m_ctrlRemoteBranch.AddString(remote);
1184 this->m_ctrlURL.AddString(url);
1186 m_OutLogList.ShowText(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_REFRESHING)));
1187 this->FetchOutList(true);
1188 theApp.DoWaitCursor(-1);
1191 BOOL CSyncDlg::PreTranslateMessage(MSG* pMsg)
1193 if (pMsg->message == WM_KEYDOWN)
1195 switch (pMsg->wParam)
1197 case VK_F5:
1199 if (m_bBlock)
1200 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
1201 Refresh();
1203 break;
1205 /* Avoid TAB control destroy but dialog exist*/
1206 case VK_ESCAPE:
1207 case VK_CANCEL:
1209 TCHAR buff[128] = { 0 };
1210 ::GetClassName(pMsg->hwnd,buff,128);
1212 /* Use MSFTEDIT_CLASS http://msdn.microsoft.com/en-us/library/bb531344.aspx */
1213 if (_wcsnicmp(buff, MSFTEDIT_CLASS, 128) == 0 || //Unicode and MFC 2012 and later
1214 _wcsnicmp(buff, RICHEDIT_CLASS, 128) == 0 || //ANSI or MFC 2010
1215 _wcsnicmp(buff, L"SysListView32", 128) == 0)
1217 this->PostMessage(WM_KEYDOWN,VK_ESCAPE,0);
1218 return TRUE;
1223 return __super::PreTranslateMessage(pMsg);
1225 void CSyncDlg::FetchOutList(bool force)
1227 if (!m_bInited || m_bWantToExit)
1228 return;
1229 m_OutChangeFileList.Clear();
1230 this->m_OutLogList.Clear();
1232 CString remote;
1233 this->m_ctrlURL.GetWindowText(remote);
1234 CString remotebranch;
1235 this->m_ctrlRemoteBranch.GetWindowText(remotebranch);
1236 remotebranch = remote + L'/' + remotebranch;
1237 CGitHash remotebranchHash;
1238 g_Git.GetHash(remotebranchHash, remotebranch);
1240 if(IsURL())
1242 CString str;
1243 str.LoadString(IDS_PROC_SYNC_PUSH_UNKNOWN);
1244 m_OutLogList.ShowText(str);
1245 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,FALSE);
1246 m_OutLocalBranch.Empty();
1247 m_OutRemoteBranch.Empty();
1249 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1250 return ;
1253 else if(remotebranchHash.IsEmpty())
1255 CString str;
1256 str.Format(IDS_PROC_SYNC_PUSH_UNKNOWNBRANCH, (LPCTSTR)remotebranch);
1257 m_OutLogList.ShowText(str);
1258 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,FALSE);
1259 m_OutLocalBranch.Empty();
1260 m_OutRemoteBranch.Empty();
1262 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1263 return ;
1265 else
1267 CString localbranch;
1268 localbranch=this->m_ctrlLocalBranch.GetString();
1270 if(localbranch != m_OutLocalBranch || m_OutRemoteBranch != remotebranch || force)
1272 m_OutLogList.ClearText();
1274 CGitHash base, localBranchHash;
1275 bool isFastForward = g_Git.IsFastForward(remotebranch, localbranch, &base);
1277 if (g_Git.GetHash(localBranchHash, localbranch))
1279 MessageBox(g_Git.GetGitLastErr(L"Could not get hash of \"" + localbranch + L"\"."), L"TortoiseGit", MB_ICONERROR);
1280 return;
1282 if (remotebranchHash == localBranchHash)
1284 CString str;
1285 str.Format(IDS_PROC_SYNC_COMMITSAHEAD, 0, (LPCTSTR)remotebranch);
1286 m_OutLogList.ShowText(str);
1287 this->m_ctrlStatus.SetWindowText(str);
1288 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,FALSE);
1289 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1291 else if (isFastForward || m_bForce)
1293 CString range;
1294 range.Format(L"%s..%s", (LPCTSTR)g_Git.FixBranchName(remotebranch), (LPCTSTR)g_Git.FixBranchName(localbranch));
1295 //fast forward
1296 m_OutLogList.FillGitLog(nullptr, &range, CGit::LOG_INFO_STAT | CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
1297 CString str;
1298 str.Format(IDS_PROC_SYNC_COMMITSAHEAD, m_OutLogList.GetItemCount(), (LPCTSTR)remotebranch);
1299 this->m_ctrlStatus.SetWindowText(str);
1301 if (isFastForward)
1302 AddDiffFileList(&m_OutChangeFileList, &m_arOutChangeList, localbranch, remotebranch);
1303 else
1305 AddDiffFileList(&m_OutChangeFileList, &m_arOutChangeList, localbranch, base.ToString());
1308 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,TRUE);
1309 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(TRUE);
1311 else
1313 CString str;
1314 str.Format(IDS_PROC_SYNC_NOFASTFORWARD, (LPCTSTR)localbranch, (LPCTSTR)remotebranch);
1315 m_OutLogList.ShowText(str);
1316 this->m_ctrlStatus.SetWindowText(str);
1317 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID() - 1, FALSE);
1318 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1321 this->m_OutLocalBranch=localbranch;
1322 this->m_OutRemoteBranch=remotebranch;
1326 bool CSyncDlg::IsURL()
1328 CString str;
1329 this->m_ctrlURL.GetWindowText(str);
1330 return str.Find(L'\\') >= 0 || str.Find(L'/') >= 0;
1333 void CSyncDlg::OnCbnEditchangeComboboxex()
1335 SetTimer(IDT_INPUT, 1000, nullptr);
1336 this->m_OutLogList.ShowText(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_WAINTINPUT)));
1338 //this->FetchOutList();
1341 UINT CSyncDlg::ProgressThread()
1343 m_startTick = GetTickCount64();
1344 m_bDone = false;
1345 STRING_VECTOR list;
1346 CProgressDlg::RunCmdList(this, m_GitCmdList, list, true, nullptr, &this->m_bAbort, &this->m_Databuf);
1347 InterlockedExchange(&m_bBlock, FALSE);
1348 return 0;
1351 LRESULT CSyncDlg::OnProgressUpdateUI(WPARAM wParam,LPARAM lParam)
1353 if (m_bWantToExit)
1354 return 0;
1355 if(wParam == MSG_PROGRESSDLG_START)
1357 m_BufStart = 0;
1358 m_ctrlAnimate.Play(0, UINT_MAX, UINT_MAX);
1359 this->m_ctrlProgress.SetPos(0);
1360 if (m_pTaskbarList)
1362 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
1363 m_pTaskbarList->SetProgressValue(m_hWnd, 0, 100);
1367 if(wParam == MSG_PROGRESSDLG_END || wParam == MSG_PROGRESSDLG_FAILED)
1369 ULONGLONG tickSpent = GetTickCount64() - m_startTick;
1370 CString strEndTime = CLoglistUtils::FormatDateAndTime(CTime::GetCurrentTime(), DATE_SHORTDATE, true, false);
1372 m_BufStart = 0;
1373 m_Databuf.m_critSec.Lock();
1374 m_Databuf.clear();
1375 m_Databuf.m_critSec.Unlock();
1377 m_bDone = true;
1378 m_ctrlAnimate.Stop();
1379 m_ctrlProgress.SetPos(100);
1380 //this->DialogEnableWindow(IDOK,TRUE);
1383 CString text;
1384 m_ctrlCmdOut.GetWindowText(text);
1385 text.Remove('\r');
1386 CAppUtils::StyleURLs(text, &m_ctrlCmdOut);
1389 DWORD exitCode = (DWORD)lParam;
1390 if (exitCode)
1392 if (m_pTaskbarList)
1394 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_ERROR);
1395 m_pTaskbarList->SetProgressValue(m_hWnd, 100, 100);
1397 CString log;
1398 log.Format(IDS_PROC_PROGRESS_GITUNCLEANEXIT, exitCode);
1399 CString err;
1400 err.Format(L"\r\n\r\n%s (%I64u ms @ %s)\r\n", (LPCTSTR)log, tickSpent, (LPCTSTR)strEndTime);
1401 CProgressDlg::InsertColorText(this->m_ctrlCmdOut, err, RGB(255,0,0));
1402 if (CRegDWORD(L"Software\\TortoiseGit\\NoSounds", FALSE) == FALSE)
1403 PlaySound((LPCTSTR)SND_ALIAS_SYSTEMEXCLAMATION, nullptr, SND_ALIAS_ID | SND_ASYNC);
1405 else
1407 if (m_pTaskbarList)
1408 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
1409 CString temp;
1410 temp.LoadString(IDS_SUCCESS);
1411 CString log;
1412 log.Format(L"\r\n%s (%I64u ms @ %s)\r\n", (LPCTSTR)temp, tickSpent, (LPCTSTR)strEndTime);
1413 CProgressDlg::InsertColorText(this->m_ctrlCmdOut, log, RGB(0,0,255));
1415 m_GitCmdStatus = exitCode;
1417 //if(wParam == MSG_PROGRESSDLG_END)
1418 RunPostAction();
1421 if(lParam != 0)
1422 ParserCmdOutput((char)lParam);
1423 else
1425 m_Databuf.m_critSec.Lock();
1426 for (size_t i = m_BufStart; i < m_Databuf.size(); ++i)
1428 char c = m_Databuf[m_BufStart];
1429 ++m_BufStart;
1430 m_Databuf.m_critSec.Unlock();
1431 ParserCmdOutput(c);
1433 m_Databuf.m_critSec.Lock();
1436 if (m_BufStart > 1000)
1438 m_Databuf.erase(m_Databuf.cbegin(), m_Databuf.cbegin() + m_BufStart);
1439 m_BufStart = 0;
1441 m_Databuf.m_critSec.Unlock();
1444 return 0;
1447 static std::map<CString, CGitHash> * HashMapToRefMap(MAP_HASH_NAME &map)
1449 auto rmap = new std::map<CString, CGitHash>();
1450 for (auto mit = map.cbegin(); mit != map.cend(); ++mit)
1452 for (auto rit = mit->second.cbegin(); rit != mit->second.cend(); ++rit)
1454 rmap->insert(std::make_pair(*rit, mit->first));
1457 return rmap;
1460 void CSyncDlg::FillNewRefMap()
1462 m_refList.Clear();
1463 m_newHashMap.clear();
1465 if (!g_Git.m_IsUseLibGit2)
1466 return;
1468 CAutoRepository repo(g_Git.GetGitRepository());
1469 if (!repo)
1471 CMessageBox::Show(m_hWnd, CGit::GetLibGit2LastErr(L"Could not open repository."), L"TortoiseGit", MB_OK | MB_ICONERROR);
1472 return;
1475 if (CGit::GetMapHashToFriendName(repo, m_newHashMap))
1477 MessageBox(CGit::GetLibGit2LastErr(L"Could not get all refs."), L"TortoiseGit", MB_ICONERROR);
1478 return;
1481 auto oldRefMap = HashMapToRefMap(m_oldHashMap);
1482 auto newRefMap = HashMapToRefMap(m_newHashMap);
1483 for (auto oit = oldRefMap->cbegin(); oit != oldRefMap->cend(); ++oit)
1485 bool found = false;
1486 for (auto nit = newRefMap->cbegin(); nit != newRefMap->cend(); ++nit)
1488 // changed ref
1489 if (oit->first == nit->first)
1491 found = true;
1492 m_refList.AddEntry(repo, oit->first, &oit->second, &nit->second);
1493 break;
1496 // deleted ref
1497 if (!found)
1498 m_refList.AddEntry(repo, oit->first, &oit->second, nullptr);
1500 for (auto nit = newRefMap->cbegin(); nit != newRefMap->cend(); ++nit)
1502 bool found = false;
1503 for (auto oit = oldRefMap->cbegin(); oit != oldRefMap->cend(); ++oit)
1505 if (oit->first == nit->first)
1507 found = true;
1508 break;
1511 // new ref
1512 if (!found)
1513 m_refList.AddEntry(repo, nit->first, nullptr, &nit->second);
1515 delete oldRefMap;
1516 delete newRefMap;
1517 m_refList.Show();
1520 void CSyncDlg::RunPostAction()
1522 if (m_bWantToExit)
1523 return;
1525 FillNewRefMap();
1527 if (this->m_CurrentCmd == GIT_COMMAND_PUSH)
1529 DWORD exitcode = 0xFFFFFFFF;
1530 CString error;
1531 if (CHooks::Instance().PostPush(g_Git.m_CurrentDir, exitcode, error))
1533 if (exitcode)
1535 CString temp;
1536 temp.Format(IDS_ERR_HOOKFAILED, (LPCTSTR)error);
1537 CMessageBox::Show(GetSafeHwnd(), temp, L"TortoiseGit", MB_OK | MB_ICONERROR);
1538 return;
1542 EnableControlButton(true);
1543 SwitchToInput();
1544 this->FetchOutList(true);
1546 else if (this->m_CurrentCmd == GIT_COMMAND_PULL)
1547 PullComplete();
1548 else if (this->m_CurrentCmd == GIT_COMMAND_FETCH || this->m_CurrentCmd == GIT_COMMAND_FETCHANDREBASE)
1549 FetchComplete();
1550 else if (this->m_CurrentCmd == GIT_COMMAND_SUBMODULE)
1552 //this->m_ctrlCmdOut.SetSel(-1,-1);
1553 //this->m_ctrlCmdOut.ReplaceSel(L"Done\r\n");
1554 //this->m_ctrlCmdOut.SetSel(-1,-1);
1555 EnableControlButton(true);
1556 SwitchToInput();
1558 else if (this->m_CurrentCmd == GIT_COMMAND_STASH)
1559 StashComplete();
1560 else if (this->m_CurrentCmd == GIT_COMMAND_REMOTE)
1562 this->FetchOutList(true);
1563 EnableControlButton(true);
1564 SwitchToInput();
1565 ShowTab(IDC_REFLIST);
1568 void CSyncDlg::ParserCmdOutput(char ch)
1570 if (m_bAbort)
1571 return;
1572 CProgressDlg::ParserCmdOutput(m_ctrlCmdOut,m_ctrlProgress,m_hWnd,m_pTaskbarList,m_LogText,ch);
1574 void CSyncDlg::OnBnClickedButtonCommit()
1576 CString cmd = L"/command:commit";
1577 cmd += L" /path:\"";
1578 cmd += g_Git.m_CurrentDir;
1579 cmd += L'"';
1581 CAppUtils::RunTortoiseGitProc(cmd);
1584 void CSyncDlg::OnOK()
1586 UpdateCombox();
1587 this->UpdateData();
1588 m_ctrlURL.SaveHistory();
1589 SaveHistory();
1590 m_regAutoLoadPutty = this->m_bAutoLoadPuttyKey;
1591 m_tooltips.Pop();
1592 __super::OnOK();
1595 void CSyncDlg::OnCancel()
1597 m_bAbort = true;
1598 m_GitProgressList.Cancel();
1599 if (m_bDone && !m_GitProgressList.IsRunning())
1601 CResizableStandAloneDialog::OnCancel();
1602 return;
1604 if (m_GitProgressList.IsRunning())
1605 WaitForSingleObject(m_GitProgressList.m_pThread->m_hThread, 10000);
1607 if (g_Git.m_CurrentGitPi.hProcess)
1609 DWORD dwConfirmKillProcess = CRegDWORD(L"Software\\TortoiseGit\\ConfirmKillProcess");
1610 if (dwConfirmKillProcess && CMessageBox::Show(m_hWnd, IDS_PROC_CONFIRMKILLPROCESS, IDS_APPNAME, MB_YESNO | MB_ICONQUESTION) != IDYES)
1611 return;
1612 if (::GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0))
1613 ::WaitForSingleObject(g_Git.m_CurrentGitPi.hProcess, 10000);
1615 CProgressDlg::KillProcessTree(g_Git.m_CurrentGitPi.dwProcessId);
1618 ::WaitForSingleObject(g_Git.m_CurrentGitPi.hProcess ,10000);
1619 if (m_pThread)
1621 if (::WaitForSingleObject(m_pThread->m_hThread, 5000) == WAIT_TIMEOUT)
1622 g_Git.KillRelatedThreads(m_pThread);
1624 m_tooltips.Pop();
1625 CResizableStandAloneDialog::OnCancel();
1628 void CSyncDlg::OnBnClickedButtonSubmodule()
1630 this->UpdateData();
1631 UpdateCombox();
1632 m_ctrlCmdOut.SetWindowText(L"");
1633 m_LogText.Empty();
1635 this->m_regSubmoduleButton = (DWORD)this->m_ctrlSubmodule.GetCurrentEntry();
1637 this->SwitchToRun();
1639 this->m_bAbort=false;
1640 this->m_GitCmdList.clear();
1642 ShowTab(IDC_CMD_LOG);
1644 CString cmd;
1646 switch (m_ctrlSubmodule.GetCurrentEntry())
1648 case 0:
1649 cmd = L"git.exe submodule update --init";
1650 break;
1651 case 1:
1652 cmd = L"git.exe submodule init";
1653 break;
1654 case 2:
1655 cmd = L"git.exe submodule sync";
1656 break;
1659 m_GitCmdList.push_back(cmd);
1661 m_CurrentCmd = GIT_COMMAND_SUBMODULE;
1663 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
1664 if (!m_pThread)
1666 // ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
1668 else
1670 m_pThread->m_bAutoDelete = TRUE;
1671 m_pThread->ResumeThread();
1675 void CSyncDlg::OnBnClickedButtonStash()
1677 UpdateData();
1678 UpdateCombox();
1679 m_ctrlCmdOut.SetWindowText(L"");
1680 m_LogText.Empty();
1682 SwitchToRun();
1684 m_bAbort = false;
1685 m_GitCmdList.clear();
1687 ShowTab(IDC_CMD_LOG);
1689 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, false);
1690 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST -1, false);
1691 m_ctrlTabCtrl.ShowTab(IDC_IN_CONFLICT -1, false);
1693 CString cmd;
1694 switch (m_ctrlStash.GetCurrentEntry())
1696 case 0:
1697 cmd = L"git.exe stash save";
1698 break;
1699 case 1:
1700 cmd = L"git.exe stash pop";
1701 break;
1702 case 2:
1703 cmd = L"git.exe stash apply";
1704 break;
1707 m_GitCmdList.push_back(cmd);
1708 m_CurrentCmd = GIT_COMMAND_STASH;
1710 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
1711 if (!m_pThread)
1713 //ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
1715 else
1717 m_pThread->m_bAutoDelete = TRUE;
1718 m_pThread->ResumeThread();
1722 void CSyncDlg::OnTimer(UINT_PTR nIDEvent)
1724 if( nIDEvent == IDT_INPUT)
1726 KillTimer(IDT_INPUT);
1727 this->FetchOutList(true);
1731 LRESULT CSyncDlg::OnTaskbarBtnCreated(WPARAM wParam, LPARAM lParam)
1733 m_pTaskbarList.Release();
1734 m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList);
1735 m_GitProgressList.m_pTaskbarList = m_pTaskbarList;
1736 return __super::OnTaskbarButtonCreated(wParam, lParam);
1739 void CSyncDlg::OnBnClickedCheckForce()
1741 UpdateData();
1744 void CSyncDlg::OnBnClickedLog()
1746 CString cmd = L"/command:log";
1747 cmd += L" /path:\"";
1748 cmd += g_Git.m_CurrentDir;
1749 cmd += L'"';
1751 CAppUtils::RunTortoiseGitProc(cmd);
1754 LRESULT CSyncDlg::OnProgCmdFinish(WPARAM /*wParam*/, LPARAM /*lParam*/)
1756 RefreshCursor();
1757 RunPostAction();
1758 return 0;
1761 void CSyncDlg::OnDestroy()
1763 m_bWantToExit = true;
1764 __super::OnDestroy();
1767 LRESULT CSyncDlg::OnThemeChanged()
1769 CMFCVisualManager::GetInstance()->DestroyInstance();
1770 return 0;
1773 void CSyncDlg::OnEnLinkLog(NMHDR *pNMHDR, LRESULT *pResult)
1775 // similar code in ProgressDlg.cpp and LogDlg.cpp
1776 ENLINK *pEnLink = reinterpret_cast<ENLINK *>(pNMHDR);
1777 if ((pEnLink->msg == WM_LBUTTONUP) || (pEnLink->msg == WM_SETCURSOR))
1779 CString msg;
1780 m_ctrlCmdOut.GetWindowText(msg);
1781 msg.Replace(L"\r\n", L"\n");
1782 CString url = msg.Mid(pEnLink->chrg.cpMin, pEnLink->chrg.cpMax - pEnLink->chrg.cpMin);
1783 // check if it's an email address
1784 auto atpos = url.Find(L'@');
1785 if ((atpos > 0) && (url.ReverseFind(L'.') > atpos) && !::PathIsURL(url))
1786 url = L"mailto:" + url;
1787 if (::PathIsURL(url))
1789 if (pEnLink->msg == WM_LBUTTONUP)
1790 ShellExecute(GetSafeHwnd(), L"open", url, nullptr, nullptr, SW_SHOWDEFAULT);
1791 else
1793 static RECT prevRect = { 0 };
1794 CWnd* pMsgView = &m_ctrlCmdOut;
1795 if (pMsgView)
1797 RECT rc;
1798 POINTL pt;
1799 pMsgView->SendMessage(EM_POSFROMCHAR, (WPARAM)&pt, pEnLink->chrg.cpMin);
1800 rc.left = pt.x;
1801 rc.top = pt.y;
1802 pMsgView->SendMessage(EM_POSFROMCHAR, (WPARAM)&pt, pEnLink->chrg.cpMax);
1803 rc.right = pt.x;
1804 rc.bottom = pt.y + 12;
1805 if ((prevRect.left != rc.left) || (prevRect.top != rc.top))
1807 m_tooltips.DelTool(pMsgView, 1);
1808 m_tooltips.AddTool(pMsgView, url, &rc, 1);
1809 prevRect = rc;
1815 *pResult = 0;
1818 void CSyncDlg::OnEnscrollLog()
1820 m_tooltips.DelTool(&m_ctrlCmdOut, 1);