OSDN Git Service

Merge branch 'master' of git://repo.or.cz/TortoiseGit
[tortoisegit/TortoiseGitJp.git] / src / TortoiseProc / AppUtils.cpp
1 // TortoiseGit - a Windows shell extension for easy version control\r
2 \r
3 // Copyright (C) 2003-2008 - TortoiseGit\r
4 \r
5 // This program is free software; you can redistribute it and/or\r
6 // modify it under the terms of the GNU General Public License\r
7 // as published by the Free Software Foundation; either version 2\r
8 // of the License, or (at your option) any later version.\r
9 \r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 \r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, write to the Free Software Foundation,\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
18 //\r
19 #include "StdAfx.h"\r
20 #include "resource.h"\r
21 #include "TortoiseProc.h"\r
22 #include "PathUtils.h"\r
23 #include "AppUtils.h"\r
24 //#include "GitProperties.h"\r
25 #include "StringUtils.h"\r
26 #include "MessageBox.h"\r
27 #include "Registry.h"\r
28 #include "TGitPath.h"\r
29 #include "Git.h"\r
30 //#include "RepositoryBrowser.h"\r
31 //#include "BrowseFolder.h"\r
32 #include "UnicodeUtils.h"\r
33 #include "ExportDlg.h"\r
34 #include "ProgressDlg.h"\r
35 #include "GitAdminDir.h"\r
36 #include "ProgressDlg.h"\r
37 #include "BrowseFolder.h"\r
38 #include "DirFileEnum.h"\r
39 #include "MessageBox.h"\r
40 #include "GitStatus.h"\r
41 #include "CreateBranchTagDlg.h"\r
42 #include "GitSwitchDlg.h"\r
43 #include "ResetDlg.h"\r
44 #include "DeleteConflictDlg.h"\r
45 \r
46 CAppUtils::CAppUtils(void)\r
47 {\r
48 }\r
49 \r
50 CAppUtils::~CAppUtils(void)\r
51 {\r
52 }\r
53 \r
54 bool CAppUtils::GetMimeType(const CTGitPath& file, CString& mimetype)\r
55 {\r
56 #if 0\r
57         GitProperties props(file, GitRev::REV_WC, false);\r
58         for (int i = 0; i < props.GetCount(); ++i)\r
59         {\r
60                 if (props.GetItemName(i).compare(_T("svn:mime-type"))==0)\r
61                 {\r
62                         mimetype = props.GetItemValue(i).c_str();\r
63                         return true;\r
64                 }\r
65         }\r
66 #endif\r
67         return false;\r
68 }\r
69 \r
70 BOOL CAppUtils::StartExtMerge(\r
71         const CTGitPath& basefile, const CTGitPath& theirfile, const CTGitPath& yourfile, const CTGitPath& mergedfile,\r
72         const CString& basename, const CString& theirname, const CString& yourname, const CString& mergedname, bool bReadOnly)\r
73 {\r
74 \r
75         CRegString regCom = CRegString(_T("Software\\TortoiseGit\\Merge"));\r
76         CString ext = mergedfile.GetFileExtension();\r
77         CString com = regCom;\r
78         bool bInternal = false;\r
79 \r
80         CString mimetype;\r
81         if (ext != "")\r
82         {\r
83                 // is there an extension specific merge tool?\r
84                 CRegString mergetool(_T("Software\\TortoiseGit\\MergeTools\\") + ext.MakeLower());\r
85                 if (CString(mergetool) != "")\r
86                 {\r
87                         com = mergetool;\r
88                 }\r
89         }\r
90         if (GetMimeType(yourfile, mimetype) || GetMimeType(theirfile, mimetype) || GetMimeType(basefile, mimetype))\r
91         {\r
92                 // is there a mime type specific merge tool?\r
93                 CRegString mergetool(_T("Software\\TortoiseGit\\MergeTools\\") + mimetype);\r
94                 if (CString(mergetool) != "")\r
95                 {\r
96                         com = mergetool;\r
97                 }\r
98         }\r
99         \r
100         if (com.IsEmpty()||(com.Left(1).Compare(_T("#"))==0))\r
101         {\r
102                 // use TortoiseMerge\r
103                 bInternal = true;\r
104                 CRegString tortoiseMergePath(_T("Software\\TortoiseGit\\TMergePath"), _T(""), false, HKEY_LOCAL_MACHINE);\r
105                 com = tortoiseMergePath;\r
106                 if (com.IsEmpty())\r
107                 {\r
108                         com = CPathUtils::GetAppDirectory();\r
109                         com += _T("TortoiseMerge.exe");\r
110                 }\r
111                 com = _T("\"") + com + _T("\"");\r
112                 com = com + _T(" /base:%base /theirs:%theirs /mine:%mine /merged:%merged");\r
113                 com = com + _T(" /basename:%bname /theirsname:%tname /minename:%yname /mergedname:%mname");\r
114         }\r
115         // check if the params are set. If not, just add the files to the command line\r
116         if ((com.Find(_T("%merged"))<0)&&(com.Find(_T("%base"))<0)&&(com.Find(_T("%theirs"))<0)&&(com.Find(_T("%mine"))<0))\r
117         {\r
118                 com += _T(" \"")+basefile.GetWinPathString()+_T("\"");\r
119                 com += _T(" \"")+theirfile.GetWinPathString()+_T("\"");\r
120                 com += _T(" \"")+yourfile.GetWinPathString()+_T("\"");\r
121                 com += _T(" \"")+mergedfile.GetWinPathString()+_T("\"");\r
122         }\r
123         if (basefile.IsEmpty())\r
124         {\r
125                 com.Replace(_T("/base:%base"), _T(""));\r
126                 com.Replace(_T("%base"), _T(""));               \r
127         }\r
128         else\r
129                 com.Replace(_T("%base"), _T("\"") + basefile.GetWinPathString() + _T("\""));\r
130         if (theirfile.IsEmpty())\r
131         {\r
132                 com.Replace(_T("/theirs:%theirs"), _T(""));\r
133                 com.Replace(_T("%theirs"), _T(""));\r
134         }\r
135         else\r
136                 com.Replace(_T("%theirs"), _T("\"") + theirfile.GetWinPathString() + _T("\""));\r
137         if (yourfile.IsEmpty())\r
138         {\r
139                 com.Replace(_T("/mine:%mine"), _T(""));\r
140                 com.Replace(_T("%mine"), _T(""));\r
141         }\r
142         else\r
143                 com.Replace(_T("%mine"), _T("\"") + yourfile.GetWinPathString() + _T("\""));\r
144         if (mergedfile.IsEmpty())\r
145         {\r
146                 com.Replace(_T("/merged:%merged"), _T(""));\r
147                 com.Replace(_T("%merged"), _T(""));\r
148         }\r
149         else\r
150                 com.Replace(_T("%merged"), _T("\"") + mergedfile.GetWinPathString() + _T("\""));\r
151         if (basename.IsEmpty())\r
152         {\r
153                 if (basefile.IsEmpty())\r
154                 {\r
155                         com.Replace(_T("/basename:%bname"), _T(""));\r
156                         com.Replace(_T("%bname"), _T(""));\r
157                 }\r
158                 else\r
159                 {\r
160                         com.Replace(_T("%bname"), _T("\"") + basefile.GetUIFileOrDirectoryName() + _T("\""));\r
161                 }\r
162         }\r
163         else\r
164                 com.Replace(_T("%bname"), _T("\"") + basename + _T("\""));\r
165         if (theirname.IsEmpty())\r
166         {\r
167                 if (theirfile.IsEmpty())\r
168                 {\r
169                         com.Replace(_T("/theirsname:%tname"), _T(""));\r
170                         com.Replace(_T("%tname"), _T(""));\r
171                 }\r
172                 else\r
173                 {\r
174                         com.Replace(_T("%tname"), _T("\"") + theirfile.GetUIFileOrDirectoryName() + _T("\""));\r
175                 }\r
176         }\r
177         else\r
178                 com.Replace(_T("%tname"), _T("\"") + theirname + _T("\""));\r
179         if (yourname.IsEmpty())\r
180         {\r
181                 if (yourfile.IsEmpty())\r
182                 {\r
183                         com.Replace(_T("/minename:%yname"), _T(""));\r
184                         com.Replace(_T("%yname"), _T(""));\r
185                 }\r
186                 else\r
187                 {\r
188                         com.Replace(_T("%yname"), _T("\"") + yourfile.GetUIFileOrDirectoryName() + _T("\""));\r
189                 }\r
190         }\r
191         else\r
192                 com.Replace(_T("%yname"), _T("\"") + yourname + _T("\""));\r
193         if (mergedname.IsEmpty())\r
194         {\r
195                 if (mergedfile.IsEmpty())\r
196                 {\r
197                         com.Replace(_T("/mergedname:%mname"), _T(""));\r
198                         com.Replace(_T("%mname"), _T(""));\r
199                 }\r
200                 else\r
201                 {\r
202                         com.Replace(_T("%mname"), _T("\"") + mergedfile.GetUIFileOrDirectoryName() + _T("\""));\r
203                 }\r
204         }\r
205         else\r
206                 com.Replace(_T("%mname"), _T("\"") + mergedname + _T("\""));\r
207 \r
208         if ((bReadOnly)&&(bInternal))\r
209                 com += _T(" /readonly");\r
210 \r
211         if(!LaunchApplication(com, IDS_ERR_EXTMERGESTART, false))\r
212         {\r
213                 return FALSE;\r
214         }\r
215 \r
216         return TRUE;\r
217 }\r
218 \r
219 BOOL CAppUtils::StartExtPatch(const CTGitPath& patchfile, const CTGitPath& dir, const CString& sOriginalDescription, const CString& sPatchedDescription, BOOL bReversed, BOOL bWait)\r
220 {\r
221         CString viewer;\r
222         // use TortoiseMerge\r
223         viewer = CPathUtils::GetAppDirectory();\r
224         viewer += _T("TortoiseMerge.exe");\r
225 \r
226         viewer = _T("\"") + viewer + _T("\"");\r
227         viewer = viewer + _T(" /diff:\"") + patchfile.GetWinPathString() + _T("\"");\r
228         viewer = viewer + _T(" /patchpath:\"") + dir.GetWinPathString() + _T("\"");\r
229         if (bReversed)\r
230                 viewer += _T(" /reversedpatch");\r
231         if (!sOriginalDescription.IsEmpty())\r
232                 viewer = viewer + _T(" /patchoriginal:\"") + sOriginalDescription + _T("\"");\r
233         if (!sPatchedDescription.IsEmpty())\r
234                 viewer = viewer + _T(" /patchpatched:\"") + sPatchedDescription + _T("\"");\r
235         if(!LaunchApplication(viewer, IDS_ERR_DIFFVIEWSTART, !!bWait))\r
236         {\r
237                 return FALSE;\r
238         }\r
239         return TRUE;\r
240 }\r
241 \r
242 CString CAppUtils::PickDiffTool(const CTGitPath& file1, const CTGitPath& file2)\r
243 {\r
244         // Is there a mime type specific diff tool?\r
245         CString mimetype;\r
246         if (GetMimeType(file1, mimetype) ||  GetMimeType(file2, mimetype))\r
247         {\r
248                 CString difftool = CRegString(_T("Software\\TortoiseGit\\DiffTools\\") + mimetype);\r
249                 if (!difftool.IsEmpty())\r
250                         return difftool;\r
251         }\r
252         \r
253         // Is there an extension specific diff tool?\r
254         CString ext = file2.GetFileExtension().MakeLower();\r
255         if (!ext.IsEmpty())\r
256         {\r
257                 CString difftool = CRegString(_T("Software\\TortoiseGit\\DiffTools\\") + ext);\r
258                 if (!difftool.IsEmpty())\r
259                         return difftool;\r
260                 // Maybe we should use TortoiseIDiff?\r
261                 if ((ext == _T(".jpg")) || (ext == _T(".jpeg")) ||\r
262                         (ext == _T(".bmp")) || (ext == _T(".gif"))  ||\r
263                         (ext == _T(".png")) || (ext == _T(".ico"))  ||\r
264                         (ext == _T(".dib")) || (ext == _T(".emf")))\r
265                 {\r
266                         return\r
267                                 _T("\"") + CPathUtils::GetAppDirectory() + _T("TortoiseIDiff.exe") + _T("\"") +\r
268                                 _T(" /left:%base /right:%mine /lefttitle:%bname /righttitle:%yname");\r
269                 }\r
270         }\r
271         \r
272         // Finally, pick a generic external diff tool\r
273         CString difftool = CRegString(_T("Software\\TortoiseGit\\Diff"));\r
274         return difftool;\r
275 }\r
276 \r
277 bool CAppUtils::StartExtDiff(\r
278         const CString& file1,  const CString& file2,\r
279         const CString& sName1, const CString& sName2, \r
280         const DiffFlags& flags)\r
281 {\r
282         CString viewer;\r
283 \r
284         CRegDWORD blamediff(_T("Software\\TortoiseGit\\DiffBlamesWithTortoiseMerge"), FALSE);\r
285         if (!flags.bBlame || !(DWORD)blamediff)\r
286         {\r
287                 viewer = PickDiffTool(file1, file2);\r
288                 // If registry entry for a diff program is commented out, use TortoiseMerge.\r
289                 bool bCommentedOut = viewer.Left(1) == _T("#");\r
290                 if (flags.bAlternativeTool)\r
291                 {\r
292                         // Invert external vs. internal diff tool selection.\r
293                         if (bCommentedOut)\r
294                                 viewer.Delete(0); // uncomment\r
295                         else\r
296                                 viewer = "";\r
297                 }\r
298                 else if (bCommentedOut)\r
299                         viewer = "";\r
300         }\r
301 \r
302         bool bInternal = viewer.IsEmpty();\r
303         if (bInternal)\r
304         {\r
305                 viewer =\r
306                         _T("\"") + CPathUtils::GetAppDirectory() + _T("TortoiseMerge.exe") + _T("\"") +\r
307                         _T(" /base:%base /mine:%mine /basename:%bname /minename:%yname");\r
308                 if (flags.bBlame)\r
309                         viewer += _T(" /blame");\r
310         }\r
311         // check if the params are set. If not, just add the files to the command line\r
312         if ((viewer.Find(_T("%base"))<0)&&(viewer.Find(_T("%mine"))<0))\r
313         {\r
314                 viewer += _T(" \"")+file1+_T("\"");\r
315                 viewer += _T(" \"")+file2+_T("\"");\r
316         }\r
317         if (viewer.Find(_T("%base")) >= 0)\r
318         {\r
319                 viewer.Replace(_T("%base"),  _T("\"")+file1+_T("\""));\r
320         }\r
321         if (viewer.Find(_T("%mine")) >= 0)\r
322         {\r
323                 viewer.Replace(_T("%mine"),  _T("\"")+file2+_T("\""));\r
324         }\r
325 \r
326         if (sName1.IsEmpty())\r
327                 viewer.Replace(_T("%bname"), _T("\"") + file1 + _T("\""));\r
328         else\r
329                 viewer.Replace(_T("%bname"), _T("\"") + sName1 + _T("\""));\r
330 \r
331         if (sName2.IsEmpty())\r
332                 viewer.Replace(_T("%yname"), _T("\"") + file2 + _T("\""));\r
333         else\r
334                 viewer.Replace(_T("%yname"), _T("\"") + sName2 + _T("\""));\r
335 \r
336         if (flags.bReadOnly && bInternal)\r
337                 viewer += _T(" /readonly");\r
338 \r
339         return LaunchApplication(viewer, IDS_ERR_EXTDIFFSTART, flags.bWait);\r
340 }\r
341 \r
342 BOOL CAppUtils::StartExtDiffProps(const CTGitPath& file1, const CTGitPath& file2, const CString& sName1, const CString& sName2, BOOL bWait, BOOL bReadOnly)\r
343 {\r
344         CRegString diffpropsexe(_T("Software\\TortoiseGit\\DiffProps"));\r
345         CString viewer = diffpropsexe;\r
346         bool bInternal = false;\r
347         if (viewer.IsEmpty()||(viewer.Left(1).Compare(_T("#"))==0))\r
348         {\r
349                 //no registry entry (or commented out) for a diff program\r
350                 //use TortoiseMerge\r
351                 bInternal = true;\r
352                 viewer = CPathUtils::GetAppDirectory();\r
353                 viewer += _T("TortoiseMerge.exe");\r
354                 viewer = _T("\"") + viewer + _T("\"");\r
355                 viewer = viewer + _T(" /base:%base /mine:%mine /basename:%bname /minename:%yname");\r
356         }\r
357         // check if the params are set. If not, just add the files to the command line\r
358         if ((viewer.Find(_T("%base"))<0)&&(viewer.Find(_T("%mine"))<0))\r
359         {\r
360                 viewer += _T(" \"")+file1.GetWinPathString()+_T("\"");\r
361                 viewer += _T(" \"")+file2.GetWinPathString()+_T("\"");\r
362         }\r
363         if (viewer.Find(_T("%base")) >= 0)\r
364         {\r
365                 viewer.Replace(_T("%base"),  _T("\"")+file1.GetWinPathString()+_T("\""));\r
366         }\r
367         if (viewer.Find(_T("%mine")) >= 0)\r
368         {\r
369                 viewer.Replace(_T("%mine"),  _T("\"")+file2.GetWinPathString()+_T("\""));\r
370         }\r
371 \r
372         if (sName1.IsEmpty())\r
373                 viewer.Replace(_T("%bname"), _T("\"") + file1.GetUIFileOrDirectoryName() + _T("\""));\r
374         else\r
375                 viewer.Replace(_T("%bname"), _T("\"") + sName1 + _T("\""));\r
376 \r
377         if (sName2.IsEmpty())\r
378                 viewer.Replace(_T("%yname"), _T("\"") + file2.GetUIFileOrDirectoryName() + _T("\""));\r
379         else\r
380                 viewer.Replace(_T("%yname"), _T("\"") + sName2 + _T("\""));\r
381 \r
382         if ((bReadOnly)&&(bInternal))\r
383                 viewer += _T(" /readonly");\r
384 \r
385         if(!LaunchApplication(viewer, IDS_ERR_EXTDIFFSTART, !!bWait))\r
386         {\r
387                 return FALSE;\r
388         }\r
389         return TRUE;\r
390 }\r
391 \r
392 BOOL CAppUtils::StartUnifiedDiffViewer(const CString& patchfile, const CString& title, BOOL bWait)\r
393 {\r
394         CString viewer;\r
395         CRegString v = CRegString(_T("Software\\TortoiseGit\\DiffViewer"));\r
396         viewer = v;\r
397         if (viewer.IsEmpty() || (viewer.Left(1).Compare(_T("#"))==0))\r
398         {\r
399                 // use TortoiseUDiff\r
400                 viewer = CPathUtils::GetAppDirectory();\r
401                 viewer += _T("TortoiseUDiff.exe");\r
402                 // enquote the path to TortoiseUDiff\r
403                 viewer = _T("\"") + viewer + _T("\"");\r
404                 // add the params\r
405                 viewer = viewer + _T(" /patchfile:%1 /title:\"%title\"");\r
406 \r
407         }\r
408         if (viewer.Find(_T("%1"))>=0)\r
409         {\r
410                 if (viewer.Find(_T("\"%1\"")) >= 0)\r
411                         viewer.Replace(_T("%1"), patchfile);\r
412                 else\r
413                         viewer.Replace(_T("%1"), _T("\"") + patchfile + _T("\""));\r
414         }\r
415         else\r
416                 viewer += _T(" \"") + patchfile + _T("\"");\r
417         if (viewer.Find(_T("%title")) >= 0)\r
418         {\r
419                 viewer.Replace(_T("%title"), title);\r
420         }\r
421 \r
422         if(!LaunchApplication(viewer, IDS_ERR_DIFFVIEWSTART, !!bWait))\r
423         {\r
424                 return FALSE;\r
425         }\r
426         return TRUE;\r
427 }\r
428 \r
429 BOOL CAppUtils::StartTextViewer(CString file)\r
430 {\r
431         CString viewer;\r
432         CRegString txt = CRegString(_T(".txt\\"), _T(""), FALSE, HKEY_CLASSES_ROOT);\r
433         viewer = txt;\r
434         viewer = viewer + _T("\\Shell\\Open\\Command\\");\r
435         CRegString txtexe = CRegString(viewer, _T(""), FALSE, HKEY_CLASSES_ROOT);\r
436         viewer = txtexe;\r
437 \r
438         DWORD len = ExpandEnvironmentStrings(viewer, NULL, 0);\r
439         TCHAR * buf = new TCHAR[len+1];\r
440         ExpandEnvironmentStrings(viewer, buf, len);\r
441         viewer = buf;\r
442         delete [] buf;\r
443         len = ExpandEnvironmentStrings(file, NULL, 0);\r
444         buf = new TCHAR[len+1];\r
445         ExpandEnvironmentStrings(file, buf, len);\r
446         file = buf;\r
447         delete [] buf;\r
448         file = _T("\"")+file+_T("\"");\r
449         if (viewer.IsEmpty())\r
450         {\r
451                 OPENFILENAME ofn = {0};                         // common dialog box structure\r
452                 TCHAR szFile[MAX_PATH] = {0};           // buffer for file name. Explorer can't handle paths longer than MAX_PATH.\r
453                 // Initialize OPENFILENAME\r
454                 ofn.lStructSize = sizeof(OPENFILENAME);\r
455                 ofn.hwndOwner = NULL;\r
456                 ofn.lpstrFile = szFile;\r
457                 ofn.nMaxFile = sizeof(szFile)/sizeof(TCHAR);\r
458                 CString sFilter;\r
459                 sFilter.LoadString(IDS_PROGRAMSFILEFILTER);\r
460                 TCHAR * pszFilters = new TCHAR[sFilter.GetLength()+4];\r
461                 _tcscpy_s (pszFilters, sFilter.GetLength()+4, sFilter);\r
462                 // Replace '|' delimiters with '\0's\r
463                 TCHAR *ptr = pszFilters + _tcslen(pszFilters);  //set ptr at the NULL\r
464                 while (ptr != pszFilters)\r
465                 {\r
466                         if (*ptr == '|')\r
467                                 *ptr = '\0';\r
468                         ptr--;\r
469                 }\r
470                 ofn.lpstrFilter = pszFilters;\r
471                 ofn.nFilterIndex = 1;\r
472                 ofn.lpstrFileTitle = NULL;\r
473                 ofn.nMaxFileTitle = 0;\r
474                 ofn.lpstrInitialDir = NULL;\r
475                 CString temp;\r
476                 temp.LoadString(IDS_UTILS_SELECTTEXTVIEWER);\r
477                 CStringUtils::RemoveAccelerators(temp);\r
478                 ofn.lpstrTitle = temp;\r
479                 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;\r
480 \r
481                 // Display the Open dialog box. \r
482 \r
483                 if (GetOpenFileName(&ofn)==TRUE)\r
484                 {\r
485                         delete [] pszFilters;\r
486                         viewer = CString(ofn.lpstrFile);\r
487                 }\r
488                 else\r
489                 {\r
490                         delete [] pszFilters;\r
491                         return FALSE;\r
492                 }\r
493         }\r
494         if (viewer.Find(_T("\"%1\"")) >= 0)\r
495         {\r
496                 viewer.Replace(_T("\"%1\""), file);\r
497         }\r
498         else if (viewer.Find(_T("%1")) >= 0)\r
499         {\r
500                 viewer.Replace(_T("%1"),  file);\r
501         }\r
502         else\r
503         {\r
504                 viewer += _T(" ");\r
505                 viewer += file;\r
506         }\r
507 \r
508         if(!LaunchApplication(viewer, IDS_ERR_TEXTVIEWSTART, false))\r
509         {\r
510                 return FALSE;\r
511         }\r
512         return TRUE;\r
513 }\r
514 \r
515 BOOL CAppUtils::CheckForEmptyDiff(const CTGitPath& sDiffPath)\r
516 {\r
517         DWORD length = 0;\r
518         HANDLE hFile = ::CreateFile(sDiffPath.GetWinPath(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);\r
519         if (hFile == INVALID_HANDLE_VALUE)\r
520                 return TRUE;\r
521         length = ::GetFileSize(hFile, NULL);\r
522         ::CloseHandle(hFile);\r
523         if (length < 4)\r
524                 return TRUE;\r
525         return FALSE;\r
526 \r
527 }\r
528 \r
529 void CAppUtils::CreateFontForLogs(CFont& fontToCreate)\r
530 {\r
531         LOGFONT logFont;\r
532         HDC hScreenDC = ::GetDC(NULL);\r
533         logFont.lfHeight         = -MulDiv((DWORD)CRegDWORD(_T("Software\\TortoiseGit\\LogFontSize"), 8), GetDeviceCaps(hScreenDC, LOGPIXELSY), 72);\r
534         ::ReleaseDC(NULL, hScreenDC);\r
535         logFont.lfWidth          = 0;\r
536         logFont.lfEscapement     = 0;\r
537         logFont.lfOrientation    = 0;\r
538         logFont.lfWeight         = FW_NORMAL;\r
539         logFont.lfItalic         = 0;\r
540         logFont.lfUnderline      = 0;\r
541         logFont.lfStrikeOut      = 0;\r
542         logFont.lfCharSet        = DEFAULT_CHARSET;\r
543         logFont.lfOutPrecision   = OUT_DEFAULT_PRECIS;\r
544         logFont.lfClipPrecision  = CLIP_DEFAULT_PRECIS;\r
545         logFont.lfQuality        = DRAFT_QUALITY;\r
546         logFont.lfPitchAndFamily = FF_DONTCARE | FIXED_PITCH;\r
547         _tcscpy_s(logFont.lfFaceName, 32, (LPCTSTR)(CString)CRegString(_T("Software\\TortoiseGit\\LogFontName"), _T("Courier New")));\r
548         VERIFY(fontToCreate.CreateFontIndirect(&logFont));\r
549 }\r
550 \r
551 bool CAppUtils::LaunchApplication(const CString& sCommandLine, UINT idErrMessageFormat, bool bWaitForStartup)\r
552 {\r
553         STARTUPINFO startup;\r
554         PROCESS_INFORMATION process;\r
555         memset(&startup, 0, sizeof(startup));\r
556         startup.cb = sizeof(startup);\r
557         memset(&process, 0, sizeof(process));\r
558 \r
559         CString cleanCommandLine(sCommandLine);\r
560 \r
561         if (CreateProcess(NULL, const_cast<TCHAR*>((LPCTSTR)cleanCommandLine), NULL, NULL, FALSE, 0, 0, sOrigCWD, &startup, &process)==0)\r
562         {\r
563                 if(idErrMessageFormat != 0)\r
564                 {\r
565                         LPVOID lpMsgBuf;\r
566                         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | \r
567                                 FORMAT_MESSAGE_FROM_SYSTEM | \r
568                                 FORMAT_MESSAGE_IGNORE_INSERTS,\r
569                                 NULL,\r
570                                 GetLastError(),\r
571                                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language\r
572                                 (LPTSTR) &lpMsgBuf,\r
573                                 0,\r
574                                 NULL \r
575                                 );\r
576                         CString temp;\r
577                         temp.Format(idErrMessageFormat, lpMsgBuf);\r
578                         CMessageBox::Show(NULL, temp, _T("TortoiseGit"), MB_OK | MB_ICONINFORMATION);\r
579                         LocalFree( lpMsgBuf );\r
580                 }\r
581                 return false;\r
582         }\r
583 \r
584         if (bWaitForStartup)\r
585         {\r
586                 WaitForInputIdle(process.hProcess, 10000);\r
587         }\r
588 \r
589         CloseHandle(process.hThread);\r
590         CloseHandle(process.hProcess);\r
591         return true;\r
592 }\r
593 \r
594 /**\r
595 * Launch the external blame viewer\r
596 */\r
597 bool CAppUtils::LaunchTortoiseBlame(const CString& sBlameFile,CString Rev,const CString& sParams)\r
598 {\r
599         CString viewer = CPathUtils::GetAppDirectory();\r
600         viewer += _T("TortoiseGitBlame.exe");\r
601         viewer += _T(" \"") + sBlameFile + _T("\"");\r
602         //viewer += _T(" \"") + sLogFile + _T("\"");\r
603         //viewer += _T(" \"") + sOriginalFile + _T("\"");\r
604         if(!Rev.IsEmpty())\r
605                 viewer += CString(_T(" /rev:"))+Rev;\r
606         viewer += _T(" ")+sParams;\r
607         \r
608         return LaunchApplication(viewer, IDS_ERR_EXTDIFFSTART, false);\r
609 }\r
610 \r
611 bool CAppUtils::FormatTextInRichEditControl(CWnd * pWnd)\r
612 {\r
613         CString sText;\r
614         if (pWnd == NULL)\r
615                 return false;\r
616         bool bStyled = false;\r
617         pWnd->GetWindowText(sText);\r
618         // the rich edit control doesn't count the CR char!\r
619         // to be exact: CRLF is treated as one char.\r
620         sText.Replace(_T("\r"), _T(""));\r
621 \r
622         // style each line separately\r
623         int offset = 0;\r
624         int nNewlinePos;\r
625         do \r
626         {\r
627                 nNewlinePos = sText.Find('\n', offset);\r
628                 CString sLine = sText.Mid(offset);\r
629                 if (nNewlinePos>=0)\r
630                         sLine = sLine.Left(nNewlinePos-offset);\r
631                 int start = 0;\r
632                 int end = 0;\r
633                 while (FindStyleChars(sLine, '*', start, end))\r
634                 {\r
635                         CHARRANGE range = {(LONG)start+offset, (LONG)end+offset};\r
636                         pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);\r
637                         CHARFORMAT2 format;\r
638                         SecureZeroMemory(&format, sizeof(CHARFORMAT2));\r
639                         format.cbSize = sizeof(CHARFORMAT2);\r
640                         format.dwMask = CFM_BOLD;\r
641                         format.dwEffects = CFE_BOLD;\r
642                         pWnd->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);\r
643                         bStyled = true;\r
644                         start = end;\r
645                 }\r
646                 start = 0;\r
647                 end = 0;\r
648                 while (FindStyleChars(sLine, '^', start, end))\r
649                 {\r
650                         CHARRANGE range = {(LONG)start+offset, (LONG)end+offset};\r
651                         pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);\r
652                         CHARFORMAT2 format;\r
653                         SecureZeroMemory(&format, sizeof(CHARFORMAT2));\r
654                         format.cbSize = sizeof(CHARFORMAT2);\r
655                         format.dwMask = CFM_ITALIC;\r
656                         format.dwEffects = CFE_ITALIC;\r
657                         pWnd->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);\r
658                         bStyled = true;\r
659                         start = end;\r
660                 }\r
661                 start = 0;\r
662                 end = 0;\r
663                 while (FindStyleChars(sLine, '_', start, end))\r
664                 {\r
665                         CHARRANGE range = {(LONG)start+offset, (LONG)end+offset};\r
666                         pWnd->SendMessage(EM_EXSETSEL, NULL, (LPARAM)&range);\r
667                         CHARFORMAT2 format;\r
668                         SecureZeroMemory(&format, sizeof(CHARFORMAT2));\r
669                         format.cbSize = sizeof(CHARFORMAT2);\r
670                         format.dwMask = CFM_UNDERLINE;\r
671                         format.dwEffects = CFE_UNDERLINE;\r
672                         pWnd->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);\r
673                         bStyled = true;\r
674                         start = end;\r
675                 }\r
676                 offset = nNewlinePos+1;\r
677         } while(nNewlinePos>=0);\r
678         return bStyled; \r
679 }\r
680 \r
681 bool CAppUtils::FindStyleChars(const CString& sText, TCHAR stylechar, int& start, int& end)\r
682 {\r
683         int i=start;\r
684         bool bFoundMarker = false;\r
685         // find a starting marker\r
686         while (sText[i] != 0)\r
687         {\r
688                 if (sText[i] == stylechar)\r
689                 {\r
690                         if (((i+1)<sText.GetLength())&&(IsCharAlphaNumeric(sText[i+1])) &&\r
691                                 (((i>0)&&(!IsCharAlphaNumeric(sText[i-1])))||(i==0)))\r
692                         {\r
693                                 start = i+1;\r
694                                 i++;\r
695                                 bFoundMarker = true;\r
696                                 break;\r
697                         }\r
698                 }\r
699                 i++;\r
700         }\r
701         if (!bFoundMarker)\r
702                 return false;\r
703         // find ending marker\r
704         bFoundMarker = false;\r
705         while (sText[i] != 0)\r
706         {\r
707                 if (sText[i] == stylechar)\r
708                 {\r
709                         if ((IsCharAlphaNumeric(sText[i-1])) &&\r
710                                 ((((i+1)<sText.GetLength())&&(!IsCharAlphaNumeric(sText[i+1])))||(i+1)==sText.GetLength()))\r
711                         {\r
712                                 end = i;\r
713                                 i++;\r
714                                 bFoundMarker = true;\r
715                                 break;\r
716                         }\r
717                 }\r
718                 i++;\r
719         }\r
720         return bFoundMarker;\r
721 }\r
722 \r
723 bool CAppUtils::BrowseRepository(CHistoryCombo& combo, CWnd * pParent, GitRev& rev)\r
724 {\r
725 #if 0\r
726         CString strUrl;\r
727         combo.GetWindowText(strUrl);\r
728         strUrl.Replace('\\', '/');\r
729         strUrl.Replace(_T("%"), _T("%25"));\r
730         strUrl = CUnicodeUtils::GetUnicode(CPathUtils::PathEscape(CUnicodeUtils::GetUTF8(strUrl)));\r
731         if (strUrl.Left(7) == _T("file://"))\r
732         {\r
733                 CString strFile(strUrl);\r
734                 Git::UrlToPath(strFile);\r
735 \r
736                 Git svn;\r
737                 if (svn.IsRepository(CTGitPath(strFile)))\r
738                 {\r
739                         // browse repository - show repository browser\r
740                         Git::preparePath(strUrl);\r
741                         CRepositoryBrowser browser(strUrl, rev, pParent);\r
742                         if (browser.DoModal() == IDOK)\r
743                         {\r
744                                 combo.SetCurSel(-1);\r
745                                 combo.SetWindowText(browser.GetPath());\r
746                                 rev = browser.GetRevision();\r
747                                 return true;\r
748                         }\r
749                 }\r
750                 else\r
751                 {\r
752                         // browse local directories\r
753                         CBrowseFolder folderBrowser;\r
754                         folderBrowser.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;\r
755                         // remove the 'file:///' so the shell can recognize the local path\r
756                         Git::UrlToPath(strUrl);\r
757                         if (folderBrowser.Show(pParent->GetSafeHwnd(), strUrl) == CBrowseFolder::OK)\r
758                         {\r
759                                 Git::PathToUrl(strUrl);\r
760 \r
761                                 combo.SetCurSel(-1);\r
762                                 combo.SetWindowText(strUrl);\r
763                                 return true;\r
764                         }\r
765                 }\r
766         }\r
767         else if ((strUrl.Left(7) == _T("http://")\r
768                 ||(strUrl.Left(8) == _T("https://"))\r
769                 ||(strUrl.Left(6) == _T("svn://"))\r
770                 ||(strUrl.Left(4) == _T("svn+"))) && strUrl.GetLength() > 6)\r
771         {\r
772                 // browse repository - show repository browser\r
773                 CRepositoryBrowser browser(strUrl, rev, pParent);\r
774                 if (browser.DoModal() == IDOK)\r
775                 {\r
776                         combo.SetCurSel(-1);\r
777                         combo.SetWindowText(browser.GetPath());\r
778                         rev = browser.GetRevision();\r
779                         return true;\r
780                 }\r
781         }\r
782         else\r
783         {\r
784                 // browse local directories\r
785                 CBrowseFolder folderBrowser;\r
786                 folderBrowser.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;\r
787                 if (folderBrowser.Show(pParent->GetSafeHwnd(), strUrl) == CBrowseFolder::OK)\r
788                 {\r
789                         Git::PathToUrl(strUrl);\r
790 \r
791                         combo.SetCurSel(-1);\r
792                         combo.SetWindowText(strUrl);\r
793                         return true;\r
794                 }\r
795         }\r
796 #endif\r
797         return false;\r
798 }\r
799 \r
800 bool CAppUtils::FileOpenSave(CString& path, int * filterindex, UINT title, UINT filter, bool bOpen, HWND hwndOwner)\r
801 {\r
802         OPENFILENAME ofn = {0};                         // common dialog box structure\r
803         TCHAR szFile[MAX_PATH] = {0};           // buffer for file name. Explorer can't handle paths longer than MAX_PATH.\r
804         ofn.lStructSize = sizeof(OPENFILENAME);\r
805         ofn.hwndOwner = hwndOwner;\r
806         _tcscpy_s(szFile, MAX_PATH, (LPCTSTR)path);\r
807         ofn.lpstrFile = szFile;\r
808         ofn.nMaxFile = sizeof(szFile)/sizeof(TCHAR);\r
809         CString sFilter;\r
810         TCHAR * pszFilters = NULL;\r
811         if (filter)\r
812         {\r
813                 sFilter.LoadString(filter);\r
814                 pszFilters = new TCHAR[sFilter.GetLength()+4];\r
815                 _tcscpy_s (pszFilters, sFilter.GetLength()+4, sFilter);\r
816                 // Replace '|' delimiters with '\0's\r
817                 TCHAR *ptr = pszFilters + _tcslen(pszFilters);  //set ptr at the NULL\r
818                 while (ptr != pszFilters)\r
819                 {\r
820                         if (*ptr == '|')\r
821                                 *ptr = '\0';\r
822                         ptr--;\r
823                 }\r
824                 ofn.lpstrFilter = pszFilters;\r
825         }\r
826         ofn.nFilterIndex = 1;\r
827         ofn.lpstrFileTitle = NULL;\r
828         ofn.nMaxFileTitle = 0;\r
829         ofn.lpstrInitialDir = NULL;\r
830         CString temp;\r
831         if (title)\r
832         {\r
833                 temp.LoadString(title);\r
834                 CStringUtils::RemoveAccelerators(temp);\r
835         }\r
836         ofn.lpstrTitle = temp;\r
837         if (bOpen)\r
838                 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_EXPLORER;\r
839         else\r
840                 ofn.Flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER;\r
841 \r
842 \r
843         // Display the Open dialog box. \r
844         bool bRet = false;\r
845         if (bOpen)\r
846         {\r
847                 bRet = !!GetOpenFileName(&ofn);\r
848         }\r
849         else\r
850         {\r
851                 bRet = !!GetSaveFileName(&ofn);\r
852         }\r
853         if (bRet)\r
854         {\r
855                 if (pszFilters)\r
856                         delete [] pszFilters;\r
857                 path = CString(ofn.lpstrFile);\r
858                 if (filterindex)\r
859                         *filterindex = ofn.nFilterIndex;\r
860                 return true;\r
861         }\r
862         if (pszFilters)\r
863                 delete [] pszFilters;\r
864         return false;\r
865 }\r
866 \r
867 bool CAppUtils::SetListCtrlBackgroundImage(HWND hListCtrl, UINT nID, int width /* = 128 */, int height /* = 128 */)\r
868 {\r
869         ListView_SetTextBkColor(hListCtrl, CLR_NONE);\r
870         COLORREF bkColor = ListView_GetBkColor(hListCtrl);\r
871         // create a bitmap from the icon\r
872         HICON hIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(nID), IMAGE_ICON, width, height, LR_DEFAULTCOLOR);\r
873         if (!hIcon)\r
874                 return false;\r
875 \r
876         RECT rect = {0};\r
877         rect.right = width;\r
878         rect.bottom = height;\r
879         HBITMAP bmp = NULL;\r
880 \r
881         HWND desktop = ::GetDesktopWindow();\r
882         if (desktop)\r
883         {\r
884                 HDC screen_dev = ::GetDC(desktop);\r
885                 if (screen_dev)\r
886                 {\r
887                         // Create a compatible DC\r
888                         HDC dst_hdc = ::CreateCompatibleDC(screen_dev);\r
889                         if (dst_hdc)\r
890                         {\r
891                                 // Create a new bitmap of icon size\r
892                                 bmp = ::CreateCompatibleBitmap(screen_dev, rect.right, rect.bottom);\r
893                                 if (bmp)\r
894                                 {\r
895                                         // Select it into the compatible DC\r
896                                         HBITMAP old_dst_bmp = (HBITMAP)::SelectObject(dst_hdc, bmp);\r
897                                         // Fill the background of the compatible DC with the given color\r
898                                         ::SetBkColor(dst_hdc, bkColor);\r
899                                         ::ExtTextOut(dst_hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);\r
900 \r
901                                         // Draw the icon into the compatible DC\r
902                                         ::DrawIconEx(dst_hdc, 0, 0, hIcon, rect.right, rect.bottom, 0, NULL, DI_NORMAL);\r
903                                         ::SelectObject(dst_hdc, old_dst_bmp);\r
904                                 }\r
905                                 ::DeleteDC(dst_hdc);\r
906                         }\r
907                 }\r
908                 ::ReleaseDC(desktop, screen_dev); \r
909         }\r
910 \r
911         // Restore settings\r
912         DestroyIcon(hIcon);\r
913 \r
914         if (bmp == NULL)\r
915                 return false;\r
916 \r
917         LVBKIMAGE lv;\r
918         lv.ulFlags = LVBKIF_TYPE_WATERMARK;\r
919         lv.hbm = bmp;\r
920         lv.xOffsetPercent = 100;\r
921         lv.yOffsetPercent = 100;\r
922         ListView_SetBkImage(hListCtrl, &lv);\r
923         return true;\r
924 }\r
925 \r
926 CString CAppUtils::GetProjectNameFromURL(CString url)\r
927 {\r
928         CString name;\r
929         while (name.IsEmpty() || (name.CompareNoCase(_T("branches"))==0) ||\r
930                 (name.CompareNoCase(_T("tags"))==0) ||\r
931                 (name.CompareNoCase(_T("trunk"))==0))\r
932         {\r
933                 name = url.Mid(url.ReverseFind('/')+1);\r
934                 url = url.Left(url.ReverseFind('/'));\r
935         }\r
936         if ((name.Compare(_T("svn")) == 0)||(name.Compare(_T("svnroot")) == 0))\r
937         {\r
938                 // a name of svn or svnroot indicates that it's not really the project name. In that\r
939                 // case, we try the first part of the URL\r
940                 // of course, this won't work in all cases (but it works for Google project hosting)\r
941                 url.Replace(_T("http://"), _T(""));\r
942                 url.Replace(_T("https://"), _T(""));\r
943                 url.Replace(_T("svn://"), _T(""));\r
944                 url.Replace(_T("svn+ssh://"), _T(""));\r
945                 url.TrimLeft(_T("/"));\r
946                 name = url.Left(url.Find('.'));\r
947         }\r
948         return name;\r
949 }\r
950 \r
951 bool CAppUtils::StartShowUnifiedDiff(HWND hWnd, const CTGitPath& url1, const git_revnum_t& rev1, \r
952                                                                                                 const CTGitPath& url2, const git_revnum_t& rev2, \r
953                                                                          //const GitRev& peg /* = GitRev */, const GitRev& headpeg /* = GitRev */,  \r
954                                                                                                 bool bAlternateDiff /* = false */, bool bIgnoreAncestry /* = false */, bool /* blame = false */)\r
955 {\r
956 \r
957         CString tempfile=GetTempFile();\r
958         CString cmd;\r
959         if(rev1 == GitRev::GetWorkingCopy())\r
960         {\r
961                 cmd.Format(_T("git.exe diff --stat -p %s"),rev2);\r
962         }else\r
963         {       \r
964                 cmd.Format(_T("git.exe diff-tree -r -p --stat %s %s"),rev1,rev2);\r
965         }\r
966         g_Git.RunLogFile(cmd,tempfile);\r
967         CAppUtils::StartUnifiedDiffViewer(tempfile,rev1.Left(6)+_T(":")+rev2.Left(6));\r
968 \r
969 \r
970 #if 0\r
971         CString sCmd;\r
972         sCmd.Format(_T("%s /command:showcompare /unified"),\r
973                 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")));\r
974         sCmd += _T(" /url1:\"") + url1.GetGitPathString() + _T("\"");\r
975         if (rev1.IsValid())\r
976                 sCmd += _T(" /revision1:") + rev1.ToString();\r
977         sCmd += _T(" /url2:\"") + url2.GetGitPathString() + _T("\"");\r
978         if (rev2.IsValid())\r
979                 sCmd += _T(" /revision2:") + rev2.ToString();\r
980         if (peg.IsValid())\r
981                 sCmd += _T(" /pegrevision:") + peg.ToString();\r
982         if (headpeg.IsValid())\r
983                 sCmd += _T(" /headpegrevision:") + headpeg.ToString();\r
984 \r
985         if (bAlternateDiff)\r
986                 sCmd += _T(" /alternatediff");\r
987 \r
988         if (bIgnoreAncestry)\r
989                 sCmd += _T(" /ignoreancestry");\r
990 \r
991         if (hWnd)\r
992         {\r
993                 sCmd += _T(" /hwnd:");\r
994                 TCHAR buf[30];\r
995                 _stprintf_s(buf, 30, _T("%d"), hWnd);\r
996                 sCmd += buf;\r
997         }\r
998 \r
999         return CAppUtils::LaunchApplication(sCmd, NULL, false);\r
1000 #endif\r
1001         return TRUE;\r
1002 }\r
1003 \r
1004 bool CAppUtils::StartShowCompare(HWND hWnd, const CTGitPath& url1, const GitRev& rev1, \r
1005                                                                  const CTGitPath& url2, const GitRev& rev2, \r
1006                                                                  const GitRev& peg /* = GitRev */, const GitRev& headpeg /* = GitRev */, \r
1007                                                                  bool bAlternateDiff /* = false */, bool bIgnoreAncestry /* = false */, bool blame /* = false */)\r
1008 {\r
1009 #if 0\r
1010         CString sCmd;\r
1011         sCmd.Format(_T("%s /command:showcompare"),\r
1012                 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")));\r
1013         sCmd += _T(" /url1:\"") + url1.GetGitPathString() + _T("\"");\r
1014         if (rev1.IsValid())\r
1015                 sCmd += _T(" /revision1:") + rev1.ToString();\r
1016         sCmd += _T(" /url2:\"") + url2.GetGitPathString() + _T("\"");\r
1017         if (rev2.IsValid())\r
1018                 sCmd += _T(" /revision2:") + rev2.ToString();\r
1019         if (peg.IsValid())\r
1020                 sCmd += _T(" /pegrevision:") + peg.ToString();\r
1021         if (headpeg.IsValid())\r
1022                 sCmd += _T(" /headpegrevision:") + headpeg.ToString();\r
1023         if (bAlternateDiff)\r
1024                 sCmd += _T(" /alternatediff");\r
1025         if (bIgnoreAncestry)\r
1026                 sCmd += _T(" /ignoreancestry");\r
1027         if (blame)\r
1028                 sCmd += _T(" /blame");\r
1029 \r
1030         if (hWnd)\r
1031         {\r
1032                 sCmd += _T(" /hwnd:");\r
1033                 TCHAR buf[30];\r
1034                 _stprintf_s(buf, 30, _T("%d"), hWnd);\r
1035                 sCmd += buf;\r
1036         }\r
1037 \r
1038         return CAppUtils::LaunchApplication(sCmd, NULL, false);\r
1039 #endif\r
1040         return true;\r
1041 }\r
1042 \r
1043 bool CAppUtils::Export(CString *BashHash)\r
1044 {\r
1045         bool bRet = false;\r
1046 \r
1047                 // ask from where the export has to be done\r
1048         CExportDlg dlg;\r
1049         if(BashHash)\r
1050                 dlg.m_Revision=*BashHash;\r
1051 \r
1052         if (dlg.DoModal() == IDOK)\r
1053         {\r
1054                 CString cmd;\r
1055                 cmd.Format(_T("git.exe archive --format=zip --verbose %s"),\r
1056                                         dlg.m_VersionName);\r
1057 \r
1058                 //g_Git.RunLogFile(cmd,dlg.m_strExportDirectory);\r
1059                 CProgressDlg pro;\r
1060                 pro.m_GitCmd=cmd;\r
1061                 pro.m_LogFile=dlg.m_strExportDirectory;\r
1062                 pro.DoModal();\r
1063                 return TRUE;\r
1064         }\r
1065         return bRet;\r
1066 }\r
1067 \r
1068 bool CAppUtils::CreateBranchTag(bool IsTag,CString *CommitHash)\r
1069 {\r
1070         CCreateBranchTagDlg dlg;\r
1071         dlg.m_bIsTag=IsTag;\r
1072         if(CommitHash)\r
1073                 dlg.m_Base = *CommitHash;\r
1074 \r
1075         if(dlg.DoModal()==IDOK)\r
1076         {\r
1077                 CString cmd;\r
1078                 CString force;\r
1079                 CString track;\r
1080                 if(dlg.m_bTrack)\r
1081                         track=_T("--track");\r
1082 \r
1083                 if(dlg.m_bForce)\r
1084                         force=_T("-f");\r
1085 \r
1086                 if(IsTag)\r
1087                 {\r
1088                         cmd.Format(_T("git.exe tag %s %s %s %s"),\r
1089                                 track,\r
1090                                 force,\r
1091                                 dlg.m_BranchTagName,\r
1092                                 dlg.m_VersionName\r
1093                                 );\r
1094 \r
1095         \r
1096                 }else\r
1097                 {\r
1098                         cmd.Format(_T("git.exe branch %s %s %s %s"),\r
1099                                 track,\r
1100                                 force,\r
1101                                 dlg.m_BranchTagName,\r
1102                                 dlg.m_VersionName\r
1103                                 );\r
1104                 }\r
1105                 CString out;\r
1106                 if(g_Git.Run(cmd,&out,CP_UTF8))\r
1107                 {\r
1108                         CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);\r
1109                 }\r
1110                 if( !IsTag  &&  dlg.m_bSwitch )\r
1111                 {\r
1112                         // it is a new branch and the user has requested to switch to it\r
1113                         cmd.Format(_T("git.exe checkout %s"), dlg.m_BranchTagName);\r
1114                         g_Git.Run(cmd,&out,CP_UTF8);\r
1115                         CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);\r
1116                 }\r
1117                 \r
1118                 return TRUE;\r
1119                 \r
1120         }\r
1121         return FALSE;\r
1122 }\r
1123 \r
1124 bool CAppUtils::Switch(CString *CommitHash)\r
1125 {\r
1126         CGitSwitchDlg dlg;\r
1127         if(CommitHash)\r
1128                 dlg.m_Base=*CommitHash;\r
1129         \r
1130         if (dlg.DoModal() == IDOK)\r
1131         {\r
1132                 CString cmd;\r
1133                 CString track;\r
1134                 CString base;\r
1135                 CString force;\r
1136                 CString branch;\r
1137 \r
1138                 if(dlg.m_bBranch)\r
1139                         branch.Format(_T("-b %s"),dlg.m_NewBranch);\r
1140                 if(dlg.m_bForce)\r
1141                         force=_T("-f");\r
1142                 if(dlg.m_bTrack)\r
1143                         track=_T("--track");\r
1144 \r
1145                 cmd.Format(_T("git.exe checkout %s %s %s %s"),\r
1146                          force,\r
1147                          track,\r
1148                          branch,\r
1149                          dlg.m_VersionName);\r
1150 \r
1151                 CProgressDlg progress;\r
1152                 progress.m_GitCmd=cmd;\r
1153                 if(progress.DoModal()==IDOK)\r
1154                         return TRUE;\r
1155 \r
1156         }\r
1157         return FALSE;\r
1158 }\r
1159 \r
1160 bool CAppUtils::IgnoreFile(CTGitPathList &path,bool IsMask)\r
1161 {\r
1162         CString ignorefile;\r
1163         ignorefile=g_Git.m_CurrentDir+_T("\\");\r
1164 \r
1165         if(IsMask)\r
1166         {\r
1167                 ignorefile+=path.GetCommonRoot().GetDirectory().GetWinPathString()+_T("\\.gitignore");\r
1168 \r
1169         }else\r
1170         {\r
1171                 ignorefile+=_T("\\.gitignore");\r
1172         }\r
1173 \r
1174         CStdioFile file;\r
1175         if(!file.Open(ignorefile,CFile::modeCreate|CFile::modeReadWrite|CFile::modeNoTruncate))\r
1176         {\r
1177                 CMessageBox::Show(NULL,ignorefile+_T(" Open Failure"),_T("TortoiseGit"),MB_OK);\r
1178                 return FALSE;\r
1179         }\r
1180 \r
1181         CString ignorelist;\r
1182         CString mask;\r
1183         try\r
1184         {\r
1185                 //file.ReadString(ignorelist);\r
1186                 file.SeekToEnd();\r
1187                 for(int i=0;i<path.GetCount();i++)\r
1188                 {\r
1189                         if(IsMask)\r
1190                         {\r
1191                                 mask=_T("*")+path[i].GetFileExtension();\r
1192                                 if(ignorelist.Find(mask)<0)\r
1193                                         ignorelist+=_T("\n")+mask;\r
1194                                 \r
1195                         }else\r
1196                         {\r
1197                                 ignorelist+=_T("\n/")+path[i].GetGitPathString();\r
1198                         }\r
1199                 }\r
1200                 file.WriteString(ignorelist);\r
1201 \r
1202                 file.Close();\r
1203 \r
1204         }catch(...)\r
1205         {\r
1206                 file.Close();\r
1207                 return FALSE;\r
1208         }\r
1209         \r
1210         return TRUE;\r
1211 }\r
1212 \r
1213 \r
1214 bool CAppUtils::GitReset(CString *CommitHash,int type)\r
1215 {\r
1216         CResetDlg dlg;\r
1217         dlg.m_ResetType=type;\r
1218         if (dlg.DoModal() == IDOK)\r
1219         {\r
1220                 CString cmd;\r
1221                 CString type;\r
1222                 switch(dlg.m_ResetType)\r
1223                 {\r
1224                 case 0:\r
1225                         type=_T("--soft");\r
1226                         break;\r
1227                 case 1:\r
1228                         type=_T("--mixed");\r
1229                         break;\r
1230                 case 2:\r
1231                         type=_T("--hard");\r
1232                         break;\r
1233                 default:\r
1234                         type=_T("--mixed");\r
1235                         break;\r
1236                 }\r
1237                 cmd.Format(_T("git.exe reset %s %s"),type, *CommitHash);\r
1238 \r
1239                 CProgressDlg progress;\r
1240                 progress.m_GitCmd=cmd;\r
1241                 if(progress.DoModal()==IDOK)\r
1242                         return TRUE;\r
1243 \r
1244         }\r
1245         return FALSE;\r
1246 }\r
1247 \r
1248 void CAppUtils::DescribeFile(bool mode, bool base,CString &descript)\r
1249 {\r
1250         if(mode == FALSE)\r
1251         {\r
1252                 descript=_T("Deleted");\r
1253                 return;\r
1254         }\r
1255         if(base)\r
1256         {\r
1257                 descript=_T("Modified");\r
1258                 return;\r
1259         }\r
1260         descript=_T("Created");\r
1261         return;\r
1262 }\r
1263 \r
1264 bool CAppUtils::ConflictEdit(CTGitPath &path,bool bAlternativeTool)\r
1265 {\r
1266         bool bRet = false;\r
1267 \r
1268         CTGitPath merge=path;\r
1269         CTGitPath directory = merge.GetDirectory();\r
1270         \r
1271         \r
1272 \r
1273         // we have the conflicted file (%merged)\r
1274         // now look for the other required files\r
1275         //GitStatus stat;\r
1276         //stat.GetStatus(merge);\r
1277         //if (stat.status == NULL)\r
1278         //      return false;\r
1279 \r
1280         BYTE_VECTOR vector;\r
1281 \r
1282         CString cmd;\r
1283         cmd.Format(_T("git.exe ls-files -u -t -z -- \"%s\""),merge.GetGitPathString());\r
1284 \r
1285         if(g_Git.Run(cmd,&vector))\r
1286         {\r
1287                 return FALSE;\r
1288         }\r
1289 \r
1290         CTGitPathList list;\r
1291         list.ParserFromLsFile(vector);\r
1292 \r
1293         if(list.GetCount() == 0)\r
1294                 return FALSE;\r
1295 \r
1296         TCHAR szTempName[512];  \r
1297         GetTempFileName(_T(""),_T(""),0,szTempName);\r
1298         CString temp(szTempName);\r
1299         temp=temp.Mid(1,temp.GetLength()-5);\r
1300 \r
1301         CTGitPath theirs;\r
1302         CTGitPath mine;\r
1303         CTGitPath base;\r
1304 \r
1305         CString format;\r
1306         format=g_Git.m_CurrentDir+_T("\\")+directory.GetWinPathString()+merge.GetFilename()+CString(_T(".%s."))+temp+merge.GetFileExtension();\r
1307 \r
1308         CString file;\r
1309         file.Format(format,_T("LOCAL"));\r
1310         mine.SetFromGit(file);\r
1311         file.Format(format,_T("REMOTE"));\r
1312         theirs.SetFromGit(file);\r
1313         file.Format(format,_T("BASE"));\r
1314         base.SetFromGit(file);\r
1315 \r
1316         \r
1317         format=_T("git.exe cat-file blob \":%d:%s\"");\r
1318         CFile tempfile;\r
1319         //create a empty file, incase stage is not three\r
1320         tempfile.Open(mine.GetWinPathString(),CFile::modeCreate|CFile::modeReadWrite);\r
1321         tempfile.Close();\r
1322         tempfile.Open(theirs.GetWinPathString(),CFile::modeCreate|CFile::modeReadWrite);\r
1323         tempfile.Close();\r
1324         tempfile.Open(base.GetWinPathString(),CFile::modeCreate|CFile::modeReadWrite);\r
1325         tempfile.Close();\r
1326 \r
1327         bool b_base=false, b_local=false, b_remote=false;\r
1328 \r
1329         for(int i=0;i<list.GetCount();i++)\r
1330         {\r
1331                 CString cmd;\r
1332                 CString outfile;\r
1333                 cmd.Format(format,list[i].m_Stage,list[i].GetGitPathString());\r
1334                 \r
1335                 if( list[i].m_Stage == 1)\r
1336                 {\r
1337                         b_base = true;\r
1338                         outfile=base.GetWinPathString();\r
1339                 }\r
1340                 if( list[i].m_Stage == 2 )\r
1341                 {\r
1342                         b_local = true;\r
1343                         outfile=mine.GetWinPathString();\r
1344                 }\r
1345                 if( list[i].m_Stage == 3 )\r
1346                 {\r
1347                         b_remote = true;\r
1348                         outfile=theirs.GetWinPathString();\r
1349                 }       \r
1350                 g_Git.RunLogFile(cmd,outfile);\r
1351         }\r
1352 \r
1353         if(b_local && b_remote )\r
1354         {\r
1355                 merge.SetFromWin(g_Git.m_CurrentDir+_T("\\")+merge.GetWinPathString());\r
1356                 bRet = !!CAppUtils::StartExtMerge(base, theirs, mine, merge,_T("BASE"),_T("REMOTE"),_T("LOCAL"));\r
1357         \r
1358         }else\r
1359         {\r
1360                 CFile::Remove(mine.GetWinPathString());\r
1361                 CFile::Remove(theirs.GetWinPathString());\r
1362                 CFile::Remove(base.GetWinPathString());\r
1363 \r
1364                 CDeleteConflictDlg dlg;\r
1365                 DescribeFile(b_local, b_base,dlg.m_LocalStatus);\r
1366                 DescribeFile(b_remote,b_base,dlg.m_RemoteStatus);\r
1367                 dlg.m_bShowModifiedButton=b_base;\r
1368                 dlg.m_File=merge.GetGitPathString();\r
1369                 if(dlg.DoModal() == IDOK)\r
1370                 {\r
1371                         CString cmd,out;\r
1372                         if(dlg.m_bIsDelete)\r
1373                         {\r
1374                                 cmd.Format(_T("git.exe rm \"%s\""),merge.GetGitPathString());\r
1375                         }else\r
1376                                 cmd.Format(_T("git.exe add \"%s\""),merge.GetGitPathString());\r
1377 \r
1378                         if(g_Git.Run(cmd,&out,CP_ACP))\r
1379                         {\r
1380                                 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK);\r
1381                                 return FALSE;\r
1382                         }\r
1383                         return TRUE;\r
1384                 }\r
1385                 else \r
1386                         return FALSE;\r
1387 \r
1388                 \r
1389 \r
1390         }\r
1391 \r
1392 #if 0\r
1393 \r
1394         CAppUtils::StartExtMerge(CAppUtils::MergeFlags().AlternativeTool(bAlternativeTool), \r
1395                         base, theirs, mine, merge);\r
1396 #endif\r
1397 #if 0\r
1398         if (stat.status->text_status == svn_wc_status_conflicted)\r
1399         {\r
1400                 // we have a text conflict, use our merge tool to resolve the conflict\r
1401 \r
1402                 CTSVNPath theirs(directory);\r
1403                 CTSVNPath mine(directory);\r
1404                 CTSVNPath base(directory);\r
1405                 bool bConflictData = false;\r
1406 \r
1407                 if ((stat.status->entry)&&(stat.status->entry->conflict_new))\r
1408                 {\r
1409                         theirs.AppendPathString(CUnicodeUtils::GetUnicode(stat.status->entry->conflict_new));\r
1410                         bConflictData = true;\r
1411                 }\r
1412                 if ((stat.status->entry)&&(stat.status->entry->conflict_old))\r
1413                 {\r
1414                         base.AppendPathString(CUnicodeUtils::GetUnicode(stat.status->entry->conflict_old));\r
1415                         bConflictData = true;\r
1416                 }\r
1417                 if ((stat.status->entry)&&(stat.status->entry->conflict_wrk))\r
1418                 {\r
1419                         mine.AppendPathString(CUnicodeUtils::GetUnicode(stat.status->entry->conflict_wrk));\r
1420                         bConflictData = true;\r
1421                 }\r
1422                 else\r
1423                 {\r
1424                         mine = merge;\r
1425                 }\r
1426                 if (bConflictData)\r
1427                         bRet = !!CAppUtils::StartExtMerge(CAppUtils::MergeFlags().AlternativeTool(bAlternativeTool), \r
1428                                                                                                 base, theirs, mine, merge);\r
1429         }\r
1430 \r
1431         if (stat.status->prop_status == svn_wc_status_conflicted)\r
1432         {\r
1433                 // we have a property conflict\r
1434                 CTSVNPath prej(directory);\r
1435                 if ((stat.status->entry)&&(stat.status->entry->prejfile))\r
1436                 {\r
1437                         prej.AppendPathString(CUnicodeUtils::GetUnicode(stat.status->entry->prejfile));\r
1438                         // there's a problem: the prej file contains a _description_ of the conflict, and\r
1439                         // that description string might be translated. That means we have no way of parsing\r
1440                         // the file to find out the conflicting values.\r
1441                         // The only thing we can do: show a dialog with the conflict description, then\r
1442                         // let the user either accept the existing property or open the property edit dialog\r
1443                         // to manually change the properties and values. And a button to mark the conflict as\r
1444                         // resolved.\r
1445                         CEditPropConflictDlg dlg;\r
1446                         dlg.SetPrejFile(prej);\r
1447                         dlg.SetConflictedItem(merge);\r
1448                         bRet = (dlg.DoModal() != IDCANCEL);\r
1449                 }\r
1450         }\r
1451 \r
1452         if (stat.status->tree_conflict)\r
1453         {\r
1454                 // we have a tree conflict\r
1455                 SVNInfo info;\r
1456                 const SVNInfoData * pInfoData = info.GetFirstFileInfo(merge, SVNRev(), SVNRev());\r
1457                 if (pInfoData)\r
1458                 {\r
1459                         if (pInfoData->treeconflict_kind == svn_wc_conflict_kind_text)\r
1460                         {\r
1461                                 CTSVNPath theirs(directory);\r
1462                                 CTSVNPath mine(directory);\r
1463                                 CTSVNPath base(directory);\r
1464                                 bool bConflictData = false;\r
1465 \r
1466                                 if (pInfoData->treeconflict_theirfile)\r
1467                                 {\r
1468                                         theirs.AppendPathString(pInfoData->treeconflict_theirfile);\r
1469                                         bConflictData = true;\r
1470                                 }\r
1471                                 if (pInfoData->treeconflict_basefile)\r
1472                                 {\r
1473                                         base.AppendPathString(pInfoData->treeconflict_basefile);\r
1474                                         bConflictData = true;\r
1475                                 }\r
1476                                 if (pInfoData->treeconflict_myfile)\r
1477                                 {\r
1478                                         mine.AppendPathString(pInfoData->treeconflict_myfile);\r
1479                                         bConflictData = true;\r
1480                                 }\r
1481                                 else\r
1482                                 {\r
1483                                         mine = merge;\r
1484                                 }\r
1485                                 if (bConflictData)\r
1486                                         bRet = !!CAppUtils::StartExtMerge(CAppUtils::MergeFlags().AlternativeTool(bAlternativeTool),\r
1487                                                                                                                 base, theirs, mine, merge);\r
1488                         }\r
1489                         else if (pInfoData->treeconflict_kind == svn_wc_conflict_kind_tree)\r
1490                         {\r
1491                                 CString sConflictAction;\r
1492                                 CString sConflictReason;\r
1493                                 CString sResolveTheirs;\r
1494                                 CString sResolveMine;\r
1495                                 CTSVNPath treeConflictPath = CTSVNPath(pInfoData->treeconflict_path);\r
1496                                 CString sItemName = treeConflictPath.GetUIFileOrDirectoryName();\r
1497                                 \r
1498                                 if (pInfoData->treeconflict_nodekind == svn_node_file)\r
1499                                 {\r
1500                                         switch (pInfoData->treeconflict_operation)\r
1501                                         {\r
1502                                         case svn_wc_operation_update:\r
1503                                                 switch (pInfoData->treeconflict_action)\r
1504                                                 {\r
1505                                                 case svn_wc_conflict_action_edit:\r
1506                                                         sConflictAction.Format(IDS_TREECONFLICT_FILEUPDATEEDIT, (LPCTSTR)sItemName);\r
1507                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYFILE);\r
1508                                                         break;\r
1509                                                 case svn_wc_conflict_action_add:\r
1510                                                         sConflictAction.Format(IDS_TREECONFLICT_FILEUPDATEADD, (LPCTSTR)sItemName);\r
1511                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYFILE);\r
1512                                                         break;\r
1513                                                 case svn_wc_conflict_action_delete:\r
1514                                                         sConflictAction.Format(IDS_TREECONFLICT_FILEUPDATEDELETE, (LPCTSTR)sItemName);\r
1515                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_REMOVEFILE);\r
1516                                                         break;\r
1517                                                 }\r
1518                                                 break;\r
1519                                         case svn_wc_operation_switch:\r
1520                                                 switch (pInfoData->treeconflict_action)\r
1521                                                 {\r
1522                                                 case svn_wc_conflict_action_edit:\r
1523                                                         sConflictAction.Format(IDS_TREECONFLICT_FILESWITCHEDIT, (LPCTSTR)sItemName);\r
1524                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYFILE);\r
1525                                                         break;\r
1526                                                 case svn_wc_conflict_action_add:\r
1527                                                         sConflictAction.Format(IDS_TREECONFLICT_FILESWITCHADD, (LPCTSTR)sItemName);\r
1528                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYFILE);\r
1529                                                         break;\r
1530                                                 case svn_wc_conflict_action_delete:\r
1531                                                         sConflictAction.Format(IDS_TREECONFLICT_FILESWITCHDELETE, (LPCTSTR)sItemName);\r
1532                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_REMOVEFILE);\r
1533                                                         break;\r
1534                                                 }\r
1535                                                 break;\r
1536                                         case svn_wc_operation_merge:\r
1537                                                 switch (pInfoData->treeconflict_action)\r
1538                                                 {\r
1539                                                 case svn_wc_conflict_action_edit:\r
1540                                                         sConflictAction.Format(IDS_TREECONFLICT_FILEMERGEEDIT, (LPCTSTR)sItemName);\r
1541                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYFILE);\r
1542                                                         break;\r
1543                                                 case svn_wc_conflict_action_add:\r
1544                                                         sResolveTheirs.Format(IDS_TREECONFLICT_FILEMERGEADD, (LPCTSTR)sItemName);\r
1545                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYFILE);\r
1546                                                         break;\r
1547                                                 case svn_wc_conflict_action_delete:\r
1548                                                         sConflictAction.Format(IDS_TREECONFLICT_FILEMERGEDELETE, (LPCTSTR)sItemName);\r
1549                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_REMOVEFILE);\r
1550                                                         break;\r
1551                                                 }\r
1552                                                 break;\r
1553                                         }\r
1554                                 }\r
1555                                 else if (pInfoData->treeconflict_nodekind == svn_node_dir)\r
1556                                 {\r
1557                                         switch (pInfoData->treeconflict_operation)\r
1558                                         {\r
1559                                         case svn_wc_operation_update:\r
1560                                                 switch (pInfoData->treeconflict_action)\r
1561                                                 {\r
1562                                                 case svn_wc_conflict_action_edit:\r
1563                                                         sConflictAction.Format(IDS_TREECONFLICT_DIRUPDATEEDIT, (LPCTSTR)sItemName);\r
1564                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYDIR);\r
1565                                                         break;\r
1566                                                 case svn_wc_conflict_action_add:\r
1567                                                         sConflictAction.Format(IDS_TREECONFLICT_DIRUPDATEADD, (LPCTSTR)sItemName);\r
1568                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYDIR);\r
1569                                                         break;\r
1570                                                 case svn_wc_conflict_action_delete:\r
1571                                                         sConflictAction.Format(IDS_TREECONFLICT_DIRUPDATEDELETE, (LPCTSTR)sItemName);\r
1572                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_REMOVEDIR);\r
1573                                                         break;\r
1574                                                 }\r
1575                                                 break;\r
1576                                         case svn_wc_operation_switch:\r
1577                                                 switch (pInfoData->treeconflict_action)\r
1578                                                 {\r
1579                                                 case svn_wc_conflict_action_edit:\r
1580                                                         sConflictAction.Format(IDS_TREECONFLICT_DIRSWITCHEDIT, (LPCTSTR)sItemName);\r
1581                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYDIR);\r
1582                                                         break;\r
1583                                                 case svn_wc_conflict_action_add:\r
1584                                                         sConflictAction.Format(IDS_TREECONFLICT_DIRSWITCHADD, (LPCTSTR)sItemName);\r
1585                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYDIR);\r
1586                                                         break;\r
1587                                                 case svn_wc_conflict_action_delete:\r
1588                                                         sConflictAction.Format(IDS_TREECONFLICT_DIRSWITCHDELETE, (LPCTSTR)sItemName);\r
1589                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_REMOVEDIR);\r
1590                                                         break;\r
1591                                                 }\r
1592                                                 break;\r
1593                                         case svn_wc_operation_merge:\r
1594                                                 switch (pInfoData->treeconflict_action)\r
1595                                                 {\r
1596                                                 case svn_wc_conflict_action_edit:\r
1597                                                         sConflictAction.Format(IDS_TREECONFLICT_DIRMERGEEDIT, (LPCTSTR)sItemName);\r
1598                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYDIR);\r
1599                                                         break;\r
1600                                                 case svn_wc_conflict_action_add:\r
1601                                                         sConflictAction.Format(IDS_TREECONFLICT_DIRMERGEADD, (LPCTSTR)sItemName);\r
1602                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_KEEPREPOSITORYDIR);\r
1603                                                         break;\r
1604                                                 case svn_wc_conflict_action_delete:\r
1605                                                         sConflictAction.Format(IDS_TREECONFLICT_DIRMERGEDELETE, (LPCTSTR)sItemName);\r
1606                                                         sResolveTheirs.LoadString(IDS_TREECONFLICT_RESOLVE_REMOVEDIR);\r
1607                                                         break;\r
1608                                                 }\r
1609                                                 break;\r
1610                                         }\r
1611                                 }\r
1612 \r
1613                                 UINT uReasonID = 0;\r
1614                                 switch (pInfoData->treeconflict_reason)\r
1615                                 { \r
1616                                 case svn_wc_conflict_reason_edited:\r
1617                                         uReasonID = IDS_TREECONFLICT_REASON_EDITED;\r
1618                                         sResolveMine.LoadString(pInfoData->treeconflict_nodekind == svn_node_dir ? IDS_TREECONFLICT_RESOLVE_KEEPLOCALDIR : IDS_TREECONFLICT_RESOLVE_KEEPLOCALFILE);\r
1619                                         break;\r
1620                                 case svn_wc_conflict_reason_obstructed:\r
1621                                         uReasonID = IDS_TREECONFLICT_REASON_OBSTRUCTED;\r
1622                                         sResolveMine.LoadString(pInfoData->treeconflict_nodekind == svn_node_dir ? IDS_TREECONFLICT_RESOLVE_KEEPLOCALDIR : IDS_TREECONFLICT_RESOLVE_KEEPLOCALFILE);\r
1623                                         break;\r
1624                                 case svn_wc_conflict_reason_deleted:\r
1625                                         uReasonID = IDS_TREECONFLICT_REASON_DELETED;\r
1626                                         sResolveMine.LoadString(pInfoData->treeconflict_nodekind == svn_node_dir ? IDS_TREECONFLICT_RESOLVE_REMOVEDIR : IDS_TREECONFLICT_RESOLVE_REMOVEFILE);\r
1627                                         break;\r
1628                                 case svn_wc_conflict_reason_added:\r
1629                                         uReasonID = IDS_TREECONFLICT_REASON_ADDED;\r
1630                                         sResolveMine.LoadString(pInfoData->treeconflict_nodekind == svn_node_dir ? IDS_TREECONFLICT_RESOLVE_KEEPLOCALDIR : IDS_TREECONFLICT_RESOLVE_KEEPLOCALFILE);\r
1631                                         break;\r
1632                                 case svn_wc_conflict_reason_missing:\r
1633                                         uReasonID = IDS_TREECONFLICT_REASON_MISSING;\r
1634                                         sResolveMine.LoadString(pInfoData->treeconflict_nodekind == svn_node_dir ? IDS_TREECONFLICT_RESOLVE_REMOVEDIR : IDS_TREECONFLICT_RESOLVE_REMOVEFILE);\r
1635                                         break;\r
1636                                 case svn_wc_conflict_reason_unversioned:\r
1637                                         uReasonID = IDS_TREECONFLICT_REASON_UNVERSIONED;\r
1638                                         sResolveMine.LoadString(pInfoData->treeconflict_nodekind == svn_node_dir ? IDS_TREECONFLICT_RESOLVE_KEEPLOCALDIR : IDS_TREECONFLICT_RESOLVE_KEEPLOCALFILE);\r
1639                                         break;\r
1640                                 }\r
1641                                 sConflictReason.Format(uReasonID, (LPCTSTR)sConflictAction);\r
1642 \r
1643                                 CTreeConflictEditorDlg dlg;\r
1644                                 dlg.SetConflictInfoText(sConflictReason);\r
1645                                 dlg.SetResolveTexts(sResolveTheirs, sResolveMine);\r
1646                                 dlg.SetPath(treeConflictPath);\r
1647                                 INT_PTR dlgRet = dlg.DoModal();\r
1648                                 bRet = (dlgRet != IDCANCEL);\r
1649                         }\r
1650                 }\r
1651         }\r
1652 #endif\r
1653         return bRet;\r
1654 }\r
1655 \r
1656 /**\r
1657  * FUNCTION    :   FormatDateAndTime\r
1658  * DESCRIPTION :   Generates a displayable string from a CTime object in\r
1659  *                 system short or long format  or as a relative value\r
1660  *                                 cTime - the time\r
1661  *                                 option - DATE_SHORTDATE or DATE_LONGDATE\r
1662  *                                 bIncluedeTime - whether to show time as well as date\r
1663  *                                 bRelative - if true then relative time is shown if reasonable \r
1664  *                                 If HKCU\Software\TortoiseGit\UseSystemLocaleForDates is 0 then use fixed format\r
1665  *                                 rather than locale\r
1666  * RETURN      :   CString containing date/time\r
1667  */\r
1668 CString CAppUtils::FormatDateAndTime( const CTime& cTime, DWORD option, bool bIncludeTime /*=true*/,\r
1669         bool bRelative /*=false*/)\r
1670 {\r
1671         CString datetime;\r
1672         if ( bRelative )\r
1673         {\r
1674                 datetime = ToRelativeTimeString( cTime );\r
1675         }\r
1676         else\r
1677         {\r
1678                 // should we use the locale settings for formatting the date/time?\r
1679                 if (CRegDWORD(_T("Software\\TortoiseGit\\UseSystemLocaleForDates"), TRUE))\r
1680                 {\r
1681                         // yes\r
1682                         SYSTEMTIME sysTime;\r
1683                         cTime.GetAsSystemTime( sysTime );\r
1684                         \r
1685                         TCHAR buf[100];\r
1686                         \r
1687                         GetDateFormat(LOCALE_USER_DEFAULT, option, &sysTime, NULL, buf, \r
1688                                 sizeof(buf)/sizeof(TCHAR)-1);\r
1689                         datetime = buf;\r
1690                         if ( bIncludeTime )\r
1691                         {\r
1692                                 datetime += _T(" ");\r
1693                                 GetTimeFormat(LOCALE_USER_DEFAULT, 0, &sysTime, NULL, buf, sizeof(buf)/sizeof(TCHAR)-1);\r
1694                                 datetime += buf;\r
1695                         }\r
1696                 }\r
1697                 else\r
1698                 {\r
1699                         // no, so fixed format\r
1700                         if ( bIncludeTime )\r
1701                         {\r
1702                                 datetime = cTime.Format(_T("%Y-%m-%d %H:%M:%S"));\r
1703                         }\r
1704                         else\r
1705                         {\r
1706                                 datetime = cTime.Format(_T("%Y-%m-%d"));\r
1707                         }\r
1708                 }\r
1709         }\r
1710         return datetime;\r
1711 }\r
1712 \r
1713 /**\r
1714  *      Converts a given time to a relative display string (relative to current time)\r
1715  *      Given time must be in local timezone\r
1716  */\r
1717 CString CAppUtils::ToRelativeTimeString(CTime time)\r
1718 {\r
1719     CString answer;\r
1720         // convert to COleDateTime\r
1721         SYSTEMTIME sysTime;\r
1722         time.GetAsSystemTime( sysTime );\r
1723         COleDateTime oleTime( sysTime );\r
1724         answer = ToRelativeTimeString(oleTime, COleDateTime::GetCurrentTime());\r
1725         return answer;\r
1726 }\r
1727 \r
1728 /**\r
1729  *      Generates a display string showing the relative time between the two given times as COleDateTimes\r
1730  */\r
1731 CString CAppUtils::ToRelativeTimeString(COleDateTime time,COleDateTime RelativeTo)\r
1732 {\r
1733     CString answer;\r
1734         COleDateTimeSpan ts = RelativeTo - time;\r
1735     //years\r
1736         if(fabs(ts.GetTotalDays()) >= 3*365)\r
1737     {\r
1738                 answer = ExpandRelativeTime( (int)ts.GetTotalDays()/365, IDS_YEAR_AGO, IDS_YEARS_AGO );\r
1739         }\r
1740         //Months\r
1741         if(fabs(ts.GetTotalDays()) >= 60)\r
1742         {\r
1743                 answer = ExpandRelativeTime( (int)ts.GetTotalDays()/30, IDS_MONTH_AGO, IDS_MONTHS_AGO );\r
1744                 return answer;\r
1745         }\r
1746         //Weeks\r
1747         if(fabs(ts.GetTotalDays()) >= 14)\r
1748         {\r
1749                 answer = ExpandRelativeTime( (int)ts.GetTotalDays()/7, IDS_WEEK_AGO, IDS_WEEKS_AGO );\r
1750                 return answer;\r
1751         }\r
1752         //Days\r
1753         if(fabs(ts.GetTotalDays()) >= 2)\r
1754         {\r
1755                 answer = ExpandRelativeTime( (int)ts.GetTotalDays(), IDS_DAY_AGO, IDS_DAYS_AGO );\r
1756                 return answer;\r
1757         }\r
1758         //hours\r
1759         if(fabs(ts.GetTotalHours()) >= 2)\r
1760         {\r
1761                 answer = ExpandRelativeTime( (int)ts.GetTotalHours(), IDS_HOUR_AGO, IDS_HOURS_AGO );\r
1762                 return answer;\r
1763         }\r
1764         //minutes\r
1765         if(fabs(ts.GetTotalMinutes()) >= 2)\r
1766         {\r
1767                 answer = ExpandRelativeTime( (int)ts.GetTotalMinutes(), IDS_MINUTE_AGO, IDS_MINUTES_AGO );\r
1768                 return answer;\r
1769         }\r
1770         //seconds\r
1771                 answer = ExpandRelativeTime( (int)ts.GetTotalSeconds(), IDS_SECOND_AGO, IDS_SECONDS_AGO );\r
1772     return answer;\r
1773 }\r
1774 \r
1775 /** \r
1776  * Passed a value and two resource string ids\r
1777  * if count is 1 then FormatString is called with format_1 and the value\r
1778  * otherwise format_2 is used\r
1779  * the formatted string is returned\r
1780 */\r
1781 CString CAppUtils::ExpandRelativeTime( int count, UINT format_1, UINT format_n )\r
1782 {\r
1783         CString answer;\r
1784         if ( count == 1 )\r
1785         {\r
1786                 answer.FormatMessage( format_1, count );\r
1787         }\r
1788         else\r
1789         {\r
1790                 answer.FormatMessage( format_n, count );\r
1791         }\r
1792         return answer;\r
1793 }\r
1794 \r