OSDN Git Service

Fix 'Explore To' in context menu in Commit Dialog
[tortoisegit/TortoiseGitJp.git] / src / Git / TGitPath.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 "TGitPath.h"\r
21 #include "UnicodeUtils.h"\r
22 #include "GitAdminDir.h"\r
23 #include "PathUtils.h"\r
24 #include <regex>\r
25 #include "git.h"\r
26 #if defined(_MFC_VER)\r
27 //#include "MessageBox.h"\r
28 //#include "AppUtils.h"\r
29 #endif\r
30 \r
31 #ifndef ASSERT\r
32 #define ASSERT()\r
33 #endif\r
34 using namespace std;\r
35 extern CGit g_Git;\r
36 \r
37 CTGitPath::CTGitPath(void) :\r
38         m_bDirectoryKnown(false),\r
39         m_bIsDirectory(false),\r
40         m_bIsURL(false),\r
41         m_bURLKnown(false),\r
42         m_bHasAdminDirKnown(false),\r
43         m_bHasAdminDir(false),\r
44         m_bIsValidOnWindowsKnown(false),\r
45         m_bIsReadOnly(false),\r
46         m_bIsAdminDirKnown(false),\r
47         m_bIsAdminDir(false),\r
48         m_bExists(false),\r
49         m_bExistsKnown(false),\r
50         m_bLastWriteTimeKnown(0),\r
51         m_lastWriteTime(0),\r
52         m_customData(NULL),\r
53         m_bIsSpecialDirectoryKnown(false),\r
54         m_bIsSpecialDirectory(false)\r
55 {\r
56         m_Action=0;\r
57 }\r
58 \r
59 CTGitPath::~CTGitPath(void)\r
60 {\r
61 }\r
62 // Create a TGitPath object from an unknown path type (same as using SetFromUnknown)\r
63 CTGitPath::CTGitPath(const CString& sUnknownPath) :\r
64         m_bDirectoryKnown(false),\r
65         m_bIsDirectory(false),\r
66         m_bIsURL(false),\r
67         m_bURLKnown(false),\r
68         m_bHasAdminDirKnown(false),\r
69         m_bHasAdminDir(false),\r
70         m_bIsValidOnWindowsKnown(false),\r
71         m_bIsReadOnly(false),\r
72         m_bIsAdminDirKnown(false),\r
73         m_bIsAdminDir(false),\r
74         m_bExists(false),\r
75         m_bExistsKnown(false),\r
76         m_bLastWriteTimeKnown(0),\r
77         m_lastWriteTime(0),\r
78         m_customData(NULL),\r
79         m_bIsSpecialDirectoryKnown(false),\r
80         m_bIsSpecialDirectory(false)\r
81 {\r
82         SetFromUnknown(sUnknownPath);\r
83         m_Action=0;\r
84         m_Stage=0;\r
85 }\r
86 \r
87 int CTGitPath::ParserAction(BYTE action)\r
88 {\r
89         //action=action.TrimLeft();\r
90         //TCHAR c=action.GetAt(0);\r
91         if(action == 'M')\r
92                 m_Action|= LOGACTIONS_MODIFIED;\r
93         if(action == 'R')\r
94                 m_Action|= LOGACTIONS_REPLACED;\r
95         if(action == 'A')\r
96                 m_Action|= LOGACTIONS_ADDED;\r
97         if(action == 'D')\r
98                 m_Action|= LOGACTIONS_DELETED;\r
99         if(action == 'U')\r
100                 m_Action|= LOGACTIONS_UNMERGED;\r
101         if(action == 'K')\r
102                 m_Action|= LOGACTIONS_DELETED;\r
103         if(action == 'H')\r
104                 m_Action|= LOGACTIONS_CACHE;\r
105         return m_Action;\r
106 }\r
107 void CTGitPath::SetFromGit(const char* pPath)\r
108 {\r
109         Reset();\r
110         if (pPath == NULL)\r
111                 return;\r
112         int len = MultiByteToWideChar(CP_UTF8, 0, pPath, -1, NULL, 0);\r
113         if (len)\r
114         {\r
115                 len = MultiByteToWideChar(CP_UTF8, 0, pPath, -1, m_sFwdslashPath.GetBuffer(len+1), len+1);\r
116                 m_sFwdslashPath.ReleaseBuffer(len-1);\r
117         }\r
118         SanitizeRootPath(m_sFwdslashPath, true);\r
119 }\r
120 \r
121 void CTGitPath::SetFromGit(const char* pPath, bool bIsDirectory)\r
122 {\r
123         SetFromGit(pPath);\r
124         m_bDirectoryKnown = true;\r
125         m_bIsDirectory = bIsDirectory;\r
126 }\r
127 \r
128 void CTGitPath::SetFromGit(const CString& sPath,CString *oldpath)\r
129 {\r
130         Reset();\r
131         m_sFwdslashPath = sPath;\r
132         SanitizeRootPath(m_sFwdslashPath, true);\r
133         if(oldpath)\r
134                 m_sOldFwdslashPath = *oldpath;\r
135 }\r
136 \r
137 void CTGitPath::SetFromWin(LPCTSTR pPath)\r
138 {\r
139         Reset();\r
140         m_sBackslashPath = pPath;\r
141         SanitizeRootPath(m_sBackslashPath, false);\r
142         ATLASSERT(m_sBackslashPath.Find('/')<0);\r
143 }\r
144 void CTGitPath::SetFromWin(const CString& sPath)\r
145 {\r
146         Reset();\r
147         m_sBackslashPath = sPath;\r
148         SanitizeRootPath(m_sBackslashPath, false);\r
149 }\r
150 void CTGitPath::SetFromWin(const CString& sPath, bool bIsDirectory)\r
151 {\r
152         Reset();\r
153         m_sBackslashPath = sPath;\r
154         m_bIsDirectory = bIsDirectory;\r
155         m_bDirectoryKnown = true;\r
156         SanitizeRootPath(m_sBackslashPath, false);\r
157 }\r
158 void CTGitPath::SetFromUnknown(const CString& sPath)\r
159 {\r
160         Reset();\r
161         // Just set whichever path we think is most likely to be used\r
162 //      GitAdminDir admin;\r
163 //      CString p;\r
164 //      if(admin.HasAdminDir(sPath,&p))\r
165 //              SetFwdslashPath(sPath.Right(sPath.GetLength()-p.GetLength()));\r
166 //      else\r
167                 SetFwdslashPath(sPath);\r
168 }\r
169 \r
170 LPCTSTR CTGitPath::GetWinPath() const\r
171 {\r
172         if(IsEmpty())\r
173         {\r
174                 return _T("");\r
175         }\r
176         if(m_sBackslashPath.IsEmpty())\r
177         {\r
178                 SetBackslashPath(m_sFwdslashPath);\r
179         }\r
180         return m_sBackslashPath;\r
181 }\r
182 // This is a temporary function, to be used during the migration to \r
183 // the path class.  Ultimately, functions consuming paths should take a CTGitPath&, not a CString\r
184 const CString& CTGitPath::GetWinPathString() const\r
185 {\r
186         if(m_sBackslashPath.IsEmpty())\r
187         {\r
188                 SetBackslashPath(m_sFwdslashPath);\r
189         }\r
190         return m_sBackslashPath;\r
191 }\r
192 \r
193 const CString& CTGitPath::GetGitPathString() const\r
194 {\r
195         if(m_sFwdslashPath.IsEmpty())\r
196         {\r
197                 SetFwdslashPath(m_sBackslashPath);\r
198         }\r
199         return m_sFwdslashPath;\r
200 }\r
201 \r
202 const CString &CTGitPath::GetGitOldPathString() const\r
203 {\r
204         return m_sOldFwdslashPath;\r
205 }\r
206 #if 0\r
207 const char* CTGitPath::GetGitApiPath(apr_pool_t *pool) const\r
208 {\r
209         // This funny-looking 'if' is to avoid a subtle problem with empty paths, whereby\r
210         // each call to GetGitApiPath returns a different pointer value.\r
211         // If you made multiple calls to GetGitApiPath on the same string, only the last\r
212         // one would give you a valid pointer to an empty string, because each \r
213         // call would invalidate the previous call's return. \r
214         if(IsEmpty())\r
215         {\r
216                 return "";\r
217         }\r
218         if(m_sFwdslashPath.IsEmpty())\r
219         {\r
220                 SetFwdslashPath(m_sBackslashPath);\r
221         }\r
222         if(m_sUTF8FwdslashPath.IsEmpty())\r
223         {\r
224                 SetUTF8FwdslashPath(m_sFwdslashPath);\r
225         }\r
226         if (svn_path_is_url(m_sUTF8FwdslashPath))\r
227         {\r
228                 m_sUTF8FwdslashPathEscaped = CPathUtils::PathEscape(m_sUTF8FwdslashPath);\r
229                 m_sUTF8FwdslashPathEscaped.Replace("file:////", "file:///\\");\r
230                 m_sUTF8FwdslashPathEscaped = svn_path_canonicalize(m_sUTF8FwdslashPathEscaped, pool);\r
231                 return m_sUTF8FwdslashPathEscaped;\r
232         }\r
233         m_sUTF8FwdslashPath = svn_path_canonicalize(m_sUTF8FwdslashPath, pool);\r
234 \r
235         return m_sUTF8FwdslashPath;\r
236 }\r
237 #endif\r
238 \r
239 const CString& CTGitPath::GetUIPathString() const\r
240 {\r
241         if (m_sUIPath.IsEmpty())\r
242         {\r
243 #if defined(_MFC_VER)\r
244                 //BUGBUG HORRIBLE!!! - CPathUtils::IsEscaped doesn't need to be MFC-only\r
245                 if (IsUrl())\r
246                 {\r
247                         m_sUIPath = CPathUtils::PathUnescape(GetGitPathString());\r
248                         m_sUIPath.Replace(_T("file:////"), _T("file:///\\"));\r
249 \r
250                 }\r
251                 else\r
252 #endif \r
253                 {\r
254                         m_sUIPath = GetWinPathString();\r
255                 }\r
256         }\r
257         return m_sUIPath;\r
258 }\r
259 \r
260 void CTGitPath::SetFwdslashPath(const CString& sPath) const\r
261 {\r
262         m_sFwdslashPath = sPath;\r
263         m_sFwdslashPath.Replace('\\', '/');\r
264 \r
265         // We don't leave a trailing /\r
266         m_sFwdslashPath.TrimRight('/'); \r
267 \r
268         SanitizeRootPath(m_sFwdslashPath, true);\r
269 \r
270         m_sFwdslashPath.Replace(_T("file:////"), _T("file:///\\"));\r
271 \r
272         m_sUTF8FwdslashPath.Empty();\r
273 }\r
274 \r
275 void CTGitPath::SetBackslashPath(const CString& sPath) const\r
276 {\r
277         m_sBackslashPath = sPath;\r
278         m_sBackslashPath.Replace('/', '\\');\r
279         m_sBackslashPath.TrimRight('\\');\r
280         SanitizeRootPath(m_sBackslashPath, false);\r
281 }\r
282 \r
283 void CTGitPath::SetUTF8FwdslashPath(const CString& sPath) const\r
284 {\r
285         m_sUTF8FwdslashPath = CUnicodeUtils::GetUTF8(sPath);\r
286 }\r
287 \r
288 void CTGitPath::SanitizeRootPath(CString& sPath, bool bIsForwardPath) const\r
289 {\r
290         // Make sure to add the trailing slash to root paths such as 'C:'\r
291         if (sPath.GetLength() == 2 && sPath[1] == ':')\r
292         {\r
293                 sPath += (bIsForwardPath) ? _T("/") : _T("\\");\r
294         }\r
295 }\r
296 \r
297 bool CTGitPath::IsUrl() const\r
298 {\r
299 #if 0\r
300         if (!m_bURLKnown)\r
301         {\r
302                 EnsureFwdslashPathSet();\r
303                 if(m_sUTF8FwdslashPath.IsEmpty())\r
304                 {\r
305                         SetUTF8FwdslashPath(m_sFwdslashPath);\r
306                 }\r
307                 m_bIsURL = !!svn_path_is_url(m_sUTF8FwdslashPath);\r
308                 m_bURLKnown = true;\r
309         }\r
310         return m_bIsURL;\r
311 #endif \r
312         return false;\r
313 }\r
314 \r
315 bool CTGitPath::IsDirectory() const\r
316 {\r
317         if(!m_bDirectoryKnown)\r
318         {\r
319                 UpdateAttributes();\r
320         }\r
321         return m_bIsDirectory;\r
322 }\r
323 \r
324 bool CTGitPath::Exists() const\r
325 {\r
326         if (!m_bExistsKnown)\r
327         {\r
328                 UpdateAttributes();\r
329         }\r
330         return m_bExists;\r
331 }\r
332 \r
333 bool CTGitPath::Delete(bool bTrash) const\r
334 {\r
335         EnsureBackslashPathSet();\r
336         ::SetFileAttributes(m_sBackslashPath, FILE_ATTRIBUTE_NORMAL);\r
337         bool bRet = false;\r
338         if (Exists())\r
339         {\r
340                 if ((bTrash)||(IsDirectory()))\r
341                 {\r
342                         TCHAR * buf = new TCHAR[m_sBackslashPath.GetLength()+2];\r
343                         _tcscpy_s(buf, m_sBackslashPath.GetLength()+2, m_sBackslashPath);\r
344                         buf[m_sBackslashPath.GetLength()] = 0;\r
345                         buf[m_sBackslashPath.GetLength()+1] = 0;\r
346                         SHFILEOPSTRUCT shop = {0};\r
347                         shop.wFunc = FO_DELETE;\r
348                         shop.pFrom = buf;\r
349                         shop.fFlags = FOF_NOCONFIRMATION|FOF_NOERRORUI|FOF_SILENT;\r
350                         if (bTrash)\r
351                                 shop.fFlags |= FOF_ALLOWUNDO;\r
352                         bRet = (SHFileOperation(&shop) == 0);\r
353                         delete [] buf;\r
354                 }\r
355                 else\r
356                 {\r
357                         bRet = !!::DeleteFile(m_sBackslashPath);\r
358                 }\r
359         }\r
360         m_bExists = false;\r
361         m_bExistsKnown = true;\r
362         return bRet;\r
363 }\r
364 \r
365 __int64  CTGitPath::GetLastWriteTime() const\r
366 {\r
367         if(!m_bLastWriteTimeKnown)\r
368         {\r
369                 UpdateAttributes();\r
370         }\r
371         return m_lastWriteTime;\r
372 }\r
373 \r
374 bool CTGitPath::IsReadOnly() const\r
375 {\r
376         if(!m_bLastWriteTimeKnown)\r
377         {\r
378                 UpdateAttributes();\r
379         }\r
380         return m_bIsReadOnly;\r
381 }\r
382 \r
383 void CTGitPath::UpdateAttributes() const\r
384 {\r
385         EnsureBackslashPathSet();\r
386         WIN32_FILE_ATTRIBUTE_DATA attribs;\r
387         if(GetFileAttributesEx(m_sBackslashPath, GetFileExInfoStandard, &attribs))\r
388         {\r
389                 m_bIsDirectory = !!(attribs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);\r
390                 m_lastWriteTime = *(__int64*)&attribs.ftLastWriteTime;\r
391                 m_bIsReadOnly = !!(attribs.dwFileAttributes & FILE_ATTRIBUTE_READONLY);\r
392                 m_bExists = true;\r
393         }\r
394         else\r
395         {\r
396                 DWORD err = GetLastError();\r
397                 if ((err == ERROR_FILE_NOT_FOUND)||(err == ERROR_PATH_NOT_FOUND)||(err == ERROR_INVALID_NAME))\r
398                 {\r
399                         m_bIsDirectory = false;\r
400                         m_lastWriteTime = 0;\r
401                         m_bExists = false;\r
402                 }\r
403                 else\r
404                 {\r
405                         m_bIsDirectory = false;\r
406                         m_lastWriteTime = 0;\r
407                         m_bExists = true;\r
408                         return;\r
409                 }\r
410         }\r
411         m_bDirectoryKnown = true;\r
412         m_bLastWriteTimeKnown = true;\r
413         m_bExistsKnown = true;\r
414 }\r
415 \r
416 \r
417 void CTGitPath::EnsureBackslashPathSet() const\r
418 {\r
419         if(m_sBackslashPath.IsEmpty())\r
420         {\r
421                 SetBackslashPath(m_sFwdslashPath);\r
422                 ATLASSERT(IsEmpty() || !m_sBackslashPath.IsEmpty());\r
423         }\r
424 }\r
425 void CTGitPath::EnsureFwdslashPathSet() const\r
426 {\r
427         if(m_sFwdslashPath.IsEmpty())\r
428         {\r
429                 SetFwdslashPath(m_sBackslashPath);\r
430                 ATLASSERT(IsEmpty() || !m_sFwdslashPath.IsEmpty());\r
431         }\r
432 }\r
433 \r
434 \r
435 // Reset all the caches\r
436 void CTGitPath::Reset()\r
437 {\r
438         m_bDirectoryKnown = false;\r
439         m_bURLKnown = false;\r
440         m_bLastWriteTimeKnown = false;\r
441         m_bHasAdminDirKnown = false;\r
442         m_bIsValidOnWindowsKnown = false;\r
443         m_bIsAdminDirKnown = false;\r
444         m_bExistsKnown = false;\r
445         m_bIsSpecialDirectoryKnown = false;\r
446         m_bIsSpecialDirectory = false;\r
447 \r
448         m_sBackslashPath.Empty();\r
449         m_sFwdslashPath.Empty();\r
450         m_sUTF8FwdslashPath.Empty();\r
451         this->m_Action=0;\r
452         this->m_StatAdd=_T("");\r
453         this->m_StatDel=_T("");\r
454         ATLASSERT(IsEmpty());\r
455 }\r
456 \r
457 CTGitPath CTGitPath::GetDirectory() const\r
458 {\r
459         if ((IsDirectory())||(!Exists()))\r
460         {\r
461                 return *this;\r
462         }\r
463         return GetContainingDirectory();\r
464 }\r
465 \r
466 CTGitPath CTGitPath::GetContainingDirectory() const\r
467 {\r
468         EnsureBackslashPathSet();\r
469 \r
470         CString sDirName = m_sBackslashPath.Left(m_sBackslashPath.ReverseFind('\\'));\r
471         if(sDirName.GetLength() == 2 && sDirName[1] == ':')\r
472         {\r
473                 // This is a root directory, which needs a trailing slash\r
474                 sDirName += '\\';\r
475                 if(sDirName == m_sBackslashPath)\r
476                 {\r
477                         // We were clearly provided with a root path to start with - we should return nothing now\r
478                         sDirName.Empty();\r
479                 }\r
480         }\r
481         if(sDirName.GetLength() == 1 && sDirName[0] == '\\')\r
482         {\r
483                 // We have an UNC path and we already are the root\r
484                 sDirName.Empty();\r
485         }\r
486         CTGitPath retVal;\r
487         retVal.SetFromWin(sDirName);\r
488         return retVal;\r
489 }\r
490 \r
491 CString CTGitPath::GetRootPathString() const\r
492 {\r
493         EnsureBackslashPathSet();\r
494         CString workingPath = m_sBackslashPath;\r
495         LPTSTR pPath = workingPath.GetBuffer(MAX_PATH);         // MAX_PATH ok here.\r
496         ATLVERIFY(::PathStripToRoot(pPath));\r
497         workingPath.ReleaseBuffer();\r
498         return workingPath;\r
499 }\r
500 \r
501 \r
502 CString CTGitPath::GetFilename() const\r
503 {\r
504         ATLASSERT(!IsDirectory());\r
505         return GetFileOrDirectoryName();\r
506 }\r
507 \r
508 CString CTGitPath::GetFileOrDirectoryName() const\r
509 {\r
510         EnsureBackslashPathSet();\r
511         return m_sBackslashPath.Mid(m_sBackslashPath.ReverseFind('\\')+1);\r
512 }\r
513 \r
514 CString CTGitPath::GetUIFileOrDirectoryName() const\r
515 {\r
516         GetUIPathString();\r
517         return m_sUIPath.Mid(m_sUIPath.ReverseFind('\\')+1);\r
518 }\r
519 \r
520 CString CTGitPath::GetFileExtension() const\r
521 {\r
522         if(!IsDirectory())\r
523         {\r
524                 EnsureBackslashPathSet();\r
525                 int dotPos = m_sBackslashPath.ReverseFind('.');\r
526                 int slashPos = m_sBackslashPath.ReverseFind('\\');\r
527                 if (dotPos > slashPos)\r
528                         return m_sBackslashPath.Mid(dotPos);\r
529         }\r
530         return CString();\r
531 }\r
532 CString CTGitPath::GetBaseFilename() const\r
533 {\r
534         int dot;\r
535         CString filename=GetFilename();\r
536         dot = filename.ReverseFind(_T('.'));\r
537         if(dot>0)\r
538                 return filename.Left(dot-1);\r
539         else\r
540                 return filename;\r
541 }\r
542 \r
543 bool CTGitPath::ArePathStringsEqual(const CString& sP1, const CString& sP2)\r
544 {\r
545         int length = sP1.GetLength();\r
546         if(length != sP2.GetLength())\r
547         {\r
548                 // Different lengths\r
549                 return false;\r
550         }\r
551         // We work from the end of the strings, because path differences\r
552         // are more likely to occur at the far end of a string\r
553         LPCTSTR pP1Start = sP1;\r
554         LPCTSTR pP1 = pP1Start+(length-1);\r
555         LPCTSTR pP2 = ((LPCTSTR)sP2)+(length-1);\r
556         while(length-- > 0)\r
557         {\r
558                 if(_totlower(*pP1--) != _totlower(*pP2--))\r
559                 {\r
560                         return false;\r
561                 }\r
562         }\r
563         return true;\r
564 }\r
565 \r
566 bool CTGitPath::ArePathStringsEqualWithCase(const CString& sP1, const CString& sP2)\r
567 {\r
568         int length = sP1.GetLength();\r
569         if(length != sP2.GetLength())\r
570         {\r
571                 // Different lengths\r
572                 return false;\r
573         }\r
574         // We work from the end of the strings, because path differences\r
575         // are more likely to occur at the far end of a string\r
576         LPCTSTR pP1Start = sP1;\r
577         LPCTSTR pP1 = pP1Start+(length-1);\r
578         LPCTSTR pP2 = ((LPCTSTR)sP2)+(length-1);\r
579         while(length-- > 0)\r
580         {\r
581                 if((*pP1--) != (*pP2--))\r
582                 {\r
583                         return false;\r
584                 }\r
585         }\r
586         return true;\r
587 }\r
588 \r
589 bool CTGitPath::IsEmpty() const\r
590 {\r
591         // Check the backward slash path first, since the chance that this\r
592         // one is set is higher. In case of a 'false' return value it's a little\r
593         // bit faster.\r
594         return m_sBackslashPath.IsEmpty() && m_sFwdslashPath.IsEmpty();\r
595 }\r
596 \r
597 // Test if both paths refer to the same item\r
598 // Ignores case and slash direction\r
599 bool CTGitPath::IsEquivalentTo(const CTGitPath& rhs) const\r
600 {\r
601         // Try and find a slash direction which avoids having to convert\r
602         // both filenames\r
603         if(!m_sBackslashPath.IsEmpty())\r
604         {\r
605                 // *We've* got a \ path - make sure that the RHS also has a \ path\r
606                 rhs.EnsureBackslashPathSet();\r
607                 return ArePathStringsEqualWithCase(m_sBackslashPath, rhs.m_sBackslashPath);\r
608         }\r
609         else\r
610         {\r
611                 // Assume we've got a fwdslash path and make sure that the RHS has one\r
612                 rhs.EnsureFwdslashPathSet();\r
613                 return ArePathStringsEqualWithCase(m_sFwdslashPath, rhs.m_sFwdslashPath);\r
614         }\r
615 }\r
616 \r
617 bool CTGitPath::IsEquivalentToWithoutCase(const CTGitPath& rhs) const\r
618 {\r
619         // Try and find a slash direction which avoids having to convert\r
620         // both filenames\r
621         if(!m_sBackslashPath.IsEmpty())\r
622         {\r
623                 // *We've* got a \ path - make sure that the RHS also has a \ path\r
624                 rhs.EnsureBackslashPathSet();\r
625                 return ArePathStringsEqual(m_sBackslashPath, rhs.m_sBackslashPath);\r
626         }\r
627         else\r
628         {\r
629                 // Assume we've got a fwdslash path and make sure that the RHS has one\r
630                 rhs.EnsureFwdslashPathSet();\r
631                 return ArePathStringsEqual(m_sFwdslashPath, rhs.m_sFwdslashPath);\r
632         }\r
633 }\r
634 \r
635 bool CTGitPath::IsAncestorOf(const CTGitPath& possibleDescendant) const\r
636 {\r
637         possibleDescendant.EnsureBackslashPathSet();\r
638         EnsureBackslashPathSet();\r
639 \r
640         bool bPathStringsEqual = ArePathStringsEqual(m_sBackslashPath, possibleDescendant.m_sBackslashPath.Left(m_sBackslashPath.GetLength()));\r
641         if (m_sBackslashPath.GetLength() >= possibleDescendant.GetWinPathString().GetLength())\r
642         {\r
643                 return bPathStringsEqual;               \r
644         }\r
645         \r
646         return (bPathStringsEqual && \r
647                         ((possibleDescendant.m_sBackslashPath[m_sBackslashPath.GetLength()] == '\\')||\r
648                         (m_sBackslashPath.GetLength()==3 && m_sBackslashPath[1]==':')));\r
649 }\r
650 \r
651 // Get a string representing the file path, optionally with a base \r
652 // section stripped off the front.\r
653 CString CTGitPath::GetDisplayString(const CTGitPath* pOptionalBasePath /* = NULL*/) const\r
654 {\r
655         EnsureFwdslashPathSet();\r
656         if(pOptionalBasePath != NULL)\r
657         {\r
658                 // Find the length of the base-path without having to do an 'ensure' on it\r
659                 int baseLength = max(pOptionalBasePath->m_sBackslashPath.GetLength(), pOptionalBasePath->m_sFwdslashPath.GetLength());\r
660 \r
661                 // Now, chop that baseLength of the front of the path\r
662                 return m_sFwdslashPath.Mid(baseLength).TrimLeft('/');\r
663         }\r
664         return m_sFwdslashPath;\r
665 }\r
666 \r
667 int CTGitPath::Compare(const CTGitPath& left, const CTGitPath& right)\r
668 {\r
669         left.EnsureBackslashPathSet();\r
670         right.EnsureBackslashPathSet();\r
671         return left.m_sBackslashPath.CompareNoCase(right.m_sBackslashPath);\r
672 }\r
673 \r
674 bool operator<(const CTGitPath& left, const CTGitPath& right)\r
675 {\r
676         return CTGitPath::Compare(left, right) < 0;\r
677 }\r
678 \r
679 bool CTGitPath::PredLeftEquivalentToRight(const CTGitPath& left, const CTGitPath& right)\r
680 {\r
681         return left.IsEquivalentTo(right);\r
682 }\r
683 \r
684 bool CTGitPath::PredLeftSameWCPathAsRight(const CTGitPath& left, const CTGitPath& right)\r
685 {\r
686         if (left.IsAdminDir() && right.IsAdminDir())\r
687         {\r
688                 CTGitPath l = left;\r
689                 CTGitPath r = right;\r
690                 do \r
691                 {\r
692                         l = l.GetContainingDirectory();\r
693                 } while(l.HasAdminDir());\r
694                 do \r
695                 {\r
696                         r = r.GetContainingDirectory();\r
697                 } while(r.HasAdminDir());\r
698                 return l.GetContainingDirectory().IsEquivalentTo(r.GetContainingDirectory());\r
699         }\r
700         return left.GetDirectory().IsEquivalentTo(right.GetDirectory());\r
701 }\r
702 \r
703 bool CTGitPath::CheckChild(const CTGitPath &parent, const CTGitPath& child)\r
704 {\r
705         return parent.IsAncestorOf(child);\r
706 }\r
707 \r
708 void CTGitPath::AppendRawString(const CString& sAppend)\r
709 {\r
710         EnsureFwdslashPathSet();\r
711         CString strCopy = m_sFwdslashPath += sAppend;\r
712         SetFromUnknown(strCopy);\r
713 }\r
714 \r
715 void CTGitPath::AppendPathString(const CString& sAppend)\r
716 {\r
717         EnsureBackslashPathSet();\r
718         CString cleanAppend(sAppend);\r
719         cleanAppend.Replace('/', '\\');\r
720         cleanAppend.TrimLeft('\\');\r
721         m_sBackslashPath.TrimRight('\\');\r
722         CString strCopy = m_sBackslashPath + _T("\\") + cleanAppend;\r
723         SetFromWin(strCopy);\r
724 }\r
725 \r
726 bool CTGitPath::HasAdminDir() const\r
727 {\r
728         if (m_bHasAdminDirKnown)\r
729                 return m_bHasAdminDir;\r
730 \r
731         EnsureBackslashPathSet();\r
732         m_bHasAdminDir = g_GitAdminDir.HasAdminDir(m_sBackslashPath, IsDirectory(), &m_sProjectRoot);\r
733         m_bHasAdminDirKnown = true;\r
734         return m_bHasAdminDir;\r
735 }\r
736 \r
737 bool CTGitPath::HasAdminDir(CString *ProjectTopDir) const\r
738 {\r
739         if (m_bHasAdminDirKnown)\r
740         {\r
741                 if (ProjectTopDir)\r
742                         *ProjectTopDir = m_sProjectRoot;\r
743                 return m_bHasAdminDir;\r
744         }\r
745 \r
746         EnsureBackslashPathSet();\r
747         m_bHasAdminDir = g_GitAdminDir.HasAdminDir(m_sBackslashPath, IsDirectory(), &m_sProjectRoot);\r
748         m_bHasAdminDirKnown = true;\r
749         if (ProjectTopDir)\r
750                 *ProjectTopDir = m_sProjectRoot;\r
751         return m_bHasAdminDir;\r
752 }\r
753 \r
754 bool CTGitPath::IsAdminDir() const\r
755 {\r
756         if (m_bIsAdminDirKnown)\r
757                 return m_bIsAdminDir;\r
758         \r
759         EnsureBackslashPathSet();\r
760         m_bIsAdminDir = g_GitAdminDir.IsAdminDirPath(m_sBackslashPath);\r
761         m_bIsAdminDirKnown = true;\r
762         return m_bIsAdminDir;\r
763 }\r
764 \r
765 bool CTGitPath::IsValidOnWindows() const\r
766 {\r
767         if (m_bIsValidOnWindowsKnown)\r
768                 return m_bIsValidOnWindows;\r
769 \r
770         m_bIsValidOnWindows = false;\r
771         EnsureBackslashPathSet();\r
772         CString sMatch = m_sBackslashPath + _T("\r\n");\r
773         wstring sPattern;\r
774         // the 'file://' URL is just a normal windows path:\r
775         if (sMatch.Left(7).CompareNoCase(_T("file:\\\\"))==0)\r
776         {\r
777                 sMatch = sMatch.Mid(7);\r
778                 sMatch.TrimLeft(_T("\\"));\r
779                 sPattern = _T("^(\\\\\\\\\\?\\\\)?(([a-zA-Z]:|\\\\)\\\\)?(((\\.)|(\\.\\.)|([^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?))\\\\)*[^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?$");\r
780         }\r
781         else if (IsUrl())\r
782         {\r
783                 sPattern = _T("^((http|https|svn|svn\\+ssh|file)\\:\\\\+([^\\\\@\\:]+\\:[^\\\\@\\:]+@)?\\\\[^\\\\]+(\\:\\d+)?)?(((\\.)|(\\.\\.)|([^\\\\/:\\*\\?\"\\|<>\\. ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?))\\\\)*[^\\\\/:\\*\\?\"\\|<>\\. ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?$");\r
784         }\r
785         else\r
786         {\r
787                 sPattern = _T("^(\\\\\\\\\\?\\\\)?(([a-zA-Z]:|\\\\)\\\\)?(((\\.)|(\\.\\.)|([^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?))\\\\)*[^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?$");\r
788         }\r
789 \r
790         try\r
791         {\r
792                 tr1::wregex rx(sPattern, tr1::regex_constants::icase | tr1::regex_constants::ECMAScript);\r
793                 tr1::wsmatch match;\r
794 \r
795                 wstring rmatch = wstring((LPCTSTR)sMatch);\r
796                 if (tr1::regex_match(rmatch, match, rx))\r
797                 {\r
798                         if (wstring(match[0]).compare(sMatch)==0)\r
799                                 m_bIsValidOnWindows = true;\r
800                 }\r
801                 if (m_bIsValidOnWindows)\r
802                 {\r
803                         // now check for illegal filenames\r
804                         tr1::wregex rx2(_T("\\\\(lpt\\d|com\\d|aux|nul|prn|con)(\\\\|$)"), tr1::regex_constants::icase | tr1::regex_constants::ECMAScript);\r
805                         rmatch = m_sBackslashPath;\r
806                         if (tr1::regex_search(rmatch, rx2, tr1::regex_constants::match_default))\r
807                                 m_bIsValidOnWindows = false;\r
808                 }\r
809         }\r
810         catch (exception) {}\r
811 \r
812         m_bIsValidOnWindowsKnown = true;\r
813         return m_bIsValidOnWindows;\r
814 }\r
815 \r
816 bool CTGitPath::IsSpecialDirectory() const\r
817 {\r
818         if (m_bIsSpecialDirectoryKnown)\r
819                 return m_bIsSpecialDirectory;\r
820 \r
821         static LPCTSTR specialDirectories[]\r
822                 = { _T("trunk"), _T("tags"), _T("branches") };\r
823 \r
824         for (int i=0 ; i<(sizeof(specialDirectories) / sizeof(specialDirectories[0])) ; ++i)\r
825         {\r
826                 CString name = GetFileOrDirectoryName();\r
827                 if (0 == name.CompareNoCase(specialDirectories[i]))\r
828                 {\r
829                         m_bIsSpecialDirectory = true;\r
830                         break;\r
831                 }\r
832         }\r
833 \r
834         m_bIsSpecialDirectoryKnown = true;\r
835 \r
836         return m_bIsSpecialDirectory;\r
837 }\r
838 \r
839 //////////////////////////////////////////////////////////////////////////\r
840 \r
841 CTGitPathList::CTGitPathList()\r
842 {\r
843 \r
844 }\r
845 \r
846 // A constructor which allows a path list to be easily built which one initial entry in\r
847 CTGitPathList::CTGitPathList(const CTGitPath& firstEntry)\r
848 {\r
849         AddPath(firstEntry);\r
850 }\r
851 int CTGitPathList::ParserFromLsFile(BYTE_VECTOR &out,bool staged)\r
852 {\r
853         int pos=0;\r
854         CString one;\r
855         CTGitPath path;\r
856         CString part;\r
857         while(pos>=0 && pos<out.size())\r
858         {\r
859                 one.Empty();\r
860                 path.Reset();\r
861 \r
862                 g_Git.StringAppend(&one,&out[pos],CP_OEMCP);\r
863                 int tabstart=0;\r
864                 path.m_Action=path.ParserAction(out[pos]);\r
865                 one.Tokenize(_T("\t"),tabstart); \r
866 \r
867                 if(tabstart>=0)\r
868                         path.SetFromGit(one.Right(one.GetLength()-tabstart));\r
869 \r
870                 tabstart=0;\r
871 \r
872                 part=one.Tokenize(_T(" "),tabstart); //Tag\r
873 \r
874                 part=one.Tokenize(_T(" "),tabstart); //Mode\r
875                 \r
876                 part=one.Tokenize(_T(" "),tabstart); //Hash\r
877 \r
878                 part=one.Tokenize(_T("\t"),tabstart); //Stage\r
879 \r
880                 path.m_Stage=_ttol(part);\r
881 \r
882                 this->AddPath(path);\r
883 \r
884                 pos=out.findNextString(pos);\r
885         }\r
886         return pos;\r
887 }\r
888 int CTGitPathList::FillUnRev(int action,CTGitPathList *list)\r
889 {\r
890         int pos=0;\r
891         this->Clear();\r
892         CTGitPath path;\r
893 \r
894         int count;\r
895         if(list==NULL)\r
896                 count=1;\r
897         else\r
898                 count=list->GetCount();\r
899         for(int i=0;i<count;i++)\r
900         {       \r
901                 CString cmd;\r
902                 pos=0;\r
903                 \r
904                 CString ignored;\r
905                 if(action & CTGitPath::LOGACTIONS_IGNORE)\r
906                         ignored= _T(" --ignored");\r
907                 \r
908                 if(list==NULL)\r
909                 {\r
910                         cmd=_T("git.exe ls-files --exclude-standard --full-name --others -z");\r
911                         cmd+=ignored;\r
912                         \r
913                 }\r
914                 else\r
915                 {       cmd.Format(_T("git.exe ls-files --exclude-standard --full-name --others -z %s-- \"%s\""),\r
916                                         ignored,\r
917                                         (*list)[i].GetWinPathString());\r
918                 }\r
919 \r
920                 BYTE_VECTOR out;\r
921                 out.clear();\r
922                 g_Git.Run(cmd,&out);\r
923                 \r
924                 pos=0;\r
925                 CString one;\r
926                 while( pos>=0 && pos<out.size())\r
927                 {\r
928                         one.Empty();\r
929                         g_Git.StringAppend(&one,&out[pos],CP_OEMCP);\r
930                         if(!one.IsEmpty())\r
931                         {\r
932                                 //SetFromGit will clear all status\r
933                                 path.SetFromGit(one);\r
934                                 path.m_Action=action;\r
935                                 AddPath(path);\r
936                         }\r
937                         pos=out.findNextString(pos);\r
938                 }\r
939 \r
940         }\r
941         return 0;\r
942 }\r
943 int CTGitPathList::ParserFromLog(BYTE_VECTOR &log)\r
944 {\r
945         this->Clear();\r
946         int pos=0;\r
947         //BYTE *p=&log[0];\r
948         //CString one;\r
949         CTGitPath path;\r
950         m_Action=0;\r
951         while( pos>=0 && pos<log.size())\r
952         {\r
953                 //one=log.Tokenize(_T("\n"),pos);\r
954                 path.Reset();\r
955                 if(log[pos]=='\n')\r
956                         pos++;\r
957 \r
958                 if(log[pos]==':')\r
959                 {\r
960                         int end=log.find(0,pos);\r
961                         int actionstart=-1;\r
962                         int numfile=1;\r
963                         int file1=-1,file2=-1;\r
964                         if( end>0 )\r
965                         {\r
966                                 actionstart=log.find(' ',end-6);\r
967                                 pos=actionstart;\r
968                         }\r
969                         if( actionstart>0 )\r
970                         {\r
971                                 actionstart++;\r
972 \r
973                                 file1 = log.find(0,actionstart);\r
974                                 if( file1>=0 )\r
975                                 {\r
976                                         file1++;\r
977                                         pos=file1;\r
978                                 }\r
979                                 if( log[actionstart] == 'C' || log[actionstart] == 'R' )\r
980                                 {\r
981                                         file2=file1;\r
982                                         numfile=2;\r
983                                         file1 = log.find(0,file1);\r
984                                         if(file1>=0 )\r
985                                         {\r
986                                                 file1++;\r
987                                                 pos=file1;\r
988                                         }\r
989 \r
990                                 }\r
991                         }\r
992                         \r
993                         CString pathname1;\r
994                         CString pathname2;\r
995 \r
996                         if( file1>=0 )\r
997                                 g_Git.StringAppend(&pathname1,&log[file1],CP_OEMCP);\r
998                         if( file2>=0 )\r
999                                 g_Git.StringAppend(&pathname2,&log[file2],CP_OEMCP);\r
1000 \r
1001                         CTGitPath *GitPath=LookForGitPath(pathname1);\r
1002 \r
1003                         if(GitPath)\r
1004                         {\r
1005                                 this->m_Action|=GitPath->ParserAction( log[actionstart] );      \r
1006                                                         \r
1007                         }else\r
1008                         {       \r
1009                                 int ac=path.ParserAction(log[actionstart] );\r
1010 \r
1011                                 path.SetFromGit(pathname1,&pathname2);\r
1012                                 path.m_Action=ac;\r
1013                                         //action must be set after setfromgit. SetFromGit will clear all status. \r
1014                                 this->m_Action|=ac;\r
1015                                 AddPath(path);\r
1016                                 \r
1017                         }\r
1018         \r
1019                 }else\r
1020                 {                       \r
1021                         int tabstart=0;\r
1022                         path.Reset();\r
1023                         CString StatAdd;\r
1024                         CString StatDel;\r
1025                         CString file1;\r
1026                         CString file2;\r
1027 \r
1028                         tabstart=log.find('\t',pos);\r
1029                         if(tabstart >=0)\r
1030                         {\r
1031                                 log[tabstart]=0;\r
1032                                 g_Git.StringAppend(&StatAdd,&log[pos],CP_UTF8);\r
1033                                 pos=tabstart+1;\r
1034                         }\r
1035 \r
1036                         tabstart=log.find('\t',pos);\r
1037                         if(tabstart >=0)\r
1038                         {\r
1039                                 log[tabstart]=0;\r
1040                                 \r
1041                                 g_Git.StringAppend(&StatDel,&log[pos],CP_UTF8);\r
1042                                 pos=tabstart+1;\r
1043                         }\r
1044                         \r
1045                         if(log[pos] == 0) //rename\r
1046                         {\r
1047                                 pos++;\r
1048                                 g_Git.StringAppend(&file2,&log[pos],CP_OEMCP);\r
1049                                 int sec=log.find(0,pos);\r
1050                                 if(sec>=0)\r
1051                                 {\r
1052                                         sec++;\r
1053                                         g_Git.StringAppend(&file1,&log[sec],CP_OEMCP);\r
1054                                 }\r
1055 \r
1056                         }else\r
1057                         {\r
1058                                 g_Git.StringAppend(&file1,&log[pos],CP_OEMCP);\r
1059                         }\r
1060                         path.SetFromGit(file1,&file2);\r
1061         \r
1062                         CTGitPath *GitPath=LookForGitPath(path.GetGitPathString());\r
1063                         if(GitPath)\r
1064                         {\r
1065                                 GitPath->m_StatAdd=StatAdd;\r
1066                                 GitPath->m_StatDel=StatDel;\r
1067                         }else\r
1068                         {\r
1069                                 //path.SetFromGit(pathname);\r
1070                                 path.m_StatAdd=StatAdd;\r
1071                                 path.m_StatDel=StatDel;\r
1072                                 AddPath(path);\r
1073                         }\r
1074 \r
1075                 }\r
1076                 pos=log.findNextString(pos);\r
1077         }\r
1078         return pos;\r
1079 }\r
1080 \r
1081 void CTGitPathList::AddPath(const CTGitPath& newPath)\r
1082 {\r
1083         m_paths.push_back(newPath);\r
1084         m_commonBaseDirectory.Reset();\r
1085 }\r
1086 int CTGitPathList::GetCount() const\r
1087 {\r
1088         return (int)m_paths.size();\r
1089 }\r
1090 void CTGitPathList::Clear()\r
1091 {\r
1092         m_paths.clear();\r
1093         m_commonBaseDirectory.Reset();\r
1094 }\r
1095 \r
1096 const CTGitPath& CTGitPathList::operator[](INT_PTR index) const\r
1097 {\r
1098         ATLASSERT(index >= 0 && index < (INT_PTR)m_paths.size());\r
1099         return m_paths[index];\r
1100 }\r
1101 \r
1102 bool CTGitPathList::AreAllPathsFiles() const\r
1103 {\r
1104         // Look through the vector for any directories - if we find them, return false\r
1105         return std::find_if(m_paths.begin(), m_paths.end(), std::mem_fun_ref(&CTGitPath::IsDirectory)) == m_paths.end();\r
1106 }\r
1107 \r
1108 \r
1109 #if defined(_MFC_VER)\r
1110 \r
1111 bool CTGitPathList::LoadFromFile(const CTGitPath& filename)\r
1112 {\r
1113         Clear();\r
1114         try\r
1115         {\r
1116                 CString strLine;\r
1117                 CStdioFile file(filename.GetWinPath(), CFile::typeBinary | CFile::modeRead | CFile::shareDenyWrite);\r
1118 \r
1119                 // for every selected file/folder\r
1120                 CTGitPath path;\r
1121                 while (file.ReadString(strLine))\r
1122                 {\r
1123                         path.SetFromUnknown(strLine);\r
1124                         AddPath(path);\r
1125                 }\r
1126                 file.Close();\r
1127         }\r
1128         catch (CFileException* pE)\r
1129         {\r
1130                 TRACE("CFileException loading target file list\n");\r
1131                 TCHAR error[10000] = {0};\r
1132                 pE->GetErrorMessage(error, 10000);\r
1133 //              CMessageBox::Show(NULL, error, _T("TortoiseGit"), MB_ICONERROR);\r
1134                 pE->Delete();\r
1135                 return false;\r
1136         }\r
1137         return true;\r
1138 }\r
1139 \r
1140 bool CTGitPathList::WriteToFile(const CString& sFilename, bool bANSI /* = false */) const\r
1141 {\r
1142         try\r
1143         {\r
1144                 if (bANSI)\r
1145                 {\r
1146                         CStdioFile file(sFilename, CFile::typeText | CFile::modeReadWrite | CFile::modeCreate);\r
1147                         PathVector::const_iterator it;\r
1148                         for(it = m_paths.begin(); it != m_paths.end(); ++it)\r
1149                         {\r
1150                                 CStringA line = CStringA(it->GetGitPathString()) + '\n';\r
1151                                 file.Write(line, line.GetLength());\r
1152                         } \r
1153                         file.Close();\r
1154                 }\r
1155                 else\r
1156                 {\r
1157                         CStdioFile file(sFilename, CFile::typeBinary | CFile::modeReadWrite | CFile::modeCreate);\r
1158                         PathVector::const_iterator it;\r
1159                         for(it = m_paths.begin(); it != m_paths.end(); ++it)\r
1160                         {\r
1161                                 file.WriteString(it->GetGitPathString()+_T("\n"));\r
1162                         } \r
1163                         file.Close();\r
1164                 }\r
1165         }\r
1166         catch (CFileException* pE)\r
1167         {\r
1168                 TRACE("CFileException in writing temp file\n");\r
1169                 pE->Delete();\r
1170                 return false;\r
1171         }\r
1172         return true;\r
1173 }\r
1174 \r
1175 \r
1176 void CTGitPathList::LoadFromAsteriskSeparatedString(const CString& sPathString)\r
1177 {\r
1178         int pos = 0;\r
1179         CString temp;\r
1180         for(;;)\r
1181         {\r
1182                 temp = sPathString.Tokenize(_T("*"),pos);\r
1183                 if(temp.IsEmpty())\r
1184                 {\r
1185                         break;\r
1186                 }\r
1187                 AddPath(CTGitPath(CPathUtils::GetLongPathname(temp)));\r
1188         } \r
1189 }\r
1190 \r
1191 CString CTGitPathList::CreateAsteriskSeparatedString() const\r
1192 {\r
1193         CString sRet;\r
1194         PathVector::const_iterator it;\r
1195         for(it = m_paths.begin(); it != m_paths.end(); ++it)\r
1196         {\r
1197                 if (!sRet.IsEmpty())\r
1198                         sRet += _T("*");\r
1199                 sRet += it->GetWinPathString();\r
1200         }\r
1201         return sRet;\r
1202 }\r
1203 #endif // _MFC_VER\r
1204 \r
1205 bool \r
1206 CTGitPathList::AreAllPathsFilesInOneDirectory() const\r
1207 {\r
1208         // Check if all the paths are files and in the same directory\r
1209         PathVector::const_iterator it;\r
1210         m_commonBaseDirectory.Reset();\r
1211         for(it = m_paths.begin(); it != m_paths.end(); ++it)\r
1212         {\r
1213                 if(it->IsDirectory())\r
1214                 {\r
1215                         return false;\r
1216                 }\r
1217                 const CTGitPath& baseDirectory = it->GetDirectory();\r
1218                 if(m_commonBaseDirectory.IsEmpty())\r
1219                 {\r
1220                         m_commonBaseDirectory = baseDirectory;\r
1221                 }\r
1222                 else if(!m_commonBaseDirectory.IsEquivalentTo(baseDirectory))\r
1223                 {\r
1224                         // Different path\r
1225                         m_commonBaseDirectory.Reset();\r
1226                         return false;\r
1227                 }\r
1228         }\r
1229         return true;\r
1230 }\r
1231 \r
1232 CTGitPath CTGitPathList::GetCommonDirectory() const\r
1233 {\r
1234         if (m_commonBaseDirectory.IsEmpty())\r
1235         {\r
1236                 PathVector::const_iterator it;\r
1237                 for(it = m_paths.begin(); it != m_paths.end(); ++it)\r
1238                 {\r
1239                         const CTGitPath& baseDirectory = it->GetDirectory();\r
1240                         if(m_commonBaseDirectory.IsEmpty())\r
1241                         {\r
1242                                 m_commonBaseDirectory = baseDirectory;\r
1243                         }\r
1244                         else if(!m_commonBaseDirectory.IsEquivalentTo(baseDirectory))\r
1245                         {\r
1246                                 // Different path\r
1247                                 m_commonBaseDirectory.Reset();\r
1248                                 break;\r
1249                         }\r
1250                 }\r
1251         }\r
1252         // since we only checked strings, not paths,\r
1253         // we have to make sure now that we really return a *path* here\r
1254         PathVector::const_iterator iter;\r
1255         for(iter = m_paths.begin(); iter != m_paths.end(); ++iter)\r
1256         {\r
1257                 if (!m_commonBaseDirectory.IsAncestorOf(*iter))\r
1258                 {\r
1259                         m_commonBaseDirectory = m_commonBaseDirectory.GetContainingDirectory();\r
1260                         break;\r
1261                 }\r
1262         }       \r
1263         return m_commonBaseDirectory;\r
1264 }\r
1265 \r
1266 CTGitPath CTGitPathList::GetCommonRoot() const\r
1267 {\r
1268         PathVector::const_iterator it;\r
1269         CString sRoot, sTempRoot;\r
1270         bool bEqual = true;\r
1271 \r
1272         if (GetCount() == 1)\r
1273                 return m_paths[0];\r
1274 \r
1275         int backSlashPos = 0;\r
1276         int searchStartPos = 0;\r
1277         while (bEqual)\r
1278         {\r
1279                 for (it = m_paths.begin(); it != m_paths.end(); ++it)\r
1280                 {\r
1281                         if (backSlashPos == 0)\r
1282                         {\r
1283                                 backSlashPos = it->GetWinPathString().Find('\\', searchStartPos+1);\r
1284                                 if ((backSlashPos < 0)&&(searchStartPos != it->GetWinPathString().GetLength()))\r
1285                                         backSlashPos = it->GetWinPathString().GetLength();\r
1286                         }\r
1287                         else if (it->GetWinPathString().Find('\\', searchStartPos+1) != backSlashPos)\r
1288                         {\r
1289                                 if (it->GetWinPathString().Find('\\', searchStartPos+1) < 0)\r
1290                                 {\r
1291                                         if (it->GetWinPathString().GetLength() != backSlashPos)\r
1292                                         {\r
1293                                                 bEqual = false;\r
1294                                                 break;\r
1295                                         }\r
1296                                 }\r
1297                                 else\r
1298                                 {\r
1299                                         bEqual = false;\r
1300                                         break;\r
1301                                 }\r
1302                         }\r
1303                         if (backSlashPos < 0)\r
1304                         {\r
1305                                 bEqual = false;\r
1306                                 break;\r
1307                         }\r
1308                 }\r
1309                 if (bEqual == false)\r
1310                 {\r
1311                         if (searchStartPos)\r
1312                                 sRoot = m_paths[0].GetWinPathString().Left(searchStartPos+1);\r
1313                 }\r
1314                 else\r
1315                 {\r
1316                         searchStartPos = backSlashPos;\r
1317                 }\r
1318                 backSlashPos = 0;\r
1319         }\r
1320 \r
1321         return CTGitPath(sRoot.TrimRight('\\'));\r
1322 }\r
1323 \r
1324 void CTGitPathList::SortByPathname(bool bReverse /*= false*/)\r
1325 {\r
1326         std::sort(m_paths.begin(), m_paths.end());\r
1327         if (bReverse)\r
1328                 std::reverse(m_paths.begin(), m_paths.end());\r
1329 }\r
1330 \r
1331 void CTGitPathList::DeleteAllFiles(bool bTrash)\r
1332 {\r
1333         PathVector::const_iterator it;\r
1334         if (bTrash)\r
1335         {\r
1336                 SortByPathname();\r
1337                 CString sPaths;\r
1338                 for (it = m_paths.begin(); it != m_paths.end(); ++it)\r
1339                 {\r
1340                         if ((it->Exists())&&(!it->IsDirectory()))\r
1341                         {\r
1342                                 ::SetFileAttributes(it->GetWinPath(), FILE_ATTRIBUTE_NORMAL);\r
1343                                 sPaths += it->GetWinPath();\r
1344                                 sPaths += '\0';\r
1345                         }\r
1346                 }\r
1347                 sPaths += '\0';\r
1348                 sPaths += '\0';\r
1349                 SHFILEOPSTRUCT shop = {0};\r
1350                 shop.wFunc = FO_DELETE;\r
1351                 shop.pFrom = (LPCTSTR)sPaths;\r
1352                 shop.fFlags = FOF_ALLOWUNDO|FOF_NOCONFIRMATION|FOF_NOERRORUI|FOF_SILENT;\r
1353                 SHFileOperation(&shop);\r
1354         }\r
1355         else\r
1356         {\r
1357                 for (it = m_paths.begin(); it != m_paths.end(); ++it)\r
1358                 {\r
1359                         if (!it->IsDirectory())\r
1360                         {\r
1361                                 ::SetFileAttributes(it->GetWinPath(), FILE_ATTRIBUTE_NORMAL);\r
1362                                 ::DeleteFile(it->GetWinPath());\r
1363                         }\r
1364                 }\r
1365         }\r
1366         Clear();\r
1367 }\r
1368 \r
1369 void CTGitPathList::RemoveDuplicates()\r
1370 {\r
1371         SortByPathname();\r
1372         // Remove the duplicates\r
1373         // (Unique moves them to the end of the vector, then erase chops them off)\r
1374         m_paths.erase(std::unique(m_paths.begin(), m_paths.end(), &CTGitPath::PredLeftEquivalentToRight), m_paths.end());\r
1375 }\r
1376 \r
1377 void CTGitPathList::RemoveAdminPaths()\r
1378 {\r
1379         PathVector::iterator it;\r
1380         for(it = m_paths.begin(); it != m_paths.end(); )\r
1381         {\r
1382                 if (it->IsAdminDir())\r
1383                 {\r
1384                         m_paths.erase(it);\r
1385                         it = m_paths.begin();\r
1386                 }\r
1387                 else\r
1388                         ++it;\r
1389         }\r
1390 }\r
1391 \r
1392 void CTGitPathList::RemovePath(const CTGitPath& path)\r
1393 {\r
1394         PathVector::iterator it;\r
1395         for(it = m_paths.begin(); it != m_paths.end(); ++it)\r
1396         {\r
1397                 if (it->IsEquivalentTo(path))\r
1398                 {\r
1399                         m_paths.erase(it);\r
1400                         return;\r
1401                 }\r
1402         }\r
1403 }\r
1404 \r
1405 void CTGitPathList::RemoveItem(CTGitPath & path)\r
1406 {\r
1407         PathVector::iterator it;\r
1408         for(it = m_paths.begin(); it != m_paths.end(); ++it)\r
1409         {\r
1410                 if (it->GetGitPathString()==path.GetGitPathString())\r
1411                 {\r
1412                         m_paths.erase(it);\r
1413                         return;\r
1414                 }\r
1415         }\r
1416 }\r
1417 void CTGitPathList::RemoveChildren()\r
1418 {\r
1419         SortByPathname();\r
1420         m_paths.erase(std::unique(m_paths.begin(), m_paths.end(), &CTGitPath::CheckChild), m_paths.end());\r
1421 }\r
1422 \r
1423 bool CTGitPathList::IsEqual(const CTGitPathList& list)\r
1424 {\r
1425         if (list.GetCount() != GetCount())\r
1426                 return false;\r
1427         for (int i=0; i<list.GetCount(); ++i)\r
1428         {\r
1429                 if (!list[i].IsEquivalentTo(m_paths[i]))\r
1430                         return false;\r
1431         }\r
1432         return true;\r
1433 }\r
1434 \r
1435 //////////////////////////////////////////////////////////////////////////\r
1436 #if 0\r
1437 apr_array_header_t * CTGitPathList::MakePathArray (apr_pool_t *pool) const\r
1438 {\r
1439         apr_array_header_t *targets = apr_array_make (pool, GetCount(), sizeof(const char *));\r
1440 \r
1441         for(int nItem = 0; nItem < GetCount(); nItem++)\r
1442         {\r
1443                 const char * target = m_paths[nItem].GetGitApiPath(pool);\r
1444                 (*((const char **) apr_array_push (targets))) = target;\r
1445         }\r
1446 \r
1447         return targets;\r
1448 }\r
1449 #endif\r
1450 //////////////////////////////////////////////////////////////////////////\r
1451 \r
1452 #if 0\r
1453 #if defined(_DEBUG)\r
1454 // Some test cases for these classes\r
1455 static class CTGitPathTests\r
1456 {\r
1457 public:\r
1458         CTGitPathTests()\r
1459         {\r
1460                 apr_initialize();\r
1461                 pool = svn_pool_create(NULL);\r
1462                 GetDirectoryTest();\r
1463                 AdminDirTest();\r
1464                 SortTest();\r
1465                 RawAppendTest();\r
1466                 PathAppendTest();\r
1467                 RemoveDuplicatesTest();\r
1468                 RemoveChildrenTest();\r
1469                 ContainingDirectoryTest();\r
1470                 AncestorTest();\r
1471                 SubversionPathTest();\r
1472                 GetCommonRootTest();\r
1473 #if defined(_MFC_VER)\r
1474                 ValidPathAndUrlTest();\r
1475                 ListLoadingTest();\r
1476 #endif\r
1477                 apr_terminate();\r
1478         }\r
1479 \r
1480 private:\r
1481 //      apr_pool_t * pool;\r
1482         void GetDirectoryTest()\r
1483         {\r
1484                 // Bit tricky, this test, because we need to know something about the file\r
1485                 // layout on the machine which is running the test\r
1486                 TCHAR winDir[MAX_PATH+1];\r
1487                 GetWindowsDirectory(winDir, MAX_PATH);\r
1488                 CString sWinDir(winDir);\r
1489 \r
1490                 CTGitPath testPath;\r
1491                 // This is a file which we know will always be there\r
1492                 testPath.SetFromUnknown(sWinDir + _T("\\win.ini"));\r
1493                 ATLASSERT(!testPath.IsDirectory());\r
1494                 ATLASSERT(testPath.GetDirectory().GetWinPathString() == sWinDir);\r
1495                 ATLASSERT(testPath.GetContainingDirectory().GetWinPathString() == sWinDir);\r
1496 \r
1497                 // Now do the test on the win directory itself - It's hard to be sure about the containing directory\r
1498                 // but we know it must be different to the directory itself\r
1499                 testPath.SetFromUnknown(sWinDir);\r
1500                 ATLASSERT(testPath.IsDirectory());\r
1501                 ATLASSERT(testPath.GetDirectory().GetWinPathString() == sWinDir);\r
1502                 ATLASSERT(testPath.GetContainingDirectory().GetWinPathString() != sWinDir);\r
1503                 ATLASSERT(testPath.GetContainingDirectory().GetWinPathString().GetLength() < sWinDir.GetLength());\r
1504 \r
1505                 // Try a root path\r
1506                 testPath.SetFromUnknown(_T("C:\\"));\r
1507                 ATLASSERT(testPath.IsDirectory());\r
1508                 ATLASSERT(testPath.GetDirectory().GetWinPathString().CompareNoCase(_T("C:\\"))==0);\r
1509                 ATLASSERT(testPath.GetContainingDirectory().IsEmpty());\r
1510                 // Try a root UNC path\r
1511                 testPath.SetFromUnknown(_T("\\MYSTATION"));\r
1512                 ATLASSERT(testPath.GetContainingDirectory().IsEmpty());\r
1513         }\r
1514 \r
1515         void AdminDirTest()\r
1516         {\r
1517                 CTGitPath testPath;\r
1518                 testPath.SetFromUnknown(_T("c:\\.svndir"));\r
1519                 ATLASSERT(!testPath.IsAdminDir());\r
1520                 testPath.SetFromUnknown(_T("c:\\test.svn"));\r
1521                 ATLASSERT(!testPath.IsAdminDir());\r
1522                 testPath.SetFromUnknown(_T("c:\\.svn"));\r
1523                 ATLASSERT(testPath.IsAdminDir());\r
1524                 testPath.SetFromUnknown(_T("c:\\.svndir\\test"));\r
1525                 ATLASSERT(!testPath.IsAdminDir());\r
1526                 testPath.SetFromUnknown(_T("c:\\.svn\\test"));\r
1527                 ATLASSERT(testPath.IsAdminDir());\r
1528                 \r
1529                 CTGitPathList pathList;\r
1530                 pathList.AddPath(CTGitPath(_T("c:\\.svndir")));\r
1531                 pathList.AddPath(CTGitPath(_T("c:\\.svn")));\r
1532                 pathList.AddPath(CTGitPath(_T("c:\\.svn\\test")));\r
1533                 pathList.AddPath(CTGitPath(_T("c:\\test")));\r
1534                 pathList.RemoveAdminPaths();\r
1535                 ATLASSERT(pathList.GetCount()==2);\r
1536                 pathList.Clear();\r
1537                 pathList.AddPath(CTGitPath(_T("c:\\test")));\r
1538                 pathList.RemoveAdminPaths();\r
1539                 ATLASSERT(pathList.GetCount()==1);\r
1540         }\r
1541         \r
1542         void SortTest()\r
1543         {\r
1544                 CTGitPathList testList;\r
1545                 CTGitPath testPath;\r
1546                 testPath.SetFromUnknown(_T("c:/Z"));\r
1547                 testList.AddPath(testPath);\r
1548                 testPath.SetFromUnknown(_T("c:/B"));\r
1549                 testList.AddPath(testPath);\r
1550                 testPath.SetFromUnknown(_T("c:\\a"));\r
1551                 testList.AddPath(testPath);\r
1552                 testPath.SetFromUnknown(_T("c:/Test"));\r
1553                 testList.AddPath(testPath);\r
1554 \r
1555                 testList.SortByPathname();\r
1556 \r
1557                 ATLASSERT(testList[0].GetWinPathString() == _T("c:\\a"));\r
1558                 ATLASSERT(testList[1].GetWinPathString() == _T("c:\\B"));\r
1559                 ATLASSERT(testList[2].GetWinPathString() == _T("c:\\Test"));\r
1560                 ATLASSERT(testList[3].GetWinPathString() == _T("c:\\Z"));\r
1561         }\r
1562 \r
1563         void RawAppendTest()\r
1564         {\r
1565                 CTGitPath testPath(_T("c:/test/"));\r
1566                 testPath.AppendRawString(_T("/Hello"));\r
1567                 ATLASSERT(testPath.GetWinPathString() == _T("c:\\test\\Hello"));\r
1568 \r
1569                 testPath.AppendRawString(_T("\\T2"));\r
1570                 ATLASSERT(testPath.GetWinPathString() == _T("c:\\test\\Hello\\T2"));\r
1571 \r
1572                 CTGitPath testFilePath(_T("C:\\windows\\win.ini"));\r
1573                 CTGitPath testBasePath(_T("c:/temp/myfile.txt"));\r
1574                 testBasePath.AppendRawString(testFilePath.GetFileExtension());\r
1575                 ATLASSERT(testBasePath.GetWinPathString() == _T("c:\\temp\\myfile.txt.ini"));\r
1576         }\r
1577 \r
1578         void PathAppendTest()\r
1579         {\r
1580                 CTGitPath testPath(_T("c:/test/"));\r
1581                 testPath.AppendPathString(_T("/Hello"));\r
1582                 ATLASSERT(testPath.GetWinPathString() == _T("c:\\test\\Hello"));\r
1583 \r
1584                 testPath.AppendPathString(_T("T2"));\r
1585                 ATLASSERT(testPath.GetWinPathString() == _T("c:\\test\\Hello\\T2"));\r
1586 \r
1587                 CTGitPath testFilePath(_T("C:\\windows\\win.ini"));\r
1588                 CTGitPath testBasePath(_T("c:/temp/myfile.txt"));\r
1589                 // You wouldn't want to do this in real life - you'd use append-raw\r
1590                 testBasePath.AppendPathString(testFilePath.GetFileExtension());\r
1591                 ATLASSERT(testBasePath.GetWinPathString() == _T("c:\\temp\\myfile.txt\\.ini"));\r
1592         }\r
1593 \r
1594         void RemoveDuplicatesTest()\r
1595         {\r
1596                 CTGitPathList list;\r
1597                 list.AddPath(CTGitPath(_T("Z")));\r
1598                 list.AddPath(CTGitPath(_T("A")));\r
1599                 list.AddPath(CTGitPath(_T("E")));\r
1600                 list.AddPath(CTGitPath(_T("E")));\r
1601 \r
1602                 ATLASSERT(list[2].IsEquivalentTo(list[3]));\r
1603                 ATLASSERT(list[2]==list[3]);\r
1604                 \r
1605                 ATLASSERT(list.GetCount() == 4);\r
1606 \r
1607                 list.RemoveDuplicates();\r
1608 \r
1609                 ATLASSERT(list.GetCount() == 3);\r
1610 \r
1611                 ATLASSERT(list[0].GetWinPathString() == _T("A"));\r
1612                 ATLASSERT(list[1].GetWinPathString().Compare(_T("E")) == 0);\r
1613                 ATLASSERT(list[2].GetWinPathString() == _T("Z"));\r
1614         }\r
1615         \r
1616         void RemoveChildrenTest()\r
1617         {\r
1618                 CTGitPathList list;\r
1619                 list.AddPath(CTGitPath(_T("c:\\test")));\r
1620                 list.AddPath(CTGitPath(_T("c:\\test\\file")));\r
1621                 list.AddPath(CTGitPath(_T("c:\\testfile")));\r
1622                 list.AddPath(CTGitPath(_T("c:\\parent")));\r
1623                 list.AddPath(CTGitPath(_T("c:\\parent\\child")));\r
1624                 list.AddPath(CTGitPath(_T("c:\\parent\\child1")));\r
1625                 list.AddPath(CTGitPath(_T("c:\\parent\\child2")));\r
1626                 \r
1627                 ATLASSERT(list.GetCount() == 7);\r
1628 \r
1629                 list.RemoveChildren();\r
1630                 \r
1631                 ATLTRACE("count = %d\n", list.GetCount());\r
1632                 ATLASSERT(list.GetCount() == 3);\r
1633 \r
1634                 list.SortByPathname();\r
1635 \r
1636                 ATLASSERT(list[0].GetWinPathString().Compare(_T("c:\\parent")) == 0);\r
1637                 ATLASSERT(list[1].GetWinPathString().Compare(_T("c:\\test")) == 0);\r
1638                 ATLASSERT(list[2].GetWinPathString().Compare(_T("c:\\testfile")) == 0);\r
1639         }\r
1640 \r
1641 #if defined(_MFC_VER)\r
1642         void ListLoadingTest()\r
1643         {\r
1644                 TCHAR buf[MAX_PATH];\r
1645                 GetCurrentDirectory(MAX_PATH, buf);\r
1646                 CString sPathList(_T("Path1*c:\\path2 with spaces and stuff*\\funnypath\\*"));\r
1647                 CTGitPathList testList;\r
1648                 testList.LoadFromAsteriskSeparatedString(sPathList);\r
1649 \r
1650                 ATLASSERT(testList.GetCount() == 3);\r
1651                 ATLASSERT(testList[0].GetWinPathString() == CString(buf) + _T("\\Path1"));\r
1652                 ATLASSERT(testList[1].GetWinPathString() == _T("c:\\path2 with spaces and stuff"));\r
1653                 ATLASSERT(testList[2].GetWinPathString() == _T("\\funnypath"));\r
1654                 \r
1655                 ATLASSERT(testList.GetCommonRoot().GetWinPathString() == _T(""));\r
1656                 testList.Clear();\r
1657                 sPathList = _T("c:\\path2 with spaces and stuff*c:\\funnypath\\*");\r
1658                 testList.LoadFromAsteriskSeparatedString(sPathList);\r
1659                 ATLASSERT(testList.GetCommonRoot().GetWinPathString() == _T("c:\\"));\r
1660         }\r
1661 #endif \r
1662 \r
1663         void ContainingDirectoryTest()\r
1664         {\r
1665 \r
1666                 CTGitPath testPath;\r
1667                 testPath.SetFromWin(_T("c:\\a\\b\\c\\d\\e"));\r
1668                 CTGitPath dir;\r
1669                 dir = testPath.GetContainingDirectory();\r
1670                 ATLASSERT(dir.GetWinPathString() == _T("c:\\a\\b\\c\\d"));\r
1671                 dir = dir.GetContainingDirectory();\r
1672                 ATLASSERT(dir.GetWinPathString() == _T("c:\\a\\b\\c"));\r
1673                 dir = dir.GetContainingDirectory();\r
1674                 ATLASSERT(dir.GetWinPathString() == _T("c:\\a\\b"));\r
1675                 dir = dir.GetContainingDirectory();\r
1676                 ATLASSERT(dir.GetWinPathString() == _T("c:\\a"));\r
1677                 dir = dir.GetContainingDirectory();\r
1678                 ATLASSERT(dir.GetWinPathString() == _T("c:\\"));\r
1679                 dir = dir.GetContainingDirectory();\r
1680                 ATLASSERT(dir.IsEmpty());\r
1681                 ATLASSERT(dir.GetWinPathString() == _T(""));\r
1682         }\r
1683         \r
1684         void AncestorTest()\r
1685         {\r
1686                 CTGitPath testPath;\r
1687                 testPath.SetFromWin(_T("c:\\windows"));\r
1688                 ATLASSERT(testPath.IsAncestorOf(CTGitPath(_T("c:\\")))==false);\r
1689                 ATLASSERT(testPath.IsAncestorOf(CTGitPath(_T("c:\\windows"))));\r
1690                 ATLASSERT(testPath.IsAncestorOf(CTGitPath(_T("c:\\windowsdummy")))==false);\r
1691                 ATLASSERT(testPath.IsAncestorOf(CTGitPath(_T("c:\\windows\\test.txt"))));\r
1692                 ATLASSERT(testPath.IsAncestorOf(CTGitPath(_T("c:\\windows\\system32\\test.txt"))));\r
1693         }\r
1694 \r
1695         void SubversionPathTest()\r
1696         {\r
1697                 CTGitPath testPath;\r
1698                 testPath.SetFromWin(_T("c:\\"));\r
1699                 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "c:") == 0);\r
1700                 testPath.SetFromWin(_T("c:\\folder"));\r
1701                 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "c:/folder") == 0);\r
1702                 testPath.SetFromWin(_T("c:\\a\\b\\c\\d\\e"));\r
1703                 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "c:/a/b/c/d/e") == 0);\r
1704                 testPath.SetFromUnknown(_T("http://testing/"));\r
1705                 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "http://testing") == 0);\r
1706                 testPath.SetFromGit(NULL);\r
1707                 ATLASSERT(strlen(testPath.GetGitApiPath(pool))==0);\r
1708 #if defined(_MFC_VER)\r
1709                 testPath.SetFromUnknown(_T("http://testing again"));\r
1710                 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "http://testing%20again") == 0);\r
1711                 testPath.SetFromUnknown(_T("http://testing%20again"));\r
1712                 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "http://testing%20again") == 0);\r
1713                 testPath.SetFromUnknown(_T("http://testing special chars \344\366\374"));\r
1714                 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "http://testing%20special%20chars%20%c3%a4%c3%b6%c3%bc") == 0);          \r
1715 #endif\r
1716         }\r
1717 \r
1718         void GetCommonRootTest()\r
1719         {\r
1720                 CTGitPath pathA (_T("C:\\Development\\LogDlg.cpp"));\r
1721                 CTGitPath pathB (_T("C:\\Development\\LogDlg.h"));\r
1722                 CTGitPath pathC (_T("C:\\Development\\SomeDir\\LogDlg.h"));\r
1723                 \r
1724                 CTGitPathList list;\r
1725                 list.AddPath(pathA);\r
1726                 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("C:\\Development\\LogDlg.cpp"))==0);\r
1727                 list.AddPath(pathB);\r
1728                 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("C:\\Development"))==0);\r
1729                 list.AddPath(pathC);\r
1730                 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("C:\\Development"))==0);\r
1731 #ifdef _MFC_VER\r
1732                 list.Clear();\r
1733                 CString sPathList = _T("D:\\Development\\StExBar\\StExBar\\src\\setup\\Setup64.wxs*D:\\Development\\StExBar\\StExBar\\src\\setup\\Setup.wxs*D:\\Development\\StExBar\\SKTimeStamp\\src\\setup\\Setup.wxs*D:\\Development\\StExBar\\SKTimeStamp\\src\\setup\\Setup64.wxs");\r
1734                 list.LoadFromAsteriskSeparatedString(sPathList);\r
1735                 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("D:\\Development\\StExBar"))==0);\r
1736 \r
1737                 list.Clear();\r
1738                 sPathList = _T("c:\\windows\\explorer.exe*c:\\windows");\r
1739                 list.LoadFromAsteriskSeparatedString(sPathList);\r
1740                 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("c:\\windows"))==0);\r
1741 \r
1742                 list.Clear();\r
1743                 sPathList = _T("c:\\windows\\*c:\\windows");\r
1744                 list.LoadFromAsteriskSeparatedString(sPathList);\r
1745                 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("c:\\windows"))==0);\r
1746 \r
1747                 list.Clear();\r
1748                 sPathList = _T("c:\\windows\\system32*c:\\windows\\system");\r
1749                 list.LoadFromAsteriskSeparatedString(sPathList);\r
1750                 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("c:\\windows"))==0);\r
1751 \r
1752                 list.Clear();\r
1753                 sPathList = _T("c:\\windowsdummy*c:\\windows");\r
1754                 list.LoadFromAsteriskSeparatedString(sPathList);\r
1755                 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("c:\\"))==0);\r
1756 #endif\r
1757         }\r
1758         \r
1759         void ValidPathAndUrlTest()\r
1760         {\r
1761                 CTGitPath testPath;\r
1762                 testPath.SetFromWin(_T("c:\\a\\b\\c.test.txt"));\r
1763                 ATLASSERT(testPath.IsValidOnWindows());\r
1764                 testPath.SetFromWin(_T("c:\\"));\r
1765                 ATLASSERT(testPath.IsValidOnWindows());\r
1766                 testPath.SetFromWin(_T("D:\\.Net\\SpindleSearch\\"));\r
1767                 ATLASSERT(testPath.IsValidOnWindows());\r
1768                 testPath.SetFromWin(_T("c"));\r
1769                 ATLASSERT(testPath.IsValidOnWindows());\r
1770                 testPath.SetFromWin(_T("c:\\test folder\\file"));\r
1771                 ATLASSERT(testPath.IsValidOnWindows());\r
1772                 testPath.SetFromWin(_T("c:\\folder\\"));\r
1773                 ATLASSERT(testPath.IsValidOnWindows());\r
1774                 testPath.SetFromWin(_T("c:\\ext.ext.ext\\ext.ext.ext.ext"));\r
1775                 ATLASSERT(testPath.IsValidOnWindows());\r
1776                 testPath.SetFromWin(_T("c:\\.svn"));\r
1777                 ATLASSERT(testPath.IsValidOnWindows());\r
1778                 testPath.SetFromWin(_T("c:\\com\\file"));\r
1779                 ATLASSERT(testPath.IsValidOnWindows());\r
1780                 testPath.SetFromWin(_T("c:\\test\\conf"));\r
1781                 ATLASSERT(testPath.IsValidOnWindows());\r
1782                 testPath.SetFromWin(_T("c:\\LPT"));\r
1783                 ATLASSERT(testPath.IsValidOnWindows());\r
1784                 testPath.SetFromWin(_T("c:\\test\\LPT"));\r
1785                 ATLASSERT(testPath.IsValidOnWindows());\r
1786                 testPath.SetFromWin(_T("c:\\com1test"));\r
1787                 ATLASSERT(testPath.IsValidOnWindows());\r
1788                 testPath.SetFromWin(_T("\\\\?\\c:\\test\\com1test"));\r
1789                 ATLASSERT(testPath.IsValidOnWindows());\r
1790 \r
1791                 testPath.SetFromWin(_T("\\\\Share\\filename"));\r
1792                 ATLASSERT(testPath.IsValidOnWindows());\r
1793                 testPath.SetFromWin(_T("\\\\Share\\filename.extension"));\r
1794                 ATLASSERT(testPath.IsValidOnWindows());\r
1795                 testPath.SetFromWin(_T("\\\\Share\\.svn"));\r
1796                 ATLASSERT(testPath.IsValidOnWindows());\r
1797 \r
1798                 // now the negative tests\r
1799                 testPath.SetFromWin(_T("c:\\test:folder"));\r
1800                 ATLASSERT(!testPath.IsValidOnWindows());\r
1801                 testPath.SetFromWin(_T("c:\\file<name"));\r
1802                 ATLASSERT(!testPath.IsValidOnWindows());\r
1803                 testPath.SetFromWin(_T("c:\\something*else"));\r
1804                 ATLASSERT(!testPath.IsValidOnWindows());\r
1805                 testPath.SetFromWin(_T("c:\\folder\\file?nofile"));\r
1806                 ATLASSERT(!testPath.IsValidOnWindows());\r
1807                 testPath.SetFromWin(_T("c:\\ext.>ension"));\r
1808                 ATLASSERT(!testPath.IsValidOnWindows());\r
1809                 testPath.SetFromWin(_T("c:\\com1\\filename"));\r
1810                 ATLASSERT(!testPath.IsValidOnWindows());\r
1811                 testPath.SetFromWin(_T("c:\\com1"));\r
1812                 ATLASSERT(!testPath.IsValidOnWindows());\r
1813                 testPath.SetFromWin(_T("c:\\com1\\AuX"));\r
1814                 ATLASSERT(!testPath.IsValidOnWindows());\r
1815 \r
1816                 testPath.SetFromWin(_T("\\\\Share\\lpt9\\filename"));\r
1817                 ATLASSERT(!testPath.IsValidOnWindows());\r
1818                 testPath.SetFromWin(_T("\\\\Share\\prn"));\r
1819                 ATLASSERT(!testPath.IsValidOnWindows());\r
1820                 testPath.SetFromWin(_T("\\\\Share\\NUL"));\r
1821                 ATLASSERT(!testPath.IsValidOnWindows());\r
1822                 \r
1823                 // now come some URL tests\r
1824                 testPath.SetFromGit(_T("http://myserver.com/repos/trunk"));\r
1825                 ATLASSERT(testPath.IsValidOnWindows());\r
1826                 testPath.SetFromGit(_T("https://myserver.com/repos/trunk/file%20with%20spaces"));\r
1827                 ATLASSERT(testPath.IsValidOnWindows());\r
1828                 testPath.SetFromGit(_T("svn://myserver.com/repos/trunk/file with spaces"));\r
1829                 ATLASSERT(testPath.IsValidOnWindows());\r
1830                 testPath.SetFromGit(_T("svn+ssh://www.myserver.com/repos/trunk"));\r
1831                 ATLASSERT(testPath.IsValidOnWindows());\r
1832                 testPath.SetFromGit(_T("http://localhost:90/repos/trunk"));\r
1833                 ATLASSERT(testPath.IsValidOnWindows());\r
1834                 testPath.SetFromGit(_T("file:///C:/GitRepos/Tester/Proj1/tags/t2"));\r
1835                 ATLASSERT(testPath.IsValidOnWindows());\r
1836                 // and some negative URL tests\r
1837                 testPath.SetFromGit(_T("httpp://myserver.com/repos/trunk"));\r
1838                 ATLASSERT(!testPath.IsValidOnWindows());\r
1839                 testPath.SetFromGit(_T("https://myserver.com/rep:os/trunk/file%20with%20spaces"));\r
1840                 ATLASSERT(!testPath.IsValidOnWindows());\r
1841                 testPath.SetFromGit(_T("svn://myserver.com/rep<os/trunk/file with spaces"));\r
1842                 ATLASSERT(!testPath.IsValidOnWindows());\r
1843                 testPath.SetFromGit(_T("svn+ssh://www.myserver.com/repos/trunk/prn/"));\r
1844                 ATLASSERT(!testPath.IsValidOnWindows());\r
1845                 testPath.SetFromGit(_T("http://localhost:90/repos/trunk/com1"));\r
1846                 ATLASSERT(!testPath.IsValidOnWindows());\r
1847                 \r
1848         }\r
1849 \r
1850 } TGitPathTestobject;\r
1851 #endif\r
1852 #endif\r
1853 \r
1854 CTGitPath * CTGitPathList::LookForGitPath(CString path)\r
1855 {\r
1856         int i=0;\r
1857         for(i=0;i<this->GetCount();i++)\r
1858         {\r
1859                 if((*this)[i].GetGitPathString() == path )\r
1860                         return (CTGitPath*)&(*this)[i];\r
1861         }\r
1862         return NULL;\r
1863 }\r
1864 \r
1865 CString CTGitPath::GetActionName()\r
1866 {\r
1867         if(m_Action  & CTGitPath::LOGACTIONS_ADDED)\r
1868                 return _T("Added");\r
1869         if(m_Action  & CTGitPath::LOGACTIONS_DELETED)\r
1870                 return _T("Deleted");\r
1871         if(m_Action  & CTGitPath::LOGACTIONS_UNMERGED)\r
1872                 return _T("Conflict");\r
1873         if(m_Action  & CTGitPath::LOGACTIONS_MODIFIED)\r
1874                 return _T("Modified");\r
1875         if(m_Action  & CTGitPath::LOGACTIONS_REPLACED)\r
1876                 return _T("Rename");\r
1877         return _T("Unknown");\r
1878 }\r
1879 \r
1880 int CTGitPathList::GetAction()\r
1881 {\r
1882         return m_Action;\r
1883 }\r