Fixed issue #4132: Error "Could not get next commit. libgit returns: -4" in Log Messa...
[TortoiseGit.git] / src / TortoiseProc / SyncDlg.cpp
blob66efaeac4b7d68508e5a711abfead3820150b760
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2024 - 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 , CBranchCombox(L"sync")
45 , m_bAutoLoadPuttyKey(CAppUtils::IsSSHPutty())
46 , m_bForce(BST_UNCHECKED)
47 , m_startTick(GetTickCount64())
49 m_pTooltip = &m_tooltips;
52 CSyncDlg::~CSyncDlg()
56 void CSyncDlg::DoDataExchange(CDataExchange* pDX)
58 CDialog::DoDataExchange(pDX);
59 DDX_Check(pDX, IDC_CHECK_PUTTY_KEY, m_bAutoLoadPuttyKey);
60 DDX_Check(pDX, IDC_CHECK_FORCE,m_bForce);
61 DDX_Control(pDX, IDC_COMBOBOXEX_URL, m_ctrlURL);
62 DDX_Control(pDX, IDC_BUTTON_TABCTRL, m_ctrlDumyButton);
63 DDX_Control(pDX, IDC_BUTTON_PULL, m_ctrlPull);
64 DDX_Control(pDX, IDC_BUTTON_PUSH, m_ctrlPush);
65 DDX_Control(pDX, IDC_STATIC_STATUS, m_ctrlStatus);
66 DDX_Control(pDX, IDC_PROGRESS_SYNC, m_ctrlProgress);
67 DDX_Control(pDX, IDC_ANIMATE_SYNC, m_ctrlAnimate);
68 DDX_Control(pDX, IDC_BUTTON_SUBMODULE,m_ctrlSubmodule);
69 DDX_Control(pDX, IDC_BUTTON_STASH, m_ctrlStash);
70 DDX_Control(pDX, IDC_PROG_LABEL, m_ctrlProgLabel);
71 BRANCH_COMBOX_DDX;
74 BEGIN_MESSAGE_MAP(CSyncDlg, CResizableStandAloneDialog)
75 ON_BN_CLICKED(IDC_BUTTON_PULL, &CSyncDlg::OnBnClickedButtonPull)
76 ON_BN_CLICKED(IDC_BUTTON_PUSH, &CSyncDlg::OnBnClickedButtonPush)
77 ON_BN_CLICKED(IDC_BUTTON_APPLY, &CSyncDlg::OnBnClickedButtonApply)
78 ON_BN_CLICKED(IDC_BUTTON_EMAIL, &CSyncDlg::OnBnClickedButtonEmail)
79 ON_BN_CLICKED(IDC_BUTTON_MANAGE, &CSyncDlg::OnBnClickedButtonManage)
80 BRANCH_COMBOX_EVENT
81 ON_CBN_EDITCHANGE(IDC_COMBOBOXEX_URL, &CSyncDlg::OnCbnEditchangeComboboxex)
82 ON_CBN_EDITCHANGE(IDC_COMBOBOXEX_REMOTE_BRANCH, &CSyncDlg::OnCbnEditchangeComboboxex)
83 ON_MESSAGE(MSG_PROGRESSDLG_UPDATE_UI, OnProgressUpdateUI)
84 ON_MESSAGE(WM_PROG_CMD_FINISH, OnProgCmdFinish)
85 ON_BN_CLICKED(IDC_BUTTON_COMMIT, &CSyncDlg::OnBnClickedButtonCommit)
86 ON_BN_CLICKED(IDC_BUTTON_SUBMODULE, &CSyncDlg::OnBnClickedButtonSubmodule)
87 ON_BN_CLICKED(IDC_BUTTON_STASH, &CSyncDlg::OnBnClickedButtonStash)
88 ON_WM_TIMER()
89 ON_REGISTERED_MESSAGE(TaskBarButtonCreated, OnTaskbarBtnCreated)
90 ON_BN_CLICKED(IDC_CHECK_FORCE, &CSyncDlg::OnBnClickedCheckForce)
91 ON_BN_CLICKED(IDC_LOG, &CSyncDlg::OnBnClickedLog)
92 ON_WM_DESTROY()
93 END_MESSAGE_MAP()
95 void CSyncDlg::EnableControlButton(bool bEnabled)
97 GetDlgItem(IDC_BUTTON_PULL)->EnableWindow(bEnabled);
98 GetDlgItem(IDC_BUTTON_PUSH)->EnableWindow(bEnabled);
99 GetDlgItem(IDC_BUTTON_APPLY)->EnableWindow(bEnabled);
100 GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(bEnabled);
101 GetDlgItem(IDOK)->EnableWindow(bEnabled);
102 GetDlgItem(IDC_BUTTON_SUBMODULE)->EnableWindow(bEnabled);
103 GetDlgItem(IDC_BUTTON_STASH)->EnableWindow(bEnabled);
105 // CSyncDlg message handlers
107 bool CSyncDlg::AskSetTrackedBranch()
109 CString remote, remoteBranch;
110 g_Git.GetRemoteTrackedBranch(m_strLocalBranch, remote, remoteBranch);
111 if (remoteBranch.IsEmpty())
113 remoteBranch = m_strRemoteBranch;
114 if (remoteBranch.IsEmpty())
115 remoteBranch = m_strLocalBranch;
116 CString temp;
117 temp.FormatMessage(IDS_NOTYET_SETTRACKEDBRANCH, static_cast<LPCWSTR>(m_strLocalBranch), static_cast<LPCWSTR>(remoteBranch));
118 BOOL dontShowAgain = FALSE;
119 auto ret = CMessageBox::ShowCheck(GetSafeHwnd(), temp, L"TortoiseGit", MB_ICONQUESTION | MB_YESNOCANCEL, nullptr, CString(MAKEINTRESOURCE(IDS_MSGBOX_DONOTSHOW)), &dontShowAgain);
120 if (dontShowAgain)
121 CRegDWORD(L"Software\\TortoiseGit\\AskSetTrackedBranch") = FALSE;
122 if (ret == IDCANCEL)
123 return false;
124 if (ret == IDYES)
126 CString key;
127 key.Format(L"branch.%s.remote", static_cast<LPCWSTR>(m_strLocalBranch));
128 g_Git.SetConfigValue(key, m_strURL);
129 key.Format(L"branch.%s.merge", static_cast<LPCWSTR>(m_strLocalBranch));
130 g_Git.SetConfigValue(key, L"refs/heads/" + g_Git.StripRefName(remoteBranch));
133 return true;
136 void CSyncDlg::OnBnClickedButtonPull()
138 const bool bShift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
140 int CurrentEntry = static_cast<int>(this->m_ctrlPull.GetCurrentEntry());
141 this->m_regPullButton = CurrentEntry;
143 if (bShift && CurrentEntry > 1)
144 return;
146 this->m_bAbort=false;
147 this->m_GitCmdList.clear();
148 m_ctrlCmdOut.SetWindowText(L"");
149 m_LogText.Empty();
151 this->UpdateData();
152 UpdateCombox();
154 if (g_Git.GetHash(m_oldHash, L"HEAD"))
156 MessageBox(g_Git.GetGitLastErr(L"Could not get HEAD hash."), L"TortoiseGit", MB_ICONERROR);
157 return;
160 m_refList.Clear();
161 m_newHashMap.clear();
162 m_oldHashMap.clear();
164 if( CurrentEntry == 0)
166 CGitHash localBranchHash;
167 if (g_Git.GetHash(localBranchHash, m_strLocalBranch))
169 MessageBox(g_Git.GetGitLastErr(L"Could not get hash of \"" + m_strLocalBranch + L"\"."), L"TortoiseGit", MB_ICONERROR);
170 return;
172 if (localBranchHash != m_oldHash || m_strLocalBranch != g_Git.GetCurrentBranch())
174 CString tmp;
175 tmp.Format(IDS_PROC_SYNC_SWITCHTO, static_cast<LPCWSTR>(m_strLocalBranch));
176 if (CMessageBox::Show(GetSafeHwnd(), CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PULLWRONGBRANCH)), L"TortoiseGit", 2, IDI_QUESTION, tmp, CString(MAKEINTRESOURCE(IDS_ABORTBUTTON))) == 2)
177 return;
179 CString endOfOptions;
180 if (CGit::ms_LastMsysGitVersion >= ConvertVersionToInt(2, 43, 1))
181 endOfOptions = L" --end-of-options";
182 CString cmd;
183 cmd.Format(L"git.exe checkout%s %s --", static_cast<LPCWSTR>(endOfOptions), static_cast<LPCWSTR>(m_strLocalBranch));
185 CProgressDlg progress(this);
186 progress.m_AutoClose = GitProgressAutoClose::AUTOCLOSE_IF_NO_ERRORS;
187 progress.m_GitCmd = cmd;
188 if (progress.DoModal() != IDOK || progress.m_GitStatus != 0)
189 return;
193 if(this->m_strURL.IsEmpty())
195 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_GITCONFIG_URLEMPTY, IDS_APPNAME, MB_OK | MB_ICONERROR);
196 return;
199 if (CurrentEntry == 6)
201 SwitchToRun();
202 m_ctrlTabCtrl.ShowTab(IDC_CMD_LOG - 1, false);
203 m_ctrlTabCtrl.ShowTab(IDC_REFLIST - 1, false);
204 m_ctrlTabCtrl.ShowTab(IDC_OUT_LOGLIST - 1, false);
205 m_ctrlTabCtrl.ShowTab(IDC_OUT_CHANGELIST - 1, false);
206 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, false);
207 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST - 1, false);
208 m_ctrlTabCtrl.ShowTab(IDC_IN_CONFLICT - 1, false);
209 m_ctrlTabCtrl.ShowTab(IDC_TAGCOMPARELIST - 1, true);
211 if (m_pTaskbarList)
212 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_INDETERMINATE);
214 CSysProgressDlg sysProgressDlg;
215 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
216 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_LOADING)));
217 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
218 sysProgressDlg.SetShowProgressBar(false);
219 sysProgressDlg.ShowModal(this, true);
220 CString err;
221 const auto ret = m_tagCompareList.Fill(m_strURL, err);
222 sysProgressDlg.Stop();
223 if (ret)
225 if (m_pTaskbarList)
227 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_ERROR);
228 m_pTaskbarList->SetProgressValue(m_hWnd, 100, 100);
230 MessageBox(err, L"TortoiseGit", MB_ICONERROR);
232 else
234 if (m_pTaskbarList)
235 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
238 BringWindowToTop();
239 SwitchToInput();
240 EnableControlButton();
241 return;
244 if (!IsURL() && !m_strRemoteBranch.IsEmpty() && CurrentEntry == 0 && CRegDWORD(L"Software\\TortoiseGit\\AskSetTrackedBranch", TRUE) == TRUE)
246 if (!AskSetTrackedBranch())
247 return;
250 if (m_bAutoLoadPuttyKey && CurrentEntry != 4) // CurrentEntry (Remote Update) handles this on its own)
252 CAppUtils::LaunchPAgent(this->GetSafeHwnd(), nullptr, &m_strURL);
255 if (g_Git.GetMapHashToFriendName(m_oldHashMap))
256 MessageBox(g_Git.GetGitLastErr(L"Could not get all refs."), L"TortoiseGit", MB_ICONERROR);
258 if (bShift && (CurrentEntry == 0 || CurrentEntry == 1))
260 if (CurrentEntry == 1 || CurrentEntry == 2 || CurrentEntry == 3)
261 CAppUtils::Fetch(GetSafeHwnd(), !IsURL() ? m_strURL : CString());
262 else
263 CAppUtils::Pull(GetSafeHwnd());
265 FillNewRefMap();
266 FetchOutList(true);
268 const int hasConflicts = g_Git.HasWorkingTreeConflicts();
269 if (hasConflicts < 0)
271 this->m_ctrlCmdOut.SetSel(-1, -1);
272 this->m_ctrlCmdOut.ReplaceSel(g_Git.GetGitLastErr(L"Checking for conflicts failed.", CGit::GIT_CMD_CHECKCONFLICTS));
274 this->ShowTab(IDC_CMD_LOG);
275 return;
278 if (hasConflicts)
280 this->m_ConflictFileList.Clear();
281 this->m_ConflictFileList.GetStatus(nullptr, true);
282 this->m_ConflictFileList.Show(CTGitPath::LOGACTIONS_UNMERGED,
283 CTGitPath::LOGACTIONS_UNMERGED);
285 this->ShowTab(IDC_IN_CONFLICT);
286 CMessageBox::ShowCheck(GetSafeHwnd(), IDS_NEED_TO_RESOLVE_CONFLICTS_HINT, IDS_APPNAME, MB_ICONINFORMATION, L"MergeConflictsNeedsCommit", IDS_MSGBOX_DONOTSHOWAGAIN);
288 else
289 ShowInCommits(L"HEAD");
291 return;
294 CString force;
295 if(this->m_bForce)
296 force = L" --force";
298 CString cmd;
300 m_iPullRebase = 0;
301 if (CurrentEntry == 0) // check whether we need to override Pull if pull.rebase is set
303 CAutoRepository repo(g_Git.GetGitRepository());
304 if (!repo)
305 MessageBox(CGit::GetLibGit2LastErr(L"Could not open repository."), L"TortoiseGit", MB_OK | MB_ICONERROR);
307 // Check config branch.<name>.rebase and pull.reabse
310 if (!repo)
311 break;
313 if (git_repository_head_detached(repo) == 1)
314 break;
316 CAutoConfig config(true);
317 if (git_repository_config(config.GetPointer(), repo))
318 break;
320 // branch.<name>.rebase overrides pull.rebase
321 if (config.GetBOOL(L"branch." + g_Git.GetCurrentBranch() + L".rebase", m_iPullRebase) == GIT_ENOTFOUND)
323 if (config.GetBOOL(L"pull.rebase", m_iPullRebase) == GIT_ENOTFOUND)
324 break;
325 else
327 CString value;
328 config.GetString(L"pull.rebase", value);
329 if (value == L"merges")
331 m_iPullRebase = 2;
332 break;
336 else
338 CString value;
339 config.GetString(L"branch." + g_Git.GetCurrentBranch() + L".rebase", value);
340 if (value == L"merges")
342 m_iPullRebase = 2;
343 break;
346 } while (0);
347 if (m_iPullRebase > 0)
349 CurrentEntry = 1;
350 if (m_strRemoteBranch.IsEmpty())
352 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_PULL_EMPTYBRANCH, IDS_APPNAME, MB_ICONEXCLAMATION);
353 return;
358 SwitchToRun();
360 ShowTab(IDC_CMD_LOG);
362 m_ctrlTabCtrl.ShowTab(IDC_REFLIST - 1, true);
363 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, false);
364 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST - 1, false);
365 m_ctrlTabCtrl.ShowTab(IDC_IN_CONFLICT - 1, false);
366 m_ctrlTabCtrl.ShowTab(IDC_TAGCOMPARELIST - 1, false);
368 ///Pull
369 if(CurrentEntry == 0) //Pull
371 CString remotebranch;
372 remotebranch = m_strRemoteBranch;
374 if(!IsURL())
376 CString pullRemote, pullBranch;
377 g_Git.GetRemoteTrackedBranch(m_strLocalBranch, pullRemote, pullBranch);
378 if(pullBranch == remotebranch && pullRemote == this->m_strURL)
379 remotebranch.Empty();
382 cmd.Format(L"git.exe pull -v --progress%s -- \"%s\" %s",
383 static_cast<LPCWSTR>(force),
384 static_cast<LPCWSTR>(m_strURL),
385 static_cast<LPCWSTR>(remotebranch));
387 m_CurrentCmd = GIT_COMMAND_PULL;
388 m_GitCmdList.push_back(cmd);
390 StartWorkerThread();
393 ///Fetch
394 if (CurrentEntry == 1 || CurrentEntry == 2 || CurrentEntry == 3)
396 m_oldRemoteHash.Empty();
397 CString remotebranch;
398 if (CurrentEntry == 3)
399 m_strRemoteBranch.Empty();
400 else if (IsURL() || m_strRemoteBranch.IsEmpty())
402 remotebranch=this->m_strRemoteBranch;
405 else
407 remotebranch.Format(L"remotes/%s/%s", static_cast<LPCWSTR>(m_strURL), static_cast<LPCWSTR>(m_strRemoteBranch));
408 g_Git.GetHash(m_oldRemoteHash, remotebranch);
409 if (m_oldRemoteHash.IsEmpty())
410 remotebranch=m_strRemoteBranch;
411 else
412 remotebranch = m_strRemoteBranch + L':' + remotebranch;
415 if (CurrentEntry == 1 || CurrentEntry == 3)
416 m_CurrentCmd = GIT_COMMAND_FETCH;
417 else
418 m_CurrentCmd = GIT_COMMAND_FETCHANDREBASE;
420 if (g_Git.UsingLibGit2(CGit::GIT_CMD_FETCH))
422 CString refspec;
423 if (!remotebranch.IsEmpty())
424 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));
426 progressCommand = std::make_unique<FetchProgressCommand>();
427 FetchProgressCommand* fetchProgressCommand = static_cast<FetchProgressCommand*>(progressCommand.get());
428 fetchProgressCommand->SetUrl(m_strURL);
429 fetchProgressCommand->SetRefSpec(refspec);
430 m_GitProgressList.SetCommand(progressCommand.get());
431 m_GitProgressList.Init();
432 ShowTab(IDC_CMD_GIT_PROG);
434 else
436 cmd.Format(L"git.exe fetch --progress -v%s -- \"%s\" %s",
437 static_cast<LPCWSTR>(force),
438 static_cast<LPCWSTR>(m_strURL),
439 static_cast<LPCWSTR>(remotebranch));
441 m_GitCmdList.push_back(cmd);
443 StartWorkerThread();
447 ///Remote Update
448 if (CurrentEntry == 4)
450 if (m_bAutoLoadPuttyKey)
452 for (size_t i = 0; i < m_remotelist.size(); ++i)
453 CAppUtils::LaunchPAgent(this->GetSafeHwnd(), nullptr, &m_remotelist[i]);
456 m_CurrentCmd = GIT_COMMAND_REMOTE;
457 cmd = L"git.exe remote update";
458 m_GitCmdList.push_back(cmd);
460 StartWorkerThread();
463 ///Cleanup stale remote banches
464 if (CurrentEntry == 5)
466 m_CurrentCmd = GIT_COMMAND_REMOTE;
467 cmd.Format(L"git.exe remote prune -- \"%s\"", static_cast<LPCWSTR>(m_strURL));
468 m_GitCmdList.push_back(cmd);
470 StartWorkerThread();
474 void CSyncDlg::ShowInCommits(const CString& friendname)
476 CGitHash newHash;
478 if (g_Git.GetHash(newHash, friendname))
480 MessageBox(g_Git.GetGitLastErr(L"Could not get " + friendname + L" hash."), L"TortoiseGit", MB_ICONERROR);
481 return;
484 if (newHash == m_oldHash)
486 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST - 1, false);
487 m_InLogList.ShowText(CString(MAKEINTRESOURCE(IDS_UPTODATE)));
488 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, true);
489 ShowTab(IDC_REFLIST);
491 else
493 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST - 1, true);
494 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, true);
496 AddDiffFileList(&m_InChangeFileList, &m_arInChangeList, newHash, m_oldHash);
498 CString range;
499 range.Format(L"%s..%s", static_cast<LPCWSTR>(m_oldHash.ToString()), static_cast<LPCWSTR>(newHash.ToString()));
500 m_InLogList.FillGitLog(nullptr, &range, CGit::LOG_INFO_STAT | CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
501 ShowTab(IDC_IN_LOGLIST);
505 void CSyncDlg::PullComplete()
507 EnableControlButton(true);
508 SwitchToInput();
509 this->FetchOutList(true);
511 if( this ->m_GitCmdStatus )
513 const int hasConflicts = g_Git.HasWorkingTreeConflicts();
514 if (hasConflicts < 0)
516 this->m_ctrlCmdOut.SetSel(-1,-1);
517 this->m_ctrlCmdOut.ReplaceSel(g_Git.GetGitLastErr(L"Checking for conflicts failed.", CGit::GIT_CMD_CHECKCONFLICTS));
519 this->ShowTab(IDC_CMD_LOG);
520 return;
523 if (hasConflicts)
525 this->m_ConflictFileList.Clear();
526 this->m_ConflictFileList.GetStatus(nullptr, true);
527 this->m_ConflictFileList.Show(CTGitPath::LOGACTIONS_UNMERGED,
528 CTGitPath::LOGACTIONS_UNMERGED);
530 this->ShowTab(IDC_IN_CONFLICT);
531 CMessageBox::ShowCheck(GetSafeHwnd(), IDS_NEED_TO_RESOLVE_CONFLICTS_HINT, IDS_APPNAME, MB_ICONINFORMATION, L"MergeConflictsNeedsCommit", IDS_MSGBOX_DONOTSHOWAGAIN);
533 else
534 this->ShowTab(IDC_CMD_LOG);
537 else
538 ShowInCommits(L"HEAD");
541 void CSyncDlg::FetchComplete()
543 EnableControlButton(true);
544 SwitchToInput();
546 if (g_Git.UsingLibGit2(CGit::GIT_CMD_FETCH))
547 ShowTab(IDC_CMD_GIT_PROG);
548 else
549 ShowTab(IDC_REFLIST);
551 if (m_GitCmdStatus || (m_CurrentCmd != GIT_COMMAND_FETCHANDREBASE && m_iPullRebase == 0))
553 FetchOutList(true);
554 return;
557 CString remote;
558 CString remotebranch;
559 CString upstream = L"FETCH_HEAD";
560 m_ctrlURL.GetWindowText(remote);
561 if (!remote.IsEmpty())
563 if (std::find(m_remotelist.cbegin(), m_remotelist.cend(), remote) == m_remotelist.cend())
564 remote.Empty();
566 m_ctrlRemoteBranch.GetWindowText(remotebranch);
567 if (!remote.IsEmpty() && !remotebranch.IsEmpty())
568 upstream = L"remotes/" + remote + L'/' + remotebranch;
570 if (m_iPullRebase > 0)
572 CAppUtils::RebaseAfterFetch(GetSafeHwnd(), upstream, m_iPullRebase ? 2 : 0, m_iPullRebase == 2);
573 FillNewRefMap();
574 FetchOutList(true);
576 ShowInCommits(L"HEAD");
578 return;
581 CGitHash remoteBranchHash;
582 g_Git.GetHash(remoteBranchHash, upstream);
583 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)
585 ShowInCommits(upstream);
586 return;
589 if (g_Git.IsFastForward(L"HEAD", upstream))
591 const 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);
592 if (ret == 3)
593 return;
594 if (ret == 1)
596 CProgressDlg mergeProgress;
597 mergeProgress.m_GitCmd = L"git.exe merge --ff-only -- " + upstream;
598 mergeProgress.m_AutoClose = GitProgressAutoClose::AUTOCLOSE_IF_NO_ERRORS;
599 mergeProgress.m_PostCmdCallback = [](DWORD status, PostCmdList& postCmdList)
601 if (status && g_Git.HasWorkingTreeConflicts())
603 // there are conflict files
604 postCmdList.emplace_back(IDI_RESOLVE, IDS_PROGRS_CMD_RESOLVE, []
606 CString sCmd;
607 sCmd.Format(L"/command:commit /path:\"%s\"", static_cast<LPCWSTR>(g_Git.m_CurrentDir));
608 CAppUtils::RunTortoiseGitProc(sCmd);
612 mergeProgress.DoModal();
613 FillNewRefMap();
614 FetchOutList(true);
616 ShowInCommits(L"HEAD");
618 return;
622 CAppUtils::RebaseAfterFetch(GetSafeHwnd(), upstream);
623 FillNewRefMap();
624 FetchOutList(true);
626 ShowInCommits(L"HEAD");
629 void CSyncDlg::StashComplete()
631 EnableControlButton(true);
632 INT_PTR entry = m_ctrlStash.GetCurrentEntry();
633 if (entry != 1 && entry != 2)
634 return;
636 SwitchToInput();
637 if (m_GitCmdStatus)
639 const int hasConflicts = g_Git.HasWorkingTreeConflicts();
640 if (hasConflicts < 0)
642 m_ctrlCmdOut.SetSel(-1, -1);
643 m_ctrlCmdOut.ReplaceSel(g_Git.GetGitLastErr(L"Checking for conflicts failed.", CGit::GIT_CMD_CHECKCONFLICTS));
645 ShowTab(IDC_CMD_LOG);
646 return;
649 if (hasConflicts)
651 m_ConflictFileList.Clear();
652 m_ConflictFileList.GetStatus(nullptr, true);
653 m_ConflictFileList.Show(CTGitPath::LOGACTIONS_UNMERGED, CTGitPath::LOGACTIONS_UNMERGED);
655 ShowTab(IDC_IN_CONFLICT);
657 else
658 ShowTab(IDC_CMD_LOG);
662 void CSyncDlg::OnBnClickedButtonPush()
664 const bool bShift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
665 this->UpdateData();
666 UpdateCombox();
668 if (bShift)
670 if (m_ctrlPush.GetCurrentEntry() == 0)
672 CAppUtils::Push(GetSafeHwnd(), g_Git.FixBranchName(m_strLocalBranch));
673 FillNewRefMap();
674 FetchOutList(true);
676 return;
679 m_ctrlCmdOut.SetWindowText(L"");
680 m_LogText.Empty();
682 if(this->m_strURL.IsEmpty())
684 CMessageBox::Show(GetSafeHwnd(), IDS_PROC_GITCONFIG_URLEMPTY, IDS_APPNAME, MB_OK | MB_ICONERROR);
685 return;
688 if (!IsURL() && m_ctrlPush.GetCurrentEntry() == 0 && CRegDWORD(L"Software\\TortoiseGit\\AskSetTrackedBranch", TRUE) == TRUE)
690 if (!AskSetTrackedBranch())
691 return;
694 this->m_regPushButton = static_cast<DWORD>(this->m_ctrlPush.GetCurrentEntry());
695 this->SwitchToRun();
696 this->m_bAbort=false;
697 this->m_GitCmdList.clear();
699 ShowTab(IDC_CMD_LOG);
701 CString cmd;
702 CString arg;
704 CString error;
705 DWORD exitcode = 0xFFFFFFFF;
706 CHooks::Instance().SetProjectProperties(g_Git.m_CurrentDir, m_ProjectProperties);
707 if (CHooks::Instance().PrePush(GetSafeHwnd(), g_Git.m_CurrentDir, exitcode, error))
709 if (exitcode)
711 CString sErrorMsg;
712 sErrorMsg.Format(IDS_HOOK_ERRORMSG, static_cast<LPCWSTR>(error));
713 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);
714 taskdlg.AddCommandControl(101, CString(MAKEINTRESOURCE(IDS_HOOKFAILED_TASK3)));
715 taskdlg.AddCommandControl(102, CString(MAKEINTRESOURCE(IDS_HOOKFAILED_TASK4)));
716 taskdlg.SetDefaultCommandControl(101);
717 taskdlg.SetMainIcon(TD_ERROR_ICON);
718 if (taskdlg.DoModal(GetSafeHwnd()) != 102)
719 return;
723 CString refName = g_Git.FixBranchName(m_strLocalBranch);
724 switch (m_ctrlPush.GetCurrentEntry())
726 case 1:
727 arg += L" --tags";
728 break;
729 case 2:
730 refName = g_Git.GetNotesRef();
731 break;
734 if(this->m_bForce)
735 arg += L" --force";
737 cmd.Format(L"git.exe push -v --progress%s -- \"%s\" %s",
738 static_cast<LPCWSTR>(arg),
739 static_cast<LPCWSTR>(m_strURL),
740 static_cast<LPCWSTR>(refName));
742 if (!m_strRemoteBranch.IsEmpty() && m_ctrlPush.GetCurrentEntry() != 2)
744 cmd += L':' + m_strRemoteBranch;
747 m_GitCmdList.push_back(cmd);
749 m_CurrentCmd = GIT_COMMAND_PUSH;
751 if(this->m_bAutoLoadPuttyKey)
753 CAppUtils::LaunchPAgent(this->GetSafeHwnd(), nullptr, &m_strURL);
756 StartWorkerThread();
759 void CSyncDlg::OnBnClickedButtonApply()
761 CGitHash oldhash;
762 if (g_Git.GetHash(oldhash, L"HEAD"))
764 MessageBox(g_Git.GetGitLastErr(L"Could not get HEAD hash."), L"TortoiseGit", MB_ICONERROR);
765 return;
768 CImportPatchDlg dlg;
769 CString cmd,output;
771 if(dlg.DoModal() == IDOK)
773 int err=0;
774 for (int i = 0; i < dlg.m_PathList.GetCount(); ++i)
776 cmd.Format(L"git.exe am -- \"%s\"", static_cast<LPCWSTR>(dlg.m_PathList[i].GetGitPathString()));
778 if (g_Git.Run(cmd, &output, CP_UTF8))
780 CMessageBox::Show(GetSafeHwnd(), output, L"TortoiseGit", MB_OK | MB_ICONERROR);
782 err=1;
783 break;
785 this->m_ctrlCmdOut.SetSel(-1,-1);
786 this->m_ctrlCmdOut.ReplaceSel(cmd + L'\n');
787 this->m_ctrlCmdOut.SetSel(-1,-1);
788 this->m_ctrlCmdOut.ReplaceSel(output);
791 CGitHash newhash;
792 if (g_Git.GetHash(newhash, L"HEAD"))
794 MessageBox(g_Git.GetGitLastErr(L"Could not get HEAD hash after applying patches."), L"TortoiseGit", MB_ICONERROR);
795 return;
798 this->m_InLogList.Clear();
799 this->m_InChangeFileList.Clear();
801 if(newhash == oldhash)
803 this->m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST-1,false);
804 this->m_InLogList.ShowText(L"No commits get from patch");
805 this->m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST-1,true);
808 else
810 this->m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST-1,true);
811 this->m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST-1,true);
813 CString range;
814 range.Format(L"%s..%s", static_cast<LPCWSTR>(m_oldHash.ToString()), static_cast<LPCWSTR>(newhash.ToString()));
815 this->AddDiffFileList(&m_InChangeFileList, &m_arInChangeList, newhash, oldhash);
816 m_InLogList.FillGitLog(nullptr, &range, CGit::LOG_INFO_STAT| CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
818 this->FetchOutList(true);
821 this->m_ctrlTabCtrl.ShowTab(IDC_CMD_LOG-1,true);
823 if(err)
825 this->ShowTab(IDC_CMD_LOG);
827 else
829 this->ShowTab(IDC_IN_LOGLIST);
834 void CSyncDlg::OnBnClickedButtonEmail()
836 CString cmd, out, err;
838 this->m_strLocalBranch = this->m_ctrlLocalBranch.GetString();
839 this->m_ctrlRemoteBranch.GetWindowText(this->m_strRemoteBranch);
840 this->m_ctrlURL.GetWindowText(this->m_strURL);
841 m_strURL=m_strURL.Trim();
842 m_strRemoteBranch=m_strRemoteBranch.Trim();
844 cmd.Format(L"git.exe format-patch -o \"%s\" --end-of-options %s/%s..%s",
845 static_cast<LPCWSTR>(g_Git.m_CurrentDir),
846 static_cast<LPCWSTR>(m_strURL), static_cast<LPCWSTR>(m_strRemoteBranch), static_cast<LPCWSTR>(g_Git.FixBranchName(m_strLocalBranch)));
848 if (g_Git.Run(cmd, &out, &err, CP_UTF8))
850 CMessageBox::Show(GetSafeHwnd(), out + L'\n' + err, L"TortoiseGit", MB_OK | MB_ICONERROR);
851 return ;
854 CAppUtils::SendPatchMail(GetSafeHwnd(), cmd, out);
856 void CSyncDlg::ShowProgressCtrl(bool bShow)
858 const int b = bShow ? SW_NORMAL : SW_HIDE;
859 this->m_ctrlAnimate.ShowWindow(b);
860 this->m_ctrlProgress.ShowWindow(b);
861 this->m_ctrlProgLabel.ShowWindow(b);
862 this->m_ctrlAnimate.Open(IDR_DOWNLOAD);
863 if (b == SW_NORMAL && CRegDWORD(L"Software\\TortoiseGit\\DownloadAnimation", TRUE) == TRUE)
864 this->m_ctrlAnimate.Play(0, UINT_MAX, UINT_MAX);
865 else
866 this->m_ctrlAnimate.Stop();
868 void CSyncDlg::ShowInputCtrl(bool bShow)
870 const int b = bShow ? SW_NORMAL : SW_HIDE;
871 this->m_ctrlURL.ShowWindow(b);
872 this->m_ctrlLocalBranch.ShowWindow(b);
873 this->m_ctrlRemoteBranch.ShowWindow(b);
874 GetDlgItem(IDC_BUTTON_LOCAL_BRANCH)->EnableWindow(bShow);
875 this->GetDlgItem(IDC_BUTTON_LOCAL_BRANCH)->ShowWindow(b);
876 GetDlgItem(IDC_BUTTON_REMOTE_BRANCH)->EnableWindow(bShow);
877 this->GetDlgItem(IDC_BUTTON_REMOTE_BRANCH)->ShowWindow(b);
878 this->GetDlgItem(IDC_STATIC_LOCAL_BRANCH)->ShowWindow(b);
879 this->GetDlgItem(IDC_STATIC_REMOTE_BRANCH)->ShowWindow(b);
880 GetDlgItem(IDC_BUTTON_MANAGE)->EnableWindow(bShow);
881 this->GetDlgItem(IDC_BUTTON_MANAGE)->ShowWindow(b);
882 GetDlgItem(IDC_CHECK_PUTTY_KEY)->EnableWindow(bShow);
883 this->GetDlgItem(IDC_CHECK_PUTTY_KEY)->ShowWindow(b);
884 GetDlgItem(IDC_CHECK_FORCE)->EnableWindow(bShow);
885 this->GetDlgItem(IDC_CHECK_FORCE)->ShowWindow(b);
886 this->GetDlgItem(IDC_STATIC_REMOTE_URL)->ShowWindow(b);
888 BOOL CSyncDlg::OnInitDialog()
890 CResizableStandAloneDialog::OnInitDialog();
891 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
893 // Let the TaskbarButtonCreated message through the UIPI filter. If we don't
894 // do this, Explorer would be unable to send that message to our window if we
895 // were running elevated. It's OK to make the call all the time, since if we're
896 // not elevated, this is a no-op.
897 CHANGEFILTERSTRUCT cfs = { sizeof(CHANGEFILTERSTRUCT) };
898 using ChangeWindowMessageFilterExDFN = BOOL(STDAPICALLTYPE)(HWND hWnd, UINT message, DWORD action, PCHANGEFILTERSTRUCT pChangeFilterStruct);
899 CAutoLibrary hUser = AtlLoadSystemLibraryUsingFullPath(L"user32.dll");
900 if (hUser)
902 auto pfnChangeWindowMessageFilterEx = reinterpret_cast<ChangeWindowMessageFilterExDFN*>(GetProcAddress(hUser, "ChangeWindowMessageFilterEx"));
903 if (pfnChangeWindowMessageFilterEx)
904 pfnChangeWindowMessageFilterEx(m_hWnd, TaskBarButtonCreated, MSGFLT_ALLOW, &cfs);
906 m_pTaskbarList.Release();
907 if (FAILED(m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList)))
908 m_pTaskbarList = nullptr;
910 this->GetDlgItem(IDC_CHECK_PUTTY_KEY)->EnableWindow(CAppUtils::IsSSHPutty());
913 this->m_ctrlAnimate.ShowWindow(SW_NORMAL);
914 this->m_ctrlAnimate.Open(IDR_DOWNLOAD);
915 this->m_ctrlAnimate.Play(0,-1,-1);
918 // ------------------ Create Tabctrl -----------
919 CWnd *pwnd=this->GetDlgItem(IDC_BUTTON_TABCTRL);
920 CRect rectDummy;
921 pwnd->GetWindowRect(&rectDummy);
922 this->ScreenToClient(rectDummy);
924 if (CTheme::Instance().IsDarkTheme())
925 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CThemeMFCVisualManager));
926 if (!m_ctrlTabCtrl.Create(CTheme::Instance().IsDarkTheme() ? CMFCTabCtrl::STYLE_3D : CMFCTabCtrl::STYLE_FLAT, rectDummy, this, IDC_SYNC_TAB))
928 TRACE0("Failed to create output tab window\n");
929 return FALSE; // fail to create
931 m_ctrlTabCtrl.SetResizeMode(CMFCTabCtrl::RESIZE_NO);
933 // -------------Create Command Log Ctrl ---------
934 DWORD dwStyle = ES_MULTILINE | ES_READONLY | WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_AUTOVSCROLL |WS_VSCROLL;
936 if( !m_ctrlCmdOut.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_CMD_LOG))
938 TRACE0("Failed to create Log commits window\n");
939 return FALSE; // fail to create
942 // set the font to use in the log message view, configured in the settings dialog
943 CFont m_logFont;
944 CAppUtils::CreateFontForLogs(GetSafeHwnd(), m_logFont);
945 m_ctrlCmdOut.SetFont(&m_logFont);
946 m_ctrlTabCtrl.InsertTab(&m_ctrlCmdOut, CString(MAKEINTRESOURCE(IDS_LOG)), -1);
947 // make the log message rich edit control send a message when the mouse pointer is over a link
948 m_ctrlCmdOut.SendMessage(EM_SETEVENTMASK, NULL, ENM_LINK | ENM_SCROLL);
950 //---------- Create in coming list ctrl -----------
951 dwStyle =LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;;
953 if( !m_InLogList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_IN_LOGLIST))
955 TRACE0("Failed to create output commits window\n");
956 return FALSE; // fail to create
958 // for some unknown reason, the SetExtendedStyle in OnCreate/PreSubclassWindow is not working here
959 m_InLogList.SetStyle();
961 m_ctrlTabCtrl.InsertTab(&m_InLogList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_INCOMMITS)), -1);
963 m_InLogList.m_ColumnRegKey = L"SyncIn";
964 m_InLogList.InsertGitColumn();
966 //----------- Create In Change file list -----------
967 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
969 if( !m_InChangeFileList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_IN_CHANGELIST))
971 TRACE0("Failed to create output change files window\n");
972 return FALSE; // fail to create
974 m_ctrlTabCtrl.InsertTab(&m_InChangeFileList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_INCHANGELIST)), -1);
976 m_InChangeFileList.Init(GITSLC_COLEXT | GITSLC_COLSTATUS |GITSLC_COLADD|GITSLC_COLDEL, L"InSyncDlg",
977 (CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_COMPARETWOREVISIONS) |
978 CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_GNUDIFF2REVISIONS)), false, false, GITSLC_COLEXT | GITSLC_COLSTATUS | GITSLC_COLADD | GITSLC_COLDEL);
981 //---------- Create Conflict List Ctrl -----------------
982 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
984 if( !m_ConflictFileList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_IN_CONFLICT))
986 TRACE0("Failed to create output change files window\n");
987 return FALSE; // fail to create
989 m_ctrlTabCtrl.InsertTab(&m_ConflictFileList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_CONFLICTS)), -1);
991 m_ConflictFileList.Init(GITSLC_COLEXT | GITSLC_COLSTATUS |GITSLC_COLADD|GITSLC_COLDEL, L"ConflictSyncDlg",
992 (GITSLC_POPEXPLORE | GITSLC_POPOPEN | GITSLC_POPSHOWLOG |
993 GITSLC_POPCONFLICT|GITSLC_POPRESOLVE),false);
996 //---------- Create Commit Out List Ctrl---------------
998 dwStyle =LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;;
1000 if( !m_OutLogList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_OUT_LOGLIST))
1002 TRACE0("Failed to create output commits window\n");
1003 return FALSE; // fail to create
1006 // for some unknown reason, the SetExtendedStyle in OnCreate/PreSubclassWindow is not working here
1007 m_OutLogList.SetStyle();
1009 m_ctrlTabCtrl.InsertTab(&m_OutLogList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_OUTCOMMITS)), -1);
1011 m_OutLogList.m_ColumnRegKey = L"SyncOut";
1012 m_OutLogList.InsertGitColumn();
1014 //------------- Create Change File List Control ----------------
1016 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
1018 if( !m_OutChangeFileList.Create(dwStyle,rectDummy,&m_ctrlTabCtrl,IDC_OUT_CHANGELIST))
1020 TRACE0("Failed to create output change files window\n");
1021 return FALSE; // fail to create
1023 m_ctrlTabCtrl.InsertTab(&m_OutChangeFileList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_OUTCHANGELIST)), -1);
1025 m_OutChangeFileList.Init(GITSLC_COLEXT | GITSLC_COLSTATUS | GITSLC_COLADD | GITSLC_COLDEL, L"OutSyncDlg",
1026 (CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_COMPARETWOREVISIONS) |
1027 CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_GNUDIFF2REVISIONS)), false, false, GITSLC_COLEXT | GITSLC_COLSTATUS | GITSLC_COLADD | GITSLC_COLDEL);
1029 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | LVS_SINGLESEL | WS_CHILD | WS_VISIBLE;
1030 if (!m_GitProgressList.Create(dwStyle | LVS_OWNERDATA, rectDummy, &m_ctrlTabCtrl, IDC_CMD_GIT_PROG))
1032 TRACE0("Failed to create Git Progress List Window\n");
1033 return FALSE; // fail to create
1035 m_ctrlTabCtrl.InsertTab(&m_GitProgressList, CString(MAKEINTRESOURCE(IDS_LOG)), -1);
1036 m_GitProgressList.m_pAnimate = &m_ctrlAnimate;
1037 m_GitProgressList.m_pPostWnd = this;
1038 m_GitProgressList.m_pProgressLabelCtrl = &m_ctrlProgLabel;
1039 m_GitProgressList.m_pProgControl = &m_ctrlProgress;
1040 m_GitProgressList.m_pTaskbarList = m_pTaskbarList;
1042 dwStyle = LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE | LVS_SINGLESEL;
1043 DWORD exStyle = LVS_EX_HEADERDRAGDROP | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP;
1044 if (CRegDWORD(L"Software\\TortoiseGit\\FullRowSelect", TRUE))
1045 exStyle |= LVS_EX_FULLROWSELECT;
1046 if (g_Git.m_IsUseLibGit2)
1048 m_refList.Create(dwStyle, rectDummy, &m_ctrlTabCtrl, IDC_REFLIST);
1049 m_refList.SetExtendedStyle(exStyle);
1050 m_refList.Init();
1051 m_ctrlTabCtrl.InsertTab(&m_refList, CString(MAKEINTRESOURCE(IDS_REFLIST)), -1);
1053 m_tagCompareList.Create(dwStyle, rectDummy, &m_ctrlTabCtrl, IDC_TAGCOMPARELIST);
1054 m_tagCompareList.SetExtendedStyle(exStyle);
1055 m_tagCompareList.Init();
1056 m_ctrlTabCtrl.InsertTab(&m_tagCompareList, CString(MAKEINTRESOURCE(IDS_PROC_SYNC_COMPARETAGS)), -1);
1058 m_ProjectProperties.ReadProps();
1060 AdjustControlSize(IDC_CHECK_PUTTY_KEY);
1061 AdjustControlSize(IDC_CHECK_FORCE);
1063 AddAnchor(IDC_SYNC_TAB,TOP_LEFT,BOTTOM_RIGHT);
1065 AddAnchor(IDC_GROUP_INFO,TOP_LEFT,TOP_RIGHT);
1066 AddAnchor(IDC_COMBOBOXEX_URL,TOP_LEFT,TOP_RIGHT);
1067 AddAnchor(IDC_BUTTON_MANAGE,TOP_RIGHT);
1068 AddAnchor(IDC_BUTTON_PULL,BOTTOM_LEFT);
1069 AddAnchor(IDC_BUTTON_PUSH,BOTTOM_LEFT);
1070 AddAnchor(IDC_BUTTON_SUBMODULE,BOTTOM_LEFT);
1071 AddAnchor(IDC_BUTTON_STASH,BOTTOM_LEFT);
1072 AddAnchor(IDC_BUTTON_APPLY,BOTTOM_RIGHT);
1073 AddAnchor(IDC_BUTTON_EMAIL,BOTTOM_RIGHT);
1074 AddAnchor(IDC_PROGRESS_SYNC,TOP_LEFT,TOP_RIGHT);
1075 AddAnchor(IDOK,BOTTOM_RIGHT);
1076 AddAnchor(IDHELP,BOTTOM_RIGHT);
1077 AddAnchor(IDC_STATIC_STATUS, BOTTOM_LEFT, BOTTOM_RIGHT);
1078 AddAnchor(IDC_ANIMATE_SYNC,TOP_LEFT);
1079 AddAnchor(IDC_BUTTON_COMMIT,BOTTOM_LEFT);
1080 AddAnchor(IDC_LOG, BOTTOM_LEFT);
1082 // do not use BRANCH_COMBOX_ADD_ANCHOR here, we want to have different stylings
1083 AddAnchor(IDC_COMBOBOXEX_LOCAL_BRANCH, TOP_LEFT,TOP_CENTER);
1084 AddAnchor(IDC_COMBOBOXEX_REMOTE_BRANCH, TOP_CENTER, TOP_RIGHT);
1085 AddAnchor(IDC_BUTTON_LOCAL_BRANCH, TOP_CENTER);
1086 AddAnchor(IDC_BUTTON_REMOTE_BRANCH, TOP_RIGHT);
1087 AddAnchor(IDC_STATIC_REMOTE_BRANCH, TOP_CENTER);
1088 AddAnchor(IDC_PROG_LABEL, TOP_LEFT);
1090 CString WorkingDir=g_Git.m_CurrentDir;
1091 WorkingDir.Replace(L':', L'_');
1092 m_RegKeyRemoteBranch = L"Software\\TortoiseGit\\History\\SyncBranch\\" + WorkingDir;
1095 this->AddOthersToAnchor();
1097 this->m_ctrlPush.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PUSH)));
1098 this->m_ctrlPush.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PUSHTAGS)));
1099 this->m_ctrlPush.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PUSHNOTES)));
1101 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_PULL)));
1102 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_FETCH)));
1103 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_FETCHREBASE)));
1104 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_FETCHALL)));
1105 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_REMOTEUPDATE)));
1106 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_CLEANUPSTALEBRANCHES)));
1107 this->m_ctrlPull.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_COMPARETAGS)));
1109 this->m_ctrlSubmodule.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_SUBKODULEUPDATE)));
1110 this->m_ctrlSubmodule.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_SUBKODULEINIT)));
1111 this->m_ctrlSubmodule.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_SUBKODULESYNC)));
1113 this->m_ctrlStash.AddEntry(CString(MAKEINTRESOURCE(IDS_MENUSTASHSAVE)));
1114 this->m_ctrlStash.AddEntry(CString(MAKEINTRESOURCE(IDS_MENUSTASHPOP)));
1115 this->m_ctrlStash.AddEntry(CString(MAKEINTRESOURCE(IDS_MENUSTASHAPPLY)));
1117 WorkingDir.Replace(L':', L'_');
1119 CString regkey ;
1120 regkey.Format(L"Software\\TortoiseGit\\TortoiseProc\\Sync\\%s", static_cast<LPCWSTR>(WorkingDir));
1122 this->m_regPullButton = CRegDWORD(regkey + L"\\Pull", 0);
1123 this->m_regPushButton = CRegDWORD(regkey + L"\\Push", 0);
1124 this->m_regSubmoduleButton = CRegDWORD(regkey + L"\\Submodule");
1125 this->m_regAutoLoadPutty = CRegDWORD(regkey + L"\\AutoLoadPutty", CAppUtils::IsSSHPutty());
1127 this->UpdateData();
1128 this->m_bAutoLoadPuttyKey = m_regAutoLoadPutty;
1129 if(!CAppUtils::IsSSHPutty())
1130 m_bAutoLoadPuttyKey = false;
1131 this->UpdateData(FALSE);
1133 this->m_ctrlPull.SetCurrentEntry(this->m_regPullButton);
1134 this->m_ctrlPush.SetCurrentEntry(this->m_regPushButton);
1135 this->m_ctrlSubmodule.SetCurrentEntry(this->m_regSubmoduleButton);
1137 CAppUtils::SetWindowTitle(*this, g_Git.m_CurrentDir);
1139 EnableSaveRestore(L"SyncDlg");
1141 m_ctrlURL.SetCaseSensitive(TRUE);
1143 m_ctrlURL.SetCustomAutoSuggest(true, true, true);
1144 m_ctrlURL.SetMaxHistoryItems(0x7FFFFFFF);
1145 this->m_ctrlURL.LoadHistory(L"Software\\TortoiseGit\\History\\SyncURL\\" + WorkingDir, L"url");
1147 m_remotelist.clear();
1148 if(!g_Git.GetRemoteList(m_remotelist))
1150 for (unsigned int i = 0; i < m_remotelist.size(); ++i)
1152 m_ctrlURL.AddString(m_remotelist[i]);
1155 m_ctrlURL.SetCurSel(0);
1156 m_ctrlRemoteBranch.SetCurSel(0);
1158 this->LoadBranchInfo();
1160 this->m_bInited=true;
1161 FetchOutList();
1163 m_ctrlTabCtrl.ShowTab(IDC_CMD_LOG-1,false);
1164 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST-1,false);
1165 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST-1,false);
1166 m_ctrlTabCtrl.ShowTab(IDC_IN_CONFLICT-1,false);
1167 m_ctrlTabCtrl.ShowTab(IDC_CMD_GIT_PROG-1, false);
1168 m_ctrlTabCtrl.ShowTab(IDC_REFLIST-1, false);
1169 m_ctrlTabCtrl.ShowTab(IDC_TAGCOMPARELIST - 1, false);
1171 m_ctrlRemoteBranch.m_bWantReturn = TRUE;
1172 m_ctrlURL.m_bWantReturn = TRUE;
1174 if (m_seq > 0 && static_cast<DWORD>(CRegDWORD(L"Software\\TortoiseGit\\SyncDialogRandomPos")))
1176 m_seq %= 5;
1177 RECT rect;
1178 GetWindowRect(&rect);
1179 rect.top -= m_seq * 30;
1180 rect.bottom -= m_seq * 30;
1181 if (rect.top < 0)
1183 rect.top += CDPIAware::Instance().ScaleY(GetSafeHwnd(), 150);
1184 rect.bottom += CDPIAware::Instance().ScaleY(GetSafeHwnd(), 150);
1186 MoveWindow(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
1189 SetTheme(CTheme::Instance().IsDarkTheme());
1191 return TRUE; // return TRUE unless you set the focus to a control
1192 // EXCEPTION: OCX Property Pages should return FALSE
1195 void CSyncDlg::OnBnClickedButtonManage()
1197 CAppUtils::LaunchRemoteSetting();
1198 Refresh();
1201 void CSyncDlg::Refresh()
1203 theApp.DoWaitCursor(1);
1205 const int lastSelected = m_ctrlURL.GetCurSel();
1206 CString url;
1207 this->m_ctrlURL.GetWindowText(url);
1209 this->m_ctrlURL.Reset();
1210 CString workingDir = g_Git.m_CurrentDir;
1211 workingDir.Replace(L':', L'_');
1212 this->m_ctrlURL.LoadHistory(L"Software\\TortoiseGit\\History\\SyncURL\\" + workingDir, L"url");
1214 bool found = false;
1215 m_remotelist.clear();
1216 if (!g_Git.GetRemoteList(m_remotelist))
1218 for (size_t i = 0; i < m_remotelist.size(); ++i)
1220 m_ctrlURL.AddString(m_remotelist[i]);
1221 if (m_remotelist[i] == url)
1222 found = true;
1225 if (lastSelected >= 0 && !found)
1227 m_ctrlURL.SetCurSel(0);
1228 m_ctrlURL.GetWindowText(url);
1231 CString local;
1232 CString remote;
1233 this->m_ctrlLocalBranch.GetWindowText(local);
1234 this->m_ctrlRemoteBranch.GetWindowText(remote);
1236 this->LoadBranchInfo();
1238 this->m_ctrlLocalBranch.AddString(local);
1239 this->m_ctrlRemoteBranch.AddString(remote);
1240 this->m_ctrlURL.AddString(url);
1242 m_OutLogList.ShowText(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_REFRESHING)));
1243 this->FetchOutList(true);
1244 theApp.DoWaitCursor(-1);
1247 BOOL CSyncDlg::PreTranslateMessage(MSG* pMsg)
1249 if (pMsg->message == WM_KEYDOWN)
1251 switch (pMsg->wParam)
1253 case VK_F5:
1255 if (m_bBlock)
1256 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
1257 Refresh();
1259 break;
1261 /* Avoid TAB control destroy but dialog exist*/
1262 case VK_ESCAPE:
1263 case VK_CANCEL:
1265 wchar_t buff[129];
1266 ::GetClassName(pMsg->hwnd, buff, _countof(buff) - 1);
1268 /* Use MSFTEDIT_CLASS http://msdn.microsoft.com/en-us/library/bb531344.aspx */
1269 if (_wcsnicmp(buff, MSFTEDIT_CLASS, _countof(buff) - 1) == 0 || //Unicode and MFC 2012 and later
1270 _wcsnicmp(buff, RICHEDIT_CLASS, _countof(buff) - 1) == 0 || //ANSI or MFC 2010
1271 _wcsnicmp(buff, L"SysListView32", _countof(buff) - 1) == 0)
1273 this->PostMessage(WM_KEYDOWN,VK_ESCAPE,0);
1274 return TRUE;
1279 return __super::PreTranslateMessage(pMsg);
1281 void CSyncDlg::FetchOutList(bool force)
1283 if (!m_bInited || m_bWantToExit)
1284 return;
1285 m_OutChangeFileList.Clear();
1286 this->m_OutLogList.Clear();
1288 m_ctrlTabCtrl.ShowTab(IDC_OUT_LOGLIST - 1, true);
1289 m_ctrlTabCtrl.ShowTab(IDC_OUT_CHANGELIST - 1, true);
1291 CString remote;
1292 this->m_ctrlURL.GetWindowText(remote);
1293 CString remotebranch;
1294 this->m_ctrlRemoteBranch.GetWindowText(remotebranch);
1295 remotebranch = remote + L'/' + remotebranch;
1296 CGitHash remotebranchHash;
1297 g_Git.GetHash(remotebranchHash, remotebranch);
1299 if(IsURL())
1301 CString str;
1302 str.LoadString(IDS_PROC_SYNC_PUSH_UNKNOWN);
1303 m_OutLogList.ShowText(str);
1304 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,FALSE);
1305 m_OutLocalBranch.Empty();
1306 m_OutRemoteBranch.Empty();
1308 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1309 return ;
1312 else if(remotebranchHash.IsEmpty())
1314 CString str;
1315 str.Format(IDS_PROC_SYNC_PUSH_UNKNOWNBRANCH, static_cast<LPCWSTR>(remotebranch));
1316 m_OutLogList.ShowText(str);
1317 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,FALSE);
1318 m_OutLocalBranch.Empty();
1319 m_OutRemoteBranch.Empty();
1321 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1322 return ;
1324 else
1326 CString localbranch;
1327 localbranch=this->m_ctrlLocalBranch.GetString();
1329 if(localbranch != m_OutLocalBranch || m_OutRemoteBranch != remotebranch || force)
1331 m_OutLogList.ClearText();
1333 CGitHash base, localBranchHash;
1334 const bool isFastForward = g_Git.IsFastForward(remotebranch, localbranch, &base);
1336 if (g_Git.GetHash(localBranchHash, localbranch))
1338 MessageBox(g_Git.GetGitLastErr(L"Could not get hash of \"" + localbranch + L"\"."), L"TortoiseGit", MB_ICONERROR);
1339 return;
1341 if (remotebranchHash == localBranchHash)
1343 CString str;
1344 str.FormatMessage(IDS_PROC_SYNC_COMMITSAHEAD, 0, static_cast<LPCWSTR>(remotebranch));
1345 m_OutLogList.ShowText(str);
1346 this->m_ctrlStatus.SetWindowText(str);
1347 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,FALSE);
1348 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1350 else if (isFastForward || m_bForce)
1352 CString range;
1353 range.Format(L"%s..%s", static_cast<LPCWSTR>(g_Git.FixBranchName(remotebranch)), static_cast<LPCWSTR>(g_Git.FixBranchName(localbranch)));
1354 //fast forward
1355 m_OutLogList.FillGitLog(nullptr, &range, CGit::LOG_INFO_STAT | CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
1356 CString str;
1357 str.FormatMessage(IDS_PROC_SYNC_COMMITSAHEAD, m_OutLogList.GetItemCount(), static_cast<LPCWSTR>(remotebranch));
1358 this->m_ctrlStatus.SetWindowText(str);
1360 if (isFastForward)
1361 AddDiffFileList(&m_OutChangeFileList, &m_arOutChangeList, localBranchHash, remotebranchHash);
1362 else
1364 AddDiffFileList(&m_OutChangeFileList, &m_arOutChangeList, localBranchHash, base);
1367 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID()-1,TRUE);
1368 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(TRUE);
1370 else
1372 CString str;
1373 str.FormatMessage(IDS_PROC_SYNC_NOFASTFORWARD, static_cast<LPCWSTR>(localbranch), static_cast<LPCWSTR>(remotebranch));
1374 m_OutLogList.ShowText(str);
1375 this->m_ctrlStatus.SetWindowText(str);
1376 this->m_ctrlTabCtrl.ShowTab(m_OutChangeFileList.GetDlgCtrlID() - 1, FALSE);
1377 this->GetDlgItem(IDC_BUTTON_EMAIL)->EnableWindow(FALSE);
1380 this->m_OutLocalBranch=localbranch;
1381 this->m_OutRemoteBranch=remotebranch;
1385 bool CSyncDlg::IsURL()
1387 CString str;
1388 this->m_ctrlURL.GetWindowText(str);
1389 return str.Find(L'\\') >= 0 || str.Find(L'/') >= 0;
1392 void CSyncDlg::OnCbnEditchangeComboboxex()
1394 SetTimer(IDT_INPUT, 1000, nullptr);
1395 this->m_OutLogList.ShowText(CString(MAKEINTRESOURCE(IDS_PROC_SYNC_WAINTINPUT)));
1397 //this->FetchOutList();
1400 UINT CSyncDlg::ProgressThread()
1402 m_startTick = GetTickCount64();
1403 m_bDone = false;
1404 STRING_VECTOR list;
1405 CProgressDlg::RunCmdList(this, m_GitCmdList, list, true, nullptr, &this->m_bAbort, &this->m_Databuf);
1406 InterlockedExchange(&m_bBlock, FALSE);
1407 return 0;
1410 LRESULT CSyncDlg::OnProgressUpdateUI(WPARAM wParam,LPARAM lParam)
1412 if (m_bWantToExit)
1413 return 0;
1414 if(wParam == MSG_PROGRESSDLG_START)
1416 m_BufStart = 0;
1417 if (CRegDWORD(L"Software\\TortoiseGit\\DownloadAnimation", TRUE) == TRUE)
1418 m_ctrlAnimate.Play(0, UINT_MAX, UINT_MAX);
1419 this->m_ctrlProgress.SetPos(0);
1420 if (m_pTaskbarList)
1422 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
1423 m_pTaskbarList->SetProgressValue(m_hWnd, 0, 100);
1427 if(wParam == MSG_PROGRESSDLG_END || wParam == MSG_PROGRESSDLG_FAILED)
1429 ULONGLONG tickSpent = GetTickCount64() - m_startTick;
1430 CString strEndTime = CLoglistUtils::FormatDateAndTime(CTime::GetCurrentTime(), DATE_SHORTDATE, true, false);
1432 m_BufStart = 0;
1433 m_Databuf.m_critSec.Lock();
1434 m_Databuf.clear();
1435 m_Databuf.m_critSec.Unlock();
1437 m_bDone = true;
1438 m_ctrlAnimate.Stop();
1439 m_ctrlProgress.SetPos(100);
1440 //this->DialogEnableWindow(IDOK,TRUE);
1443 CString text;
1444 m_ctrlCmdOut.GetWindowText(text);
1445 text.Remove('\r');
1446 if (static_cast<DWORD>(CRegStdDWORD(L"Software\\TortoiseGit\\StyleGitOutput", TRUE)) == TRUE)
1447 CAppUtils::StyleWarningsErrors(text, &m_ctrlCmdOut);
1448 CAppUtils::StyleURLs(text, &m_ctrlCmdOut);
1451 auto exitCode = static_cast<DWORD>(lParam);
1452 if (exitCode)
1454 if (m_pTaskbarList)
1456 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_ERROR);
1457 m_pTaskbarList->SetProgressValue(m_hWnd, 100, 100);
1459 CString log;
1460 log.Format(IDS_PROC_PROGRESS_GITUNCLEANEXIT, exitCode);
1461 CString err;
1462 err.Format(L"\r\n\r\n%s (%I64u ms @ %s)\r\n", static_cast<LPCWSTR>(log), tickSpent, static_cast<LPCWSTR>(strEndTime));
1463 CProgressDlg::InsertColorText(this->m_ctrlCmdOut, err, RGB(255,0,0));
1464 if (CRegDWORD(L"Software\\TortoiseGit\\NoSounds", FALSE) == FALSE)
1465 PlaySound(reinterpret_cast<LPCWSTR>(SND_ALIAS_SYSTEMEXCLAMATION), nullptr, SND_ALIAS_ID | SND_ASYNC);
1467 else
1469 if (m_pTaskbarList)
1470 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
1471 CString temp;
1472 temp.LoadString(IDS_SUCCESS);
1473 CString log;
1474 log.Format(L"\r\n%s (%I64u ms @ %s)\r\n", static_cast<LPCWSTR>(temp), tickSpent, static_cast<LPCWSTR>(strEndTime));
1475 CProgressDlg::InsertColorText(this->m_ctrlCmdOut, log, RGB(0,0,255));
1477 m_GitCmdStatus = exitCode;
1479 //if(wParam == MSG_PROGRESSDLG_END)
1480 RunPostAction();
1483 if(lParam != 0)
1484 ParserCmdOutput(static_cast<char>(lParam));
1485 else
1487 m_Databuf.m_critSec.Lock();
1488 for (size_t i = m_BufStart; i < m_Databuf.size(); ++i)
1490 char c = m_Databuf[m_BufStart];
1491 ++m_BufStart;
1492 m_Databuf.m_critSec.Unlock();
1493 ParserCmdOutput(c);
1495 m_Databuf.m_critSec.Lock();
1498 if (m_BufStart > 1000)
1500 m_Databuf.erase(m_Databuf.cbegin(), m_Databuf.cbegin() + m_BufStart);
1501 m_BufStart = 0;
1503 m_Databuf.m_critSec.Unlock();
1506 return 0;
1509 static REF_VECTOR HashMapToRefMap(MAP_HASH_NAME& map)
1511 auto rmap = REF_VECTOR();
1512 for (auto mit = map.cbegin(); mit != map.cend(); ++mit)
1514 for (auto rit = mit->second.cbegin(); rit != mit->second.cend(); ++rit)
1516 rmap.emplace_back(TGitRef{ *rit, mit->first });
1519 return rmap;
1522 void CSyncDlg::FillNewRefMap()
1524 m_refList.Clear();
1525 m_newHashMap.clear();
1527 if (!g_Git.m_IsUseLibGit2)
1528 return;
1530 CAutoRepository repo(g_Git.GetGitRepository());
1531 if (!repo)
1533 CMessageBox::Show(m_hWnd, CGit::GetLibGit2LastErr(L"Could not open repository."), L"TortoiseGit", MB_OK | MB_ICONERROR);
1534 return;
1537 if (CGit::GetMapHashToFriendName(repo, m_newHashMap))
1539 MessageBox(CGit::GetLibGit2LastErr(L"Could not get all refs."), L"TortoiseGit", MB_ICONERROR);
1540 return;
1543 auto oldRefMap = HashMapToRefMap(m_oldHashMap);
1544 auto newRefMap = HashMapToRefMap(m_newHashMap);
1545 for (auto oit = oldRefMap.cbegin(); oit != oldRefMap.cend(); ++oit)
1547 bool found = false;
1548 for (auto nit = newRefMap.cbegin(); nit != newRefMap.cend(); ++nit)
1550 // changed ref
1551 if (oit->name == nit->name)
1553 found = true;
1554 m_refList.AddEntry(repo, oit->name, &oit->hash, &nit->hash);
1555 break;
1558 // deleted ref
1559 if (!found)
1560 m_refList.AddEntry(repo, oit->name, &oit->hash, nullptr);
1562 for (auto nit = newRefMap.cbegin(); nit != newRefMap.cend(); ++nit)
1564 bool found = false;
1565 for (auto oit = oldRefMap.cbegin(); oit != oldRefMap.cend(); ++oit)
1567 if (oit->name == nit->name)
1569 found = true;
1570 break;
1573 // new ref
1574 if (!found)
1575 m_refList.AddEntry(repo, nit->name, nullptr, &nit->hash);
1577 m_refList.Show();
1580 void CSyncDlg::RunPostAction()
1582 if (m_bWantToExit)
1583 return;
1585 FillNewRefMap();
1587 if (this->m_CurrentCmd == GIT_COMMAND_PUSH)
1589 DWORD exitcode = 0xFFFFFFFF;
1590 CString error;
1591 CHooks::Instance().SetProjectProperties(g_Git.m_CurrentDir, m_ProjectProperties);
1592 if (CHooks::Instance().PostPush(GetSafeHwnd(), g_Git.m_CurrentDir, exitcode, error))
1594 if (exitcode)
1596 CString temp;
1597 temp.Format(IDS_ERR_HOOKFAILED, static_cast<LPCWSTR>(error));
1598 CMessageBox::Show(GetSafeHwnd(), temp, L"TortoiseGit", MB_OK | MB_ICONERROR);
1599 return;
1603 EnableControlButton(true);
1604 SwitchToInput();
1605 this->FetchOutList(true);
1607 else if (this->m_CurrentCmd == GIT_COMMAND_PULL)
1608 PullComplete();
1609 else if (this->m_CurrentCmd == GIT_COMMAND_FETCH || this->m_CurrentCmd == GIT_COMMAND_FETCHANDREBASE)
1610 FetchComplete();
1611 else if (this->m_CurrentCmd == GIT_COMMAND_SUBMODULE)
1613 //this->m_ctrlCmdOut.SetSel(-1,-1);
1614 //this->m_ctrlCmdOut.ReplaceSel(L"Done\r\n");
1615 //this->m_ctrlCmdOut.SetSel(-1,-1);
1616 EnableControlButton(true);
1617 SwitchToInput();
1619 else if (this->m_CurrentCmd == GIT_COMMAND_STASH)
1620 StashComplete();
1621 else if (this->m_CurrentCmd == GIT_COMMAND_REMOTE)
1623 this->FetchOutList(true);
1624 EnableControlButton(true);
1625 SwitchToInput();
1626 ShowTab(IDC_REFLIST);
1629 void CSyncDlg::ParserCmdOutput(char ch)
1631 if (m_bAbort)
1632 return;
1633 CProgressDlg::ParserCmdOutput(m_ctrlCmdOut,m_ctrlProgress,m_hWnd,m_pTaskbarList,m_LogText,ch);
1635 void CSyncDlg::OnBnClickedButtonCommit()
1637 CString cmd = L"/command:commit";
1638 cmd += L" /path:\"";
1639 cmd += g_Git.m_CurrentDir;
1640 cmd += L'"';
1642 CAppUtils::RunTortoiseGitProc(cmd);
1645 void CSyncDlg::OnOK()
1647 UpdateCombox();
1648 this->UpdateData();
1649 m_ctrlURL.SaveHistory();
1650 SaveHistory();
1651 m_regAutoLoadPutty = this->m_bAutoLoadPuttyKey;
1652 m_tooltips.Pop();
1653 __super::OnOK();
1656 void CSyncDlg::OnCancel()
1658 m_bAbort = true;
1659 m_GitProgressList.Cancel();
1660 if (m_bDone && !m_GitProgressList.IsRunning())
1662 CResizableStandAloneDialog::OnCancel();
1663 return;
1665 if (m_GitProgressList.IsRunning())
1666 WaitForSingleObject(m_GitProgressList.m_pThread->m_hThread, 10000);
1668 if (g_Git.m_CurrentGitPi.hProcess)
1670 DWORD dwConfirmKillProcess = CRegDWORD(L"Software\\TortoiseGit\\ConfirmKillProcess");
1671 if (dwConfirmKillProcess && CMessageBox::Show(m_hWnd, IDS_PROC_CONFIRMKILLPROCESS, IDS_APPNAME, MB_YESNO | MB_ICONQUESTION) != IDYES)
1672 return;
1673 if (::GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0))
1674 ::WaitForSingleObject(g_Git.m_CurrentGitPi.hProcess, 10000);
1676 CProgressDlg::KillProcessTree(g_Git.m_CurrentGitPi.dwProcessId);
1679 ::WaitForSingleObject(g_Git.m_CurrentGitPi.hProcess ,10000);
1680 if (m_pThread)
1682 if (::WaitForSingleObject(m_pThread->m_hThread, 5000) == WAIT_TIMEOUT)
1683 g_Git.KillRelatedThreads(m_pThread);
1685 m_tooltips.Pop();
1686 CResizableStandAloneDialog::OnCancel();
1689 void CSyncDlg::OnBnClickedButtonSubmodule()
1691 const bool bShift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
1692 this->UpdateData();
1693 UpdateCombox();
1695 if (bShift)
1697 switch (m_ctrlSubmodule.GetCurrentEntry())
1699 case 0:
1700 [[fallthrough]];
1701 case 1:
1702 CAppUtils::RunTortoiseGitProc(L"/command:subupdate /bkpath:\"" + g_Git.m_CurrentDir + L"\"");
1703 break;
1704 case 2:
1705 CAppUtils::RunTortoiseGitProc(L"/command:subsync /bkpath:\"" + g_Git.m_CurrentDir + L"\"");
1706 break;
1708 return;
1711 m_ctrlCmdOut.SetWindowText(L"");
1712 m_LogText.Empty();
1714 this->m_regSubmoduleButton = static_cast<DWORD>(this->m_ctrlSubmodule.GetCurrentEntry());
1716 this->SwitchToRun();
1718 this->m_bAbort=false;
1719 this->m_GitCmdList.clear();
1721 ShowTab(IDC_CMD_LOG);
1723 CString cmd;
1725 switch (m_ctrlSubmodule.GetCurrentEntry())
1727 case 0:
1728 cmd = L"git.exe submodule update --init --recursive";
1729 if (m_bForce)
1730 cmd += L" --force";
1731 break;
1732 case 1:
1733 cmd = L"git.exe submodule init";
1734 break;
1735 case 2:
1736 cmd = L"git.exe submodule sync --recursive";
1737 break;
1740 m_GitCmdList.push_back(cmd);
1742 m_CurrentCmd = GIT_COMMAND_SUBMODULE;
1744 StartWorkerThread();
1747 void CSyncDlg::OnBnClickedButtonStash()
1749 const bool bShift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
1750 UpdateData();
1751 UpdateCombox();
1753 if (bShift)
1755 if (m_ctrlStash.GetCurrentEntry() == 0)
1756 CAppUtils::RunTortoiseGitProc(L"/command:stashsave");
1757 return;
1760 m_ctrlCmdOut.SetWindowText(L"");
1761 m_LogText.Empty();
1763 SwitchToRun();
1765 m_bAbort = false;
1766 m_GitCmdList.clear();
1768 ShowTab(IDC_CMD_LOG);
1770 m_ctrlTabCtrl.ShowTab(IDC_IN_LOGLIST - 1, false);
1771 m_ctrlTabCtrl.ShowTab(IDC_IN_CHANGELIST -1, false);
1772 m_ctrlTabCtrl.ShowTab(IDC_IN_CONFLICT -1, false);
1774 CString cmd;
1775 switch (m_ctrlStash.GetCurrentEntry())
1777 case 0:
1778 cmd = L"git.exe stash save";
1779 break;
1780 case 1:
1781 cmd = L"git.exe stash pop";
1782 break;
1783 case 2:
1784 cmd = L"git.exe stash apply";
1785 break;
1788 m_GitCmdList.push_back(cmd);
1789 m_CurrentCmd = GIT_COMMAND_STASH;
1791 StartWorkerThread();
1794 void CSyncDlg::OnTimer(UINT_PTR nIDEvent)
1796 if( nIDEvent == IDT_INPUT)
1798 KillTimer(IDT_INPUT);
1799 this->FetchOutList(true);
1800 m_ctrlTabCtrl.ShowTab(IDC_TAGCOMPARELIST - 1, false);
1804 LRESULT CSyncDlg::OnTaskbarBtnCreated(WPARAM wParam, LPARAM lParam)
1806 m_pTaskbarList.Release();
1807 m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList);
1808 m_GitProgressList.m_pTaskbarList = m_pTaskbarList;
1809 return __super::OnTaskbarButtonCreated(wParam, lParam);
1812 void CSyncDlg::OnBnClickedCheckForce()
1814 UpdateData();
1817 void CSyncDlg::OnBnClickedLog()
1819 CString cmd = L"/command:log";
1820 cmd += L" /path:\"";
1821 cmd += g_Git.m_CurrentDir;
1822 cmd += L'"';
1824 CAppUtils::RunTortoiseGitProc(cmd);
1827 LRESULT CSyncDlg::OnProgCmdFinish(WPARAM /*wParam*/, LPARAM /*lParam*/)
1829 RefreshCursor();
1830 RunPostAction();
1831 return 0;
1834 void CSyncDlg::OnDestroy()
1836 m_bWantToExit = true;
1837 __super::OnDestroy();
1840 void CSyncDlg::SetTheme(bool bDark)
1842 __super::SetTheme(bDark);
1843 CMFCVisualManager::GetInstance()->DestroyInstance();
1844 if (bDark)
1846 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CThemeMFCVisualManager));
1847 m_ctrlTabCtrl.ModifyTabStyle(CMFCTabCtrl::STYLE_3D);
1849 else
1851 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
1852 m_ctrlTabCtrl.ModifyTabStyle(CMFCTabCtrl::STYLE_FLAT);
1854 CMFCVisualManager::RedrawAll();
1857 void CSyncDlg::OnEnLinkLog(NMHDR *pNMHDR, LRESULT *pResult)
1859 // similar code in ProgressDlg.cpp and LogDlg.cpp
1860 ENLINK *pEnLink = reinterpret_cast<ENLINK *>(pNMHDR);
1861 if ((pEnLink->msg == WM_LBUTTONUP) || (pEnLink->msg == WM_SETCURSOR))
1863 CString msg;
1864 m_ctrlCmdOut.GetWindowText(msg);
1865 msg.Replace(L"\r\n", L"\n");
1866 CString url = msg.Mid(pEnLink->chrg.cpMin, pEnLink->chrg.cpMax - pEnLink->chrg.cpMin);
1867 // check if it's an email address
1868 auto atpos = url.Find(L'@');
1869 if ((atpos > 0) && (url.ReverseFind(L'.') > atpos) && !::PathIsURL(url))
1870 url = L"mailto:" + url;
1871 if (::PathIsURL(url))
1873 if (pEnLink->msg == WM_LBUTTONUP)
1874 ShellExecute(GetSafeHwnd(), L"open", url, nullptr, nullptr, SW_SHOWDEFAULT);
1875 else
1877 static RECT prevRect = { 0 };
1878 CWnd* pMsgView = &m_ctrlCmdOut;
1879 if (pMsgView)
1881 RECT rc;
1882 POINTL pt;
1883 pMsgView->SendMessage(EM_POSFROMCHAR, reinterpret_cast<WPARAM>(&pt), pEnLink->chrg.cpMin);
1884 rc.left = pt.x;
1885 rc.top = pt.y;
1886 pMsgView->SendMessage(EM_POSFROMCHAR, reinterpret_cast<WPARAM>(&pt), pEnLink->chrg.cpMax);
1887 rc.right = pt.x;
1888 rc.bottom = pt.y + 12;
1889 if ((prevRect.left != rc.left) || (prevRect.top != rc.top))
1891 m_tooltips.DelTool(pMsgView, 1);
1892 m_tooltips.AddTool(pMsgView, url, &rc, 1);
1893 prevRect = rc;
1899 *pResult = 0;
1902 void CSyncDlg::OnEnscrollLog()
1904 m_tooltips.DelTool(&m_ctrlCmdOut, 1);
1907 void CSyncDlg::StartWorkerThread()
1909 if (InterlockedExchange(&m_bBlock, TRUE))
1910 return;
1912 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL);
1913 if (!m_pThread)
1915 InterlockedExchange(&m_bBlock, FALSE);
1916 CMessageBox::Show(this->m_hWnd, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
1917 SwitchToInput();
1918 EnableControlButton(true);