Drop support for Git < 2.24 (released in November 2019)
[TortoiseGit.git] / src / TortoiseProc / SyncDlg.cpp
blob13cdf904b18af5fb8e7d1c9f279d987d54b60a1c
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2021 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 // SyncDlg.cpp : implementation file
23 #include "stdafx.h"
24 #include "TortoiseProc.h"
25 #include "SyncDlg.h"
26 #include "AppUtils.h"
27 #include "ProgressDlg.h"
28 #include "MessageBox.h"
29 #include "ImportPatchDlg.h"
30 #include "RebaseDlg.h"
31 #include "Hooks.h"
32 #include "SmartHandle.h"
33 #include "ProgressCommands/FetchProgressCommand.h"
34 #include "SyncTabCtrl.h"
35 #include "SysProgressDlg.h"
36 #include "ThemeMFCVisualManager.h"
38 // CSyncDlg dialog
40 IMPLEMENT_DYNAMIC(CSyncDlg, CResizableStandAloneDialog)
42 CSyncDlg::CSyncDlg(CWnd* pParent /*=nullptr*/)
43 : CResizableStandAloneDialog(CSyncDlg::IDD, pParent)
44 , m_iPullRebase(0)
45 , m_CurrentCmd(0)
46 , m_bInited(false)
47 , m_CmdOutCurrentPos(0)
48 , m_bAutoLoadPuttyKey(CAppUtils::IsSSHPutty())
49 , m_bForce(BST_UNCHECKED)
50 , m_bBlock(false)
51 , m_BufStart(0)
52 , m_pThread(nullptr)
53 , m_bAbort(false)
54 , m_bDone(false)
55 , m_bWantToExit(false)
56 , m_GitCmdStatus(-1)
57 , m_startTick(GetTickCount64())
58 , m_seq(0)
60 m_pTooltip = &m_tooltips;
63 CSyncDlg::~CSyncDlg()
67 void CSyncDlg::DoDataExchange(CDataExchange* pDX)
69 CDialog::DoDataExchange(pDX);
70 DDX_Check(pDX, IDC_CHECK_PUTTY_KEY, m_bAutoLoadPuttyKey);
71 DDX_Check(pDX, IDC_CHECK_FORCE,m_bForce);
72 DDX_Control(pDX, IDC_COMBOBOXEX_URL, m_ctrlURL);
73 DDX_Control(pDX, IDC_BUTTON_TABCTRL, m_ctrlDumyButton);
74 DDX_Control(pDX, IDC_BUTTON_PULL, m_ctrlPull);
75 DDX_Control(pDX, IDC_BUTTON_PUSH, m_ctrlPush);
76 DDX_Control(pDX, IDC_STATIC_STATUS, m_ctrlStatus);
77 DDX_Control(pDX, IDC_PROGRESS_SYNC, m_ctrlProgress);
78 DDX_Control(pDX, IDC_ANIMATE_SYNC, m_ctrlAnimate);
79 DDX_Control(pDX, IDC_BUTTON_SUBMODULE,m_ctrlSubmodule);
80 DDX_Control(pDX, IDC_BUTTON_STASH, m_ctrlStash);
81 DDX_Control(pDX, IDC_PROG_LABEL, m_ctrlProgLabel);
82 BRANCH_COMBOX_DDX;
85 BEGIN_MESSAGE_MAP(CSyncDlg, CResizableStandAloneDialog)
86 ON_BN_CLICKED(IDC_BUTTON_PULL, &CSyncDlg::OnBnClickedButtonPull)
87 ON_BN_CLICKED(IDC_BUTTON_PUSH, &CSyncDlg::OnBnClickedButtonPush)
88 ON_BN_CLICKED(IDC_BUTTON_APPLY, &CSyncDlg::OnBnClickedButtonApply)
89 ON_BN_CLICKED(IDC_BUTTON_EMAIL, &CSyncDlg::OnBnClickedButtonEmail)
90 ON_BN_CLICKED(IDC_BUTTON_MANAGE, &CSyncDlg::OnBnClickedButtonManage)
91 BRANCH_COMBOX_EVENT
92 ON_CBN_EDITCHANGE(IDC_COMBOBOXEX_URL, &CSyncDlg::OnCbnEditchangeComboboxex)
93 ON_CBN_EDITCHANGE(IDC_COMBOBOXEX_REMOTE_BRANCH, &CSyncDlg::OnCbnEditchangeComboboxex)
94 ON_MESSAGE(MSG_PROGRESSDLG_UPDATE_UI, OnProgressUpdateUI)
95 ON_MESSAGE(WM_PROG_CMD_FINISH, OnProgCmdFinish)
96 ON_BN_CLICKED(IDC_BUTTON_COMMIT, &CSyncDlg::OnBnClickedButtonCommit)
97 ON_BN_CLICKED(IDC_BUTTON_SUBMODULE, &CSyncDlg::OnBnClickedButtonSubmodule)
98 ON_BN_CLICKED(IDC_BUTTON_STASH, &CSyncDlg::OnBnClickedButtonStash)
99 ON_WM_TIMER()
100 ON_REGISTERED_MESSAGE(TaskBarButtonCreated, OnTaskbarBtnCreated)
101 ON_BN_CLICKED(IDC_CHECK_FORCE, &CSyncDlg::OnBnClickedCheckForce)
102 ON_BN_CLICKED(IDC_LOG, &CSyncDlg::OnBnClickedLog)
103 ON_WM_DESTROY()
104 END_MESSAGE_MAP()
106 void CSyncDlg::EnableControlButton(bool bEnabled)
108 GetDlgItem(IDC_BUTTON_PULL)->EnableWindow(bEnabled);
109 GetDlgItem(IDC_BUTTON_PUSH)->EnableWindow(bEnabled);
110 GetDlgItem(IDC_BUTTON_APPLY)->EnableWindow(bEnabled);
111 GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(bEnabled);
112 GetDlgItem(IDOK)->EnableWindow(bEnabled);
113 GetDlgItem(IDC_BUTTON_SUBMODULE)->EnableWindow(bEnabled);
114 GetDlgItem(IDC_BUTTON_STASH)->EnableWindow(bEnabled);
116 // CSyncDlg message handlers
118 bool CSyncDlg::AskSetTrackedBranch()
120 CString remote, remoteBranch;
121 g_Git.GetRemoteTrackedBranch(m_strLocalBranch, remote, remoteBranch);
122 if (remoteBranch.IsEmpty())
124 remoteBranch = m_strRemoteBranch;
125 if (remoteBranch.IsEmpty())
126 remoteBranch = m_strLocalBranch;
127 CString temp;
128 temp.FormatMessage(IDS_NOTYET_SETTRACKEDBRANCH, static_cast<LPCWSTR>(m_strLocalBranch), static_cast<LPCWSTR>(remoteBranch));
129 BOOL dontShowAgain = FALSE;
130 auto ret = CMessageBox::ShowCheck(GetSafeHwnd(), temp, L"TortoiseGit", MB_ICONQUESTION | MB_YESNOCANCEL, nullptr, CString(MAKEINTRESOURCE(IDS_MSGBOX_DONOTSHOW)), &dontShowAgain);
131 if (dontShowAgain)
132 CRegDWORD(L"Software\\TortoiseGit\\AskSetTrackedBranch") = FALSE;
133 if (ret == IDCANCEL)
134 return false;
135 if (ret == IDYES)
137 CString key;
138 key.Format(L"branch.%s.remote", static_cast<LPCWSTR>(m_strLocalBranch));
139 g_Git.SetConfigValue(key, m_strURL);
140 key.Format(L"branch.%s.merge", static_cast<LPCWSTR>(m_strLocalBranch));
141 g_Git.SetConfigValue(key, L"refs/heads/" + g_Git.StripRefName(remoteBranch));
144 return true;
147 void CSyncDlg::OnBnClickedButtonPull()
149 bool bShift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
151 int CurrentEntry = static_cast<int>(this->m_ctrlPull.GetCurrentEntry());
152 this->m_regPullButton = CurrentEntry;
154 if (bShift && CurrentEntry > 1)
155 return;
157 this->m_bAbort=false;
158 this->m_GitCmdList.clear();
159 m_ctrlCmdOut.SetWindowText(L"");
160 m_LogText.Empty();
162 this->UpdateData();
163 UpdateCombox();
165 if (g_Git.GetHash(m_oldHash, L"HEAD"))
167 MessageBox(g_Git.GetGitLastErr(L"Could not get HEAD hash."), L"TortoiseGit", MB_ICONERROR);
168 return;
171 m_refList.Clear();
172 m_newHashMap.clear();
173 m_oldHashMap.clear();
175 if( CurrentEntry == 0)
177 CGitHash localBranchHash;
178 if (g_Git.GetHash(localBranchHash, m_strLocalBranch))
180 MessageBox(g_Git.GetGitLastErr(L"Could not get hash of \"" + m_strLocalBranch + L"\"."), L"TortoiseGit", MB_ICONERROR);
181 return;
183 if (localBranchHash != m_oldHash || m_strLocalBranch != g_Git.GetCurrentBranch())
185 CString tmp;
186 tmp.Format(IDS_PROC_SYNC_SWITCHTO, static_cast<LPCWSTR>(m_strLocalBranch));
187 if (CMessageBox::Show(GetSafeHwnd(), CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PULLWRONGBRANCH)), L"TortoiseGit", 2, IDI_QUESTION, tmp, CString(MAKEINTRESOURCE(IDS_ABORTBUTTON))) == 2)
188 return;
190 CString cmd;
191 cmd.Format(L"git.exe checkout %s --", static_cast<LPCWSTR>(m_strLocalBranch));
193 CProgressDlg progress(this);
194 progress.m_AutoClose = AUTOCLOSE_IF_NO_ERRORS;
195 progress.m_GitCmd = cmd;
196 if (progress.DoModal() != IDOK || progress.m_GitStatus != 0)
197 return;
201 if(this->m_strURL.IsEmpty())
203 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_GITCONFIG_URLEMPTY, IDS_APPNAME, MB_OK | MB_ICONERROR);
204 return;
207 if (CurrentEntry == 6)
209 SwitchToRun();
210 m_ctrlTabCtrl.ShowTab(IDC_LOG - 1, false);
211 m_ctrlTabCtrl.ShowTab(IDC_REFLIST - 1, false);
212 m_ctrlTabCtrl.ShowTab(IDC_OUT_LOGLIST - 1, false);
213 m_ctrlTabCtrl.ShowTab(IDC_OUT_CHANGELIST - 1, false);
214 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, false);
215 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST - 1, false);
216 m_ctrlTabCtrl.ShowTab(IDC_IN_CONFLICT - 1, false);
217 m_ctrlTabCtrl.ShowTab(IDC_TAGCOMPARELIST - 1, true);
219 CSysProgressDlg sysProgressDlg;
220 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
221 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_LOADING)));
222 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
223 sysProgressDlg.SetShowProgressBar(false);
224 sysProgressDlg.ShowModal(this, true);
225 CString err;
226 auto ret = m_tagCompareList.Fill(m_strURL, err);
227 sysProgressDlg.Stop();
228 if (ret)
229 MessageBox(err, L"TortoiseGit", MB_ICONERROR);
231 BringWindowToTop();
232 SwitchToInput();
233 EnableControlButton();
234 return;
237 if (!IsURL() && !m_strRemoteBranch.IsEmpty() && CurrentEntry == 0 && CRegDWORD(L"Software\\TortoiseGit\\AskSetTrackedBranch", TRUE) == TRUE)
239 if (!AskSetTrackedBranch())
240 return;
243 if (m_bAutoLoadPuttyKey && CurrentEntry != 4) // CurrentEntry (Remote Update) handles this on its own)
245 CAppUtils::LaunchPAgent(this->GetSafeHwnd(), nullptr, &m_strURL);
248 if (g_Git.GetMapHashToFriendName(m_oldHashMap))
249 MessageBox(g_Git.GetGitLastErr(L"Could not get all refs."), L"TortoiseGit", MB_ICONERROR);
251 if (bShift && (CurrentEntry == 0 || CurrentEntry == 1))
253 if (CurrentEntry == 1 || CurrentEntry == 2 || CurrentEntry == 3)
254 CAppUtils::Fetch(GetSafeHwnd(), !IsURL() ? m_strURL : L"");
255 else
256 CAppUtils::Pull(GetSafeHwnd());
258 FillNewRefMap();
259 FetchOutList(true);
261 int hasConflicts = g_Git.HasWorkingTreeConflicts();
262 if (hasConflicts < 0)
264 this->m_ctrlCmdOut.SetSel(-1, -1);
265 this->m_ctrlCmdOut.ReplaceSel(g_Git.GetGitLastErr(L"Checking for conflicts failed.", CGit::GIT_CMD_CHECKCONFLICTS));
267 this->ShowTab(IDC_CMD_LOG);
268 return;
271 if (hasConflicts)
273 this->m_ConflictFileList.Clear();
274 this->m_ConflictFileList.GetStatus(nullptr, true);
275 this->m_ConflictFileList.Show(CTGitPath::LOGACTIONS_UNMERGED,
276 CTGitPath::LOGACTIONS_UNMERGED);
278 this->ShowTab(IDC_IN_CONFLICT);
279 CMessageBox::ShowCheck(GetSafeHwnd(), IDS_NEED_TO_RESOLVE_CONFLICTS_HINT, IDS_APPNAME, MB_ICONINFORMATION, L"MergeConflictsNeedsCommit", IDS_MSGBOX_DONOTSHOWAGAIN);
281 else
282 ShowInCommits(L"HEAD");
284 return;
287 CString force;
288 if(this->m_bForce)
289 force = L" --force";
291 CString cmd;
293 m_iPullRebase = 0;
294 if (CurrentEntry == 0) // check whether we need to override Pull if pull.rebase is set
296 CAutoRepository repo(g_Git.GetGitRepository());
297 if (!repo)
298 MessageBox(CGit::GetLibGit2LastErr(L"Could not open repository."), L"TortoiseGit", MB_OK | MB_ICONERROR);
300 // Check config branch.<name>.rebase and pull.reabse
303 if (!repo)
304 break;
306 if (git_repository_head_detached(repo) == 1)
307 break;
309 CAutoConfig config(true);
310 if (git_repository_config(config.GetPointer(), repo))
311 break;
313 // branch.<name>.rebase overrides pull.rebase
314 if (config.GetBOOL(L"branch." + g_Git.GetCurrentBranch() + L".rebase", m_iPullRebase) == GIT_ENOTFOUND)
316 if (config.GetBOOL(L"pull.rebase", m_iPullRebase) == GIT_ENOTFOUND)
317 break;
318 else
320 CString value;
321 config.GetString(L"pull.rebase", value);
322 if (value == L"preserve")
324 m_iPullRebase = 2;
325 break;
329 else
331 CString value;
332 config.GetString(L"branch." + g_Git.GetCurrentBranch() + L".rebase", value);
333 if (value == L"preserve")
335 m_iPullRebase = 2;
336 break;
339 } while (0);
340 if (m_iPullRebase > 0)
342 CurrentEntry = 1;
343 if (m_strRemoteBranch.IsEmpty())
345 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_PULL_EMPTYBRANCH, IDS_APPNAME, MB_ICONEXCLAMATION);
346 return;
351 SwitchToRun();
353 ShowTab(IDC_CMD_LOG);
355 m_ctrlTabCtrl.ShowTab(IDC_REFLIST - 1, true);
356 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, false);
357 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST - 1, false);
358 m_ctrlTabCtrl.ShowTab(IDC_IN_CONFLICT - 1, false);
359 m_ctrlTabCtrl.ShowTab(IDC_TAGCOMPARELIST - 1, false);
361 ///Pull
362 if(CurrentEntry == 0) //Pull
364 CString remotebranch;
365 remotebranch = m_strRemoteBranch;
367 if(!IsURL())
369 CString pullRemote, pullBranch;
370 g_Git.GetRemoteTrackedBranch(m_strLocalBranch, pullRemote, pullBranch);
371 if(pullBranch == remotebranch && pullRemote == this->m_strURL)
372 remotebranch.Empty();
375 cmd.Format(L"git.exe pull -v --progress%s \"%s\" %s",
376 static_cast<LPCWSTR>(force),
377 static_cast<LPCWSTR>(m_strURL),
378 static_cast<LPCWSTR>(remotebranch));
380 m_CurrentCmd = GIT_COMMAND_PULL;
381 m_GitCmdList.push_back(cmd);
383 StartWorkerThread();
386 ///Fetch
387 if (CurrentEntry == 1 || CurrentEntry == 2 || CurrentEntry == 3)
389 m_oldRemoteHash.Empty();
390 CString remotebranch;
391 if (CurrentEntry == 3)
392 m_strRemoteBranch.Empty();
393 else if (IsURL() || m_strRemoteBranch.IsEmpty())
395 remotebranch=this->m_strRemoteBranch;
398 else
400 remotebranch.Format(L"remotes/%s/%s", static_cast<LPCWSTR>(m_strURL), static_cast<LPCWSTR>(m_strRemoteBranch));
401 g_Git.GetHash(m_oldRemoteHash, remotebranch);
402 if (m_oldRemoteHash.IsEmpty())
403 remotebranch=m_strRemoteBranch;
404 else
405 remotebranch = m_strRemoteBranch + L':' + remotebranch;
408 if (CurrentEntry == 1 || CurrentEntry == 3)
409 m_CurrentCmd = GIT_COMMAND_FETCH;
410 else
411 m_CurrentCmd = GIT_COMMAND_FETCHANDREBASE;
413 if (g_Git.UsingLibGit2(CGit::GIT_CMD_FETCH))
415 CString refspec;
416 if (!remotebranch.IsEmpty())
417 refspec.Format(L"refs/heads/%s:refs/remotes/%s/%s", static_cast<LPCWSTR>(m_strRemoteBranch), static_cast<LPCWSTR>(m_strURL), static_cast<LPCWSTR>(m_strRemoteBranch));
419 progressCommand = std::make_unique<FetchProgressCommand>();
420 FetchProgressCommand* fetchProgressCommand = static_cast<FetchProgressCommand*>(progressCommand.get());
421 fetchProgressCommand->SetUrl(m_strURL);
422 fetchProgressCommand->SetRefSpec(refspec);
423 m_GitProgressList.SetCommand(progressCommand.get());
424 m_GitProgressList.Init();
425 ShowTab(IDC_CMD_GIT_PROG);
427 else
429 cmd.Format(L"git.exe fetch --progress -v%s \"%s\" %s",
430 static_cast<LPCWSTR>(force),
431 static_cast<LPCWSTR>(m_strURL),
432 static_cast<LPCWSTR>(remotebranch));
434 m_GitCmdList.push_back(cmd);
436 StartWorkerThread();
440 ///Remote Update
441 if (CurrentEntry == 4)
443 if (m_bAutoLoadPuttyKey)
445 for (size_t i = 0; i < m_remotelist.size(); ++i)
446 CAppUtils::LaunchPAgent(this->GetSafeHwnd(), nullptr, &m_remotelist[i]);
449 m_CurrentCmd = GIT_COMMAND_REMOTE;
450 cmd = L"git.exe remote update";
451 m_GitCmdList.push_back(cmd);
453 StartWorkerThread();
456 ///Cleanup stale remote banches
457 if (CurrentEntry == 5)
459 m_CurrentCmd = GIT_COMMAND_REMOTE;
460 cmd.Format(L"git.exe remote prune \"%s\"", static_cast<LPCWSTR>(m_strURL));
461 m_GitCmdList.push_back(cmd);
463 StartWorkerThread();
467 void CSyncDlg::ShowInCommits(const CString& friendname)
469 CGitHash newHash;
471 if (g_Git.GetHash(newHash, friendname))
473 MessageBox(g_Git.GetGitLastErr(L"Could not get " + friendname + L" hash."), L"TortoiseGit", MB_ICONERROR);
474 return;
477 if (newHash == m_oldHash)
479 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST - 1, false);
480 m_InLogList.ShowText(CString(MAKEINTRESOURCE(IDS_UPTODATE)));
481 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, true);
482 ShowTab(IDC_REFLIST);
484 else
486 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST - 1, true);
487 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, true);
489 AddDiffFileList(&m_InChangeFileList, &m_arInChangeList, newHash, m_oldHash);
491 CString range;
492 range.Format(L"%s..%s", static_cast<LPCWSTR>(m_oldHash.ToString()), static_cast<LPCWSTR>(newHash.ToString()));
493 m_InLogList.FillGitLog(nullptr, &range, CGit::LOG_INFO_STAT | CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
494 ShowTab(IDC_IN_LOGLIST);
498 void CSyncDlg::PullComplete()
500 EnableControlButton(true);
501 SwitchToInput();
502 this->FetchOutList(true);
504 if( this ->m_GitCmdStatus )
506 int hasConflicts = g_Git.HasWorkingTreeConflicts();
507 if (hasConflicts < 0)
509 this->m_ctrlCmdOut.SetSel(-1,-1);
510 this->m_ctrlCmdOut.ReplaceSel(g_Git.GetGitLastErr(L"Checking for conflicts failed.", CGit::GIT_CMD_CHECKCONFLICTS));
512 this->ShowTab(IDC_CMD_LOG);
513 return;
516 if (hasConflicts)
518 this->m_ConflictFileList.Clear();
519 this->m_ConflictFileList.GetStatus(nullptr, true);
520 this->m_ConflictFileList.Show(CTGitPath::LOGACTIONS_UNMERGED,
521 CTGitPath::LOGACTIONS_UNMERGED);
523 this->ShowTab(IDC_IN_CONFLICT);
524 CMessageBox::ShowCheck(GetSafeHwnd(), IDS_NEED_TO_RESOLVE_CONFLICTS_HINT, IDS_APPNAME, MB_ICONINFORMATION, L"MergeConflictsNeedsCommit", IDS_MSGBOX_DONOTSHOWAGAIN);
526 else
527 this->ShowTab(IDC_CMD_LOG);
530 else
531 ShowInCommits(L"HEAD");
534 void CSyncDlg::FetchComplete()
536 EnableControlButton(true);
537 SwitchToInput();
539 if (g_Git.UsingLibGit2(CGit::GIT_CMD_FETCH))
540 ShowTab(IDC_CMD_GIT_PROG);
541 else
542 ShowTab(IDC_REFLIST);
544 if (m_GitCmdStatus || (m_CurrentCmd != GIT_COMMAND_FETCHANDREBASE && m_iPullRebase == 0))
546 FetchOutList(true);
547 return;
550 CString remote;
551 CString remotebranch;
552 CString upstream = L"FETCH_HEAD";
553 m_ctrlURL.GetWindowText(remote);
554 if (!remote.IsEmpty())
556 if (std::find(m_remotelist.cbegin(), m_remotelist.cend(), remote) == m_remotelist.cend())
557 remote.Empty();
559 m_ctrlRemoteBranch.GetWindowText(remotebranch);
560 if (!remote.IsEmpty() && !remotebranch.IsEmpty())
561 upstream = L"remotes/" + remote + L'/' + remotebranch;
563 if (m_iPullRebase > 0)
565 CAppUtils::RebaseAfterFetch(GetSafeHwnd(), upstream, m_iPullRebase ? 2 : 0, m_iPullRebase == 2);
566 FillNewRefMap();
567 FetchOutList(true);
569 ShowInCommits(L"HEAD");
571 return;
574 CGitHash remoteBranchHash;
575 g_Git.GetHash(remoteBranchHash, upstream);
576 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)
578 ShowInCommits(upstream);
579 return;
582 if (g_Git.IsFastForward(L"HEAD", upstream))
584 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);
585 if (ret == 3)
586 return;
587 if (ret == 1)
589 CProgressDlg mergeProgress;
590 mergeProgress.m_GitCmd = L"git.exe merge --ff-only " + upstream;
591 mergeProgress.m_AutoClose = AUTOCLOSE_IF_NO_ERRORS;
592 mergeProgress.m_PostCmdCallback = [](DWORD status, PostCmdList& postCmdList)
594 if (status && g_Git.HasWorkingTreeConflicts())
596 // there are conflict files
597 postCmdList.emplace_back(IDI_RESOLVE, IDS_PROGRS_CMD_RESOLVE, []
599 CString sCmd;
600 sCmd.Format(L"/command:commit /path:\"%s\"", static_cast<LPCWSTR>(g_Git.m_CurrentDir));
601 CAppUtils::RunTortoiseGitProc(sCmd);
605 mergeProgress.DoModal();
606 FillNewRefMap();
607 FetchOutList(true);
609 ShowInCommits(L"HEAD");
611 return;
615 CAppUtils::RebaseAfterFetch(GetSafeHwnd(), upstream);
616 FillNewRefMap();
617 FetchOutList(true);
619 ShowInCommits(L"HEAD");
622 void CSyncDlg::StashComplete()
624 EnableControlButton(true);
625 INT_PTR entry = m_ctrlStash.GetCurrentEntry();
626 if (entry != 1 && entry != 2)
627 return;
629 SwitchToInput();
630 if (m_GitCmdStatus)
632 int hasConflicts = g_Git.HasWorkingTreeConflicts();
633 if (hasConflicts < 0)
635 m_ctrlCmdOut.SetSel(-1, -1);
636 m_ctrlCmdOut.ReplaceSel(g_Git.GetGitLastErr(L"Checking for conflicts failed.", CGit::GIT_CMD_CHECKCONFLICTS));
638 ShowTab(IDC_CMD_LOG);
639 return;
642 if (hasConflicts)
644 m_ConflictFileList.Clear();
645 m_ConflictFileList.GetStatus(nullptr, true);
646 m_ConflictFileList.Show(CTGitPath::LOGACTIONS_UNMERGED, CTGitPath::LOGACTIONS_UNMERGED);
648 ShowTab(IDC_IN_CONFLICT);
650 else
651 ShowTab(IDC_CMD_LOG);
655 void CSyncDlg::OnBnClickedButtonPush()
657 bool bShift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
658 this->UpdateData();
659 UpdateCombox();
661 if (bShift)
663 if (m_ctrlPush.GetCurrentEntry() == 0)
665 CAppUtils::Push(GetSafeHwnd(), g_Git.FixBranchName(m_strLocalBranch));
666 FillNewRefMap();
667 FetchOutList(true);
669 return;
672 m_ctrlCmdOut.SetWindowText(L"");
673 m_LogText.Empty();
675 if(this->m_strURL.IsEmpty())
677 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_GITCONFIG_URLEMPTY, IDS_APPNAME, MB_OK | MB_ICONERROR);
678 return;
681 if (!IsURL() && m_ctrlPush.GetCurrentEntry() == 0 && CRegDWORD(L"Software\\TortoiseGit\\AskSetTrackedBranch", TRUE) == TRUE)
683 if (!AskSetTrackedBranch())
684 return;
687 this->m_regPushButton = static_cast<DWORD>(this->m_ctrlPush.GetCurrentEntry());
688 this->SwitchToRun();
689 this->m_bAbort=false;
690 this->m_GitCmdList.clear();
692 ShowTab(IDC_CMD_LOG);
694 CString cmd;
695 CString arg;
697 CString error;
698 DWORD exitcode = 0xFFFFFFFF;
699 CHooks::Instance().SetProjectProperties(g_Git.m_CurrentDir, m_ProjectProperties);
700 if (CHooks::Instance().PrePush(GetSafeHwnd(), g_Git.m_CurrentDir, exitcode, error))
702 if (exitcode)
704 CString sErrorMsg;
705 sErrorMsg.Format(IDS_HOOK_ERRORMSG, static_cast<LPCWSTR>(error));
706 CTaskDialog taskdlg(sErrorMsg, CString(MAKEINTRESOURCE(IDS_HOOKFAILED_TASK2)), L"TortoiseGit", 0, TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW | TDF_SIZE_TO_CONTENT);
707 taskdlg.AddCommandControl(101, CString(MAKEINTRESOURCE(IDS_HOOKFAILED_TASK3)));
708 taskdlg.AddCommandControl(102, CString(MAKEINTRESOURCE(IDS_HOOKFAILED_TASK4)));
709 taskdlg.SetDefaultCommandControl(101);
710 taskdlg.SetMainIcon(TD_ERROR_ICON);
711 if (taskdlg.DoModal(GetSafeHwnd()) != 102)
712 return;
716 CString refName = g_Git.FixBranchName(m_strLocalBranch);
717 switch (m_ctrlPush.GetCurrentEntry())
719 case 1:
720 arg += L" --tags";
721 break;
722 case 2:
723 refName = L"refs/notes/commits"; //default ref for notes
724 break;
727 if(this->m_bForce)
728 arg += L" --force";
730 cmd.Format(L"git.exe push -v --progress%s \"%s\" %s",
731 static_cast<LPCWSTR>(arg),
732 static_cast<LPCWSTR>(m_strURL),
733 static_cast<LPCWSTR>(refName));
735 if (!m_strRemoteBranch.IsEmpty() && m_ctrlPush.GetCurrentEntry() != 2)
737 cmd += L':' + m_strRemoteBranch;
740 m_GitCmdList.push_back(cmd);
742 m_CurrentCmd = GIT_COMMAND_PUSH;
744 if(this->m_bAutoLoadPuttyKey)
746 CAppUtils::LaunchPAgent(this->GetSafeHwnd(), nullptr, &m_strURL);
749 StartWorkerThread();
752 void CSyncDlg::OnBnClickedButtonApply()
754 CGitHash oldhash;
755 if (g_Git.GetHash(oldhash, L"HEAD"))
757 MessageBox(g_Git.GetGitLastErr(L"Could not get HEAD hash."), L"TortoiseGit", MB_ICONERROR);
758 return;
761 CImportPatchDlg dlg;
762 CString cmd,output;
764 if(dlg.DoModal() == IDOK)
766 int err=0;
767 for (int i = 0; i < dlg.m_PathList.GetCount(); ++i)
769 cmd.Format(L"git.exe am \"%s\"", static_cast<LPCWSTR>(dlg.m_PathList[i].GetGitPathString()));
771 if (g_Git.Run(cmd, &output, CP_UTF8))
773 CMessageBox::Show(GetSafeHwnd(), output, L"TortoiseGit", MB_OK | MB_ICONERROR);
775 err=1;
776 break;
778 this->m_ctrlCmdOut.SetSel(-1,-1);
779 this->m_ctrlCmdOut.ReplaceSel(cmd + L'\n');
780 this->m_ctrlCmdOut.SetSel(-1,-1);
781 this->m_ctrlCmdOut.ReplaceSel(output);
784 CGitHash newhash;
785 if (g_Git.GetHash(newhash, L"HEAD"))
787 MessageBox(g_Git.GetGitLastErr(L"Could not get HEAD hash after applying patches."), L"TortoiseGit", MB_ICONERROR);
788 return;
791 this->m_InLogList.Clear();
792 this->m_InChangeFileList.Clear();
794 if(newhash == oldhash)
796 this->m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST-1,false);
797 this->m_InLogList.ShowText(L"No commits get from patch");
798 this->m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST-1,true);
801 else
803 this->m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST-1,true);
804 this->m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST-1,true);
806 CString range;
807 range.Format(L"%s..%s", static_cast<LPCWSTR>(m_oldHash.ToString()), static_cast<LPCWSTR>(newhash.ToString()));
808 this->AddDiffFileList(&m_InChangeFileList, &m_arInChangeList, newhash, oldhash);
809 m_InLogList.FillGitLog(nullptr, &range, CGit::LOG_INFO_STAT| CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
811 this->FetchOutList(true);
814 this->m_ctrlTabCtrl.ShowTab(IDC_CMD_LOG-1,true);
816 if(err)
818 this->ShowTab(IDC_CMD_LOG);
820 else
822 this->ShowTab(IDC_IN_LOGLIST);
827 void CSyncDlg::OnBnClickedButtonEmail()
829 CString cmd, out, err;
831 this->m_strLocalBranch = this->m_ctrlLocalBranch.GetString();
832 this->m_ctrlRemoteBranch.GetWindowText(this->m_strRemoteBranch);
833 this->m_ctrlURL.GetWindowText(this->m_strURL);
834 m_strURL=m_strURL.Trim();
835 m_strRemoteBranch=m_strRemoteBranch.Trim();
837 cmd.Format(L"git.exe format-patch -o \"%s\" %s/%s..%s",
838 static_cast<LPCWSTR>(g_Git.m_CurrentDir),
839 static_cast<LPCWSTR>(m_strURL), static_cast<LPCWSTR>(m_strRemoteBranch), static_cast<LPCWSTR>(g_Git.FixBranchName(m_strLocalBranch)));
841 if (g_Git.Run(cmd, &out, &err, CP_UTF8))
843 CMessageBox::Show(GetSafeHwnd(), out + L'\n' + err, L"TortoiseGit", MB_OK | MB_ICONERROR);
844 return ;
847 CAppUtils::SendPatchMail(GetSafeHwnd(), cmd, out);
849 void CSyncDlg::ShowProgressCtrl(bool bShow)
851 int b=bShow?SW_NORMAL:SW_HIDE;
852 this->m_ctrlAnimate.ShowWindow(b);
853 this->m_ctrlProgress.ShowWindow(b);
854 this->m_ctrlProgLabel.ShowWindow(b);
855 this->m_ctrlAnimate.Open(IDR_DOWNLOAD);
856 if (b == SW_NORMAL && CRegDWORD(L"Software\\TortoiseGit\\DownloadAnimation", TRUE) == TRUE)
857 this->m_ctrlAnimate.Play(0, UINT_MAX, UINT_MAX);
858 else
859 this->m_ctrlAnimate.Stop();
861 void CSyncDlg::ShowInputCtrl(bool bShow)
863 int b=bShow?SW_NORMAL:SW_HIDE;
864 this->m_ctrlURL.ShowWindow(b);
865 this->m_ctrlLocalBranch.ShowWindow(b);
866 this->m_ctrlRemoteBranch.ShowWindow(b);
867 this->GetDlgItem(IDC_BUTTON_LOCAL_BRANCH)->ShowWindow(b);
868 this->GetDlgItem(IDC_BUTTON_REMOTE_BRANCH)->ShowWindow(b);
869 this->GetDlgItem(IDC_STATIC_LOCAL_BRANCH)->ShowWindow(b);
870 this->GetDlgItem(IDC_STATIC_REMOTE_BRANCH)->ShowWindow(b);
871 this->GetDlgItem(IDC_BUTTON_MANAGE)->ShowWindow(b);
872 this->GetDlgItem(IDC_CHECK_PUTTY_KEY)->ShowWindow(b);
873 this->GetDlgItem(IDC_CHECK_FORCE)->ShowWindow(b);
874 this->GetDlgItem(IDC_STATIC_REMOTE_URL)->ShowWindow(b);
876 BOOL CSyncDlg::OnInitDialog()
878 CResizableStandAloneDialog::OnInitDialog();
879 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
881 // Let the TaskbarButtonCreated message through the UIPI filter. If we don't
882 // do this, Explorer would be unable to send that message to our window if we
883 // were running elevated. It's OK to make the call all the time, since if we're
884 // not elevated, this is a no-op.
885 CHANGEFILTERSTRUCT cfs = { sizeof(CHANGEFILTERSTRUCT) };
886 typedef BOOL STDAPICALLTYPE ChangeWindowMessageFilterExDFN(HWND hWnd, UINT message, DWORD action, PCHANGEFILTERSTRUCT pChangeFilterStruct);
887 CAutoLibrary hUser = AtlLoadSystemLibraryUsingFullPath(L"user32.dll");
888 if (hUser)
890 auto pfnChangeWindowMessageFilterEx = reinterpret_cast<ChangeWindowMessageFilterExDFN*>(GetProcAddress(hUser, "ChangeWindowMessageFilterEx"));
891 if (pfnChangeWindowMessageFilterEx)
892 pfnChangeWindowMessageFilterEx(m_hWnd, TaskBarButtonCreated, MSGFLT_ALLOW, &cfs);
894 m_pTaskbarList.Release();
895 if (FAILED(m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList)))
896 m_pTaskbarList = nullptr;
898 this->GetDlgItem(IDC_CHECK_PUTTY_KEY)->EnableWindow(CAppUtils::IsSSHPutty());
901 this->m_ctrlAnimate.ShowWindow(SW_NORMAL);
902 this->m_ctrlAnimate.Open(IDR_DOWNLOAD);
903 this->m_ctrlAnimate.Play(0,-1,-1);
906 // ------------------ Create Tabctrl -----------
907 CWnd *pwnd=this->GetDlgItem(IDC_BUTTON_TABCTRL);
908 CRect rectDummy;
909 pwnd->GetWindowRect(&rectDummy);
910 this->ScreenToClient(rectDummy);
912 if (CTheme::Instance().IsDarkTheme())
913 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CThemeMFCVisualManager));
914 if (!m_ctrlTabCtrl.Create(CTheme::Instance().IsDarkTheme() ? CMFCTabCtrl::STYLE_3D : CMFCTabCtrl::STYLE_FLAT, rectDummy, this, IDC_SYNC_TAB))
916 TRACE0("Failed to create output tab window\n");
917 return FALSE; // fail to create
919 m_ctrlTabCtrl.SetResizeMode(CMFCTabCtrl::RESIZE_NO);
921 // -------------Create Command Log Ctrl ---------
922 DWORD dwStyle = ES_MULTILINE | ES_READONLY | WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_AUTOVSCROLL |WS_VSCROLL;
924 if( !m_ctrlCmdOut.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_CMD_LOG))
926 TRACE0("Failed to create Log commits window\n");
927 return FALSE; // fail to create
930 // set the font to use in the log message view, configured in the settings dialog
931 CFont m_logFont;
932 CAppUtils::CreateFontForLogs(m_logFont);
933 m_ctrlCmdOut.SetFont(&m_logFont);
934 m_ctrlTabCtrl.InsertTab(&m_ctrlCmdOut, CString(MAKEINTRESOURCE(IDS_LOG)), -1);
935 // make the log message rich edit control send a message when the mouse pointer is over a link
936 m_ctrlCmdOut.SendMessage(EM_SETEVENTMASK, NULL, ENM_LINK | ENM_SCROLL);
938 //---------- Create in coming list ctrl -----------
939 dwStyle =LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;;
941 if( !m_InLogList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_IN_LOGLIST))
943 TRACE0("Failed to create output commits window\n");
944 return FALSE; // fail to create
946 // for some unknown reason, the SetExtendedStyle in OnCreate/PreSubclassWindow is not working here
947 m_InLogList.SetStyle();
949 m_ctrlTabCtrl.InsertTab(&m_InLogList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_INCOMMITS)), -1);
951 m_InLogList.m_ColumnRegKey = L"SyncIn";
952 m_InLogList.InsertGitColumn();
954 //----------- Create In Change file list -----------
955 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
957 if( !m_InChangeFileList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_IN_CHANGELIST))
959 TRACE0("Failed to create output change files window\n");
960 return FALSE; // fail to create
962 m_ctrlTabCtrl.InsertTab(&m_InChangeFileList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_INCHANGELIST)), -1);
964 m_InChangeFileList.Init(GITSLC_COLEXT | GITSLC_COLSTATUS |GITSLC_COLADD|GITSLC_COLDEL, L"InSyncDlg",
965 (CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_COMPARETWOREVISIONS) |
966 CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_GNUDIFF2REVISIONS)), false, false, GITSLC_COLEXT | GITSLC_COLSTATUS | GITSLC_COLADD | GITSLC_COLDEL);
969 //---------- Create Conflict List Ctrl -----------------
970 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
972 if( !m_ConflictFileList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_IN_CONFLICT))
974 TRACE0("Failed to create output change files window\n");
975 return FALSE; // fail to create
977 m_ctrlTabCtrl.InsertTab(&m_ConflictFileList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_CONFLICTS)), -1);
979 m_ConflictFileList.Init(GITSLC_COLEXT | GITSLC_COLSTATUS |GITSLC_COLADD|GITSLC_COLDEL, L"ConflictSyncDlg",
980 (GITSLC_POPEXPLORE | GITSLC_POPOPEN | GITSLC_POPSHOWLOG |
981 GITSLC_POPCONFLICT|GITSLC_POPRESOLVE),false);
984 //---------- Create Commit Out List Ctrl---------------
986 dwStyle =LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;;
988 if( !m_OutLogList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_OUT_LOGLIST))
990 TRACE0("Failed to create output commits window\n");
991 return FALSE; // fail to create
994 // for some unknown reason, the SetExtendedStyle in OnCreate/PreSubclassWindow is not working here
995 m_OutLogList.SetStyle();
997 m_ctrlTabCtrl.InsertTab(&m_OutLogList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_OUTCOMMITS)), -1);
999 m_OutLogList.m_ColumnRegKey = L"SyncOut";
1000 m_OutLogList.InsertGitColumn();
1002 //------------- Create Change File List Control ----------------
1004 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
1006 if( !m_OutChangeFileList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_OUT_CHANGELIST))
1008 TRACE0("Failed to create output change files window\n");
1009 return FALSE; // fail to create
1011 m_ctrlTabCtrl.InsertTab(&m_OutChangeFileList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_OUTCHANGELIST)), -1);
1013 m_OutChangeFileList.Init(GITSLC_COLEXT | GITSLC_COLSTATUS | GITSLC_COLADD | GITSLC_COLDEL, L"OutSyncDlg",
1014 (CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_COMPARETWOREVISIONS) |
1015 CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_GNUDIFF2REVISIONS)), false, false, GITSLC_COLEXT | GITSLC_COLSTATUS | GITSLC_COLADD | GITSLC_COLDEL);
1017 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | LVS_SINGLESEL | WS_CHILD | WS_VISIBLE;
1018 if (!m_GitProgressList.Create(dwStyle | LVS_OWNERDATA, rectDummy, &m_ctrlTabCtrl, IDC_CMD_GIT_PROG))
1020 TRACE0("Failed to create Git Progress List Window\n");
1021 return FALSE; // fail to create
1023 m_ctrlTabCtrl.InsertTab(&m_GitProgressList, CString(MAKEINTRESOURCE(IDS_LOG)), -1);
1024 m_GitProgressList.m_pAnimate = &m_ctrlAnimate;
1025 m_GitProgressList.m_pPostWnd = this;
1026 m_GitProgressList.m_pProgressLabelCtrl = &m_ctrlProgLabel;
1027 m_GitProgressList.m_pProgControl = &m_ctrlProgress;
1028 m_GitProgressList.m_pTaskbarList = m_pTaskbarList;
1030 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE | LVS_SINGLESEL;
1031 DWORD exStyle = LVS_EX_HEADERDRAGDROP | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP;
1032 if (CRegDWORD(L"Software\\TortoiseGit\\FullRowSelect", TRUE))
1033 exStyle |= LVS_EX_FULLROWSELECT;
1034 if (g_Git.m_IsUseLibGit2)
1036 m_refList.Create(dwStyle, rectDummy, &m_ctrlTabCtrl, IDC_REFLIST);
1037 m_refList.SetExtendedStyle(exStyle);
1038 m_refList.Init();
1039 m_ctrlTabCtrl.InsertTab(&m_refList, CString(MAKEINTRESOURCE(IDS_REFLIST)), -1);
1041 m_tagCompareList.Create(dwStyle, rectDummy, &m_ctrlTabCtrl, IDC_TAGCOMPARELIST);
1042 m_tagCompareList.SetExtendedStyle(exStyle);
1043 m_tagCompareList.Init();
1044 m_ctrlTabCtrl.InsertTab(&m_tagCompareList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_COMPARETAGS)), -1);
1046 m_ProjectProperties.ReadProps();
1048 AdjustControlSize(IDC_CHECK_PUTTY_KEY);
1049 AdjustControlSize(IDC_CHECK_FORCE);
1051 AddAnchor(IDC_SYNC_TAB,TOP_LEFT,BOTTOM_RIGHT);
1053 AddAnchor(IDC_GROUP_INFO,TOP_LEFT,TOP_RIGHT);
1054 AddAnchor(IDC_COMBOBOXEX_URL,TOP_LEFT,TOP_RIGHT);
1055 AddAnchor(IDC_BUTTON_MANAGE,TOP_RIGHT);
1056 AddAnchor(IDC_BUTTON_PULL,BOTTOM_LEFT);
1057 AddAnchor(IDC_BUTTON_PUSH,BOTTOM_LEFT);
1058 AddAnchor(IDC_BUTTON_SUBMODULE,BOTTOM_LEFT);
1059 AddAnchor(IDC_BUTTON_STASH,BOTTOM_LEFT);
1060 AddAnchor(IDC_BUTTON_APPLY,BOTTOM_RIGHT);
1061 AddAnchor(IDC_BUTTON_EMAIL,BOTTOM_RIGHT);
1062 AddAnchor(IDC_PROGRESS_SYNC,TOP_LEFT,TOP_RIGHT);
1063 AddAnchor(IDOK,BOTTOM_RIGHT);
1064 AddAnchor(IDHELP,BOTTOM_RIGHT);
1065 AddAnchor(IDC_STATIC_STATUS, BOTTOM_LEFT, BOTTOM_RIGHT);
1066 AddAnchor(IDC_ANIMATE_SYNC,TOP_LEFT);
1067 AddAnchor(IDC_BUTTON_COMMIT,BOTTOM_LEFT);
1068 AddAnchor(IDC_LOG, BOTTOM_LEFT);
1070 // do not use BRANCH_COMBOX_ADD_ANCHOR here, we want to have different stylings
1071 AddAnchor(IDC_COMBOBOXEX_LOCAL_BRANCH, TOP_LEFT,TOP_CENTER);
1072 AddAnchor(IDC_COMBOBOXEX_REMOTE_BRANCH, TOP_CENTER, TOP_RIGHT);
1073 AddAnchor(IDC_BUTTON_LOCAL_BRANCH, TOP_CENTER);
1074 AddAnchor(IDC_BUTTON_REMOTE_BRANCH, TOP_RIGHT);
1075 AddAnchor(IDC_STATIC_REMOTE_BRANCH, TOP_CENTER);
1076 AddAnchor(IDC_PROG_LABEL, TOP_LEFT);
1078 CString WorkingDir=g_Git.m_CurrentDir;
1079 WorkingDir.Replace(L':', L'_');
1080 m_RegKeyRemoteBranch = L"Software\\TortoiseGit\\History\\SyncBranch\\" + WorkingDir;
1083 this->AddOthersToAnchor();
1085 this->m_ctrlPush.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PUSH)));
1086 this->m_ctrlPush.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PUSHTAGS)));
1087 this->m_ctrlPush.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PUSHNOTES)));
1089 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PULL)));
1090 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_FETCH)));
1091 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_FETCHREBASE)));
1092 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_FETCHALL)));
1093 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_REMOTEUPDATE)));
1094 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_CLEANUPSTALEBRANCHES)));
1095 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_COMPARETAGS)));
1097 this->m_ctrlSubmodule.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_SUBKODULEUPDATE)));
1098 this->m_ctrlSubmodule.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_SUBKODULEINIT)));
1099 this->m_ctrlSubmodule.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_SUBKODULESYNC)));
1101 this->m_ctrlStash.AddEntry(CString(MAKEINTRESOURCE(IDS_MENUSTASHSAVE)));
1102 this->m_ctrlStash.AddEntry(CString(MAKEINTRESOURCE(IDS_MENUSTASHPOP)));
1103 this->m_ctrlStash.AddEntry(CString(MAKEINTRESOURCE(IDS_MENUSTASHAPPLY)));
1105 WorkingDir.Replace(L':', L'_');
1107 CString regkey ;
1108 regkey.Format(L"Software\\TortoiseGit\\TortoiseProc\\Sync\\%s", static_cast<LPCWSTR>(WorkingDir));
1110 this->m_regPullButton = CRegDWORD(regkey + L"\\Pull", 0);
1111 this->m_regPushButton = CRegDWORD(regkey + L"\\Push", 0);
1112 this->m_regSubmoduleButton = CRegDWORD(regkey + L"\\Submodule");
1113 this->m_regAutoLoadPutty = CRegDWORD(regkey + L"\\AutoLoadPutty", CAppUtils::IsSSHPutty());
1115 this->UpdateData();
1116 this->m_bAutoLoadPuttyKey = m_regAutoLoadPutty;
1117 if(!CAppUtils::IsSSHPutty())
1118 m_bAutoLoadPuttyKey = false;
1119 this->UpdateData(FALSE);
1121 this->m_ctrlPull.SetCurrentEntry(this->m_regPullButton);
1122 this->m_ctrlPush.SetCurrentEntry(this->m_regPushButton);
1123 this->m_ctrlSubmodule.SetCurrentEntry(this->m_regSubmoduleButton);
1125 CString sWindowTitle;
1126 GetWindowText(sWindowTitle);
1127 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
1129 EnableSaveRestore(L"SyncDlg");
1131 m_ctrlURL.SetCaseSensitive(TRUE);
1133 m_ctrlURL.SetCustomAutoSuggest(true, true, true);
1134 m_ctrlURL.SetMaxHistoryItems(0x7FFFFFFF);
1135 this->m_ctrlURL.LoadHistory(L"Software\\TortoiseGit\\History\\SyncURL\\" + WorkingDir, L"url");
1137 m_remotelist.clear();
1138 if(!g_Git.GetRemoteList(m_remotelist))
1140 for (unsigned int i = 0; i < m_remotelist.size(); ++i)
1142 m_ctrlURL.AddString(m_remotelist[i]);
1145 m_ctrlURL.SetCurSel(0);
1146 m_ctrlRemoteBranch.SetCurSel(0);
1148 this->LoadBranchInfo();
1150 this->m_bInited=true;
1151 FetchOutList();
1153 m_ctrlTabCtrl.ShowTab(IDC_CMD_LOG-1,false);
1154 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST-1,false);
1155 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST-1,false);
1156 m_ctrlTabCtrl.ShowTab(IDC_IN_CONFLICT-1,false);
1157 m_ctrlTabCtrl.ShowTab(IDC_CMD_GIT_PROG-1, false);
1158 m_ctrlTabCtrl.ShowTab(IDC_REFLIST-1, false);
1159 m_ctrlTabCtrl.ShowTab(IDC_TAGCOMPARELIST - 1, false);
1161 m_ctrlRemoteBranch.m_bWantReturn = TRUE;
1162 m_ctrlURL.m_bWantReturn = TRUE;
1164 if (m_seq > 0 && static_cast<DWORD>(CRegDWORD(L"Software\\TortoiseGit\\SyncDialogRandomPos")))
1166 m_seq %= 5;
1167 RECT rect;
1168 GetWindowRect(&rect);
1169 rect.top -= m_seq * 30;
1170 rect.bottom -= m_seq * 30;
1171 if (rect.top < 0)
1173 rect.top += CDPIAware::Instance().ScaleY(150);
1174 rect.bottom += CDPIAware::Instance().ScaleY(150);
1176 MoveWindow(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
1179 SetTheme(CTheme::Instance().IsDarkTheme());
1181 return TRUE; // return TRUE unless you set the focus to a control
1182 // EXCEPTION: OCX Property Pages should return FALSE
1185 void CSyncDlg::OnBnClickedButtonManage()
1187 CAppUtils::LaunchRemoteSetting();
1188 Refresh();
1191 void CSyncDlg::Refresh()
1193 theApp.DoWaitCursor(1);
1195 int lastSelected = m_ctrlURL.GetCurSel();
1196 CString url;
1197 this->m_ctrlURL.GetWindowText(url);
1199 this->m_ctrlURL.Reset();
1200 CString workingDir = g_Git.m_CurrentDir;
1201 workingDir.Replace(L':', L'_');
1202 this->m_ctrlURL.LoadHistory(L"Software\\TortoiseGit\\History\\SyncURL\\" + workingDir, L"url");
1204 bool found = false;
1205 m_remotelist.clear();
1206 if (!g_Git.GetRemoteList(m_remotelist))
1208 for (size_t i = 0; i < m_remotelist.size(); ++i)
1210 m_ctrlURL.AddString(m_remotelist[i]);
1211 if (m_remotelist[i] == url)
1212 found = true;
1215 if (lastSelected >= 0 && !found)
1217 m_ctrlURL.SetCurSel(0);
1218 m_ctrlURL.GetWindowText(url);
1221 CString local;
1222 CString remote;
1223 this->m_ctrlLocalBranch.GetWindowText(local);
1224 this->m_ctrlRemoteBranch.GetWindowText(remote);
1226 this->LoadBranchInfo();
1228 this->m_ctrlLocalBranch.AddString(local);
1229 this->m_ctrlRemoteBranch.AddString(remote);
1230 this->m_ctrlURL.AddString(url);
1232 m_OutLogList.ShowText(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_REFRESHING)));
1233 this->FetchOutList(true);
1234 theApp.DoWaitCursor(-1);
1237 BOOL CSyncDlg::PreTranslateMessage(MSG* pMsg)
1239 if (pMsg->message == WM_KEYDOWN)
1241 switch (pMsg->wParam)
1243 case VK_F5:
1245 if (m_bBlock)
1246 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
1247 Refresh();
1249 break;
1251 /* Avoid TAB control destroy but dialog exist*/
1252 case VK_ESCAPE:
1253 case VK_CANCEL:
1255 wchar_t buff[129];
1256 ::GetClassName(pMsg->hwnd, buff, _countof(buff) - 1);
1258 /* Use MSFTEDIT_CLASS http://msdn.microsoft.com/en-us/library/bb531344.aspx */
1259 if (_wcsnicmp(buff, MSFTEDIT_CLASS, _countof(buff) - 1) == 0 || //Unicode and MFC 2012 and later
1260 _wcsnicmp(buff, RICHEDIT_CLASS, _countof(buff) - 1) == 0 || //ANSI or MFC 2010
1261 _wcsnicmp(buff, L"SysListView32", _countof(buff) - 1) == 0)
1263 this->PostMessage(WM_KEYDOWN,VK_ESCAPE,0);
1264 return TRUE;
1269 return __super::PreTranslateMessage(pMsg);
1271 void CSyncDlg::FetchOutList(bool force)
1273 if (!m_bInited || m_bWantToExit)
1274 return;
1275 m_OutChangeFileList.Clear();
1276 this->m_OutLogList.Clear();
1278 m_ctrlTabCtrl.ShowTab(IDC_OUT_LOGLIST - 1, true);
1279 m_ctrlTabCtrl.ShowTab(IDC_OUT_CHANGELIST - 1, true);
1281 CString remote;
1282 this->m_ctrlURL.GetWindowText(remote);
1283 CString remotebranch;
1284 this->m_ctrlRemoteBranch.GetWindowText(remotebranch);
1285 remotebranch = remote + L'/' + remotebranch;
1286 CGitHash remotebranchHash;
1287 g_Git.GetHash(remotebranchHash, remotebranch);
1289 if(IsURL())
1291 CString str;
1292 str.LoadString(IDS_PROC_SYNC_PUSH_UNKNOWN);
1293 m_OutLogList.ShowText(str);
1294 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,FALSE);
1295 m_OutLocalBranch.Empty();
1296 m_OutRemoteBranch.Empty();
1298 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1299 return ;
1302 else if(remotebranchHash.IsEmpty())
1304 CString str;
1305 str.Format(IDS_PROC_SYNC_PUSH_UNKNOWNBRANCH, static_cast<LPCWSTR>(remotebranch));
1306 m_OutLogList.ShowText(str);
1307 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,FALSE);
1308 m_OutLocalBranch.Empty();
1309 m_OutRemoteBranch.Empty();
1311 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1312 return ;
1314 else
1316 CString localbranch;
1317 localbranch=this->m_ctrlLocalBranch.GetString();
1319 if(localbranch != m_OutLocalBranch || m_OutRemoteBranch != remotebranch || force)
1321 m_OutLogList.ClearText();
1323 CGitHash base, localBranchHash;
1324 bool isFastForward = g_Git.IsFastForward(remotebranch, localbranch, &base);
1326 if (g_Git.GetHash(localBranchHash, localbranch))
1328 MessageBox(g_Git.GetGitLastErr(L"Could not get hash of \"" + localbranch + L"\"."), L"TortoiseGit", MB_ICONERROR);
1329 return;
1331 if (remotebranchHash == localBranchHash)
1333 CString str;
1334 str.FormatMessage(IDS_PROC_SYNC_COMMITSAHEAD, 0, static_cast<LPCWSTR>(remotebranch));
1335 m_OutLogList.ShowText(str);
1336 this->m_ctrlStatus.SetWindowText(str);
1337 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,FALSE);
1338 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1340 else if (isFastForward || m_bForce)
1342 CString range;
1343 range.Format(L"%s..%s", static_cast<LPCWSTR>(g_Git.FixBranchName(remotebranch)), static_cast<LPCWSTR>(g_Git.FixBranchName(localbranch)));
1344 //fast forward
1345 m_OutLogList.FillGitLog(nullptr, &range, CGit::LOG_INFO_STAT | CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
1346 CString str;
1347 str.FormatMessage(IDS_PROC_SYNC_COMMITSAHEAD, m_OutLogList.GetItemCount(), static_cast<LPCWSTR>(remotebranch));
1348 this->m_ctrlStatus.SetWindowText(str);
1350 if (isFastForward)
1351 AddDiffFileList(&m_OutChangeFileList, &m_arOutChangeList, localBranchHash, remotebranchHash);
1352 else
1354 AddDiffFileList(&m_OutChangeFileList, &m_arOutChangeList, localBranchHash, base);
1357 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,TRUE);
1358 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(TRUE);
1360 else
1362 CString str;
1363 str.FormatMessage(IDS_PROC_SYNC_NOFASTFORWARD, static_cast<LPCWSTR>(localbranch), static_cast<LPCWSTR>(remotebranch));
1364 m_OutLogList.ShowText(str);
1365 this->m_ctrlStatus.SetWindowText(str);
1366 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID() - 1, FALSE);
1367 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1370 this->m_OutLocalBranch=localbranch;
1371 this->m_OutRemoteBranch=remotebranch;
1375 bool CSyncDlg::IsURL()
1377 CString str;
1378 this->m_ctrlURL.GetWindowText(str);
1379 return str.Find(L'\\') >= 0 || str.Find(L'/') >= 0;
1382 void CSyncDlg::OnCbnEditchangeComboboxex()
1384 SetTimer(IDT_INPUT, 1000, nullptr);
1385 this->m_OutLogList.ShowText(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_WAINTINPUT)));
1387 //this->FetchOutList();
1390 UINT CSyncDlg::ProgressThread()
1392 m_startTick = GetTickCount64();
1393 m_bDone = false;
1394 STRING_VECTOR list;
1395 CProgressDlg::RunCmdList(this, m_GitCmdList, list, true, nullptr, &this->m_bAbort, &this->m_Databuf);
1396 InterlockedExchange(&m_bBlock, FALSE);
1397 return 0;
1400 LRESULT CSyncDlg::OnProgressUpdateUI(WPARAM wParam,LPARAM lParam)
1402 if (m_bWantToExit)
1403 return 0;
1404 if(wParam == MSG_PROGRESSDLG_START)
1406 m_BufStart = 0;
1407 if (CRegDWORD(L"Software\\TortoiseGit\\DownloadAnimation", TRUE) == TRUE)
1408 m_ctrlAnimate.Play(0, UINT_MAX, UINT_MAX);
1409 this->m_ctrlProgress.SetPos(0);
1410 if (m_pTaskbarList)
1412 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
1413 m_pTaskbarList->SetProgressValue(m_hWnd, 0, 100);
1417 if(wParam == MSG_PROGRESSDLG_END || wParam == MSG_PROGRESSDLG_FAILED)
1419 ULONGLONG tickSpent = GetTickCount64() - m_startTick;
1420 CString strEndTime = CLoglistUtils::FormatDateAndTime(CTime::GetCurrentTime(), DATE_SHORTDATE, true, false);
1422 m_BufStart = 0;
1423 m_Databuf.m_critSec.Lock();
1424 m_Databuf.clear();
1425 m_Databuf.m_critSec.Unlock();
1427 m_bDone = true;
1428 m_ctrlAnimate.Stop();
1429 m_ctrlProgress.SetPos(100);
1430 //this->DialogEnableWindow(IDOK,TRUE);
1433 CString text;
1434 m_ctrlCmdOut.GetWindowText(text);
1435 text.Remove('\r');
1436 CAppUtils::StyleURLs(text, &m_ctrlCmdOut);
1439 auto exitCode = static_cast<DWORD>(lParam);
1440 if (exitCode)
1442 if (m_pTaskbarList)
1444 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_ERROR);
1445 m_pTaskbarList->SetProgressValue(m_hWnd, 100, 100);
1447 CString log;
1448 log.Format(IDS_PROC_PROGRESS_GITUNCLEANEXIT, exitCode);
1449 CString err;
1450 err.Format(L"\r\n\r\n%s (%I64u ms @ %s)\r\n", static_cast<LPCWSTR>(log), tickSpent, static_cast<LPCWSTR>(strEndTime));
1451 CProgressDlg::InsertColorText(this->m_ctrlCmdOut, err, RGB(255,0,0));
1452 if (CRegDWORD(L"Software\\TortoiseGit\\NoSounds", FALSE) == FALSE)
1453 PlaySound(reinterpret_cast<LPCWSTR>(SND_ALIAS_SYSTEMEXCLAMATION), nullptr, SND_ALIAS_ID | SND_ASYNC);
1455 else
1457 if (m_pTaskbarList)
1458 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
1459 CString temp;
1460 temp.LoadString(IDS_SUCCESS);
1461 CString log;
1462 log.Format(L"\r\n%s (%I64u ms @ %s)\r\n", static_cast<LPCWSTR>(temp), tickSpent, static_cast<LPCWSTR>(strEndTime));
1463 CProgressDlg::InsertColorText(this->m_ctrlCmdOut, log, RGB(0,0,255));
1465 m_GitCmdStatus = exitCode;
1467 //if(wParam == MSG_PROGRESSDLG_END)
1468 RunPostAction();
1471 if(lParam != 0)
1472 ParserCmdOutput(static_cast<char>(lParam));
1473 else
1475 m_Databuf.m_critSec.Lock();
1476 for (size_t i = m_BufStart; i < m_Databuf.size(); ++i)
1478 char c = m_Databuf[m_BufStart];
1479 ++m_BufStart;
1480 m_Databuf.m_critSec.Unlock();
1481 ParserCmdOutput(c);
1483 m_Databuf.m_critSec.Lock();
1486 if (m_BufStart > 1000)
1488 m_Databuf.erase(m_Databuf.cbegin(), m_Databuf.cbegin() + m_BufStart);
1489 m_BufStart = 0;
1491 m_Databuf.m_critSec.Unlock();
1494 return 0;
1497 static REF_VECTOR HashMapToRefMap(MAP_HASH_NAME& map)
1499 auto rmap = REF_VECTOR();
1500 for (auto mit = map.cbegin(); mit != map.cend(); ++mit)
1502 for (auto rit = mit->second.cbegin(); rit != mit->second.cend(); ++rit)
1504 rmap.emplace_back(TGitRef{ *rit, mit->first });
1507 return rmap;
1510 void CSyncDlg::FillNewRefMap()
1512 m_refList.Clear();
1513 m_newHashMap.clear();
1515 if (!g_Git.m_IsUseLibGit2)
1516 return;
1518 CAutoRepository repo(g_Git.GetGitRepository());
1519 if (!repo)
1521 CMessageBox::Show(m_hWnd, CGit::GetLibGit2LastErr(L"Could not open repository."), L"TortoiseGit", MB_OK | MB_ICONERROR);
1522 return;
1525 if (CGit::GetMapHashToFriendName(repo, m_newHashMap))
1527 MessageBox(CGit::GetLibGit2LastErr(L"Could not get all refs."), L"TortoiseGit", MB_ICONERROR);
1528 return;
1531 auto oldRefMap = HashMapToRefMap(m_oldHashMap);
1532 auto newRefMap = HashMapToRefMap(m_newHashMap);
1533 for (auto oit = oldRefMap.cbegin(); oit != oldRefMap.cend(); ++oit)
1535 bool found = false;
1536 for (auto nit = newRefMap.cbegin(); nit != newRefMap.cend(); ++nit)
1538 // changed ref
1539 if (oit->name == nit->name)
1541 found = true;
1542 m_refList.AddEntry(repo, oit->name, &oit->hash, &nit->hash);
1543 break;
1546 // deleted ref
1547 if (!found)
1548 m_refList.AddEntry(repo, oit->name, &oit->hash, nullptr);
1550 for (auto nit = newRefMap.cbegin(); nit != newRefMap.cend(); ++nit)
1552 bool found = false;
1553 for (auto oit = oldRefMap.cbegin(); oit != oldRefMap.cend(); ++oit)
1555 if (oit->name == nit->name)
1557 found = true;
1558 break;
1561 // new ref
1562 if (!found)
1563 m_refList.AddEntry(repo, nit->name, nullptr, &nit->hash);
1565 m_refList.Show();
1568 void CSyncDlg::RunPostAction()
1570 if (m_bWantToExit)
1571 return;
1573 FillNewRefMap();
1575 if (this->m_CurrentCmd == GIT_COMMAND_PUSH)
1577 DWORD exitcode = 0xFFFFFFFF;
1578 CString error;
1579 CHooks::Instance().SetProjectProperties(g_Git.m_CurrentDir, m_ProjectProperties);
1580 if (CHooks::Instance().PostPush(GetSafeHwnd(), g_Git.m_CurrentDir, exitcode, error))
1582 if (exitcode)
1584 CString temp;
1585 temp.Format(IDS_ERR_HOOKFAILED, static_cast<LPCWSTR>(error));
1586 CMessageBox::Show(GetSafeHwnd(), temp, L"TortoiseGit", MB_OK | MB_ICONERROR);
1587 return;
1591 EnableControlButton(true);
1592 SwitchToInput();
1593 this->FetchOutList(true);
1595 else if (this->m_CurrentCmd == GIT_COMMAND_PULL)
1596 PullComplete();
1597 else if (this->m_CurrentCmd == GIT_COMMAND_FETCH || this->m_CurrentCmd == GIT_COMMAND_FETCHANDREBASE)
1598 FetchComplete();
1599 else if (this->m_CurrentCmd == GIT_COMMAND_SUBMODULE)
1601 //this->m_ctrlCmdOut.SetSel(-1,-1);
1602 //this->m_ctrlCmdOut.ReplaceSel(L"Done\r\n");
1603 //this->m_ctrlCmdOut.SetSel(-1,-1);
1604 EnableControlButton(true);
1605 SwitchToInput();
1607 else if (this->m_CurrentCmd == GIT_COMMAND_STASH)
1608 StashComplete();
1609 else if (this->m_CurrentCmd == GIT_COMMAND_REMOTE)
1611 this->FetchOutList(true);
1612 EnableControlButton(true);
1613 SwitchToInput();
1614 ShowTab(IDC_REFLIST);
1617 void CSyncDlg::ParserCmdOutput(char ch)
1619 if (m_bAbort)
1620 return;
1621 CProgressDlg::ParserCmdOutput(m_ctrlCmdOut,m_ctrlProgress,m_hWnd,m_pTaskbarList,m_LogText,ch);
1623 void CSyncDlg::OnBnClickedButtonCommit()
1625 CString cmd = L"/command:commit";
1626 cmd += L" /path:\"";
1627 cmd += g_Git.m_CurrentDir;
1628 cmd += L'"';
1630 CAppUtils::RunTortoiseGitProc(cmd);
1633 void CSyncDlg::OnOK()
1635 UpdateCombox();
1636 this->UpdateData();
1637 m_ctrlURL.SaveHistory();
1638 SaveHistory();
1639 m_regAutoLoadPutty = this->m_bAutoLoadPuttyKey;
1640 m_tooltips.Pop();
1641 __super::OnOK();
1644 void CSyncDlg::OnCancel()
1646 m_bAbort = true;
1647 m_GitProgressList.Cancel();
1648 if (m_bDone && !m_GitProgressList.IsRunning())
1650 CResizableStandAloneDialog::OnCancel();
1651 return;
1653 if (m_GitProgressList.IsRunning())
1654 WaitForSingleObject(m_GitProgressList.m_pThread->m_hThread, 10000);
1656 if (g_Git.m_CurrentGitPi.hProcess)
1658 DWORD dwConfirmKillProcess = CRegDWORD(L"Software\\TortoiseGit\\ConfirmKillProcess");
1659 if (dwConfirmKillProcess && CMessageBox::Show(m_hWnd, IDS_PROC_CONFIRMKILLPROCESS, IDS_APPNAME, MB_YESNO | MB_ICONQUESTION) != IDYES)
1660 return;
1661 if (::GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0))
1662 ::WaitForSingleObject(g_Git.m_CurrentGitPi.hProcess, 10000);
1664 CProgressDlg::KillProcessTree(g_Git.m_CurrentGitPi.dwProcessId);
1667 ::WaitForSingleObject(g_Git.m_CurrentGitPi.hProcess ,10000);
1668 if (m_pThread)
1670 if (::WaitForSingleObject(m_pThread->m_hThread, 5000) == WAIT_TIMEOUT)
1671 g_Git.KillRelatedThreads(m_pThread);
1673 m_tooltips.Pop();
1674 CResizableStandAloneDialog::OnCancel();
1677 void CSyncDlg::OnBnClickedButtonSubmodule()
1679 bool bShift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
1680 this->UpdateData();
1681 UpdateCombox();
1683 if (bShift)
1685 switch (m_ctrlSubmodule.GetCurrentEntry())
1687 case 0:
1688 [[fallthrough]];
1689 case 1:
1690 CAppUtils::RunTortoiseGitProc(L"/command:subupdate /bkpath:\"" + g_Git.m_CurrentDir + L"\"");
1691 break;
1692 case 2:
1693 CAppUtils::RunTortoiseGitProc(L"/command:subsync /bkpath:\"" + g_Git.m_CurrentDir + L"\"");
1694 break;
1696 return;
1699 m_ctrlCmdOut.SetWindowText(L"");
1700 m_LogText.Empty();
1702 this->m_regSubmoduleButton = static_cast<DWORD>(this->m_ctrlSubmodule.GetCurrentEntry());
1704 this->SwitchToRun();
1706 this->m_bAbort=false;
1707 this->m_GitCmdList.clear();
1709 ShowTab(IDC_CMD_LOG);
1711 CString cmd;
1713 switch (m_ctrlSubmodule.GetCurrentEntry())
1715 case 0:
1716 cmd = L"git.exe submodule update --init";
1717 break;
1718 case 1:
1719 cmd = L"git.exe submodule init";
1720 break;
1721 case 2:
1722 cmd = L"git.exe submodule sync";
1723 break;
1726 m_GitCmdList.push_back(cmd);
1728 m_CurrentCmd = GIT_COMMAND_SUBMODULE;
1730 StartWorkerThread();
1733 void CSyncDlg::OnBnClickedButtonStash()
1735 bool bShift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
1736 UpdateData();
1737 UpdateCombox();
1739 if (bShift)
1741 if (m_ctrlStash.GetCurrentEntry() == 0)
1742 CAppUtils::RunTortoiseGitProc(L"/command:stashsave");
1743 return;
1746 m_ctrlCmdOut.SetWindowText(L"");
1747 m_LogText.Empty();
1749 SwitchToRun();
1751 m_bAbort = false;
1752 m_GitCmdList.clear();
1754 ShowTab(IDC_CMD_LOG);
1756 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, false);
1757 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST -1, false);
1758 m_ctrlTabCtrl.ShowTab(IDC_IN_CONFLICT -1, false);
1760 CString cmd;
1761 switch (m_ctrlStash.GetCurrentEntry())
1763 case 0:
1764 cmd = L"git.exe stash save";
1765 break;
1766 case 1:
1767 cmd = L"git.exe stash pop";
1768 break;
1769 case 2:
1770 cmd = L"git.exe stash apply";
1771 break;
1774 m_GitCmdList.push_back(cmd);
1775 m_CurrentCmd = GIT_COMMAND_STASH;
1777 StartWorkerThread();
1780 void CSyncDlg::OnTimer(UINT_PTR nIDEvent)
1782 if( nIDEvent == IDT_INPUT)
1784 KillTimer(IDT_INPUT);
1785 this->FetchOutList(true);
1786 m_ctrlTabCtrl.ShowTab(IDC_TAGCOMPARELIST - 1, false);
1790 LRESULT CSyncDlg::OnTaskbarBtnCreated(WPARAM wParam, LPARAM lParam)
1792 m_pTaskbarList.Release();
1793 m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList);
1794 m_GitProgressList.m_pTaskbarList = m_pTaskbarList;
1795 return __super::OnTaskbarButtonCreated(wParam, lParam);
1798 void CSyncDlg::OnBnClickedCheckForce()
1800 UpdateData();
1803 void CSyncDlg::OnBnClickedLog()
1805 CString cmd = L"/command:log";
1806 cmd += L" /path:\"";
1807 cmd += g_Git.m_CurrentDir;
1808 cmd += L'"';
1810 CAppUtils::RunTortoiseGitProc(cmd);
1813 LRESULT CSyncDlg::OnProgCmdFinish(WPARAM /*wParam*/, LPARAM /*lParam*/)
1815 RefreshCursor();
1816 RunPostAction();
1817 return 0;
1820 void CSyncDlg::OnDestroy()
1822 m_bWantToExit = true;
1823 __super::OnDestroy();
1826 void CSyncDlg::SetTheme(bool bDark)
1828 __super::SetTheme(bDark);
1829 CMFCVisualManager::GetInstance()->DestroyInstance();
1830 if (bDark)
1832 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CThemeMFCVisualManager));
1833 m_ctrlTabCtrl.ModifyTabStyle(CMFCTabCtrl::STYLE_3D);
1835 else
1837 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
1838 m_ctrlTabCtrl.ModifyTabStyle(CMFCTabCtrl::STYLE_FLAT);
1840 CMFCVisualManager::RedrawAll();
1843 void CSyncDlg::OnEnLinkLog(NMHDR *pNMHDR, LRESULT *pResult)
1845 // similar code in ProgressDlg.cpp and LogDlg.cpp
1846 ENLINK *pEnLink = reinterpret_cast<ENLINK *>(pNMHDR);
1847 if ((pEnLink->msg == WM_LBUTTONUP) || (pEnLink->msg == WM_SETCURSOR))
1849 CString msg;
1850 m_ctrlCmdOut.GetWindowText(msg);
1851 msg.Replace(L"\r\n", L"\n");
1852 CString url = msg.Mid(pEnLink->chrg.cpMin, pEnLink->chrg.cpMax - pEnLink->chrg.cpMin);
1853 // check if it's an email address
1854 auto atpos = url.Find(L'@');
1855 if ((atpos > 0) && (url.ReverseFind(L'.') > atpos) && !::PathIsURL(url))
1856 url = L"mailto:" + url;
1857 if (::PathIsURL(url))
1859 if (pEnLink->msg == WM_LBUTTONUP)
1860 ShellExecute(GetSafeHwnd(), L"open", url, nullptr, nullptr, SW_SHOWDEFAULT);
1861 else
1863 static RECT prevRect = { 0 };
1864 CWnd* pMsgView = &m_ctrlCmdOut;
1865 if (pMsgView)
1867 RECT rc;
1868 POINTL pt;
1869 pMsgView->SendMessage(EM_POSFROMCHAR, reinterpret_cast<WPARAM>(&pt), pEnLink->chrg.cpMin);
1870 rc.left = pt.x;
1871 rc.top = pt.y;
1872 pMsgView->SendMessage(EM_POSFROMCHAR, reinterpret_cast<WPARAM>(&pt), pEnLink->chrg.cpMax);
1873 rc.right = pt.x;
1874 rc.bottom = pt.y + 12;
1875 if ((prevRect.left != rc.left) || (prevRect.top != rc.top))
1877 m_tooltips.DelTool(pMsgView, 1);
1878 m_tooltips.AddTool(pMsgView, url, &rc, 1);
1879 prevRect = rc;
1885 *pResult = 0;
1888 void CSyncDlg::OnEnscrollLog()
1890 m_tooltips.DelTool(&m_ctrlCmdOut, 1);
1893 void CSyncDlg::StartWorkerThread()
1895 if (InterlockedExchange(&m_bBlock, TRUE))
1896 return;
1898 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL);
1899 if (!m_pThread)
1901 InterlockedExchange(&m_bBlock, FALSE);
1902 CMessageBox::Show(this->m_hWnd, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
1903 SwitchToInput();
1904 EnableControlButton(true);