OSDN Git Service

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