OSDN Git Service

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