Add retry button when fetch failed
[TortoiseGit.git] / src / TortoiseProc / AppUtils.cpp
blobd264335733fc317efbac02770ca03f49be91e057
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2014 - TortoiseGit
4 // Copyright (C) 2003-2011, 2013 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "TortoiseProc.h"
22 #include "PathUtils.h"
23 #include "AppUtils.h"
24 #include "StringUtils.h"
25 #include "MessageBox.h"
26 #include "registry.h"
27 #include "TGitPath.h"
28 #include "Git.h"
29 #include "UnicodeUtils.h"
30 #include "ExportDlg.h"
31 #include "ProgressDlg.h"
32 #include "GitAdminDir.h"
33 #include "ProgressDlg.h"
34 #include "BrowseFolder.h"
35 #include "DirFileEnum.h"
36 #include "MessageBox.h"
37 #include "GitStatus.h"
38 #include "CreateBranchTagDlg.h"
39 #include "GitSwitchDlg.h"
40 #include "ResetDlg.h"
41 #include "DeleteConflictDlg.h"
42 #include "ChangedDlg.h"
43 #include "SendMailDlg.h"
44 #include "GitProgressDlg.h"
45 #include "PushDlg.h"
46 #include "CommitDlg.h"
47 #include "MergeDlg.h"
48 #include "MergeAbortDlg.h"
49 #include "Hooks.h"
50 #include "..\Settings\Settings.h"
51 #include "InputDlg.h"
52 #include "SVNDCommitDlg.h"
53 #include "requestpulldlg.h"
54 #include "PullFetchDlg.h"
55 #include "FileDiffDlg.h"
56 #include "RebaseDlg.h"
57 #include "PropKey.h"
58 #include "StashSave.h"
59 #include "IgnoreDlg.h"
60 #include "FormatMessageWrapper.h"
61 #include "SmartHandle.h"
62 #include "BisectStartDlg.h"
63 #include "SysProgressDlg.h"
64 #include "UserPassword.h"
65 #include "Patch.h"
66 #include "Globals.h"
68 CAppUtils::CAppUtils(void)
72 CAppUtils::~CAppUtils(void)
76 bool CAppUtils::StashSave()
78 CStashSaveDlg dlg;
80 if (dlg.DoModal() == IDOK)
82 CString cmd;
83 cmd = _T("git.exe stash save");
85 if (CAppUtils::GetMsysgitVersion() >= 0x01070700)
87 if (dlg.m_bIncludeUntracked)
88 cmd += _T(" --include-untracked");
89 else if (dlg.m_bAll)
90 cmd += _T(" --all");
93 if (!dlg.m_sMessage.IsEmpty())
95 CString message = dlg.m_sMessage;
96 message.Replace(_T("\""), _T("\"\""));
97 cmd += _T(" -- \"") + message + _T("\"");
100 CProgressDlg progress;
101 progress.m_GitCmd = cmd;
102 return (progress.DoModal() == IDOK);
104 return false;
107 bool CAppUtils::StashApply(CString ref, bool showChanges /* true */)
109 CString cmd,out;
110 cmd = _T("git.exe stash apply ");
111 if (ref.Find(_T("refs/")) == 0)
112 ref = ref.Mid(5);
113 if (ref.Find(_T("stash{")) == 0)
114 ref = _T("stash@") + ref.Mid(5);
115 cmd += ref;
117 CSysProgressDlg sysProgressDlg;
118 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
119 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_PROC_STASHRUNNING)));
120 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
121 sysProgressDlg.SetShowProgressBar(false);
122 sysProgressDlg.SetCancelMsg(IDS_PROGRS_INFOFAILED);
123 sysProgressDlg.ShowModeless((HWND)NULL, true);
125 int ret = g_Git.Run(cmd, &out, CP_UTF8);
127 sysProgressDlg.Stop();
129 bool hasConflicts = (out.Find(_T("CONFLICT")) >= 0);
130 if (ret && !(ret == 1 && hasConflicts))
132 CMessageBox::Show(NULL, CString(MAKEINTRESOURCE(IDS_PROC_STASHAPPLYFAILED)) + _T("\n") + out, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
134 else
136 CString message;
137 message.LoadString(IDS_PROC_STASHAPPLYSUCCESS);
138 if (hasConflicts)
139 message.LoadString(IDS_PROC_STASHAPPLYFAILEDCONFLICTS);
140 if (showChanges)
142 if(CMessageBox::Show(NULL,message + _T("\n") + CString(MAKEINTRESOURCE(IDS_SEECHANGES))
143 ,_T("TortoiseGit"),MB_YESNO|MB_ICONINFORMATION) == IDYES)
145 CChangedDlg dlg;
146 dlg.m_pathList.AddPath(CTGitPath());
147 dlg.DoModal();
149 return true;
151 else
153 CMessageBox::Show(NULL, message ,_T("TortoiseGit"), MB_OK | MB_ICONINFORMATION);
154 return true;
157 return false;
160 bool CAppUtils::StashPop(bool showChanges /* true */)
162 CString cmd,out;
163 cmd=_T("git.exe stash pop ");
165 CSysProgressDlg sysProgressDlg;
166 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
167 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_PROC_STASHRUNNING)));
168 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
169 sysProgressDlg.SetShowProgressBar(false);
170 sysProgressDlg.SetCancelMsg(IDS_PROGRS_INFOFAILED);
171 sysProgressDlg.ShowModeless((HWND)NULL, true);
173 int ret = g_Git.Run(cmd, &out, CP_UTF8);
175 sysProgressDlg.Stop();
177 bool hasConflicts = (out.Find(_T("CONFLICT")) >= 0);
178 if (ret && !(ret == 1 && hasConflicts))
180 CMessageBox::Show(NULL,CString(MAKEINTRESOURCE(IDS_PROC_STASHPOPFAILED)) + _T("\n") + out, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
182 else
184 CString message;
185 message.LoadString(IDS_PROC_STASHPOPSUCCESS);
186 if (hasConflicts)
187 message.LoadString(IDS_PROC_STASHPOPFAILEDCONFLICTS);
188 if (showChanges)
190 if(CMessageBox::Show(NULL,CString(message + _T("\n") + CString(MAKEINTRESOURCE(IDS_SEECHANGES)))
191 ,_T("TortoiseGit"),MB_YESNO|MB_ICONINFORMATION) == IDYES)
193 CChangedDlg dlg;
194 dlg.m_pathList.AddPath(CTGitPath());
195 dlg.DoModal();
197 return true;
199 else
201 CMessageBox::Show(NULL, message ,_T("TortoiseGit"), MB_OK | MB_ICONINFORMATION);
202 return true;
205 return false;
208 BOOL CAppUtils::StartExtMerge(
209 const CTGitPath& basefile, const CTGitPath& theirfile, const CTGitPath& yourfile, const CTGitPath& mergedfile,
210 const CString& basename, const CString& theirname, const CString& yourname, const CString& mergedname, bool bReadOnly,
211 HWND resolveMsgHwnd)
214 CRegString regCom = CRegString(_T("Software\\TortoiseGit\\Merge"));
215 CString ext = mergedfile.GetFileExtension();
216 CString com = regCom;
217 bool bInternal = false;
219 if (!ext.IsEmpty())
221 // is there an extension specific merge tool?
222 CRegString mergetool(_T("Software\\TortoiseGit\\MergeTools\\") + ext.MakeLower());
223 if (!CString(mergetool).IsEmpty())
225 com = mergetool;
228 // is there a filename specific merge tool?
229 CRegString mergetool(_T("Software\\TortoiseGit\\MergeTools\\.") + mergedfile.GetFilename().MakeLower());
230 if (!CString(mergetool).IsEmpty())
232 com = mergetool;
235 if (com.IsEmpty()||(com.Left(1).Compare(_T("#"))==0))
237 // Maybe we should use TortoiseIDiff?
238 if ((ext == _T(".jpg")) || (ext == _T(".jpeg")) ||
239 (ext == _T(".bmp")) || (ext == _T(".gif")) ||
240 (ext == _T(".png")) || (ext == _T(".ico")) ||
241 (ext == _T(".tif")) || (ext == _T(".tiff")) ||
242 (ext == _T(".dib")) || (ext == _T(".emf")))
244 com = CPathUtils::GetAppDirectory() + _T("TortoiseGitIDiff.exe");
245 com = _T("\"") + com + _T("\"");
246 com = com + _T(" /base:%base /theirs:%theirs /mine:%mine /result:%merged");
247 com = com + _T(" /basetitle:%bname /theirstitle:%tname /minetitle:%yname");
249 else
251 // use TortoiseGitMerge
252 bInternal = true;
253 com = CPathUtils::GetAppDirectory() + _T("TortoiseGitMerge.exe");
254 com = _T("\"") + com + _T("\"");
255 com = com + _T(" /base:%base /theirs:%theirs /mine:%mine /merged:%merged");
256 com = com + _T(" /basename:%bname /theirsname:%tname /minename:%yname /mergedname:%mname");
257 com += _T(" /saverequired");
258 if (resolveMsgHwnd)
260 CString s;
261 s.Format(L" /resolvemsghwnd:%I64d", (__int64)resolveMsgHwnd);
262 com += s;
265 if (!g_sGroupingUUID.IsEmpty())
267 com += L" /groupuuid:\"";
268 com += g_sGroupingUUID;
269 com += L"\"";
272 // check if the params are set. If not, just add the files to the command line
273 if ((com.Find(_T("%merged"))<0)&&(com.Find(_T("%base"))<0)&&(com.Find(_T("%theirs"))<0)&&(com.Find(_T("%mine"))<0))
275 com += _T(" \"")+basefile.GetWinPathString()+_T("\"");
276 com += _T(" \"")+theirfile.GetWinPathString()+_T("\"");
277 com += _T(" \"")+yourfile.GetWinPathString()+_T("\"");
278 com += _T(" \"")+mergedfile.GetWinPathString()+_T("\"");
280 if (basefile.IsEmpty())
282 com.Replace(_T("/base:%base"), _T(""));
283 com.Replace(_T("%base"), _T(""));
285 else
286 com.Replace(_T("%base"), _T("\"") + basefile.GetWinPathString() + _T("\""));
287 if (theirfile.IsEmpty())
289 com.Replace(_T("/theirs:%theirs"), _T(""));
290 com.Replace(_T("%theirs"), _T(""));
292 else
293 com.Replace(_T("%theirs"), _T("\"") + theirfile.GetWinPathString() + _T("\""));
294 if (yourfile.IsEmpty())
296 com.Replace(_T("/mine:%mine"), _T(""));
297 com.Replace(_T("%mine"), _T(""));
299 else
300 com.Replace(_T("%mine"), _T("\"") + yourfile.GetWinPathString() + _T("\""));
301 if (mergedfile.IsEmpty())
303 com.Replace(_T("/merged:%merged"), _T(""));
304 com.Replace(_T("%merged"), _T(""));
306 else
307 com.Replace(_T("%merged"), _T("\"") + mergedfile.GetWinPathString() + _T("\""));
308 if (basename.IsEmpty())
310 if (basefile.IsEmpty())
312 com.Replace(_T("/basename:%bname"), _T(""));
313 com.Replace(_T("%bname"), _T(""));
315 else
317 com.Replace(_T("%bname"), _T("\"") + basefile.GetUIFileOrDirectoryName() + _T("\""));
320 else
321 com.Replace(_T("%bname"), _T("\"") + basename + _T("\""));
322 if (theirname.IsEmpty())
324 if (theirfile.IsEmpty())
326 com.Replace(_T("/theirsname:%tname"), _T(""));
327 com.Replace(_T("%tname"), _T(""));
329 else
331 com.Replace(_T("%tname"), _T("\"") + theirfile.GetUIFileOrDirectoryName() + _T("\""));
334 else
335 com.Replace(_T("%tname"), _T("\"") + theirname + _T("\""));
336 if (yourname.IsEmpty())
338 if (yourfile.IsEmpty())
340 com.Replace(_T("/minename:%yname"), _T(""));
341 com.Replace(_T("%yname"), _T(""));
343 else
345 com.Replace(_T("%yname"), _T("\"") + yourfile.GetUIFileOrDirectoryName() + _T("\""));
348 else
349 com.Replace(_T("%yname"), _T("\"") + yourname + _T("\""));
350 if (mergedname.IsEmpty())
352 if (mergedfile.IsEmpty())
354 com.Replace(_T("/mergedname:%mname"), _T(""));
355 com.Replace(_T("%mname"), _T(""));
357 else
359 com.Replace(_T("%mname"), _T("\"") + mergedfile.GetUIFileOrDirectoryName() + _T("\""));
362 else
363 com.Replace(_T("%mname"), _T("\"") + mergedname + _T("\""));
365 if ((bReadOnly)&&(bInternal))
366 com += _T(" /readonly");
368 if(!LaunchApplication(com, IDS_ERR_EXTMERGESTART, false))
370 return FALSE;
373 return TRUE;
376 BOOL CAppUtils::StartExtPatch(const CTGitPath& patchfile, const CTGitPath& dir, const CString& sOriginalDescription, const CString& sPatchedDescription, BOOL bReversed, BOOL bWait)
378 CString viewer;
379 // use TortoiseGitMerge
380 viewer = CPathUtils::GetAppDirectory();
381 viewer += _T("TortoiseGitMerge.exe");
383 viewer = _T("\"") + viewer + _T("\"");
384 viewer = viewer + _T(" /diff:\"") + patchfile.GetWinPathString() + _T("\"");
385 viewer = viewer + _T(" /patchpath:\"") + dir.GetWinPathString() + _T("\"");
386 if (bReversed)
387 viewer += _T(" /reversedpatch");
388 if (!sOriginalDescription.IsEmpty())
389 viewer = viewer + _T(" /patchoriginal:\"") + sOriginalDescription + _T("\"");
390 if (!sPatchedDescription.IsEmpty())
391 viewer = viewer + _T(" /patchpatched:\"") + sPatchedDescription + _T("\"");
392 if (!g_sGroupingUUID.IsEmpty())
394 viewer += L" /groupuuid:\"";
395 viewer += g_sGroupingUUID;
396 viewer += L"\"";
398 if(!LaunchApplication(viewer, IDS_ERR_DIFFVIEWSTART, !!bWait))
400 return FALSE;
402 return TRUE;
405 CString CAppUtils::PickDiffTool(const CTGitPath& file1, const CTGitPath& file2)
407 CString difftool = CRegString(_T("Software\\TortoiseGit\\DiffTools\\.") + file2.GetFilename().MakeLower());
408 if (!difftool.IsEmpty())
409 return difftool;
410 difftool = CRegString(_T("Software\\TortoiseGit\\DiffTools\\.") + file1.GetFilename().MakeLower());
411 if (!difftool.IsEmpty())
412 return difftool;
414 // Is there an extension specific diff tool?
415 CString ext = file2.GetFileExtension().MakeLower();
416 if (!ext.IsEmpty())
418 difftool = CRegString(_T("Software\\TortoiseGit\\DiffTools\\") + ext);
419 if (!difftool.IsEmpty())
420 return difftool;
421 // Maybe we should use TortoiseIDiff?
422 if ((ext == _T(".jpg")) || (ext == _T(".jpeg")) ||
423 (ext == _T(".bmp")) || (ext == _T(".gif")) ||
424 (ext == _T(".png")) || (ext == _T(".ico")) ||
425 (ext == _T(".dib")) || (ext == _T(".emf")))
427 return
428 _T("\"") + CPathUtils::GetAppDirectory() + _T("TortoiseGitIDiff.exe") + _T("\"") +
429 _T(" /left:%base /right:%mine /lefttitle:%bname /righttitle:%yname") +
430 L" /groupuuid:\"" + g_sGroupingUUID + L"\"";
434 // Finally, pick a generic external diff tool
435 difftool = CRegString(_T("Software\\TortoiseGit\\Diff"));
436 return difftool;
439 bool CAppUtils::StartExtDiff(
440 const CString& file1, const CString& file2,
441 const CString& sName1, const CString& sName2,
442 const CString& originalFile1, const CString& originalFile2,
443 const git_revnum_t& hash1, const git_revnum_t& hash2,
444 const DiffFlags& flags, int jumpToLine)
446 CString viewer;
448 CRegDWORD blamediff(_T("Software\\TortoiseGit\\DiffBlamesWithTortoiseMerge"), FALSE);
449 if (!flags.bBlame || !(DWORD)blamediff)
451 viewer = PickDiffTool(file1, file2);
452 // If registry entry for a diff program is commented out, use TortoiseGitMerge.
453 bool bCommentedOut = viewer.Left(1) == _T("#");
454 if (flags.bAlternativeTool)
456 // Invert external vs. internal diff tool selection.
457 if (bCommentedOut)
458 viewer.Delete(0); // uncomment
459 else
460 viewer = "";
462 else if (bCommentedOut)
463 viewer = "";
466 bool bInternal = viewer.IsEmpty();
467 if (bInternal)
469 viewer =
470 _T("\"") + CPathUtils::GetAppDirectory() + _T("TortoiseGitMerge.exe") + _T("\"") +
471 _T(" /base:%base /mine:%mine /basename:%bname /minename:%yname") +
472 _T(" /basereflectedname:%bpath /minereflectedname:%ypath");
473 if (!g_sGroupingUUID.IsEmpty())
475 viewer += L" /groupuuid:\"";
476 viewer += g_sGroupingUUID;
477 viewer += L"\"";
479 if (flags.bBlame)
480 viewer += _T(" /blame");
482 // check if the params are set. If not, just add the files to the command line
483 if ((viewer.Find(_T("%base"))<0)&&(viewer.Find(_T("%mine"))<0))
485 viewer += _T(" \"")+file1+_T("\"");
486 viewer += _T(" \"")+file2+_T("\"");
488 if (viewer.Find(_T("%base")) >= 0)
490 viewer.Replace(_T("%base"), _T("\"")+file1+_T("\""));
492 if (viewer.Find(_T("%mine")) >= 0)
494 viewer.Replace(_T("%mine"), _T("\"")+file2+_T("\""));
497 if (sName1.IsEmpty())
498 viewer.Replace(_T("%bname"), _T("\"") + file1 + _T("\""));
499 else
500 viewer.Replace(_T("%bname"), _T("\"") + sName1 + _T("\""));
502 if (sName2.IsEmpty())
503 viewer.Replace(_T("%yname"), _T("\"") + file2 + _T("\""));
504 else
505 viewer.Replace(_T("%yname"), _T("\"") + sName2 + _T("\""));
507 viewer.Replace(_T("%bpath"), _T("\"") + originalFile1 + _T("\""));
508 viewer.Replace(_T("%ypath"), _T("\"") + originalFile2 + _T("\""));
510 viewer.Replace(_T("%brev"), _T("\"") + hash1 + _T("\""));
511 viewer.Replace(_T("%yrev"), _T("\"") + hash2 + _T("\""));
513 if (flags.bReadOnly && bInternal)
514 viewer += _T(" /readonly");
516 if (jumpToLine > 0)
518 CString temp;
519 temp.Format(_T(" /line:%d"), jumpToLine);
520 viewer += temp;
523 return LaunchApplication(viewer, IDS_ERR_EXTDIFFSTART, flags.bWait);
526 BOOL CAppUtils::StartUnifiedDiffViewer(const CString& patchfile, const CString& title, BOOL bWait)
528 CString viewer;
529 CRegString v = CRegString(_T("Software\\TortoiseGit\\DiffViewer"));
530 viewer = v;
531 if (viewer.IsEmpty() || (viewer.Left(1).Compare(_T("#"))==0))
533 // use TortoiseGitUDiff
534 viewer = CPathUtils::GetAppDirectory();
535 viewer += _T("TortoiseGitUDiff.exe");
536 // enquote the path to TortoiseGitUDiff
537 viewer = _T("\"") + viewer + _T("\"");
538 // add the params
539 viewer = viewer + _T(" /patchfile:%1 /title:\"%title\"");
540 if (!g_sGroupingUUID.IsEmpty())
542 viewer += L" /groupuuid:\"";
543 viewer += g_sGroupingUUID;
544 viewer += L"\"";
547 if (viewer.Find(_T("%1"))>=0)
549 if (viewer.Find(_T("\"%1\"")) >= 0)
550 viewer.Replace(_T("%1"), patchfile);
551 else
552 viewer.Replace(_T("%1"), _T("\"") + patchfile + _T("\""));
554 else
555 viewer += _T(" \"") + patchfile + _T("\"");
556 if (viewer.Find(_T("%title")) >= 0)
558 viewer.Replace(_T("%title"), title);
561 if(!LaunchApplication(viewer, IDS_ERR_DIFFVIEWSTART, !!bWait))
563 return FALSE;
565 return TRUE;
568 BOOL CAppUtils::StartTextViewer(CString file)
570 CString viewer;
571 CRegString txt = CRegString(_T(".txt\\"), _T(""), FALSE, HKEY_CLASSES_ROOT);
572 viewer = txt;
573 viewer = viewer + _T("\\Shell\\Open\\Command\\");
574 CRegString txtexe = CRegString(viewer, _T(""), FALSE, HKEY_CLASSES_ROOT);
575 viewer = txtexe;
577 DWORD len = ExpandEnvironmentStrings(viewer, NULL, 0);
578 std::unique_ptr<TCHAR[]> buf(new TCHAR[len + 1]);
579 ExpandEnvironmentStrings(viewer, buf.get(), len);
580 viewer = buf.get();
581 len = ExpandEnvironmentStrings(file, NULL, 0);
582 std::unique_ptr<TCHAR[]> buf2(new TCHAR[len + 1]);
583 ExpandEnvironmentStrings(file, buf2.get(), len);
584 file = buf2.get();
585 file = _T("\"")+file+_T("\"");
586 if (viewer.IsEmpty())
588 viewer = _T("RUNDLL32 Shell32,OpenAs_RunDLL");
590 if (viewer.Find(_T("\"%1\"")) >= 0)
592 viewer.Replace(_T("\"%1\""), file);
594 else if (viewer.Find(_T("%1")) >= 0)
596 viewer.Replace(_T("%1"), file);
598 else
600 viewer += _T(" ");
601 viewer += file;
604 if(!LaunchApplication(viewer, IDS_ERR_TEXTVIEWSTART, false))
606 return FALSE;
608 return TRUE;
611 BOOL CAppUtils::CheckForEmptyDiff(const CTGitPath& sDiffPath)
613 DWORD length = 0;
614 CAutoFile hFile = ::CreateFile(sDiffPath.GetWinPath(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
615 if (!hFile)
616 return TRUE;
617 length = ::GetFileSize(hFile, NULL);
618 if (length < 4)
619 return TRUE;
620 return FALSE;
624 void CAppUtils::CreateFontForLogs(CFont& fontToCreate)
626 LOGFONT logFont;
627 HDC hScreenDC = ::GetDC(NULL);
628 logFont.lfHeight = -MulDiv((DWORD)CRegDWORD(_T("Software\\TortoiseGit\\LogFontSize"), 8), GetDeviceCaps(hScreenDC, LOGPIXELSY), 72);
629 ::ReleaseDC(NULL, hScreenDC);
630 logFont.lfWidth = 0;
631 logFont.lfEscapement = 0;
632 logFont.lfOrientation = 0;
633 logFont.lfWeight = FW_NORMAL;
634 logFont.lfItalic = 0;
635 logFont.lfUnderline = 0;
636 logFont.lfStrikeOut = 0;
637 logFont.lfCharSet = DEFAULT_CHARSET;
638 logFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
639 logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
640 logFont.lfQuality = DRAFT_QUALITY;
641 logFont.lfPitchAndFamily = FF_DONTCARE | FIXED_PITCH;
642 _tcscpy_s(logFont.lfFaceName, 32, (LPCTSTR)(CString)CRegString(_T("Software\\TortoiseGit\\LogFontName"), _T("Courier New")));
643 VERIFY(fontToCreate.CreateFontIndirect(&logFont));
646 bool CAppUtils::LaunchPAgent(CString *keyfile,CString * pRemote)
648 CString key,remote;
649 CString cmd,out;
650 if( pRemote == NULL)
652 remote=_T("origin");
654 else
656 remote=*pRemote;
658 if(keyfile == NULL)
660 cmd.Format(_T("remote.%s.puttykeyfile"),remote);
661 key = g_Git.GetConfigValue(cmd);
663 else
664 key=*keyfile;
666 if(key.IsEmpty())
667 return false;
669 CString proc=CPathUtils::GetAppDirectory();
670 proc += _T("pageant.exe \"");
671 proc += key;
672 proc += _T("\"");
674 CString tempfile = GetTempFile();
675 ::DeleteFile(tempfile);
677 proc += _T(" -c \"");
678 proc += CPathUtils::GetAppDirectory();
679 proc += _T("tgittouch.exe\"");
680 proc += _T(" \"");
681 proc += tempfile;
682 proc += _T("\"");
684 CString appDir = CPathUtils::GetAppDirectory();
685 bool b = LaunchApplication(proc, IDS_ERR_PAGEANT, true, &appDir);
686 if(!b)
687 return b;
689 int i=0;
690 while(!::PathFileExists(tempfile))
692 Sleep(100);
693 ++i;
694 if(i>10*60*5)
695 break; //timeout 5 minutes
698 if( i== 10*60*5)
700 CMessageBox::Show(NULL, IDS_ERR_PAEGENTTIMEOUT, IDS_APPNAME, MB_OK | MB_ICONERROR);
702 ::DeleteFile(tempfile);
703 return true;
705 bool CAppUtils::LaunchAlternativeEditor(const CString& filename, bool uac)
707 CString editTool = CRegString(_T("Software\\TortoiseGit\\AlternativeEditor"));
708 if (editTool.IsEmpty() || (editTool.Left(1).Compare(_T("#"))==0)) {
709 editTool = CPathUtils::GetAppDirectory() + _T("notepad2.exe");
712 CString sCmd;
713 sCmd.Format(_T("\"%s\" \"%s\""), editTool, filename);
715 LaunchApplication(sCmd, NULL, false, NULL, uac);
716 return true;
718 bool CAppUtils::LaunchRemoteSetting()
720 CTGitPath path(g_Git.m_CurrentDir);
721 CSettings dlg(IDS_PROC_SETTINGS_TITLE, &path);
722 dlg.SetTreeViewMode(TRUE, TRUE, TRUE);
723 dlg.SetTreeWidth(220);
724 dlg.m_DefaultPage = _T("gitremote");
726 dlg.DoModal();
727 dlg.HandleRestart();
728 return true;
731 * Launch the external blame viewer
733 bool CAppUtils::LaunchTortoiseBlame(const CString& sBlameFile,CString Rev,const CString& sParams)
735 CString viewer = _T("\"") + CPathUtils::GetAppDirectory();
736 viewer += _T("TortoiseGitBlame.exe");
737 viewer += _T("\" \"") + sBlameFile + _T("\"");
738 //viewer += _T(" \"") + sLogFile + _T("\"");
739 //viewer += _T(" \"") + sOriginalFile + _T("\"");
740 if(!Rev.IsEmpty() && Rev != GIT_REV_ZERO)
741 viewer += CString(_T(" /rev:"))+Rev;
742 if (!g_sGroupingUUID.IsEmpty())
744 viewer += L" /groupuuid:\"";
745 viewer += g_sGroupingUUID;
746 viewer += L"\"";
748 viewer += _T(" ")+sParams;
750 return LaunchApplication(viewer, IDS_ERR_TGITBLAME, false);
753 bool CAppUtils::FormatTextInRichEditControl(CWnd * pWnd)
755 CString sText;
756 if (pWnd == NULL)
757 return false;
758 bool bStyled = false;
759 pWnd->GetWindowText(sText);
760 // the rich edit control doesn't count the CR char!
761 // to be exact: CRLF is treated as one char.
762 sText.Remove(_T('\r'));
764 // style each line separately
765 int offset = 0;
766 int nNewlinePos;
769 nNewlinePos = sText.Find('\n', offset);
770 CString sLine = nNewlinePos >= 0 ? sText.Mid(offset, nNewlinePos - offset) : sText.Mid(offset);
772 int start = 0;
773 int end = 0;
774 while (FindStyleChars(sLine, '*', start, end))
776 CHARRANGE range = {(LONG)start+offset, (LONG)end+offset};
777 pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);
778 SetCharFormat(pWnd, CFM_BOLD, CFE_BOLD);
779 bStyled = true;
780 start = end;
782 start = 0;
783 end = 0;
784 while (FindStyleChars(sLine, '^', start, end))
786 CHARRANGE range = {(LONG)start+offset, (LONG)end+offset};
787 pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);
788 SetCharFormat(pWnd, CFM_ITALIC, CFE_ITALIC);
789 bStyled = true;
790 start = end;
792 start = 0;
793 end = 0;
794 while (FindStyleChars(sLine, '_', start, end))
796 CHARRANGE range = {(LONG)start+offset, (LONG)end+offset};
797 pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);
798 SetCharFormat(pWnd, CFM_UNDERLINE, CFE_UNDERLINE);
799 bStyled = true;
800 start = end;
802 offset = nNewlinePos+1;
803 } while(nNewlinePos>=0);
804 return bStyled;
807 bool CAppUtils::FindStyleChars(const CString& sText, TCHAR stylechar, int& start, int& end)
809 int i=start;
810 int last = sText.GetLength() - 1;
811 bool bFoundMarker = false;
812 TCHAR c = i == 0 ? _T('\0') : sText[i - 1];
813 TCHAR nextChar = i >= last ? _T('\0') : sText[i + 1];
815 // find a starting marker
816 while (i < last)
818 TCHAR prevChar = c;
819 c = nextChar;
820 nextChar = sText[i + 1];
822 // IsCharAlphaNumeric can be somewhat expensive.
823 // Long lines of "*****" or "----" will be pre-empted efficiently
824 // by the (c != nextChar) condition.
826 if ((c == stylechar) && (c != nextChar))
828 if (IsCharAlphaNumeric(nextChar) && !IsCharAlphaNumeric(prevChar))
830 start = ++i;
831 bFoundMarker = true;
832 break;
835 ++i;
837 if (!bFoundMarker)
838 return false;
840 // find ending marker
841 // c == sText[i - 1]
843 bFoundMarker = false;
844 while (i <= last)
846 TCHAR prevChar = c;
847 c = sText[i];
848 if (c == stylechar)
850 if ((i == last) || (!IsCharAlphaNumeric(sText[i + 1]) && IsCharAlphaNumeric(prevChar)))
852 end = i;
853 ++i;
854 bFoundMarker = true;
855 break;
858 ++i;
860 return bFoundMarker;
863 bool CAppUtils::StartShowUnifiedDiff(HWND hWnd, const CTGitPath& url1, const git_revnum_t& rev1,
864 const CTGitPath& /*url2*/, const git_revnum_t& rev2,
865 //const GitRev& peg /* = GitRev */, const GitRev& headpeg /* = GitRev */,
866 bool /*bAlternateDiff*/ /* = false */, bool /*bIgnoreAncestry*/ /* = false */,
867 bool /* blame = false */,
868 bool bMerge,
869 bool bCombine)
871 int diffContext = 0;
872 if (GetMsysgitVersion() > 0x01080100)
873 diffContext = _ttoi(g_Git.GetConfigValue(_T("diff.context")));
874 CString tempfile=GetTempFile();
875 if (g_Git.GetUnifiedDiff(url1, rev1, rev2, tempfile, bMerge, bCombine, diffContext))
877 CMessageBox::Show(hWnd, g_Git.GetGitLastErr(_T("Could not get unified diff."), CGit::GIT_CMD_DIFF), _T("TortoiseGit"), MB_OK);
878 return false;
880 CAppUtils::StartUnifiedDiffViewer(tempfile, rev1 + _T(":") + rev2);
882 #if 0
883 CString sCmd;
884 sCmd.Format(_T("%s /command:showcompare /unified"),
885 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseGitProc.exe")));
886 sCmd += _T(" /url1:\"") + url1.GetGitPathString() + _T("\"");
887 if (rev1.IsValid())
888 sCmd += _T(" /revision1:") + rev1.ToString();
889 sCmd += _T(" /url2:\"") + url2.GetGitPathString() + _T("\"");
890 if (rev2.IsValid())
891 sCmd += _T(" /revision2:") + rev2.ToString();
892 if (peg.IsValid())
893 sCmd += _T(" /pegrevision:") + peg.ToString();
894 if (headpeg.IsValid())
895 sCmd += _T(" /headpegrevision:") + headpeg.ToString();
897 if (bAlternateDiff)
898 sCmd += _T(" /alternatediff");
900 if (bIgnoreAncestry)
901 sCmd += _T(" /ignoreancestry");
903 if (hWnd)
905 sCmd += _T(" /hwnd:");
906 TCHAR buf[30];
907 _stprintf_s(buf, 30, _T("%p"), (void*)hWnd);
908 sCmd += buf;
911 return CAppUtils::LaunchApplication(sCmd, NULL, false);
912 #endif
913 return TRUE;
916 bool CAppUtils::SetupDiffScripts(bool force, const CString& type)
918 CString scriptsdir = CPathUtils::GetAppParentDirectory();
919 scriptsdir += _T("Diff-Scripts");
920 CSimpleFileFind files(scriptsdir);
921 while (files.FindNextFileNoDirectories())
923 CString file = files.GetFilePath();
924 CString filename = files.GetFileName();
925 CString ext = file.Mid(file.ReverseFind('-') + 1);
926 ext = _T(".") + ext.Left(ext.ReverseFind('.'));
927 std::set<CString> extensions;
928 extensions.insert(ext);
929 CString kind;
930 if (file.Right(3).CompareNoCase(_T("vbs"))==0)
932 kind = _T(" //E:vbscript");
934 if (file.Right(2).CompareNoCase(_T("js"))==0)
936 kind = _T(" //E:javascript");
938 // open the file, read the first line and find possible extensions
939 // this script can handle
942 CStdioFile f(file, CFile::modeRead | CFile::shareDenyNone);
943 CString extline;
944 if (f.ReadString(extline))
946 if ((extline.GetLength() > 15 ) &&
947 ((extline.Left(15).Compare(_T("// extensions: ")) == 0) ||
948 (extline.Left(14).Compare(_T("' extensions: ")) == 0)))
950 if (extline[0] == '/')
951 extline = extline.Mid(15);
952 else
953 extline = extline.Mid(14);
954 CString sToken;
955 int curPos = 0;
956 sToken = extline.Tokenize(_T(";"), curPos);
957 while (!sToken.IsEmpty())
959 if (!sToken.IsEmpty())
961 if (sToken[0] != '.')
962 sToken = _T(".") + sToken;
963 extensions.insert(sToken);
965 sToken = extline.Tokenize(_T(";"), curPos);
969 f.Close();
971 catch (CFileException* e)
973 e->Delete();
976 for (std::set<CString>::const_iterator it = extensions.begin(); it != extensions.end(); ++it)
978 if (type.IsEmpty() || (type.Compare(_T("Diff")) == 0))
980 if (filename.Left(5).CompareNoCase(_T("diff-")) == 0)
982 CRegString diffreg = CRegString(_T("Software\\TortoiseGit\\DiffTools\\") + *it);
983 CString diffregstring = diffreg;
984 if (force || (diffregstring.IsEmpty()) || (diffregstring.Find(filename) >= 0))
985 diffreg = _T("wscript.exe \"") + file + _T("\" %base %mine") + kind;
988 if (type.IsEmpty() || (type.Compare(_T("Merge"))==0))
990 if (filename.Left(6).CompareNoCase(_T("merge-"))==0)
992 CRegString diffreg = CRegString(_T("Software\\TortoiseGit\\MergeTools\\") + *it);
993 CString diffregstring = diffreg;
994 if (force || (diffregstring.IsEmpty()) || (diffregstring.Find(filename) >= 0))
995 diffreg = _T("wscript.exe \"") + file + _T("\" %merged %theirs %mine %base") + kind;
1001 return true;
1004 bool CAppUtils::Export(CString *BashHash, const CTGitPath *orgPath)
1006 // ask from where the export has to be done
1007 CExportDlg dlg;
1008 if(BashHash)
1009 dlg.m_Revision=*BashHash;
1010 if (orgPath)
1012 if (PathIsRelative(orgPath->GetWinPath()))
1013 dlg.m_orgPath = g_Git.CombinePath(orgPath);
1014 else
1015 dlg.m_orgPath = *orgPath;
1018 if (dlg.DoModal() == IDOK)
1020 CString cmd;
1021 cmd.Format(_T("git.exe archive --output=\"%s\" --format=zip --verbose %s --"),
1022 dlg.m_strFile, g_Git.FixBranchName(dlg.m_VersionName));
1024 CProgressDlg pro;
1025 pro.m_GitCmd=cmd;
1026 CGit git;
1027 if (!dlg.m_bWholeProject && !dlg.m_orgPath.IsEmpty() && PathIsDirectory(dlg.m_orgPath.GetWinPathString()))
1029 git.m_CurrentDir = dlg.m_orgPath.GetWinPathString();
1030 pro.m_Git = &git;
1032 return (pro.DoModal() == IDOK);
1034 return false;
1037 bool CAppUtils::CreateBranchTag(bool IsTag,CString *CommitHash, bool switch_new_brach)
1039 CCreateBranchTagDlg dlg;
1040 dlg.m_bIsTag=IsTag;
1041 dlg.m_bSwitch=switch_new_brach;
1043 if(CommitHash)
1044 dlg.m_initialRefName = *CommitHash;
1046 if(dlg.DoModal()==IDOK)
1048 CString cmd;
1049 CString force;
1050 CString track;
1051 if(dlg.m_bTrack == TRUE)
1052 track=_T(" --track ");
1053 else if(dlg.m_bTrack == FALSE)
1054 track=_T(" --no-track");
1056 if(dlg.m_bForce)
1057 force=_T(" -f ");
1059 if(IsTag)
1061 CString sign;
1062 if(dlg.m_bSign)
1063 sign=_T("-s");
1065 cmd.Format(_T("git.exe tag %s %s %s %s"),
1066 force,
1067 sign,
1068 dlg.m_BranchTagName,
1069 g_Git.FixBranchName(dlg.m_VersionName)
1072 CString tempfile=::GetTempFile();
1073 if(!dlg.m_Message.Trim().IsEmpty())
1075 CAppUtils::SaveCommitUnicodeFile(tempfile,dlg.m_Message);
1076 cmd += _T(" -F ")+tempfile;
1079 else
1081 cmd.Format(_T("git.exe branch %s %s %s %s"),
1082 track,
1083 force,
1084 dlg.m_BranchTagName,
1085 g_Git.FixBranchName(dlg.m_VersionName)
1088 CString out;
1089 if(g_Git.Run(cmd,&out,CP_UTF8))
1091 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
1092 return FALSE;
1094 if( !IsTag && dlg.m_bSwitch )
1096 // it is a new branch and the user has requested to switch to it
1097 PerformSwitch(dlg.m_BranchTagName);
1100 return TRUE;
1102 return FALSE;
1105 bool CAppUtils::Switch(CString initialRefName)
1107 CGitSwitchDlg dlg;
1108 if(!initialRefName.IsEmpty())
1109 dlg.m_initialRefName = initialRefName;
1111 if (dlg.DoModal() == IDOK)
1113 CString branch;
1114 if (dlg.m_bBranch)
1115 branch = dlg.m_NewBranch;
1117 // if refs/heads/ is not stripped, checkout will detach HEAD
1118 // checkout prefers branches on name clashes (with tags)
1119 if (dlg.m_VersionName.Left(11) ==_T("refs/heads/") && dlg.m_bBranchOverride != TRUE)
1120 dlg.m_VersionName = dlg.m_VersionName.Mid(11);
1122 return PerformSwitch(dlg.m_VersionName, dlg.m_bForce == TRUE , branch, dlg.m_bBranchOverride == TRUE, dlg.m_bTrack, dlg.m_bMerge == TRUE);
1124 return FALSE;
1127 bool CAppUtils::PerformSwitch(CString ref, bool bForce /* false */, CString sNewBranch /* CString() */, bool bBranchOverride /* false */, BOOL bTrack /* 2 */, bool bMerge /* false */)
1129 CString cmd;
1130 CString track;
1131 CString force;
1132 CString branch;
1133 CString merge;
1135 if(!sNewBranch.IsEmpty()){
1136 if (bBranchOverride)
1138 branch.Format(_T("-B %s"), sNewBranch);
1140 else
1142 branch.Format(_T("-b %s"), sNewBranch);
1144 if (bTrack == TRUE)
1145 track = _T("--track");
1146 else if (bTrack == FALSE)
1147 track = _T("--no-track");
1149 if (bForce)
1150 force = _T("-f");
1151 if (bMerge)
1152 merge = _T("--merge");
1154 cmd.Format(_T("git.exe checkout %s %s %s %s %s --"),
1155 force,
1156 track,
1157 merge,
1158 branch,
1159 g_Git.FixBranchName(ref));
1161 while (true)
1163 CProgressDlg progress;
1164 progress.m_GitCmd = cmd;
1166 INT_PTR idPull = -1;
1167 INT_PTR idSubmoduleUpdate = -1;
1168 INT_PTR idMerge = -1;
1170 CTGitPath gitPath = g_Git.m_CurrentDir;
1171 if (gitPath.HasSubmodules())
1172 idSubmoduleUpdate = progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_PROC_SUBMODULESUPDATE)));
1173 CString currentBranch;
1174 bool hasBranch = CGit::GetCurrentBranchFromFile(g_Git.m_CurrentDir, currentBranch) == 0;
1175 if (hasBranch)
1176 idMerge = progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_MENUMERGE)));
1178 progress.m_PostCmdCallback = [&] ()
1180 if (!progress.m_GitStatus)
1182 CString newBranch;
1183 if (!CGit::GetCurrentBranchFromFile(g_Git.m_CurrentDir, newBranch))
1184 idPull = progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_MENUPULL)));
1186 else
1188 progress.m_PostCmdList.RemoveAll();
1189 progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_MSGBOX_RETRY)));
1190 progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_SWITCH_WITH_MERGE)));
1194 INT_PTR ret = progress.DoModal();
1195 if (progress.m_GitStatus == 0)
1197 if (idSubmoduleUpdate >= 0 && ret == IDC_PROGRESS_BUTTON1 + idSubmoduleUpdate)
1199 CString sCmd;
1200 sCmd.Format(_T("/command:subupdate /bkpath:\"%s\""), g_Git.m_CurrentDir);
1202 RunTortoiseGitProc(sCmd);
1203 return TRUE;
1205 else if (ret == IDC_PROGRESS_BUTTON1 + idPull)
1207 Pull();
1208 return TRUE;
1210 else if (ret == IDC_PROGRESS_BUTTON1 + idMerge)
1212 Merge(&currentBranch);
1213 return TRUE;
1215 else if (ret == IDOK)
1216 return TRUE;
1218 else if (ret == IDC_PROGRESS_BUTTON1)
1219 continue; // retry
1220 else if (ret == IDC_PROGRESS_BUTTON1 + 1)
1222 merge = _T("--merge");
1223 cmd.Format(_T("git.exe checkout %s %s %s %s %s --"),
1224 force, track, merge, branch, g_Git.FixBranchName(ref));
1225 continue; // retry
1228 return FALSE;
1232 class CIgnoreFile : public CStdioFile
1234 public:
1235 STRING_VECTOR m_Items;
1236 CString m_eol;
1238 virtual BOOL ReadString(CString& rString)
1240 if (GetPosition() == 0)
1242 unsigned char utf8bom[] = { 0xEF, 0xBB, 0xBF };
1243 char buf[3] = { 0, 0, 0 };
1244 Read(buf, 3);
1245 if (memcpy(buf, utf8bom, sizeof(utf8bom)))
1247 SeekToBegin();
1251 CStringA strA;
1252 char lastChar = '\0';
1253 for (char c = '\0'; Read(&c, 1) == 1; lastChar = c)
1255 if (c == '\r')
1256 continue;
1257 if (c == '\n')
1259 m_eol = lastChar == '\r' ? _T("\r\n") : _T("\n");
1260 break;
1262 strA.AppendChar(c);
1264 if (strA.IsEmpty())
1265 return FALSE;
1267 rString = CUnicodeUtils::GetUnicode(strA);
1268 return TRUE;
1271 void ResetState()
1273 m_Items.clear();
1274 m_eol = _T("");
1278 bool CAppUtils::OpenIgnoreFile(CIgnoreFile &file, const CString& filename)
1280 file.ResetState();
1281 if (!file.Open(filename, CFile::modeCreate | CFile::modeReadWrite | CFile::modeNoTruncate | CFile::typeBinary))
1283 CMessageBox::Show(NULL, filename + _T(" Open Failure"), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1284 return false;
1287 if (file.GetLength() > 0)
1289 CString fileText;
1290 while (file.ReadString(fileText))
1291 file.m_Items.push_back(fileText);
1292 file.Seek(file.GetLength() - 1, 0);
1293 char lastchar[1] = { 0 };
1294 file.Read(lastchar, 1);
1295 file.SeekToEnd();
1296 if (lastchar[0] != '\n')
1298 CStringA eol = CStringA(file.m_eol.IsEmpty() ? _T("\n") : file.m_eol);
1299 file.Write(eol, eol.GetLength());
1302 else
1303 file.SeekToEnd();
1305 return true;
1308 bool CAppUtils::IgnoreFile(CTGitPathList &path,bool IsMask)
1310 CIgnoreDlg ignoreDlg;
1311 if (ignoreDlg.DoModal() == IDOK)
1313 CString ignorefile;
1314 ignorefile = g_Git.m_CurrentDir + _T("\\");
1316 switch (ignoreDlg.m_IgnoreFile)
1318 case 0:
1319 ignorefile += _T(".gitignore");
1320 break;
1321 case 2:
1322 g_GitAdminDir.GetAdminDirPath(g_Git.m_CurrentDir, ignorefile);
1323 ignorefile += _T("info/exclude");
1324 break;
1327 CIgnoreFile file;
1330 if (ignoreDlg.m_IgnoreFile != 1 && !OpenIgnoreFile(file, ignorefile))
1331 return false;
1333 for (int i = 0; i < path.GetCount(); ++i)
1335 if (ignoreDlg.m_IgnoreFile == 1)
1337 ignorefile = g_Git.CombinePath(path[i].GetContainingDirectory()) + _T("\\.gitignore");
1338 if (!OpenIgnoreFile(file, ignorefile))
1339 return false;
1342 CString ignorePattern;
1343 if (ignoreDlg.m_IgnoreType == 0)
1345 if (ignoreDlg.m_IgnoreFile != 1 && !path[i].GetContainingDirectory().GetGitPathString().IsEmpty())
1346 ignorePattern += _T("/") + path[i].GetContainingDirectory().GetGitPathString();
1348 ignorePattern += _T("/");
1350 if (IsMask)
1352 ignorePattern += _T("*") + path[i].GetFileExtension();
1354 else
1356 ignorePattern += path[i].GetFileOrDirectoryName();
1359 // escape [ and ] so that files get ignored correctly
1360 ignorePattern.Replace(_T("["), _T("\\["));
1361 ignorePattern.Replace(_T("]"), _T("\\]"));
1363 bool found = false;
1364 for (size_t j = 0; j < file.m_Items.size(); ++j)
1366 if (file.m_Items[j] == ignorePattern)
1368 found = true;
1369 break;
1372 if (!found)
1374 file.m_Items.push_back(ignorePattern);
1375 ignorePattern += file.m_eol.IsEmpty() ? _T("\n") : file.m_eol;
1376 CStringA ignorePatternA = CUnicodeUtils::GetUTF8(ignorePattern);
1377 file.Write(ignorePatternA, ignorePatternA.GetLength());
1380 if (ignoreDlg.m_IgnoreFile == 1)
1381 file.Close();
1384 if (ignoreDlg.m_IgnoreFile != 1)
1385 file.Close();
1387 catch(...)
1389 file.Abort();
1390 return false;
1393 return true;
1395 return false;
1399 bool CAppUtils::GitReset(CString *CommitHash,int type)
1401 CResetDlg dlg;
1402 dlg.m_ResetType=type;
1403 dlg.m_ResetToVersion=*CommitHash;
1404 dlg.m_initialRefName = *CommitHash;
1405 if (dlg.DoModal() == IDOK)
1407 CString cmd;
1408 CString type;
1409 switch(dlg.m_ResetType)
1411 case 0:
1412 type=_T("--soft");
1413 break;
1414 case 1:
1415 type=_T("--mixed");
1416 break;
1417 case 2:
1418 type=_T("--hard");
1419 break;
1420 default:
1421 dlg.m_ResetType = 1;
1422 type=_T("--mixed");
1423 break;
1425 cmd.Format(_T("git.exe reset %s %s --"),type, dlg.m_ResetToVersion);
1427 while (true)
1429 CProgressDlg progress;
1430 progress.m_GitCmd=cmd;
1432 CTGitPath gitPath = g_Git.m_CurrentDir;
1433 if (gitPath.HasSubmodules() && dlg.m_ResetType == 2)
1434 progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_PROC_SUBMODULESUPDATE)));
1436 progress.m_PostFailCmdList.Add(CString(MAKEINTRESOURCE(IDS_MSGBOX_RETRY)));
1438 INT_PTR ret;
1439 if (g_Git.UsingLibGit2(CGit::GIT_CMD_RESET))
1441 CGitProgressDlg gitdlg;
1442 gitdlg.SetCommand(CGitProgressList::GitProgress_Reset);
1443 gitdlg.SetRevision(dlg.m_ResetToVersion);
1444 gitdlg.SetResetType(dlg.m_ResetType);
1445 ret = gitdlg.DoModal();
1447 else
1448 ret = progress.DoModal();
1450 if (progress.m_GitStatus == 0 && gitPath.HasSubmodules() && dlg.m_ResetType == 2 && ret == IDC_PROGRESS_BUTTON1)
1452 CString sCmd;
1453 sCmd.Format(_T("/command:subupdate /bkpath:\"%s\""), g_Git.m_CurrentDir);
1455 RunTortoiseGitProc(sCmd);
1456 return TRUE;
1458 else if (progress.m_GitStatus != 0 && ret == IDC_PROGRESS_BUTTON1)
1459 continue; // retry
1460 else if (ret == IDOK)
1461 return TRUE;
1462 else
1463 break;
1466 return FALSE;
1469 void CAppUtils::DescribeConflictFile(bool mode, bool base,CString &descript)
1471 if(mode == FALSE)
1473 descript = CString(MAKEINTRESOURCE(IDS_SVNACTION_DELETE));
1474 return;
1476 if(base)
1478 descript = CString(MAKEINTRESOURCE(IDS_SVNACTION_MODIFIED));
1479 return;
1481 descript = CString(MAKEINTRESOURCE(IDS_PROC_CREATED));
1482 return;
1485 void CAppUtils::RemoveTempMergeFile(CTGitPath &path)
1487 ::DeleteFile(CAppUtils::GetMergeTempFile(_T("LOCAL"), path));
1488 ::DeleteFile(CAppUtils::GetMergeTempFile(_T("REMOTE"), path));
1489 ::DeleteFile(CAppUtils::GetMergeTempFile(_T("BASE"), path));
1491 CString CAppUtils::GetMergeTempFile(CString type,CTGitPath &merge)
1493 CString file;
1494 file = g_Git.CombinePath(merge.GetWinPathString() + _T(".") + type + merge.GetFileExtension());
1496 return file;
1499 bool ParseHashesFromLsFile(BYTE_VECTOR &out, CString &hash1, CString &hash2, CString &hash3)
1501 unsigned int pos = 0;
1502 CString one;
1503 CString part;
1505 while (pos >= 0 && pos < out.size())
1507 one.Empty();
1509 g_Git.StringAppend(&one, &out[pos], CP_UTF8);
1510 int tabstart = 0;
1511 one.Tokenize(_T("\t"), tabstart);
1513 tabstart = 0;
1514 part = one.Tokenize(_T(" "), tabstart); //Tag
1515 part = one.Tokenize(_T(" "), tabstart); //Mode
1516 part = one.Tokenize(_T(" "), tabstart); //Hash
1517 CString hash = part;
1518 part = one.Tokenize(_T("\t"), tabstart); //Stage
1519 int stage = _ttol(part);
1520 if (stage == 1)
1521 hash1 = hash;
1522 else if (stage == 2)
1523 hash2 = hash;
1524 else if (stage == 3)
1526 hash3 = hash;
1527 return true;
1530 pos = out.findNextString(pos);
1533 return false;
1536 bool CAppUtils::ConflictEdit(CTGitPath& path, bool /*bAlternativeTool = false*/, bool revertTheirMy /*= false*/, HWND resolveMsgHwnd /*= nullptr*/)
1538 bool bRet = false;
1540 CTGitPath merge=path;
1541 CTGitPath directory = merge.GetDirectory();
1543 // we have the conflicted file (%merged)
1544 // now look for the other required files
1545 //GitStatus stat;
1546 //stat.GetStatus(merge);
1547 //if (stat.status == NULL)
1548 // return false;
1550 BYTE_VECTOR vector;
1552 CString cmd;
1553 cmd.Format(_T("git.exe ls-files -u -t -z -- \"%s\""),merge.GetGitPathString());
1555 if (g_Git.Run(cmd, &vector))
1557 return FALSE;
1560 if (merge.IsDirectory())
1562 CString baseHash, localHash, remoteHash;
1563 if (!ParseHashesFromLsFile(vector, baseHash, localHash, remoteHash))
1564 return FALSE;
1566 CString msg;
1567 msg.Format(_T("BASE: %s\nLOCAL: %s\nREMOTE: %s"), baseHash, localHash, remoteHash);
1568 CMessageBox::Show(NULL, msg, _T("TortoiseGit"), MB_OK);
1569 return TRUE;
1572 CTGitPathList list;
1573 if (list.ParserFromLsFile(vector))
1575 CMessageBox::Show(NULL, _T("Parse ls-files failed!"), _T("TortoiseGit"), MB_OK);
1576 return FALSE;
1579 if (list.IsEmpty())
1580 return FALSE;
1582 CTGitPath theirs;
1583 CTGitPath mine;
1584 CTGitPath base;
1586 if (revertTheirMy)
1588 mine.SetFromGit(GetMergeTempFile(_T("REMOTE"), merge));
1589 theirs.SetFromGit(GetMergeTempFile(_T("LOCAL"), merge));
1591 else
1593 mine.SetFromGit(GetMergeTempFile(_T("LOCAL"), merge));
1594 theirs.SetFromGit(GetMergeTempFile(_T("REMOTE"), merge));
1596 base.SetFromGit(GetMergeTempFile(_T("BASE"),merge));
1598 CString format;
1600 //format=_T("git.exe cat-file blob \":%d:%s\"");
1601 format = _T("git.exe checkout-index --temp --stage=%d -- \"%s\"");
1602 CFile tempfile;
1603 //create a empty file, incase stage is not three
1604 tempfile.Open(mine.GetWinPathString(),CFile::modeCreate|CFile::modeReadWrite);
1605 tempfile.Close();
1606 tempfile.Open(theirs.GetWinPathString(),CFile::modeCreate|CFile::modeReadWrite);
1607 tempfile.Close();
1608 tempfile.Open(base.GetWinPathString(),CFile::modeCreate|CFile::modeReadWrite);
1609 tempfile.Close();
1611 bool b_base=false, b_local=false, b_remote=false;
1613 for (int i = 0; i< list.GetCount(); ++i)
1615 CString cmd;
1616 CString outfile;
1617 cmd.Empty();
1618 outfile.Empty();
1620 if( list[i].m_Stage == 1)
1622 cmd.Format(format, list[i].m_Stage, list[i].GetGitPathString());
1623 b_base = true;
1624 outfile = base.GetWinPathString();
1627 if( list[i].m_Stage == 2 )
1629 cmd.Format(format, list[i].m_Stage, list[i].GetGitPathString());
1630 b_local = true;
1631 outfile = mine.GetWinPathString();
1634 if( list[i].m_Stage == 3 )
1636 cmd.Format(format, list[i].m_Stage, list[i].GetGitPathString());
1637 b_remote = true;
1638 outfile = theirs.GetWinPathString();
1640 CString output, err;
1641 if(!outfile.IsEmpty())
1642 if (!g_Git.Run(cmd, &output, &err, CP_UTF8))
1644 CString file;
1645 int start =0 ;
1646 file = output.Tokenize(_T("\t"), start);
1647 ::MoveFileEx(file,outfile,MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED);
1649 else
1651 CMessageBox::Show(NULL, output + L"\n" + err, _T("TortoiseGit"), MB_OK|MB_ICONERROR);
1655 if(b_local && b_remote )
1657 merge.SetFromWin(g_Git.CombinePath(merge));
1658 if( revertTheirMy )
1659 bRet = !!CAppUtils::StartExtMerge(base, mine, theirs, merge, _T("BASE"), _T("REMOTE"), _T("LOCAL"), CString(), false, resolveMsgHwnd);
1660 else
1661 bRet = !!CAppUtils::StartExtMerge(base, theirs, mine, merge, _T("BASE"), _T("REMOTE"), _T("LOCAL"), CString(), false, resolveMsgHwnd);
1664 else
1666 ::DeleteFile(mine.GetWinPathString());
1667 ::DeleteFile(theirs.GetWinPathString());
1668 ::DeleteFile(base.GetWinPathString());
1670 CDeleteConflictDlg dlg;
1671 DescribeConflictFile(b_local, b_base,dlg.m_LocalStatus);
1672 DescribeConflictFile(b_remote,b_base,dlg.m_RemoteStatus);
1673 CGitHash localHash, remoteHash;
1674 if (!g_Git.GetHash(localHash, _T("HEAD")))
1675 dlg.m_LocalHash = localHash.ToString();
1676 if (!g_Git.GetHash(remoteHash, _T("MERGE_HEAD")))
1677 dlg.m_RemoteHash = remoteHash.ToString();
1678 else if (!g_Git.GetHash(remoteHash, _T("rebase-apply/original-commit")))
1679 dlg.m_RemoteHash = remoteHash.ToString();
1680 else if (!g_Git.GetHash(remoteHash, _T("CHERRY_PICK_HEAD")))
1681 dlg.m_RemoteHash = remoteHash.ToString();
1682 else if (!g_Git.GetHash(remoteHash, _T("REVERT_HEAD")))
1683 dlg.m_RemoteHash = remoteHash.ToString();
1684 dlg.m_bShowModifiedButton=b_base;
1685 dlg.m_File=merge.GetGitPathString();
1686 if(dlg.DoModal() == IDOK)
1688 CString cmd,out;
1689 if(dlg.m_bIsDelete)
1691 cmd.Format(_T("git.exe rm -- \"%s\""),merge.GetGitPathString());
1693 else
1694 cmd.Format(_T("git.exe add -- \"%s\""),merge.GetGitPathString());
1696 if (g_Git.Run(cmd, &out, CP_UTF8))
1698 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
1699 return FALSE;
1701 return TRUE;
1703 else
1704 return FALSE;
1707 #if 0
1708 CAppUtils::StartExtMerge(CAppUtils::MergeFlags().AlternativeTool(bAlternativeTool),
1709 base, theirs, mine, merge);
1710 #endif
1711 #if 0
1712 if (stat.status->text_status == svn_wc_status_conflicted)
1714 // we have a text conflict, use our merge tool to resolve the conflict
1716 CTSVNPath theirs(directory);
1717 CTSVNPath mine(directory);
1718 CTSVNPath base(directory);
1719 bool bConflictData = false;
1721 if ((stat.status->entry)&&(stat.status->entry->conflict_new))
1723 theirs.AppendPathString(CUnicodeUtils::GetUnicode(stat.status->entry->conflict_new));
1724 bConflictData = true;
1726 if ((stat.status->entry)&&(stat.status->entry->conflict_old))
1728 base.AppendPathString(CUnicodeUtils::GetUnicode(stat.status->entry->conflict_old));
1729 bConflictData = true;
1731 if ((stat.status->entry)&&(stat.status->entry->conflict_wrk))
1733 mine.AppendPathString(CUnicodeUtils::GetUnicode(stat.status->entry->conflict_wrk));
1734 bConflictData = true;
1736 else
1738 mine = merge;
1740 if (bConflictData)
1741 bRet = !!CAppUtils::StartExtMerge(CAppUtils::MergeFlags().AlternativeTool(bAlternativeTool),
1742 base, theirs, mine, merge);
1745 if (stat.status->prop_status == svn_wc_status_conflicted)
1747 // we have a property conflict
1748 CTSVNPath prej(directory);
1749 if ((stat.status->entry)&&(stat.status->entry->prejfile))
1751 prej.AppendPathString(CUnicodeUtils::GetUnicode(stat.status->entry->prejfile));
1752 // there's a problem: the prej file contains a _description_ of the conflict, and
1753 // that description string might be translated. That means we have no way of parsing
1754 // the file to find out the conflicting values.
1755 // The only thing we can do: show a dialog with the conflict description, then
1756 // let the user either accept the existing property or open the property edit dialog
1757 // to manually change the properties and values. And a button to mark the conflict as
1758 // resolved.
1759 CEditPropConflictDlg dlg;
1760 dlg.SetPrejFile(prej);
1761 dlg.SetConflictedItem(merge);
1762 bRet = (dlg.DoModal() != IDCANCEL);
1766 if (stat.status->tree_conflict)
1768 // we have a tree conflict
1769 SVNInfo info;
1770 const SVNInfoData * pInfoData = info.GetFirstFileInfo(merge, SVNRev(), SVNRev());
1771 if (pInfoData)
1773 if (pInfoData->treeconflict_kind == svn_wc_conflict_kind_text)
1775 CTSVNPath theirs(directory);
1776 CTSVNPath mine(directory);
1777 CTSVNPath base(directory);
1778 bool bConflictData = false;
1780 if (pInfoData->treeconflict_theirfile)
1782 theirs.AppendPathString(pInfoData->treeconflict_theirfile);
1783 bConflictData = true;
1785 if (pInfoData->treeconflict_basefile)
1787 base.AppendPathString(pInfoData->treeconflict_basefile);
1788 bConflictData = true;
1790 if (pInfoData->treeconflict_myfile)
1792 mine.AppendPathString(pInfoData->treeconflict_myfile);
1793 bConflictData = true;
1795 else
1797 mine = merge;
1799 if (bConflictData)
1800 bRet = !!CAppUtils::StartExtMerge(CAppUtils::MergeFlags().AlternativeTool(bAlternativeTool),
1801 base, theirs, mine, merge);
1803 else if (pInfoData->treeconflict_kind == svn_wc_conflict_kind_tree)
1805 CString sConflictAction;
1806 CString sConflictReason;
1807 CString sResolveTheirs;
1808 CString sResolveMine;
1809 CTSVNPath treeConflictPath = CTSVNPath(pInfoData->treeconflict_path);
1810 CString sItemName = treeConflictPath.GetUIFileOrDirectoryName();
1812 if (pInfoData->treeconflict_nodekind == svn_node_file)
1814 switch (pInfoData->treeconflict_operation)
1816 case svn_wc_operation_update:
1817 switch (pInfoData->treeconflict_action)
1819 case svn_wc_conflict_action_edit:
1820 sConflictAction.Format(IDS_TREECONFLICT_FILEUPDATEEDIT, (LPCTSTR)sItemName);
1821 sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYFILE);
1822 break;
1823 case svn_wc_conflict_action_add:
1824 sConflictAction.Format(IDS_TREECONFLICT_FILEUPDATEADD, (LPCTSTR)sItemName);
1825 sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYFILE);
1826 break;
1827 case svn_wc_conflict_action_delete:
1828 sConflictAction.Format(IDS_TREECONFLICT_FILEUPDATEDELETE, (LPCTSTR)sItemName);
1829 sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_REMOVEFILE);
1830 break;
1832 break;
1833 case svn_wc_operation_switch:
1834 switch (pInfoData->treeconflict_action)
1836 case svn_wc_conflict_action_edit:
1837 sConflictAction.Format(IDS_TREECONFLICT_FILESWITCHEDIT, (LPCTSTR)sItemName);
1838 sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYFILE);
1839 break;
1840 case svn_wc_conflict_action_add:
1841 sConflictAction.Format(IDS_TREECONFLICT_FILESWITCHADD, (LPCTSTR)sItemName);
1842 sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYFILE);
1843 break;
1844 case svn_wc_conflict_action_delete:
1845 sConflictAction.Format(IDS_TREECONFLICT_FILESWITCHDELETE, (LPCTSTR)sItemName);
1846 sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_REMOVEFILE);
1847 break;
1849 break;
1850 case svn_wc_operation_merge:
1851 switch (pInfoData->treeconflict_action)
1853 case svn_wc_conflict_action_edit:
1854 sConflictAction.Format(IDS_TREECONFLICT_FILEMERGEEDIT, (LPCTSTR)sItemName);
1855 sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYFILE);
1856 break;
1857 case svn_wc_conflict_action_add:
1858 sResolveTheirs.Format(IDS_TREECONFLICT_FILEMERGEADD, (LPCTSTR)sItemName);
1859 sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYFILE);
1860 break;
1861 case svn_wc_conflict_action_delete:
1862 sConflictAction.Format(IDS_TREECONFLICT_FILEMERGEDELETE, (LPCTSTR)sItemName);
1863 sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_REMOVEFILE);
1864 break;
1866 break;
1869 else if (pInfoData->treeconflict_nodekind == svn_node_dir)
1871 switch (pInfoData->treeconflict_operation)
1873 case svn_wc_operation_update:
1874 switch (pInfoData->treeconflict_action)
1876 case svn_wc_conflict_action_edit:
1877 sConflictAction.Format(IDS_TREECONFLICT_DIRUPDATEEDIT, (LPCTSTR)sItemName);
1878 sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYDIR);
1879 break;
1880 case svn_wc_conflict_action_add:
1881 sConflictAction.Format(IDS_TREECONFLICT_DIRUPDATEADD, (LPCTSTR)sItemName);
1882 sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYDIR);
1883 break;
1884 case svn_wc_conflict_action_delete:
1885 sConflictAction.Format(IDS_TREECONFLICT_DIRUPDATEDELETE, (LPCTSTR)sItemName);
1886 sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_REMOVEDIR);
1887 break;
1889 break;
1890 case svn_wc_operation_switch:
1891 switch (pInfoData->treeconflict_action)
1893 case svn_wc_conflict_action_edit:
1894 sConflictAction.Format(IDS_TREECONFLICT_DIRSWITCHEDIT, (LPCTSTR)sItemName);
1895 sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYDIR);
1896 break;
1897 case svn_wc_conflict_action_add:
1898 sConflictAction.Format(IDS_TREECONFLICT_DIRSWITCHADD, (LPCTSTR)sItemName);
1899 sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYDIR);
1900 break;
1901 case svn_wc_conflict_action_delete:
1902 sConflictAction.Format(IDS_TREECONFLICT_DIRSWITCHDELETE, (LPCTSTR)sItemName);
1903 sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_REMOVEDIR);
1904 break;
1906 break;
1907 case svn_wc_operation_merge:
1908 switch (pInfoData->treeconflict_action)
1910 case svn_wc_conflict_action_edit:
1911 sConflictAction.Format(IDS_TREECONFLICT_DIRMERGEEDIT, (LPCTSTR)sItemName);
1912 sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYDIR);
1913 break;
1914 case svn_wc_conflict_action_add:
1915 sConflictAction.Format(IDS_TREECONFLICT_DIRMERGEADD, (LPCTSTR)sItemName);
1916 sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYDIR);
1917 break;
1918 case svn_wc_conflict_action_delete:
1919 sConflictAction.Format(IDS_TREECONFLICT_DIRMERGEDELETE, (LPCTSTR)sItemName);
1920 sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_REMOVEDIR);
1921 break;
1923 break;
1927 UINT uReasonID = 0;
1928 switch (pInfoData->treeconflict_reason)
1930 case svn_wc_conflict_reason_edited:
1931 uReasonID = IDS_TREECONFLICT_REASON_EDITED;
1932 sResolveMine.LoadString(pInfoData->treeconflict_nodekind == svn_node_dir ? IDS_TREECONFLICT_RESOLVE_KEEPLOCALDIR : IDS_TREECONFLICT_RESOLVE_KEEPLOCALFILE);
1933 break;
1934 case svn_wc_conflict_reason_obstructed:
1935 uReasonID = IDS_TREECONFLICT_REASON_OBSTRUCTED;
1936 sResolveMine.LoadString(pInfoData->treeconflict_nodekind == svn_node_dir ? IDS_TREECONFLICT_RESOLVE_KEEPLOCALDIR : IDS_TREECONFLICT_RESOLVE_KEEPLOCALFILE);
1937 break;
1938 case svn_wc_conflict_reason_deleted:
1939 uReasonID = IDS_TREECONFLICT_REASON_DELETED;
1940 sResolveMine.LoadString(pInfoData->treeconflict_nodekind == svn_node_dir ? IDS_TREECONFLICT_RESOLVE_REMOVEDIR : IDS_TREECONFLICT_RESOLVE_REMOVEFILE);
1941 break;
1942 case svn_wc_conflict_reason_added:
1943 uReasonID = IDS_TREECONFLICT_REASON_ADDED;
1944 sResolveMine.LoadString(pInfoData->treeconflict_nodekind == svn_node_dir ? IDS_TREECONFLICT_RESOLVE_KEEPLOCALDIR : IDS_TREECONFLICT_RESOLVE_KEEPLOCALFILE);
1945 break;
1946 case svn_wc_conflict_reason_missing:
1947 uReasonID = IDS_TREECONFLICT_REASON_MISSING;
1948 sResolveMine.LoadString(pInfoData->treeconflict_nodekind == svn_node_dir ? IDS_TREECONFLICT_RESOLVE_REMOVEDIR : IDS_TREECONFLICT_RESOLVE_REMOVEFILE);
1949 break;
1950 case svn_wc_conflict_reason_unversioned:
1951 uReasonID = IDS_TREECONFLICT_REASON_UNVERSIONED;
1952 sResolveMine.LoadString(pInfoData->treeconflict_nodekind == svn_node_dir ? IDS_TREECONFLICT_RESOLVE_KEEPLOCALDIR : IDS_TREECONFLICT_RESOLVE_KEEPLOCALFILE);
1953 break;
1955 sConflictReason.Format(uReasonID, (LPCTSTR)sConflictAction);
1957 CTreeConflictEditorDlg dlg;
1958 dlg.SetConflictInfoText(sConflictReason);
1959 dlg.SetResolveTexts(sResolveTheirs, sResolveMine);
1960 dlg.SetPath(treeConflictPath);
1961 INT_PTR dlgRet = dlg.DoModal();
1962 bRet = (dlgRet != IDCANCEL);
1966 #endif
1967 return bRet;
1970 bool CAppUtils::IsSSHPutty()
1972 CString sshclient=g_Git.m_Environment.GetEnv(_T("GIT_SSH"));
1973 sshclient=sshclient.MakeLower();
1974 if(sshclient.Find(_T("plink.exe"),0)>=0)
1976 return true;
1978 return false;
1981 CString CAppUtils::GetClipboardLink(const CString &skipGitPrefix, int paramsCount)
1983 if (!OpenClipboard(NULL))
1984 return CString();
1986 CString sClipboardText;
1987 HGLOBAL hglb = GetClipboardData(CF_TEXT);
1988 if (hglb)
1990 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
1991 sClipboardText = CString(lpstr);
1992 GlobalUnlock(hglb);
1994 hglb = GetClipboardData(CF_UNICODETEXT);
1995 if (hglb)
1997 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);
1998 sClipboardText = lpstr;
1999 GlobalUnlock(hglb);
2001 CloseClipboard();
2003 if(!sClipboardText.IsEmpty())
2005 if(sClipboardText[0] == _T('\"') && sClipboardText[sClipboardText.GetLength()-1] == _T('\"'))
2006 sClipboardText=sClipboardText.Mid(1,sClipboardText.GetLength()-2);
2008 if(sClipboardText.Find( _T("http://")) == 0)
2009 return sClipboardText;
2011 if(sClipboardText.Find( _T("https://")) == 0)
2012 return sClipboardText;
2014 if(sClipboardText.Find( _T("git://")) == 0)
2015 return sClipboardText;
2017 if(sClipboardText.Find( _T("ssh://")) == 0)
2018 return sClipboardText;
2020 if(sClipboardText.GetLength()>=2)
2021 if( sClipboardText[1] == _T(':') )
2022 if( (sClipboardText[0] >= 'A' && sClipboardText[0] <= 'Z')
2023 || (sClipboardText[0] >= 'a' && sClipboardText[0] <= 'z') )
2024 return sClipboardText;
2026 // trim prefixes like "git clone "
2027 if (!skipGitPrefix.IsEmpty() && sClipboardText.Find(skipGitPrefix) == 0)
2029 sClipboardText = sClipboardText.Mid(skipGitPrefix.GetLength()).Trim();
2030 int spacePos = -1;
2031 while (paramsCount >= 0)
2033 --paramsCount;
2034 spacePos = sClipboardText.Find(_T(' '), spacePos + 1);
2035 if (spacePos == -1)
2036 break;
2038 if (spacePos > 0 && paramsCount < 0)
2039 sClipboardText = sClipboardText.Left(spacePos);
2040 return sClipboardText;
2044 return CString(_T(""));
2047 CString CAppUtils::ChooseRepository(CString *path)
2049 CBrowseFolder browseFolder;
2050 CRegString regLastResopitory = CRegString(_T("Software\\TortoiseGit\\TortoiseProc\\LastRepo"),_T(""));
2052 browseFolder.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;
2053 CString strCloneDirectory;
2054 if(path)
2055 strCloneDirectory=*path;
2056 else
2058 strCloneDirectory = regLastResopitory;
2061 CString title;
2062 title.LoadString(IDS_CHOOSE_REPOSITORY);
2064 browseFolder.SetInfo(title);
2066 if (browseFolder.Show(NULL, strCloneDirectory) == CBrowseFolder::OK)
2068 regLastResopitory = strCloneDirectory;
2069 return strCloneDirectory;
2071 else
2073 return CString();
2077 bool CAppUtils::SendPatchMail(CTGitPathList& list)
2079 CSendMailDlg dlg;
2081 dlg.m_PathList = list;
2083 if(dlg.DoModal()==IDOK)
2085 if (dlg.m_PathList.IsEmpty())
2086 return FALSE;
2088 CGitProgressDlg progDlg;
2090 theApp.m_pMainWnd = &progDlg;
2091 progDlg.SetCommand(CGitProgressList::GitProgress_SendMail);
2093 progDlg.SetPathList(dlg.m_PathList);
2094 //ProjectProperties props;
2095 //props.ReadPropsPathList(dlg.m_pathList);
2096 //progDlg.SetProjectProperties(props);
2097 progDlg.SetItemCount(dlg.m_PathList.GetCount());
2099 CSendMailPatch sendMailPatch(dlg.m_To, dlg.m_CC, dlg.m_Subject, !!dlg.m_bAttachment, !!dlg.m_bCombine);
2100 progDlg.SetSendMailOption(&sendMailPatch);
2102 progDlg.DoModal();
2104 return true;
2106 return false;
2109 bool CAppUtils::SendPatchMail(CString &cmd,CString &formatpatchoutput)
2111 CTGitPathList list;
2112 CString log=formatpatchoutput;
2113 int start=log.Find(cmd);
2114 if(start >=0)
2115 CString one=log.Tokenize(_T("\n"),start);
2116 else
2117 start = 0;
2119 while(start>=0)
2121 CString one=log.Tokenize(_T("\n"),start);
2122 one=one.Trim();
2123 if(one.IsEmpty() || one.Find(_T("Success")) == 0)
2124 continue;
2125 one.Replace(_T('/'),_T('\\'));
2126 CTGitPath path;
2127 path.SetFromWin(one);
2128 list.AddPath(path);
2130 if (!list.IsEmpty())
2132 return SendPatchMail(list);
2134 else
2136 CMessageBox::Show(NULL, IDS_ERR_NOPATCHES, IDS_APPNAME, MB_ICONINFORMATION);
2137 return true;
2142 int CAppUtils::GetLogOutputEncode(CGit *pGit)
2144 CString output;
2145 output = pGit->GetConfigValue(_T("i18n.logOutputEncoding"));
2146 if(output.IsEmpty())
2147 return CUnicodeUtils::GetCPCode(pGit->GetConfigValue(_T("i18n.commitencoding")));
2148 else
2150 return CUnicodeUtils::GetCPCode(output);
2153 int CAppUtils::SaveCommitUnicodeFile(CString &filename, CString &message)
2155 CFile file(filename,CFile::modeReadWrite|CFile::modeCreate );
2156 int cp = CUnicodeUtils::GetCPCode(g_Git.GetConfigValue(_T("i18n.commitencoding")));
2158 bool stripComments = (CRegDWORD(_T("Software\\TortoiseGit\\StripCommentedLines"), FALSE) == TRUE);
2160 if (CRegDWORD(_T("Software\\TortoiseGit\\SanitizeCommitMsg"), TRUE) == TRUE)
2161 message.TrimRight(L" \r\n");
2163 int len = message.GetLength();
2164 int start = 0;
2165 while (start >= 0 && start < len)
2167 int oldStart = start;
2168 start = message.Find(L"\n", oldStart);
2169 CString line = message.Mid(oldStart);
2170 if (start != -1)
2172 line = line.Left(start - oldStart);
2173 ++start; // move forward so we don't find the same char again
2175 if (stripComments && (line.GetLength() >= 1 && line.GetAt(0) == '#') || (start < 0 && line.IsEmpty()))
2176 continue;
2177 line.TrimRight(L" \r");
2178 CStringA lineA = CUnicodeUtils::GetMulti(line + L"\n", cp);
2179 file.Write(lineA.GetBuffer(), lineA.GetLength());
2181 file.Close();
2182 return 0;
2185 bool CAppUtils::Pull(bool showPush)
2187 CPullFetchDlg dlg;
2188 dlg.m_IsPull = TRUE;
2189 if (dlg.DoModal() == IDOK)
2191 CString url = dlg.m_RemoteURL;
2193 if (dlg.m_bAutoLoad)
2195 CAppUtils::LaunchPAgent(NULL, &dlg.m_RemoteURL);
2198 CString cmd;
2199 CGitHash hashOld;
2200 if (g_Git.GetHash(hashOld, _T("HEAD")))
2202 MessageBox(NULL, g_Git.GetGitLastErr(_T("Could not get HEAD hash.")), _T("TortoiseGit"), MB_ICONERROR);
2203 return false;
2206 CString cmdRebase;
2207 CString noff;
2208 CString ffonly;
2209 CString squash;
2210 CString nocommit;
2211 CString depth;
2212 CString notags;
2213 CString prune;
2215 if (dlg.m_bRebase)
2216 cmdRebase = "--rebase ";
2218 if (!dlg.m_bFetchTags)
2219 notags = _T("--no-tags");
2221 if (dlg.m_bNoFF)
2222 noff=_T("--no-ff");
2224 if (dlg.m_bFFonly)
2225 ffonly = _T("--ff-only");
2227 if (dlg.m_bSquash)
2228 squash = _T("--squash");
2230 if (dlg.m_bNoCommit)
2231 nocommit = _T("--no-commit");
2233 if (dlg.m_bDepth)
2234 depth.Format(_T("--depth %d "), dlg.m_nDepth);
2236 int ver = CAppUtils::GetMsysgitVersion();
2238 if (dlg.m_bPrune == TRUE)
2239 prune = _T("--prune ");
2240 else if (dlg.m_bPrune == FALSE && ver >= 0x01080500)
2241 prune = _T("--no-prune ");
2243 if(ver >= 0x01070203) //above 1.7.0.2
2244 cmdRebase += _T("--progress ");
2246 cmd.Format(_T("git.exe pull -v %s %s %s %s %s %s %s %s \"%s\" %s"), cmdRebase, noff, ffonly, squash, nocommit, depth, notags, prune, url, dlg.m_RemoteBranchName);
2247 CProgressDlg progress;
2248 progress.m_GitCmd = cmd;
2249 progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_PROC_PULL_DIFFS)));
2250 progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_PROC_PULL_LOG)));
2251 INT_PTR pushButton = showPush ? progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_MENUPUSH))) + IDC_PROGRESS_BUTTON1 : -1;
2253 CTGitPath gitPath = g_Git.m_CurrentDir;
2254 INT_PTR smUpdateButton = gitPath.HasSubmodules() ? progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_PROC_SUBMODULESUPDATE))) + IDC_PROGRESS_BUTTON1 : -1;
2256 INT_PTR ret = progress.DoModal();
2258 if (ret == IDOK && progress.m_GitStatus == 1 && progress.m_LogText.Find(_T("CONFLICT")) >= 0 && CMessageBox::Show(NULL, IDS_SEECHANGES, IDS_APPNAME, MB_YESNO | MB_ICONINFORMATION) == IDYES)
2260 CChangedDlg dlg;
2261 dlg.m_pathList.AddPath(CTGitPath());
2262 dlg.DoModal();
2264 return true;
2267 CGitHash hashNew;
2268 if (g_Git.GetHash(hashNew, _T("HEAD")))
2270 MessageBox(NULL, g_Git.GetGitLastErr(_T("Could not get HEAD hash after pulling.")), _T("TortoiseGit"), MB_ICONERROR);
2271 return FALSE;
2274 if( ret == IDC_PROGRESS_BUTTON1)
2276 if(hashOld == hashNew)
2278 if(progress.m_GitStatus == 0)
2279 CMessageBox::Show(NULL, IDS_UPTODATE, IDS_APPNAME, MB_OK | MB_ICONINFORMATION);
2280 return TRUE;
2283 CFileDiffDlg dlg;
2284 dlg.SetDiff(NULL, hashNew.ToString(), hashOld.ToString());
2285 dlg.DoModal();
2287 return true;
2289 else if ( ret == IDC_PROGRESS_BUTTON1 +1 )
2291 if(hashOld == hashNew)
2293 if(progress.m_GitStatus == 0)
2294 CMessageBox::Show(NULL, IDS_UPTODATE, IDS_APPNAME, MB_OK | MB_ICONINFORMATION);
2295 return true;
2298 CLogDlg dlg;
2299 dlg.SetParams(CTGitPath(_T("")), CTGitPath(_T("")), _T(""), hashOld.ToString() + _T("..") + hashNew.ToString(), 0);
2300 dlg.DoModal();
2302 else if (ret == pushButton)
2304 Push(_T(""));
2305 return true;
2307 else if (ret == smUpdateButton)
2309 CString sCmd;
2310 sCmd.Format(_T("/command:subupdate /bkpath:\"%s\""), g_Git.m_CurrentDir);
2312 CAppUtils::RunTortoiseGitProc(sCmd);
2313 return true;
2317 return true;
2320 bool CAppUtils::Fetch(CString remoteName, bool allowRebase, bool allRemotes)
2322 CPullFetchDlg dlg;
2323 dlg.m_PreSelectRemote = remoteName;
2324 dlg.m_bAllowRebase = allowRebase;
2325 dlg.m_IsPull=FALSE;
2326 dlg.m_bAllRemotes = allRemotes;
2328 if(dlg.DoModal()==IDOK)
2330 if(dlg.m_bAutoLoad)
2332 if (dlg.m_bAllRemotes)
2334 STRING_VECTOR list;
2335 g_Git.GetRemoteList(list);
2337 STRING_VECTOR::const_iterator it = list.begin();
2338 while (it != list.end())
2340 CString remote(*it);
2341 CAppUtils::LaunchPAgent(NULL, &remote);
2342 ++it;
2345 else
2346 CAppUtils::LaunchPAgent(NULL, &dlg.m_RemoteURL);
2349 CString url;
2350 url=dlg.m_RemoteURL;
2351 CString cmd;
2352 CString arg;
2354 int ver = CAppUtils::GetMsysgitVersion();
2356 if(ver >= 0x01070203) //above 1.7.0.2
2357 arg = _T("--progress ");
2359 if (dlg.m_bDepth)
2360 arg.AppendFormat(_T("--depth %d "), dlg.m_nDepth);
2362 if (dlg.m_bPrune == TRUE)
2363 arg += _T("--prune ");
2364 else if (dlg.m_bPrune == FALSE && ver >= 0x01080500)
2365 arg += _T("--no-prune ");
2367 if (dlg.m_bFetchTags == 1)
2369 arg += _T("--tags ");
2371 else if (dlg.m_bFetchTags == 0)
2373 arg += _T("--no-tags ");
2376 if (dlg.m_bAllRemotes)
2377 cmd.Format(_T("git.exe fetch --all -v %s"), arg);
2378 else
2379 cmd.Format(_T("git.exe fetch -v %s \"%s\" %s"), arg, url, dlg.m_RemoteBranchName);
2381 while (true)
2383 CProgressDlg progress;
2385 progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_MENULOG)));
2386 progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_PROC_RESET)));
2388 if (!dlg.m_bRebase && !g_GitAdminDir.IsBareRepo(g_Git.m_CurrentDir))
2389 progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_MENUREBASE)));
2391 progress.m_PostFailCmdList.Add(CString(MAKEINTRESOURCE(IDS_MSGBOX_RETRY)));
2393 progress.m_GitCmd=cmd;
2394 INT_PTR userResponse;
2396 if (g_Git.UsingLibGit2(CGit::GIT_CMD_FETCH))
2398 CGitProgressDlg gitdlg;
2399 if (!dlg.m_bAllRemotes)
2400 gitdlg.SetUrl(url);
2401 gitdlg.SetCommand(CGitProgressList::GitProgress_Fetch);
2402 gitdlg.SetAutoTag(dlg.m_bFetchTags == 1 ? GIT_REMOTE_DOWNLOAD_TAGS_ALL : dlg.m_bFetchTags == 2 ? GIT_REMOTE_DOWNLOAD_TAGS_AUTO : GIT_REMOTE_DOWNLOAD_TAGS_NONE);
2403 if (!dlg.m_bAllRemotes)
2404 gitdlg.SetRefSpec(dlg.m_RemoteBranchName);
2405 userResponse = gitdlg.DoModal();
2407 else
2408 userResponse = progress.DoModal();
2410 if (!progress.m_GitStatus)
2412 if (userResponse == IDC_PROGRESS_BUTTON1)
2414 CString cmd = _T("/command:log");
2415 cmd += _T(" /path:\"") + g_Git.m_CurrentDir + _T("\"");
2416 RunTortoiseGitProc(cmd);
2417 return TRUE;
2419 else if (userResponse == IDC_PROGRESS_BUTTON1 + 1)
2421 CString pullRemote, pullBranch;
2422 g_Git.GetRemoteTrackedBranchForHEAD(pullRemote, pullBranch);
2424 CString defaultUpstream;
2425 if (!pullRemote.IsEmpty() && !pullBranch.IsEmpty())
2426 defaultUpstream.Format(_T("remotes/%s/%s"), pullRemote, pullBranch);
2427 GitReset(&defaultUpstream, 2);
2428 return TRUE;
2430 else if ((userResponse == IDC_PROGRESS_BUTTON1 + 2) || dlg.m_bRebase)
2432 while(1)
2434 CRebaseDlg dlg;
2435 dlg.m_PostButtonTexts.Add(CString(MAKEINTRESOURCE(IDS_MENULOG)));
2436 dlg.m_PostButtonTexts.Add(CString(MAKEINTRESOURCE(IDS_MENUDESSENDMAIL)));
2437 dlg.m_PostButtonTexts.Add(CString(MAKEINTRESOURCE(IDS_MENUREBASE)));
2438 INT_PTR response = dlg.DoModal();
2439 if(response == IDOK)
2441 return TRUE;
2443 else if (response == IDC_REBASE_POST_BUTTON)
2445 CString cmd = _T("/command:log");
2446 cmd += _T(" /path:\"") + g_Git.m_CurrentDir + _T("\"");
2447 RunTortoiseGitProc(cmd);
2448 return TRUE;
2450 else if (response == IDC_REBASE_POST_BUTTON + 1)
2452 CString cmd, out, err;
2453 cmd.Format(_T("git.exe format-patch -o \"%s\" %s..%s"),
2454 g_Git.m_CurrentDir,
2455 g_Git.FixBranchName(dlg.m_Upstream),
2456 g_Git.FixBranchName(dlg.m_Branch));
2457 if (g_Git.Run(cmd, &out, &err, CP_UTF8))
2459 CMessageBox::Show(NULL, out + L"\n" + err, _T("TortoiseGit"), MB_OK|MB_ICONERROR);
2460 return FALSE;
2463 CAppUtils::SendPatchMail(cmd,out);
2464 return TRUE;
2466 else if (response == IDC_REBASE_POST_BUTTON + 2)
2467 continue;
2468 else if(response == IDCANCEL)
2469 return FALSE;
2471 return TRUE;
2473 else if (userResponse != IDCANCEL)
2474 return TRUE;
2475 return FALSE;
2477 else
2479 if (userResponse == IDC_PROGRESS_BUTTON1)
2480 continue;
2481 return FALSE;
2485 return FALSE;
2488 bool CAppUtils::Push(CString selectLocalBranch)
2490 CPushDlg dlg;
2491 dlg.m_BranchSourceName = selectLocalBranch;
2493 if (dlg.DoModal() == IDOK)
2495 CString error;
2496 DWORD exitcode = 0xFFFFFFFF;
2497 if (CHooks::Instance().PrePush(g_Git.m_CurrentDir, exitcode, error))
2499 if (exitcode)
2501 CString temp;
2502 temp.Format(IDS_ERR_HOOKFAILED, (LPCTSTR)error);
2503 CMessageBox::Show(nullptr, temp, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
2504 return false;
2508 CString arg;
2510 if(dlg.m_bPack)
2511 arg += _T("--thin ");
2512 if(dlg.m_bTags && !dlg.m_bPushAllBranches)
2513 arg += _T("--tags ");
2514 if(dlg.m_bForce)
2515 arg += _T("--force ");
2516 if (dlg.m_bSetUpstream)
2517 arg += _T("--set-upstream ");
2518 if (dlg.m_RecurseSubmodules == 1)
2519 arg += _T("--recurse-submodules=check ");
2520 if (dlg.m_RecurseSubmodules == 2)
2521 arg += _T("--recurse-submodules=on-demand ");
2523 int ver = CAppUtils::GetMsysgitVersion();
2525 if(ver >= 0x01070203) //above 1.7.0.2
2526 arg += _T("--progress ");
2528 CProgressDlg progress;
2530 STRING_VECTOR remotesList;
2531 if (dlg.m_bPushAllRemotes)
2532 g_Git.GetRemoteList(remotesList);
2533 else
2534 remotesList.push_back(dlg.m_URL);
2536 for (unsigned int i = 0; i < remotesList.size(); ++i)
2538 if (dlg.m_bAutoLoad)
2539 CAppUtils::LaunchPAgent(NULL, &remotesList[i]);
2541 CString cmd;
2542 if (dlg.m_bPushAllBranches)
2544 cmd.Format(_T("git.exe push --all %s \"%s\""),
2545 arg,
2546 remotesList[i]);
2548 if (dlg.m_bTags)
2550 progress.m_GitCmdList.push_back(cmd);
2551 cmd.Format(_T("git.exe push --tags %s \"%s\""), arg, remotesList[i]);
2554 else
2556 cmd.Format(_T("git.exe push %s \"%s\" %s"),
2557 arg,
2558 remotesList[i],
2559 dlg.m_BranchSourceName);
2560 if (!dlg.m_BranchRemoteName.IsEmpty())
2562 cmd += _T(":") + dlg.m_BranchRemoteName;
2565 progress.m_GitCmdList.push_back(cmd);
2568 progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_PROC_REQUESTPULL)));
2569 progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_MENUPUSH)));
2570 bool rejected = false;
2571 progress.m_PostCmdCallback = [&] ()
2573 if (progress.m_GitStatus)
2575 progress.m_PostCmdList.RemoveAll();
2576 rejected = progress.GetLogText().Find(_T("! [rejected]")) > 0;
2577 if (rejected)
2579 progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_MENUPULL)));
2580 progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_MENUFETCH)));
2582 progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_MENUPUSH)));
2586 INT_PTR ret = progress.DoModal();
2588 exitcode = 0xFFFFFFFF;
2589 error.Empty();
2590 if (CHooks::Instance().PostPush(g_Git.m_CurrentDir, exitcode, error))
2592 if (exitcode)
2594 CString temp;
2595 temp.Format(IDS_ERR_HOOKFAILED, (LPCTSTR)error);
2596 CMessageBox::Show(nullptr, temp, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
2597 return false;
2601 if (!progress.m_GitStatus)
2603 if(ret == IDC_PROGRESS_BUTTON1)
2605 RequestPull(dlg.m_BranchRemoteName);
2607 else if(ret == IDC_PROGRESS_BUTTON1 + 1)
2609 Push(selectLocalBranch);
2611 return TRUE;
2613 else
2615 if (rejected)
2617 // failed, pull first
2618 if (ret == IDC_PROGRESS_BUTTON1)
2620 Pull(true);
2622 else if (ret == IDC_PROGRESS_BUTTON1 + 1)
2624 Fetch(dlg.m_bPushAllRemotes ? _T("") : dlg.m_URL, true, !!dlg.m_bPushAllRemotes);
2626 else if (ret == IDC_PROGRESS_BUTTON1 + 2)
2628 Push(selectLocalBranch);
2631 else
2633 if (ret == IDC_PROGRESS_BUTTON1)
2635 Push(selectLocalBranch);
2640 return FALSE;
2643 bool CAppUtils::RequestPull(CString endrevision, CString repositoryUrl)
2645 CRequestPullDlg dlg;
2646 dlg.m_RepositoryURL = repositoryUrl;
2647 dlg.m_EndRevision = endrevision;
2648 if (dlg.DoModal()==IDOK)
2650 CString cmd;
2651 cmd.Format(_T("git.exe request-pull %s \"%s\" %s"), dlg.m_StartRevision, dlg.m_RepositoryURL, dlg.m_EndRevision);
2653 CSysProgressDlg sysProgressDlg;
2654 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
2655 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_PROC_CREATINGPULLREUQEST)));
2656 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
2657 sysProgressDlg.SetShowProgressBar(false);
2658 sysProgressDlg.ShowModeless((HWND)NULL, true);
2660 CString tempFileName = GetTempFile();
2661 CString err;
2662 DeleteFile(tempFileName);
2663 CreateDirectory(tempFileName, NULL);
2664 tempFileName += _T("\\pullrequest.txt");
2665 if (g_Git.RunLogFile(cmd, tempFileName, &err))
2667 CString msg;
2668 msg.LoadString(IDS_ERR_PULLREUQESTFAILED);
2669 CMessageBox::Show(NULL, msg + _T("\n") + err, _T("TortoiseGit"), MB_OK);
2670 return false;
2673 if (sysProgressDlg.HasUserCancelled())
2675 CMessageBox::Show(NULL, IDS_SVN_USERCANCELLED, IDS_APPNAME, MB_OK);
2676 ::DeleteFile(tempFileName);
2677 return false;
2680 sysProgressDlg.Stop();
2682 if (dlg.m_bSendMail)
2684 CSendMailDlg dlg;
2685 dlg.m_PathList = CTGitPathList(CTGitPath(tempFileName));
2686 dlg.m_bCustomSubject = true;
2688 if (dlg.DoModal() == IDOK)
2690 if (dlg.m_PathList.IsEmpty())
2691 return FALSE;
2693 CGitProgressDlg progDlg;
2695 theApp.m_pMainWnd = &progDlg;
2696 progDlg.SetCommand(CGitProgressList::GitProgress_SendMail);
2698 progDlg.SetPathList(dlg.m_PathList);
2699 progDlg.SetItemCount(dlg.m_PathList.GetCount());
2701 CSendMailCombineable sendMailCombineable(dlg.m_To, dlg.m_CC, dlg.m_Subject, !!dlg.m_bAttachment, !!dlg.m_bCombine);
2702 progDlg.SetSendMailOption(&sendMailCombineable);
2704 progDlg.DoModal();
2706 return true;
2708 return false;
2711 CAppUtils::LaunchAlternativeEditor(tempFileName);
2713 return true;
2716 bool CAppUtils::CreateMultipleDirectory(const CString& szPath)
2718 CString strDir(szPath);
2719 if (strDir.GetAt(strDir.GetLength()-1)!=_T('\\'))
2721 strDir.AppendChar(_T('\\'));
2723 std::vector<CString> vPath;
2724 CString strTemp;
2725 bool bSuccess = false;
2727 for (int i=0;i<strDir.GetLength();++i)
2729 if (strDir.GetAt(i) != _T('\\'))
2731 strTemp.AppendChar(strDir.GetAt(i));
2733 else
2735 vPath.push_back(strTemp);
2736 strTemp.AppendChar(_T('\\'));
2740 for (auto vIter = vPath.begin(); vIter != vPath.end(); ++vIter)
2742 bSuccess = CreateDirectory(*vIter, NULL) ? true : false;
2745 return bSuccess;
2748 void CAppUtils::RemoveTrailSlash(CString &path)
2750 if(path.IsEmpty())
2751 return ;
2753 // For URL, do not trim the slash just after the host name component.
2754 int index = path.Find(_T("://"));
2755 if (index >= 0)
2757 index += 4;
2758 index = path.Find(_T('/'), index);
2759 if (index == path.GetLength() - 1)
2760 return;
2763 while(path[path.GetLength()-1] == _T('\\') || path[path.GetLength()-1] == _T('/' ) )
2765 path=path.Left(path.GetLength()-1);
2766 if(path.IsEmpty())
2767 return;
2771 bool CAppUtils::CheckUserData()
2773 while(g_Git.GetUserName().IsEmpty() || g_Git.GetUserEmail().IsEmpty())
2775 if(CMessageBox::Show(NULL, IDS_PROC_NOUSERDATA, IDS_APPNAME, MB_YESNO| MB_ICONERROR) == IDYES)
2777 CTGitPath path(g_Git.m_CurrentDir);
2778 CSettings dlg(IDS_PROC_SETTINGS_TITLE,&path);
2779 dlg.SetTreeViewMode(TRUE, TRUE, TRUE);
2780 dlg.SetTreeWidth(220);
2781 dlg.m_DefaultPage = _T("gitconfig");
2783 dlg.DoModal();
2784 dlg.HandleRestart();
2787 else
2788 return false;
2791 return true;
2794 BOOL CAppUtils::Commit(CString bugid,BOOL bWholeProject,CString &sLogMsg,
2795 CTGitPathList &pathList,
2796 CTGitPathList &selectedList,
2797 bool bSelectFilesForCommit)
2799 bool bFailed = true;
2801 if (!CheckUserData())
2802 return false;
2804 while (bFailed)
2806 bFailed = false;
2807 CCommitDlg dlg;
2808 dlg.m_sBugID = bugid;
2810 dlg.m_bWholeProject = bWholeProject;
2812 dlg.m_sLogMessage = sLogMsg;
2813 dlg.m_pathList = pathList;
2814 dlg.m_checkedPathList = selectedList;
2815 dlg.m_bSelectFilesForCommit = bSelectFilesForCommit;
2816 if (dlg.DoModal() == IDOK)
2818 if (dlg.m_pathList.IsEmpty())
2819 return false;
2820 // if the user hasn't changed the list of selected items
2821 // we don't use that list. Because if we would use the list
2822 // of pre-checked items, the dialog would show different
2823 // checked items on the next startup: it would only try
2824 // to check the parent folder (which might not even show)
2825 // instead, we simply use an empty list and let the
2826 // default checking do its job.
2827 if (!dlg.m_pathList.IsEqual(pathList))
2828 selectedList = dlg.m_pathList;
2829 pathList = dlg.m_updatedPathList;
2830 sLogMsg = dlg.m_sLogMessage;
2831 bSelectFilesForCommit = true;
2833 switch (dlg.m_PostCmd)
2835 case GIT_POSTCOMMIT_CMD_DCOMMIT:
2836 CAppUtils::SVNDCommit();
2837 break;
2838 case GIT_POSTCOMMIT_CMD_PUSH:
2839 CAppUtils::Push();
2840 break;
2841 case GIT_POSTCOMMIT_CMD_CREATETAG:
2842 CAppUtils::CreateBranchTag(TRUE);
2843 break;
2844 case GIT_POSTCOMMIT_CMD_PULL:
2845 CAppUtils::Pull(true);
2846 break;
2847 default:
2848 break;
2851 // CGitProgressDlg progDlg;
2852 // progDlg.SetChangeList(dlg.m_sChangeList, !!dlg.m_bKeepChangeList);
2853 // if (parser.HasVal(_T("closeonend")))
2854 // progDlg.SetAutoClose(parser.GetLongVal(_T("closeonend")));
2855 // progDlg.SetCommand(CGitProgressDlg::GitProgress_Commit);
2856 // progDlg.SetOptions(dlg.m_bKeepLocks ? ProgOptKeeplocks : ProgOptNone);
2857 // progDlg.SetPathList(dlg.m_pathList);
2858 // progDlg.SetCommitMessage(dlg.m_sLogMessage);
2859 // progDlg.SetDepth(dlg.m_bRecursive ? Git_depth_infinity : svn_depth_empty);
2860 // progDlg.SetSelectedList(dlg.m_selectedPathList);
2861 // progDlg.SetItemCount(dlg.m_itemsCount);
2862 // progDlg.SetBugTraqProvider(dlg.m_BugTraqProvider);
2863 // progDlg.DoModal();
2864 // CRegDWORD err = CRegDWORD(_T("Software\\TortoiseGit\\ErrorOccurred"), FALSE);
2865 // err = (DWORD)progDlg.DidErrorsOccur();
2866 // bFailed = progDlg.DidErrorsOccur();
2867 // bRet = progDlg.DidErrorsOccur();
2868 // CRegDWORD bFailRepeat = CRegDWORD(_T("Software\\TortoiseGit\\CommitReopen"), FALSE);
2869 // if (DWORD(bFailRepeat)==0)
2870 // bFailed = false; // do not repeat if the user chose not to in the settings.
2873 return true;
2877 BOOL CAppUtils::SVNDCommit()
2879 CSVNDCommitDlg dcommitdlg;
2880 CString gitSetting = g_Git.GetConfigValue(_T("svn.rmdir"));
2881 if (gitSetting == _T("")) {
2882 if (dcommitdlg.DoModal() != IDOK)
2884 return false;
2886 else
2888 if (dcommitdlg.m_remember)
2890 if (dcommitdlg.m_rmdir)
2892 gitSetting = _T("true");
2894 else
2896 gitSetting = _T("false");
2898 if(g_Git.SetConfigValue(_T("svn.rmdir"),gitSetting))
2900 CString msg;
2901 msg.Format(IDS_PROC_SAVECONFIGFAILED, _T("svn.rmdir"), gitSetting);
2902 CMessageBox::Show(NULL, msg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
2908 BOOL IsStash = false;
2909 if(!g_Git.CheckCleanWorkTree())
2911 if (CMessageBox::Show(NULL, IDS_ERROR_NOCLEAN_STASH, IDS_APPNAME, 1, IDI_QUESTION, IDS_STASHBUTTON, IDS_ABORTBUTTON) == 1)
2913 CSysProgressDlg sysProgressDlg;
2914 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
2915 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_PROC_STASHRUNNING)));
2916 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
2917 sysProgressDlg.SetShowProgressBar(false);
2918 sysProgressDlg.SetCancelMsg(IDS_PROGRS_INFOFAILED);
2919 sysProgressDlg.ShowModeless((HWND)NULL, true);
2921 CString cmd,out;
2922 cmd=_T("git.exe stash");
2923 if (g_Git.Run(cmd, &out, CP_UTF8))
2925 sysProgressDlg.Stop();
2926 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
2927 return false;
2929 sysProgressDlg.Stop();
2931 IsStash =true;
2933 else
2935 return false;
2939 CProgressDlg progress;
2940 if (dcommitdlg.m_rmdir)
2942 progress.m_GitCmd=_T("git.exe svn dcommit --rmdir");
2944 else
2946 progress.m_GitCmd=_T("git.exe svn dcommit");
2948 if(progress.DoModal()==IDOK && progress.m_GitStatus == 0)
2950 ::DeleteFile(g_Git.m_CurrentDir + _T("\\sys$command"));
2951 if( IsStash)
2953 if(CMessageBox::Show(NULL,IDS_DCOMMIT_STASH_POP,IDS_APPNAME,MB_YESNO|MB_ICONINFORMATION)==IDYES)
2955 CSysProgressDlg sysProgressDlg;
2956 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
2957 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_PROC_STASHRUNNING)));
2958 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
2959 sysProgressDlg.SetShowProgressBar(false);
2960 sysProgressDlg.SetCancelMsg(IDS_PROGRS_INFOFAILED);
2961 sysProgressDlg.ShowModeless((HWND)NULL, true);
2963 CString cmd,out;
2964 cmd=_T("git.exe stash pop");
2965 if (g_Git.Run(cmd, &out, CP_UTF8))
2967 sysProgressDlg.Stop();
2968 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);
2969 return false;
2971 sysProgressDlg.Stop();
2973 else
2975 return false;
2978 return TRUE;
2980 return FALSE;
2983 BOOL CAppUtils::Merge(CString *commit)
2985 if (!CheckUserData())
2986 return FALSE;
2988 CMergeDlg dlg;
2989 if(commit)
2990 dlg.m_initialRefName = *commit;
2992 if(dlg.DoModal()==IDOK)
2994 CString cmd;
2995 CString args;
2997 if(dlg.m_bNoFF)
2998 args += _T(" --no-ff");
3000 if(dlg.m_bSquash)
3001 args += _T(" --squash");
3003 if(dlg.m_bNoCommit)
3004 args += _T(" --no-commit");
3006 if (dlg.m_bLog)
3008 CString fmt;
3009 fmt.Format(_T(" --log=%d"), dlg.m_nLog);
3010 args += fmt;
3013 if (!dlg.m_MergeStrategy.IsEmpty())
3015 args += _T(" --strategy=") + dlg.m_MergeStrategy;
3016 if (!dlg.m_StrategyOption.IsEmpty())
3018 args += _T(" --strategy-option=") + dlg.m_StrategyOption;
3019 if (!dlg.m_StrategyParam.IsEmpty())
3020 args += _T("=") + dlg.m_StrategyParam;
3024 if(!dlg.m_strLogMesage.IsEmpty())
3026 CString logmsg = dlg.m_strLogMesage;
3027 logmsg.Replace(_T("\""), _T("\\\""));
3028 args += _T(" -m \"") + logmsg + _T("\"");
3030 cmd.Format(_T("git.exe merge %s %s"), args, g_Git.FixBranchName(dlg.m_VersionName));
3032 CProgressDlg Prodlg;
3033 Prodlg.m_GitCmd = cmd;
3035 BOOL hasGitSVN = CTGitPath(g_Git.m_CurrentDir).GetAdminDirMask() & ITEMIS_GITSVN;
3036 if (dlg.m_bNoCommit)
3037 Prodlg.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_MENUCOMMIT)));
3038 else
3040 if (dlg.m_bIsBranch)
3041 Prodlg.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_PROC_REMOVEBRANCH)));
3043 if (hasGitSVN)
3044 Prodlg.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_MENUSVNDCOMMIT)));
3047 Prodlg.m_PostCmdCallback = [&] ()
3049 if (Prodlg.m_GitStatus)
3051 Prodlg.m_PostCmdList.RemoveAll();
3053 CTGitPathList list;
3054 if (!g_Git.ListConflictFile(list) && !list.IsEmpty())
3056 // there are conflict files
3057 Prodlg.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_RESOLVE)));
3062 INT_PTR ret = Prodlg.DoModal();
3063 if (Prodlg.m_GitStatus != 0 && ret == IDC_PROGRESS_BUTTON1)
3065 CTGitPathList pathlist;
3066 CTGitPathList selectedlist;
3067 pathlist.AddPath(g_Git.m_CurrentDir);
3068 bool bSelectFilesForCommit = !!DWORD(CRegStdDWORD(_T("Software\\TortoiseGit\\SelectFilesForCommit"), TRUE));
3069 CString str;
3070 CAppUtils::Commit(CString(), false, str, pathlist, selectedlist, bSelectFilesForCommit);
3072 else if (ret == IDC_PROGRESS_BUTTON1)
3074 if (dlg.m_bNoCommit)
3076 CString sLogMsg;
3077 CTGitPathList pathList;
3078 CTGitPathList selectedList;
3079 return Commit(_T(""), TRUE, sLogMsg, pathList, selectedList, true);
3081 else if (dlg.m_bIsBranch)
3083 CString msg;
3084 msg.Format(IDS_PROC_DELETEBRANCHTAG, dlg.m_VersionName);
3085 if (CMessageBox::Show(NULL, msg, _T("TortoiseGit"), 2, IDI_QUESTION, CString(MAKEINTRESOURCE(IDS_DELETEBUTTON)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON))) == 1)
3087 CString cmd, out;
3088 cmd.Format(_T("git.exe branch -D -- %s"), dlg.m_VersionName);
3089 if (g_Git.Run(cmd, &out, CP_UTF8))
3090 MessageBox(NULL, out, _T("TortoiseGit"), MB_OK);
3093 else if (hasGitSVN)
3094 CAppUtils::SVNDCommit();
3096 else if (ret == IDC_PROGRESS_BUTTON1 + 1 && hasGitSVN)
3097 CAppUtils::SVNDCommit();
3099 return !Prodlg.m_GitStatus;
3101 return false;
3104 BOOL CAppUtils::MergeAbort()
3106 CMergeAbortDlg dlg;
3107 if (dlg.DoModal() == IDOK)
3109 CString cmd;
3110 CString type;
3111 switch (dlg.m_ResetType)
3113 case 0:
3114 type = _T("--mixed");
3115 break;
3116 case 1:
3117 type = _T("--hard");
3118 break;
3119 default:
3120 dlg.m_ResetType = 0;
3121 type = _T("--mixed");
3122 break;
3124 cmd.Format(_T("git.exe reset %s HEAD --"), type);
3126 while (true)
3128 CProgressDlg progress;
3129 progress.m_GitCmd = cmd;
3131 CTGitPath gitPath = g_Git.m_CurrentDir;
3132 if (gitPath.HasSubmodules() && dlg.m_ResetType == 1)
3133 progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_PROC_SUBMODULESUPDATE)));
3135 progress.m_PostFailCmdList.Add(CString(MAKEINTRESOURCE(IDS_MSGBOX_RETRY)));
3137 INT_PTR ret;
3138 if (g_Git.UsingLibGit2(CGit::GIT_CMD_RESET))
3140 CGitProgressDlg gitdlg;
3141 gitdlg.SetCommand(CGitProgressList::GitProgress_Reset);
3142 gitdlg.SetRevision(_T("HEAD"));
3143 gitdlg.SetResetType(dlg.m_ResetType + 1);
3144 ret = gitdlg.DoModal();
3146 else
3147 ret = progress.DoModal();
3149 if (progress.m_GitStatus == 0 && gitPath.HasSubmodules() && dlg.m_ResetType == 1 && ret == IDC_PROGRESS_BUTTON1)
3151 CString sCmd;
3152 sCmd.Format(_T("/command:subupdate /bkpath:\"%s\""), g_Git.m_CurrentDir);
3154 CCommonAppUtils::RunTortoiseGitProc(sCmd);
3155 return TRUE;
3157 else if (progress.m_GitStatus != 0 && ret == IDC_PROGRESS_BUTTON1)
3158 continue; // retry
3159 else if (ret == IDOK)
3160 return TRUE;
3161 else
3162 break;
3165 return FALSE;
3168 void CAppUtils::EditNote(GitRev *rev)
3170 if (!CheckUserData())
3171 return;
3173 CInputDlg dlg;
3174 dlg.m_sHintText = CString(MAKEINTRESOURCE(IDS_PROGS_TITLE_EDITNOTES));
3175 dlg.m_sInputText = rev->m_Notes;
3176 dlg.m_sTitle = CString(MAKEINTRESOURCE(IDS_PROGS_TITLE_EDITNOTES));
3177 //dlg.m_pProjectProperties = &m_ProjectProperties;
3178 dlg.m_bUseLogWidth = true;
3179 if(dlg.DoModal() == IDOK)
3181 CString cmd,output;
3182 cmd=_T("notes add -f -F \"");
3184 CString tempfile=::GetTempFile();
3185 CAppUtils::SaveCommitUnicodeFile(tempfile,dlg.m_sInputText);
3186 cmd += tempfile;
3187 cmd += _T("\" ");
3188 cmd += rev->m_CommitHash.ToString();
3192 if (git_run_cmd("notes", CUnicodeUtils::GetMulti(cmd, CP_UTF8).GetBuffer()))
3194 CMessageBox::Show(NULL, IDS_PROC_FAILEDSAVINGNOTES, IDS_APPNAME, MB_OK | MB_ICONERROR);
3197 else
3199 rev->m_Notes = dlg.m_sInputText;
3201 }catch(...)
3203 CMessageBox::Show(NULL, IDS_PROC_FAILEDSAVINGNOTES, IDS_APPNAME, MB_OK | MB_ICONERROR);
3205 ::DeleteFile(tempfile);
3210 int CAppUtils::GetMsysgitVersion()
3212 if (g_Git.ms_LastMsysGitVersion)
3213 return g_Git.ms_LastMsysGitVersion;
3215 CString cmd;
3216 CString versiondebug;
3217 CString version;
3219 CRegDWORD regTime = CRegDWORD(_T("Software\\TortoiseGit\\git_file_time"));
3220 CRegDWORD regVersion = CRegDWORD(_T("Software\\TortoiseGit\\git_cached_version"));
3222 CString gitpath = CGit::ms_LastMsysGitDir+_T("\\git.exe");
3224 __int64 time=0;
3225 if (!g_Git.GetFileModifyTime(gitpath, &time))
3227 if((DWORD)time == regTime)
3229 g_Git.ms_LastMsysGitVersion = regVersion;
3230 return regVersion;
3234 CString err;
3235 cmd = _T("git.exe --version");
3236 if (g_Git.Run(cmd, &version, &err, CP_UTF8))
3238 CMessageBox::Show(NULL, _T("git.exe not correctly set up (") + err + _T(")\nCheck TortoiseGit settings and consult help file for \"Git.exe Path\"."), _T("TortoiseGit"), MB_OK|MB_ICONERROR);
3239 return -1;
3242 int start=0;
3243 int ver = 0;
3245 versiondebug = version;
3249 CString str=version.Tokenize(_T("."), start);
3250 int space = str.ReverseFind(_T(' '));
3251 str = str.Mid(space+1,start);
3252 ver = _ttol(str);
3253 ver <<=24;
3255 version = version.Mid(start);
3256 start = 0;
3258 str = version.Tokenize(_T("."), start);
3260 ver |= (_ttol(str) & 0xFF) << 16;
3262 str = version.Tokenize(_T("."), start);
3263 ver |= (_ttol(str) & 0xFF) << 8;
3265 str = version.Tokenize(_T("."), start);
3266 ver |= (_ttol(str) & 0xFF);
3268 catch(...)
3270 if (!ver)
3272 CMessageBox::Show(NULL, _T("Could not parse git.exe version number: \"") + versiondebug + _T("\""), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
3273 return -1;
3277 regTime = time&0xFFFFFFFF;
3278 regVersion = ver;
3279 g_Git.ms_LastMsysGitVersion = ver;
3281 return ver;
3284 void CAppUtils::MarkWindowAsUnpinnable(HWND hWnd)
3286 typedef HRESULT (WINAPI *SHGPSFW) (HWND hwnd,REFIID riid,void** ppv);
3288 CAutoLibrary hShell = AtlLoadSystemLibraryUsingFullPath(_T("Shell32.dll"));
3290 if (hShell.IsValid()) {
3291 SHGPSFW pfnSHGPSFW = (SHGPSFW)::GetProcAddress(hShell, "SHGetPropertyStoreForWindow");
3292 if (pfnSHGPSFW) {
3293 IPropertyStore *pps;
3294 HRESULT hr = pfnSHGPSFW(hWnd, IID_PPV_ARGS(&pps));
3295 if (SUCCEEDED(hr)) {
3296 PROPVARIANT var;
3297 var.vt = VT_BOOL;
3298 var.boolVal = VARIANT_TRUE;
3299 hr = pps->SetValue(PKEY_AppUserModel_PreventPinning, var);
3300 pps->Release();
3306 void CAppUtils::SetWindowTitle(HWND hWnd, const CString& urlorpath, const CString& dialogname)
3308 ASSERT(dialogname.GetLength() < 70);
3309 ASSERT(urlorpath.GetLength() < MAX_PATH);
3310 WCHAR pathbuf[MAX_PATH] = {0};
3312 PathCompactPathEx(pathbuf, urlorpath, 70 - dialogname.GetLength(), 0);
3314 wcscat_s(pathbuf, L" - ");
3315 wcscat_s(pathbuf, dialogname);
3316 wcscat_s(pathbuf, L" - ");
3317 wcscat_s(pathbuf, CString(MAKEINTRESOURCE(IDS_APPNAME)));
3318 SetWindowText(hWnd, pathbuf);
3321 bool CAppUtils::BisectStart(CString lastGood, CString firstBad)
3323 if (!g_Git.CheckCleanWorkTree())
3325 if (CMessageBox::Show(NULL, IDS_ERROR_NOCLEAN_STASH, IDS_APPNAME, 1, IDI_QUESTION, IDS_STASHBUTTON, IDS_ABORTBUTTON) == 1)
3327 CSysProgressDlg sysProgressDlg;
3328 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
3329 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_PROC_STASHRUNNING)));
3330 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
3331 sysProgressDlg.SetShowProgressBar(false);
3332 sysProgressDlg.SetCancelMsg(IDS_PROGRS_INFOFAILED);
3333 sysProgressDlg.ShowModeless((HWND)NULL, true);
3335 CString cmd, out;
3336 cmd = _T("git.exe stash");
3337 if (g_Git.Run(cmd, &out, CP_UTF8))
3339 sysProgressDlg.Stop();
3340 CMessageBox::Show(NULL, out, _T("TortoiseGit"), MB_OK);
3341 return false;
3343 sysProgressDlg.Stop();
3345 else
3346 return false;
3349 CBisectStartDlg bisectStartDlg;
3351 if (!lastGood.IsEmpty())
3352 bisectStartDlg.m_sLastGood = lastGood;
3353 if (!firstBad.IsEmpty())
3354 bisectStartDlg.m_sFirstBad = firstBad;
3356 if (bisectStartDlg.DoModal() == IDOK)
3358 CProgressDlg progress;
3359 theApp.m_pMainWnd = &progress;
3360 progress.m_GitCmdList.push_back(_T("git.exe bisect start"));
3361 progress.m_GitCmdList.push_back(_T("git.exe bisect good ") + bisectStartDlg.m_LastGoodRevision);
3362 progress.m_GitCmdList.push_back(_T("git.exe bisect bad ") + bisectStartDlg.m_FirstBadRevision);
3364 CTGitPath path(g_Git.m_CurrentDir);
3366 if (path.HasSubmodules())
3367 progress.m_PostCmdList.Add(CString(MAKEINTRESOURCE(IDS_PROC_SUBMODULESUPDATE)));
3369 INT_PTR ret = progress.DoModal();
3370 if (path.HasSubmodules() && ret == IDC_PROGRESS_BUTTON1)
3372 CString sCmd;
3373 sCmd.Format(_T("/command:subupdate /bkpath:\"%s\""), g_Git.m_CurrentDir);
3375 CAppUtils::RunTortoiseGitProc(sCmd);
3376 return true;
3378 else if (ret == IDOK)
3379 return true;
3382 return false;
3385 int CAppUtils::Git2GetUserPassword(git_cred **out, const char *url, const char *username_from_url, unsigned int /*allowed_types*/, void * /*payload*/)
3387 CUserPassword dlg;
3388 dlg.m_URL = CUnicodeUtils::GetUnicode(url, CP_UTF8);
3389 if (username_from_url)
3390 dlg.m_UserName = CUnicodeUtils::GetUnicode(username_from_url, CP_UTF8);
3392 CStringA username, password;
3393 if (dlg.DoModal() == IDOK)
3395 username = CUnicodeUtils::GetMulti(dlg.m_UserName, CP_UTF8);
3396 password = CUnicodeUtils::GetMulti(dlg.m_Password, CP_UTF8);
3397 return git_cred_userpass_plaintext_new(out, username, password);
3399 return -1;