OSDN Git Service

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