SyncDlg: Disallow in/out changes to include local context menu
[TortoiseGit.git] / src / TortoiseProc / SyncDlg.cpp
blob0cab6ac43419121e69ad4caf9cd4f2352e1ad68e
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\"", 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
911 m_ctrlTabCtrl.InsertTab(&m_InLogList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_INCOMMITS)), -1);
913 m_InLogList.m_ColumnRegKey = L"SyncIn";
914 m_InLogList.InsertGitColumn();
916 //----------- Create In Change file list -----------
917 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
919 if( !m_InChangeFileList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_IN_CHANGELIST))
921 TRACE0("Failed to create output change files window\n");
922 return FALSE; // fail to create
924 m_ctrlTabCtrl.InsertTab(&m_InChangeFileList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_INCHANGELIST)), -1);
926 m_InChangeFileList.Init(GITSLC_COLEXT | GITSLC_COLSTATUS |GITSLC_COLADD|GITSLC_COLDEL, L"InSyncDlg",
927 (CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_COMPARETWOREVISIONS) |
928 CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_GNUDIFF2REVISIONS)), false, false, GITSLC_COLEXT | GITSLC_COLSTATUS | GITSLC_COLADD | GITSLC_COLDEL);
931 //---------- Create Conflict List Ctrl -----------------
932 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
934 if( !m_ConflictFileList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_IN_CONFLICT))
936 TRACE0("Failed to create output change files window\n");
937 return FALSE; // fail to create
939 m_ctrlTabCtrl.InsertTab(&m_ConflictFileList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_CONFLICTS)), -1);
941 m_ConflictFileList.Init(GITSLC_COLEXT | GITSLC_COLSTATUS |GITSLC_COLADD|GITSLC_COLDEL, L"ConflictSyncDlg",
942 (GITSLC_POPEXPLORE | GITSLC_POPOPEN | GITSLC_POPSHOWLOG |
943 GITSLC_POPCONFLICT|GITSLC_POPRESOLVE),false);
946 //---------- Create Commit Out List Ctrl---------------
948 dwStyle =LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;;
950 if( !m_OutLogList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_OUT_LOGLIST))
952 TRACE0("Failed to create output commits window\n");
953 return FALSE; // fail to create
957 m_ctrlTabCtrl.InsertTab(&m_OutLogList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_OUTCOMMITS)), -1);
959 m_OutLogList.m_ColumnRegKey = L"SyncOut";
960 m_OutLogList.InsertGitColumn();
962 //------------- Create Change File List Control ----------------
964 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
966 if( !m_OutChangeFileList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_OUT_CHANGELIST))
968 TRACE0("Failed to create output change files window\n");
969 return FALSE; // fail to create
971 m_ctrlTabCtrl.InsertTab(&m_OutChangeFileList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_OUTCHANGELIST)), -1);
973 m_OutChangeFileList.Init(GITSLC_COLEXT | GITSLC_COLSTATUS | GITSLC_COLADD | GITSLC_COLDEL, L"OutSyncDlg",
974 (CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_COMPARETWOREVISIONS) |
975 CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_GNUDIFF2REVISIONS)), false, false, GITSLC_COLEXT | GITSLC_COLSTATUS | GITSLC_COLADD | GITSLC_COLDEL);
977 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | LVS_SINGLESEL | WS_CHILD | WS_VISIBLE;
978 if (!m_GitProgressList.Create(dwStyle | LVS_OWNERDATA, rectDummy, &m_ctrlTabCtrl, IDC_CMD_GIT_PROG))
980 TRACE0("Failed to create Git Progress List Window\n");
981 return FALSE; // fail to create
983 m_ctrlTabCtrl.InsertTab(&m_GitProgressList, CString(MAKEINTRESOURCE(IDS_LOG)), -1);
984 m_GitProgressList.m_pAnimate = &m_ctrlAnimate;
985 m_GitProgressList.m_pPostWnd = this;
986 m_GitProgressList.m_pProgressLabelCtrl = &m_ctrlProgLabel;
987 m_GitProgressList.m_pProgControl = &m_ctrlProgress;
988 m_GitProgressList.m_pTaskbarList = m_pTaskbarList;
990 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE | LVS_SINGLESEL;
991 DWORD exStyle = LVS_EX_HEADERDRAGDROP | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP;
992 if (CRegDWORD(L"Software\\TortoiseGit\\FullRowSelect", TRUE))
993 exStyle |= LVS_EX_FULLROWSELECT;
994 if (g_Git.m_IsUseLibGit2)
996 m_refList.Create(dwStyle, rectDummy, &m_ctrlTabCtrl, IDC_REFLIST);
997 m_refList.SetExtendedStyle(exStyle);
998 m_refList.Init();
999 m_ctrlTabCtrl.InsertTab(&m_refList, CString(MAKEINTRESOURCE(IDS_REFLIST)), -1);
1002 AddAnchor(IDC_SYNC_TAB,TOP_LEFT,BOTTOM_RIGHT);
1004 AddAnchor(IDC_GROUP_INFO,TOP_LEFT,TOP_RIGHT);
1005 AddAnchor(IDC_COMBOBOXEX_URL,TOP_LEFT,TOP_RIGHT);
1006 AddAnchor(IDC_BUTTON_MANAGE,TOP_RIGHT);
1007 AddAnchor(IDC_BUTTON_PULL,BOTTOM_LEFT);
1008 AddAnchor(IDC_BUTTON_PUSH,BOTTOM_LEFT);
1009 AddAnchor(IDC_BUTTON_SUBMODULE,BOTTOM_LEFT);
1010 AddAnchor(IDC_BUTTON_STASH,BOTTOM_LEFT);
1011 AddAnchor(IDC_BUTTON_APPLY,BOTTOM_RIGHT);
1012 AddAnchor(IDC_BUTTON_EMAIL,BOTTOM_RIGHT);
1013 AddAnchor(IDC_PROGRESS_SYNC,TOP_LEFT,TOP_RIGHT);
1014 AddAnchor(IDOK,BOTTOM_RIGHT);
1015 AddAnchor(IDHELP,BOTTOM_RIGHT);
1016 AddAnchor(IDC_STATIC_STATUS, BOTTOM_LEFT, BOTTOM_RIGHT);
1017 AddAnchor(IDC_ANIMATE_SYNC,TOP_LEFT);
1018 AddAnchor(IDC_BUTTON_COMMIT,BOTTOM_LEFT);
1019 AddAnchor(IDC_LOG, BOTTOM_LEFT);
1021 // do not use BRANCH_COMBOX_ADD_ANCHOR here, we want to have different stylings
1022 AddAnchor(IDC_COMBOBOXEX_LOCAL_BRANCH, TOP_LEFT,TOP_CENTER);
1023 AddAnchor(IDC_COMBOBOXEX_REMOTE_BRANCH, TOP_CENTER, TOP_RIGHT);
1024 AddAnchor(IDC_BUTTON_LOCAL_BRANCH, TOP_CENTER);
1025 AddAnchor(IDC_BUTTON_REMOTE_BRANCH, TOP_RIGHT);
1026 AddAnchor(IDC_STATIC_REMOTE_BRANCH, TOP_CENTER);
1027 AddAnchor(IDC_PROG_LABEL, TOP_LEFT);
1029 AdjustControlSize(IDC_CHECK_PUTTY_KEY);
1030 AdjustControlSize(IDC_CHECK_FORCE);
1032 CString WorkingDir=g_Git.m_CurrentDir;
1033 WorkingDir.Replace(L':', L'_');
1034 m_RegKeyRemoteBranch = L"Software\\TortoiseGit\\History\\SyncBranch\\" + WorkingDir;
1037 this->AddOthersToAnchor();
1039 this->m_ctrlPush.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PUSH)));
1040 this->m_ctrlPush.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PUSHTAGS)));
1041 this->m_ctrlPush.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PUSHNOTES)));
1043 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PULL)));
1044 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_FETCH)));
1045 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_FETCHREBASE)));
1046 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_FETCHALL)));
1047 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_REMOTEUPDATE)));
1048 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_CLEANUPSTALEBRANCHES)));
1050 this->m_ctrlSubmodule.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_SUBKODULEUPDATE)));
1051 this->m_ctrlSubmodule.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_SUBKODULEINIT)));
1052 this->m_ctrlSubmodule.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_SUBKODULESYNC)));
1054 this->m_ctrlStash.AddEntry(CString(MAKEINTRESOURCE(IDS_MENUSTASHSAVE)));
1055 this->m_ctrlStash.AddEntry(CString(MAKEINTRESOURCE(IDS_MENUSTASHPOP)));
1056 this->m_ctrlStash.AddEntry(CString(MAKEINTRESOURCE(IDS_MENUSTASHAPPLY)));
1058 WorkingDir.Replace(L':', L'_');
1060 CString regkey ;
1061 regkey.Format(L"Software\\TortoiseGit\\TortoiseProc\\Sync\\%s", (LPCTSTR)WorkingDir);
1063 this->m_regPullButton = CRegDWORD(regkey + L"\\Pull", 0);
1064 this->m_regPushButton = CRegDWORD(regkey + L"\\Push", 0);
1065 this->m_regSubmoduleButton = CRegDWORD(regkey + L"\\Submodule");
1066 this->m_regAutoLoadPutty = CRegDWORD(regkey + L"\\AutoLoadPutty", CAppUtils::IsSSHPutty());
1068 this->UpdateData();
1069 this->m_bAutoLoadPuttyKey = m_regAutoLoadPutty;
1070 if(!CAppUtils::IsSSHPutty())
1071 m_bAutoLoadPuttyKey = false;
1072 this->UpdateData(FALSE);
1074 this->m_ctrlPull.SetCurrentEntry(this->m_regPullButton);
1075 this->m_ctrlPush.SetCurrentEntry(this->m_regPushButton);
1076 this->m_ctrlSubmodule.SetCurrentEntry(this->m_regSubmoduleButton);
1078 CString sWindowTitle;
1079 GetWindowText(sWindowTitle);
1080 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
1082 EnableSaveRestore(L"SyncDlg");
1084 m_ctrlURL.SetCaseSensitive(TRUE);
1085 m_ctrlURL.SetURLHistory(true);
1086 m_ctrlURL.SetMaxHistoryItems(0x7FFFFFFF);
1087 this->m_ctrlURL.LoadHistory(L"Software\\TortoiseGit\\History\\SyncURL\\" + WorkingDir, L"url");
1089 STRING_VECTOR list;
1091 if(!g_Git.GetRemoteList(list))
1093 for (unsigned int i = 0; i < list.size(); ++i)
1095 m_ctrlURL.AddString(list[i]);
1098 m_ctrlURL.SetCurSel(0);
1099 m_ctrlRemoteBranch.SetCurSel(0);
1101 this->LoadBranchInfo();
1103 this->m_bInited=true;
1104 FetchOutList();
1106 m_ctrlTabCtrl.ShowTab(IDC_CMD_LOG-1,false);
1107 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST-1,false);
1108 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST-1,false);
1109 m_ctrlTabCtrl.ShowTab(IDC_IN_CONFLICT-1,false);
1110 m_ctrlTabCtrl.ShowTab(IDC_CMD_GIT_PROG-1, false);
1111 m_ctrlTabCtrl.ShowTab(IDC_REFLIST-1, false);
1113 m_ctrlRemoteBranch.m_bWantReturn = TRUE;
1114 m_ctrlURL.m_bWantReturn = TRUE;
1116 if (m_seq > 0 && (DWORD)CRegDWORD(L"Software\\TortoiseGit\\SyncDialogRandomPos"))
1118 m_seq %= 5;
1119 RECT rect;
1120 GetWindowRect(&rect);
1121 rect.top -= m_seq * 30;
1122 rect.bottom -= m_seq * 30;
1123 if (rect.top < 0)
1125 rect.top += 150;
1126 rect.bottom += 150;
1128 MoveWindow(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
1131 return TRUE; // return TRUE unless you set the focus to a control
1132 // EXCEPTION: OCX Property Pages should return FALSE
1135 void CSyncDlg::OnBnClickedButtonManage()
1137 CAppUtils::LaunchRemoteSetting();
1138 Refresh();
1141 void CSyncDlg::Refresh()
1143 theApp.DoWaitCursor(1);
1145 int lastSelected = m_ctrlURL.GetCurSel();
1146 CString url;
1147 this->m_ctrlURL.GetWindowText(url);
1149 this->m_ctrlURL.Reset();
1150 CString workingDir = g_Git.m_CurrentDir;
1151 workingDir.Replace(L':', L'_');
1152 this->m_ctrlURL.LoadHistory(L"Software\\TortoiseGit\\History\\SyncURL\\" + workingDir, L"url");
1154 STRING_VECTOR list;
1155 bool found = false;
1156 if (!g_Git.GetRemoteList(list))
1158 for (size_t i = 0; i < list.size(); ++i)
1160 m_ctrlURL.AddString(list[i]);
1161 if (list[i] == url)
1162 found = true;
1165 if (lastSelected >= 0 && !found)
1167 m_ctrlURL.SetCurSel(0);
1168 m_ctrlURL.GetWindowText(url);
1171 CString local;
1172 CString remote;
1173 this->m_ctrlLocalBranch.GetWindowText(local);
1174 this->m_ctrlRemoteBranch.GetWindowText(remote);
1176 this->LoadBranchInfo();
1178 this->m_ctrlLocalBranch.AddString(local);
1179 this->m_ctrlRemoteBranch.AddString(remote);
1180 this->m_ctrlURL.AddString(url);
1182 m_OutLogList.ShowText(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_REFRESHING)));
1183 this->FetchOutList(true);
1184 theApp.DoWaitCursor(-1);
1187 BOOL CSyncDlg::PreTranslateMessage(MSG* pMsg)
1189 if (pMsg->message == WM_KEYDOWN)
1191 switch (pMsg->wParam)
1193 case VK_F5:
1195 if (m_bBlock)
1196 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
1197 Refresh();
1199 break;
1201 /* Avoid TAB control destroy but dialog exist*/
1202 case VK_ESCAPE:
1203 case VK_CANCEL:
1205 TCHAR buff[128] = { 0 };
1206 ::GetClassName(pMsg->hwnd,buff,128);
1208 /* Use MSFTEDIT_CLASS http://msdn.microsoft.com/en-us/library/bb531344.aspx */
1209 if (_wcsnicmp(buff, MSFTEDIT_CLASS, 128) == 0 || //Unicode and MFC 2012 and later
1210 _wcsnicmp(buff, RICHEDIT_CLASS, 128) == 0 || //ANSI or MFC 2010
1211 _wcsnicmp(buff, L"SysListView32", 128) == 0)
1213 this->PostMessage(WM_KEYDOWN,VK_ESCAPE,0);
1214 return TRUE;
1219 return __super::PreTranslateMessage(pMsg);
1221 void CSyncDlg::FetchOutList(bool force)
1223 if(!m_bInited)
1224 return;
1225 m_OutChangeFileList.Clear();
1226 this->m_OutLogList.Clear();
1228 CString remote;
1229 this->m_ctrlURL.GetWindowText(remote);
1230 CString remotebranch;
1231 this->m_ctrlRemoteBranch.GetWindowText(remotebranch);
1232 remotebranch = remote + L'/' + remotebranch;
1233 CGitHash remotebranchHash;
1234 g_Git.GetHash(remotebranchHash, remotebranch);
1236 if(IsURL())
1238 CString str;
1239 str.LoadString(IDS_PROC_SYNC_PUSH_UNKNOWN);
1240 m_OutLogList.ShowText(str);
1241 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,FALSE);
1242 m_OutLocalBranch.Empty();
1243 m_OutRemoteBranch.Empty();
1245 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1246 return ;
1249 else if(remotebranchHash.IsEmpty())
1251 CString str;
1252 str.Format(IDS_PROC_SYNC_PUSH_UNKNOWNBRANCH, (LPCTSTR)remotebranch);
1253 m_OutLogList.ShowText(str);
1254 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,FALSE);
1255 m_OutLocalBranch.Empty();
1256 m_OutRemoteBranch.Empty();
1258 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1259 return ;
1261 else
1263 CString localbranch;
1264 localbranch=this->m_ctrlLocalBranch.GetString();
1266 if(localbranch != m_OutLocalBranch || m_OutRemoteBranch != remotebranch || force)
1268 m_OutLogList.ClearText();
1270 CGitHash base, localBranchHash;
1271 bool isFastForward = g_Git.IsFastForward(remotebranch, localbranch, &base);
1273 if (g_Git.GetHash(localBranchHash, localbranch))
1275 MessageBox(g_Git.GetGitLastErr(L"Could not get hash of \"" + localbranch + L"\"."), L"TortoiseGit", MB_ICONERROR);
1276 return;
1278 if (remotebranchHash == localBranchHash)
1280 CString str;
1281 str.Format(IDS_PROC_SYNC_COMMITSAHEAD, 0, (LPCTSTR)remotebranch);
1282 m_OutLogList.ShowText(str);
1283 this->m_ctrlStatus.SetWindowText(str);
1284 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,FALSE);
1285 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1287 else if (isFastForward || m_bForce)
1289 CString range;
1290 range.Format(L"%s..%s", (LPCTSTR)g_Git.FixBranchName(remotebranch), (LPCTSTR)g_Git.FixBranchName(localbranch));
1291 //fast forward
1292 m_OutLogList.FillGitLog(nullptr, &range, CGit::LOG_INFO_STAT | CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
1293 CString str;
1294 str.Format(IDS_PROC_SYNC_COMMITSAHEAD, m_OutLogList.GetItemCount(), (LPCTSTR)remotebranch);
1295 this->m_ctrlStatus.SetWindowText(str);
1297 if (isFastForward)
1298 AddDiffFileList(&m_OutChangeFileList, &m_arOutChangeList, localbranch, remotebranch);
1299 else
1301 AddDiffFileList(&m_OutChangeFileList, &m_arOutChangeList, localbranch, base.ToString());
1304 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,TRUE);
1305 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(TRUE);
1307 else
1309 CString str;
1310 str.Format(IDS_PROC_SYNC_NOFASTFORWARD, (LPCTSTR)localbranch, (LPCTSTR)remotebranch);
1311 m_OutLogList.ShowText(str);
1312 this->m_ctrlStatus.SetWindowText(str);
1313 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID() - 1, FALSE);
1314 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1317 this->m_OutLocalBranch=localbranch;
1318 this->m_OutRemoteBranch=remotebranch;
1322 bool CSyncDlg::IsURL()
1324 CString str;
1325 this->m_ctrlURL.GetWindowText(str);
1326 return str.Find(L'\\') >= 0 || str.Find(L'/') >= 0;
1329 void CSyncDlg::OnCbnEditchangeComboboxex()
1331 SetTimer(IDT_INPUT, 1000, nullptr);
1332 this->m_OutLogList.ShowText(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_WAINTINPUT)));
1334 //this->FetchOutList();
1337 UINT CSyncDlg::ProgressThread()
1339 m_startTick = GetTickCount64();
1340 m_bDone = false;
1341 STRING_VECTOR list;
1342 CProgressDlg::RunCmdList(this, m_GitCmdList, list, true, nullptr, &this->m_bAbort, &this->m_Databuf);
1343 InterlockedExchange(&m_bBlock, FALSE);
1344 return 0;
1347 LRESULT CSyncDlg::OnProgressUpdateUI(WPARAM wParam,LPARAM lParam)
1349 if (m_bWantToExit)
1350 return 0;
1351 if(wParam == MSG_PROGRESSDLG_START)
1353 m_BufStart = 0;
1354 m_ctrlAnimate.Play(0, UINT_MAX, UINT_MAX);
1355 this->m_ctrlProgress.SetPos(0);
1356 if (m_pTaskbarList)
1358 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
1359 m_pTaskbarList->SetProgressValue(m_hWnd, 0, 100);
1363 if(wParam == MSG_PROGRESSDLG_END || wParam == MSG_PROGRESSDLG_FAILED)
1365 ULONGLONG tickSpent = GetTickCount64() - m_startTick;
1366 CString strEndTime = CLoglistUtils::FormatDateAndTime(CTime::GetCurrentTime(), DATE_SHORTDATE, true, false);
1368 m_BufStart = 0;
1369 m_Databuf.m_critSec.Lock();
1370 m_Databuf.clear();
1371 m_Databuf.m_critSec.Unlock();
1373 m_bDone = true;
1374 m_ctrlAnimate.Stop();
1375 m_ctrlProgress.SetPos(100);
1376 //this->DialogEnableWindow(IDOK,TRUE);
1379 CString text;
1380 m_ctrlCmdOut.GetWindowText(text);
1381 text.Remove('\r');
1382 CAppUtils::StyleURLs(text, &m_ctrlCmdOut);
1385 DWORD exitCode = (DWORD)lParam;
1386 if (exitCode)
1388 if (m_pTaskbarList)
1390 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_ERROR);
1391 m_pTaskbarList->SetProgressValue(m_hWnd, 100, 100);
1393 CString log;
1394 log.Format(IDS_PROC_PROGRESS_GITUNCLEANEXIT, exitCode);
1395 CString err;
1396 err.Format(L"\r\n\r\n%s (%I64u ms @ %s)\r\n", (LPCTSTR)log, tickSpent, (LPCTSTR)strEndTime);
1397 CProgressDlg::InsertColorText(this->m_ctrlCmdOut, err, RGB(255,0,0));
1398 if (CRegDWORD(L"Software\\TortoiseGit\\NoSounds", FALSE) == FALSE)
1399 PlaySound((LPCTSTR)SND_ALIAS_SYSTEMEXCLAMATION, nullptr, SND_ALIAS_ID | SND_ASYNC);
1401 else
1403 if (m_pTaskbarList)
1404 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
1405 CString temp;
1406 temp.LoadString(IDS_SUCCESS);
1407 CString log;
1408 log.Format(L"\r\n%s (%I64u ms @ %s)\r\n", (LPCTSTR)temp, tickSpent, (LPCTSTR)strEndTime);
1409 CProgressDlg::InsertColorText(this->m_ctrlCmdOut, log, RGB(0,0,255));
1411 m_GitCmdStatus = exitCode;
1413 //if(wParam == MSG_PROGRESSDLG_END)
1414 RunPostAction();
1417 if(lParam != 0)
1418 ParserCmdOutput((char)lParam);
1419 else
1421 m_Databuf.m_critSec.Lock();
1422 for (size_t i = m_BufStart; i < m_Databuf.size(); ++i)
1424 char c = m_Databuf[m_BufStart];
1425 ++m_BufStart;
1426 m_Databuf.m_critSec.Unlock();
1427 ParserCmdOutput(c);
1429 m_Databuf.m_critSec.Lock();
1432 if (m_BufStart > 1000)
1434 m_Databuf.erase(m_Databuf.cbegin(), m_Databuf.cbegin() + m_BufStart);
1435 m_BufStart = 0;
1437 m_Databuf.m_critSec.Unlock();
1440 return 0;
1443 static std::map<CString, CGitHash> * HashMapToRefMap(MAP_HASH_NAME &map)
1445 auto rmap = new std::map<CString, CGitHash>();
1446 for (auto mit = map.cbegin(); mit != map.cend(); ++mit)
1448 for (auto rit = mit->second.cbegin(); rit != mit->second.cend(); ++rit)
1450 rmap->insert(std::make_pair(*rit, mit->first));
1453 return rmap;
1456 void CSyncDlg::FillNewRefMap()
1458 m_refList.Clear();
1459 m_newHashMap.clear();
1461 if (!g_Git.m_IsUseLibGit2)
1462 return;
1464 CAutoRepository repo(g_Git.GetGitRepository());
1465 if (!repo)
1467 CMessageBox::Show(m_hWnd, CGit::GetLibGit2LastErr(L"Could not open repository."), L"TortoiseGit", MB_OK | MB_ICONERROR);
1468 return;
1471 if (CGit::GetMapHashToFriendName(repo, m_newHashMap))
1473 MessageBox(CGit::GetLibGit2LastErr(L"Could not get all refs."), L"TortoiseGit", MB_ICONERROR);
1474 return;
1477 auto oldRefMap = HashMapToRefMap(m_oldHashMap);
1478 auto newRefMap = HashMapToRefMap(m_newHashMap);
1479 for (auto oit = oldRefMap->cbegin(); oit != oldRefMap->cend(); ++oit)
1481 bool found = false;
1482 for (auto nit = newRefMap->cbegin(); nit != newRefMap->cend(); ++nit)
1484 // changed ref
1485 if (oit->first == nit->first)
1487 found = true;
1488 m_refList.AddEntry(repo, oit->first, &oit->second, &nit->second);
1489 break;
1492 // deleted ref
1493 if (!found)
1494 m_refList.AddEntry(repo, oit->first, &oit->second, nullptr);
1496 for (auto nit = newRefMap->cbegin(); nit != newRefMap->cend(); ++nit)
1498 bool found = false;
1499 for (auto oit = oldRefMap->cbegin(); oit != oldRefMap->cend(); ++oit)
1501 if (oit->first == nit->first)
1503 found = true;
1504 break;
1507 // new ref
1508 if (!found)
1509 m_refList.AddEntry(repo, nit->first, nullptr, &nit->second);
1511 delete oldRefMap;
1512 delete newRefMap;
1513 m_refList.Show();
1516 void CSyncDlg::RunPostAction()
1518 if (m_bWantToExit)
1519 return;
1521 FillNewRefMap();
1523 if (this->m_CurrentCmd == GIT_COMMAND_PUSH)
1525 DWORD exitcode = 0xFFFFFFFF;
1526 CString error;
1527 if (CHooks::Instance().PostPush(g_Git.m_CurrentDir, exitcode, error))
1529 if (exitcode)
1531 CString temp;
1532 temp.Format(IDS_ERR_HOOKFAILED, (LPCTSTR)error);
1533 CMessageBox::Show(GetSafeHwnd(), temp, L"TortoiseGit", MB_OK | MB_ICONERROR);
1534 return;
1538 EnableControlButton(true);
1539 SwitchToInput();
1540 this->FetchOutList(true);
1542 else if (this->m_CurrentCmd == GIT_COMMAND_PULL)
1543 PullComplete();
1544 else if (this->m_CurrentCmd == GIT_COMMAND_FETCH || this->m_CurrentCmd == GIT_COMMAND_FETCHANDREBASE)
1545 FetchComplete();
1546 else if (this->m_CurrentCmd == GIT_COMMAND_SUBMODULE)
1548 //this->m_ctrlCmdOut.SetSel(-1,-1);
1549 //this->m_ctrlCmdOut.ReplaceSel(L"Done\r\n");
1550 //this->m_ctrlCmdOut.SetSel(-1,-1);
1551 EnableControlButton(true);
1552 SwitchToInput();
1554 else if (this->m_CurrentCmd == GIT_COMMAND_STASH)
1555 StashComplete();
1556 else if (this->m_CurrentCmd == GIT_COMMAND_REMOTE)
1558 this->FetchOutList(true);
1559 EnableControlButton(true);
1560 SwitchToInput();
1561 ShowTab(IDC_REFLIST);
1564 void CSyncDlg::ParserCmdOutput(char ch)
1566 if (m_bAbort)
1567 return;
1568 CProgressDlg::ParserCmdOutput(m_ctrlCmdOut,m_ctrlProgress,m_hWnd,m_pTaskbarList,m_LogText,ch);
1570 void CSyncDlg::OnBnClickedButtonCommit()
1572 CString cmd = L"/command:commit";
1573 cmd += L" /path:\"";
1574 cmd += g_Git.m_CurrentDir;
1575 cmd += L'"';
1577 CAppUtils::RunTortoiseGitProc(cmd);
1580 void CSyncDlg::OnOK()
1582 UpdateCombox();
1583 this->UpdateData();
1584 m_ctrlURL.SaveHistory();
1585 SaveHistory();
1586 m_regAutoLoadPutty = this->m_bAutoLoadPuttyKey;
1587 m_tooltips.Pop();
1588 __super::OnOK();
1591 void CSyncDlg::OnCancel()
1593 m_bAbort = true;
1594 m_GitProgressList.Cancel();
1595 if (m_bDone && !m_GitProgressList.IsRunning())
1597 CResizableStandAloneDialog::OnCancel();
1598 return;
1600 if (m_GitProgressList.IsRunning())
1601 WaitForSingleObject(m_GitProgressList.m_pThread->m_hThread, 10000);
1603 if (g_Git.m_CurrentGitPi.hProcess)
1605 DWORD dwConfirmKillProcess = CRegDWORD(L"Software\\TortoiseGit\\ConfirmKillProcess");
1606 if (dwConfirmKillProcess && CMessageBox::Show(m_hWnd, IDS_PROC_CONFIRMKILLPROCESS, IDS_APPNAME, MB_YESNO | MB_ICONQUESTION) != IDYES)
1607 return;
1608 if (::GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0))
1609 ::WaitForSingleObject(g_Git.m_CurrentGitPi.hProcess, 10000);
1610 else
1611 GetLastError();
1613 CProgressDlg::KillProcessTree(g_Git.m_CurrentGitPi.dwProcessId);
1616 ::WaitForSingleObject(g_Git.m_CurrentGitPi.hProcess ,10000);
1617 if (m_pThread)
1619 if (::WaitForSingleObject(m_pThread->m_hThread, 5000) == WAIT_TIMEOUT)
1620 g_Git.KillRelatedThreads(m_pThread);
1622 m_tooltips.Pop();
1623 CResizableStandAloneDialog::OnCancel();
1626 void CSyncDlg::OnBnClickedButtonSubmodule()
1628 this->UpdateData();
1629 UpdateCombox();
1630 m_ctrlCmdOut.SetWindowText(L"");
1631 m_LogText.Empty();
1633 this->m_regSubmoduleButton = (DWORD)this->m_ctrlSubmodule.GetCurrentEntry();
1635 this->SwitchToRun();
1637 this->m_bAbort=false;
1638 this->m_GitCmdList.clear();
1640 ShowTab(IDC_CMD_LOG);
1642 CString cmd;
1644 switch (m_ctrlSubmodule.GetCurrentEntry())
1646 case 0:
1647 cmd = L"git.exe submodule update --init";
1648 break;
1649 case 1:
1650 cmd = L"git.exe submodule init";
1651 break;
1652 case 2:
1653 cmd = L"git.exe submodule sync";
1654 break;
1657 m_GitCmdList.push_back(cmd);
1659 m_CurrentCmd = GIT_COMMAND_SUBMODULE;
1661 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
1662 if (!m_pThread)
1664 // ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
1666 else
1668 m_pThread->m_bAutoDelete = TRUE;
1669 m_pThread->ResumeThread();
1673 void CSyncDlg::OnBnClickedButtonStash()
1675 UpdateData();
1676 UpdateCombox();
1677 m_ctrlCmdOut.SetWindowText(L"");
1678 m_LogText.Empty();
1680 SwitchToRun();
1682 m_bAbort = false;
1683 m_GitCmdList.clear();
1685 ShowTab(IDC_CMD_LOG);
1687 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, false);
1688 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST -1, false);
1689 m_ctrlTabCtrl.ShowTab(IDC_IN_CONFLICT -1, false);
1691 CString cmd;
1692 switch (m_ctrlStash.GetCurrentEntry())
1694 case 0:
1695 cmd = L"git.exe stash save";
1696 break;
1697 case 1:
1698 cmd = L"git.exe stash pop";
1699 break;
1700 case 2:
1701 cmd = L"git.exe stash apply";
1702 break;
1705 m_GitCmdList.push_back(cmd);
1706 m_CurrentCmd = GIT_COMMAND_STASH;
1708 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
1709 if (!m_pThread)
1711 //ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
1713 else
1715 m_pThread->m_bAutoDelete = TRUE;
1716 m_pThread->ResumeThread();
1720 void CSyncDlg::OnTimer(UINT_PTR nIDEvent)
1722 if( nIDEvent == IDT_INPUT)
1724 KillTimer(IDT_INPUT);
1725 this->FetchOutList(true);
1729 LRESULT CSyncDlg::OnTaskbarBtnCreated(WPARAM wParam, LPARAM lParam)
1731 m_pTaskbarList.Release();
1732 m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList);
1733 m_GitProgressList.m_pTaskbarList = m_pTaskbarList;
1734 return __super::OnTaskbarButtonCreated(wParam, lParam);
1737 void CSyncDlg::OnBnClickedCheckForce()
1739 UpdateData();
1742 void CSyncDlg::OnBnClickedLog()
1744 CString cmd = L"/command:log";
1745 cmd += L" /path:\"";
1746 cmd += g_Git.m_CurrentDir;
1747 cmd += L'"';
1749 CAppUtils::RunTortoiseGitProc(cmd);
1752 LRESULT CSyncDlg::OnProgCmdFinish(WPARAM /*wParam*/, LPARAM /*lParam*/)
1754 RefreshCursor();
1755 RunPostAction();
1756 return 0;
1759 void CSyncDlg::OnDestroy()
1761 m_bWantToExit = true;
1762 __super::OnDestroy();
1765 LRESULT CSyncDlg::OnThemeChanged()
1767 CMFCVisualManager::GetInstance()->DestroyInstance();
1768 return 0;
1771 void CSyncDlg::OnEnLinkLog(NMHDR *pNMHDR, LRESULT *pResult)
1773 // similar code in ProgressDlg.cpp and LogDlg.cpp
1774 ENLINK *pEnLink = reinterpret_cast<ENLINK *>(pNMHDR);
1775 if ((pEnLink->msg == WM_LBUTTONUP) || (pEnLink->msg == WM_SETCURSOR))
1777 CString msg;
1778 m_ctrlCmdOut.GetWindowText(msg);
1779 msg.Replace(L"\r\n", L"\n");
1780 CString url = msg.Mid(pEnLink->chrg.cpMin, pEnLink->chrg.cpMax - pEnLink->chrg.cpMin);
1781 // check if it's an email address
1782 auto atpos = url.Find(L'@');
1783 if ((atpos > 0) && (url.ReverseFind(L'.') > atpos) && !::PathIsURL(url))
1784 url = L"mailto:" + url;
1785 if (::PathIsURL(url))
1787 if (pEnLink->msg == WM_LBUTTONUP)
1788 ShellExecute(GetSafeHwnd(), L"open", url, nullptr, nullptr, SW_SHOWDEFAULT);
1789 else
1791 static RECT prevRect = { 0 };
1792 CWnd* pMsgView = &m_ctrlCmdOut;
1793 if (pMsgView)
1795 RECT rc;
1796 POINTL pt;
1797 pMsgView->SendMessage(EM_POSFROMCHAR, (WPARAM)&pt, pEnLink->chrg.cpMin);
1798 rc.left = pt.x;
1799 rc.top = pt.y;
1800 pMsgView->SendMessage(EM_POSFROMCHAR, (WPARAM)&pt, pEnLink->chrg.cpMax);
1801 rc.right = pt.x;
1802 rc.bottom = pt.y + 12;
1803 if ((prevRect.left != rc.left) || (prevRect.top != rc.top))
1805 m_tooltips.DelTool(pMsgView, 1);
1806 m_tooltips.AddTool(pMsgView, url, &rc, 1);
1807 prevRect = rc;
1813 *pResult = 0;
1816 void CSyncDlg::OnEnscrollLog()
1818 m_tooltips.DelTool(&m_ctrlCmdOut, 1);