1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "UnicodeUtils.h"
22 #include "GitAdminDir.h"
23 #include "PathUtils.h"
29 //#include "MessageBox.h"
30 //#include "AppUtils.h"
39 CTGitPath::CTGitPath(void) :
40 m_bDirectoryKnown(false),
41 m_bIsDirectory(false),
44 m_bHasAdminDirKnown(false),
45 m_bHasAdminDir(false),
46 m_bIsValidOnWindowsKnown(false),
48 m_bIsAdminDirKnown(false),
51 m_bExistsKnown(false),
52 m_bLastWriteTimeKnown(0),
55 m_bIsSpecialDirectoryKnown(false),
56 m_bIsSpecialDirectory(false)
61 CTGitPath::~CTGitPath(void)
64 // Create a TGitPath object from an unknown path type (same as using SetFromUnknown)
65 CTGitPath::CTGitPath(const CString
& sUnknownPath
) :
66 m_bDirectoryKnown(false),
67 m_bIsDirectory(false),
70 m_bHasAdminDirKnown(false),
71 m_bHasAdminDir(false),
72 m_bIsValidOnWindowsKnown(false),
74 m_bIsAdminDirKnown(false),
77 m_bExistsKnown(false),
78 m_bLastWriteTimeKnown(0),
81 m_bIsSpecialDirectoryKnown(false),
82 m_bIsSpecialDirectory(false)
84 SetFromUnknown(sUnknownPath
);
89 int CTGitPath::ParserAction(BYTE action
)
91 //action=action.TrimLeft();
92 //TCHAR c=action.GetAt(0);
94 m_Action
|= LOGACTIONS_MODIFIED
;
96 m_Action
|= LOGACTIONS_REPLACED
;
98 m_Action
|= LOGACTIONS_ADDED
;
100 m_Action
|= LOGACTIONS_DELETED
;
102 m_Action
|= LOGACTIONS_UNMERGED
;
104 m_Action
|= LOGACTIONS_DELETED
;
106 m_Action
|= LOGACTIONS_CACHE
;
108 m_Action
|= LOGACTIONS_COPY
;
112 void CTGitPath::SetFromGit(const char* pPath
)
117 int len
= MultiByteToWideChar(CP_UTF8
, 0, pPath
, -1, NULL
, 0);
120 len
= MultiByteToWideChar(CP_UTF8
, 0, pPath
, -1, m_sFwdslashPath
.GetBuffer(len
+1), len
+1);
121 m_sFwdslashPath
.ReleaseBuffer(len
-1);
123 SanitizeRootPath(m_sFwdslashPath
, true);
126 void CTGitPath::SetFromGit(const char* pPath
, bool bIsDirectory
)
129 m_bDirectoryKnown
= true;
130 m_bIsDirectory
= bIsDirectory
;
133 void CTGitPath::SetFromGit(const TCHAR
* pPath
, bool bIsDirectory
)
138 m_sFwdslashPath
= pPath
;
139 SanitizeRootPath(m_sFwdslashPath
, true);
141 m_bDirectoryKnown
= true;
142 m_bIsDirectory
= bIsDirectory
;
145 void CTGitPath::SetFromGit(const CString
& sPath
,CString
*oldpath
)
148 m_sFwdslashPath
= sPath
;
149 SanitizeRootPath(m_sFwdslashPath
, true);
151 m_sOldFwdslashPath
= *oldpath
;
154 void CTGitPath::SetFromWin(LPCTSTR pPath
)
157 m_sBackslashPath
= pPath
;
158 SanitizeRootPath(m_sBackslashPath
, false);
159 ATLASSERT(m_sBackslashPath
.Find('/')<0);
161 void CTGitPath::SetFromWin(const CString
& sPath
)
164 m_sBackslashPath
= sPath
;
165 SanitizeRootPath(m_sBackslashPath
, false);
167 void CTGitPath::SetFromWin(const CString
& sPath
, bool bIsDirectory
)
170 m_sBackslashPath
= sPath
;
171 m_bIsDirectory
= bIsDirectory
;
172 m_bDirectoryKnown
= true;
173 SanitizeRootPath(m_sBackslashPath
, false);
175 void CTGitPath::SetFromUnknown(const CString
& sPath
)
178 // Just set whichever path we think is most likely to be used
179 // GitAdminDir admin;
181 // if(admin.HasAdminDir(sPath,&p))
182 // SetFwdslashPath(sPath.Right(sPath.GetLength()-p.GetLength()));
184 SetFwdslashPath(sPath
);
187 LPCTSTR
CTGitPath::GetWinPath() const
193 if(m_sBackslashPath
.IsEmpty())
195 SetBackslashPath(m_sFwdslashPath
);
197 return m_sBackslashPath
;
199 // This is a temporary function, to be used during the migration to
200 // the path class. Ultimately, functions consuming paths should take a CTGitPath&, not a CString
201 const CString
& CTGitPath::GetWinPathString() const
203 if(m_sBackslashPath
.IsEmpty())
205 SetBackslashPath(m_sFwdslashPath
);
207 return m_sBackslashPath
;
210 const CString
& CTGitPath::GetGitPathString() const
212 if(m_sFwdslashPath
.IsEmpty())
214 SetFwdslashPath(m_sBackslashPath
);
216 return m_sFwdslashPath
;
219 const CString
&CTGitPath::GetGitOldPathString() const
221 return m_sOldFwdslashPath
;
224 const char* CTGitPath::GetGitApiPath(apr_pool_t
*pool
) const
226 // This funny-looking 'if' is to avoid a subtle problem with empty paths, whereby
227 // each call to GetGitApiPath returns a different pointer value.
228 // If you made multiple calls to GetGitApiPath on the same string, only the last
229 // one would give you a valid pointer to an empty string, because each
230 // call would invalidate the previous call's return.
235 if(m_sFwdslashPath
.IsEmpty())
237 SetFwdslashPath(m_sBackslashPath
);
239 if(m_sUTF8FwdslashPath
.IsEmpty())
241 SetUTF8FwdslashPath(m_sFwdslashPath
);
243 if (svn_path_is_url(m_sUTF8FwdslashPath
))
245 m_sUTF8FwdslashPathEscaped
= CPathUtils::PathEscape(m_sUTF8FwdslashPath
);
246 m_sUTF8FwdslashPathEscaped
.Replace("file:////", "file:///\\");
247 m_sUTF8FwdslashPathEscaped
= svn_path_canonicalize(m_sUTF8FwdslashPathEscaped
, pool
);
248 return m_sUTF8FwdslashPathEscaped
;
250 m_sUTF8FwdslashPath
= svn_path_canonicalize(m_sUTF8FwdslashPath
, pool
);
252 return m_sUTF8FwdslashPath
;
256 const CString
& CTGitPath::GetUIPathString() const
258 if (m_sUIPath
.IsEmpty())
260 #if defined(_MFC_VER)
261 //BUGBUG HORRIBLE!!! - CPathUtils::IsEscaped doesn't need to be MFC-only
264 m_sUIPath
= CPathUtils::PathUnescape(GetGitPathString());
265 m_sUIPath
.Replace(_T("file:////"), _T("file:///\\"));
271 m_sUIPath
= GetWinPathString();
277 void CTGitPath::SetFwdslashPath(const CString
& sPath
) const
279 m_sFwdslashPath
= sPath
;
280 m_sFwdslashPath
.Replace('\\', '/');
282 // We don't leave a trailing /
283 m_sFwdslashPath
.TrimRight('/');
285 SanitizeRootPath(m_sFwdslashPath
, true);
287 m_sFwdslashPath
.Replace(_T("file:////"), _T("file:///\\"));
289 m_sUTF8FwdslashPath
.Empty();
292 void CTGitPath::SetBackslashPath(const CString
& sPath
) const
294 m_sBackslashPath
= sPath
;
295 m_sBackslashPath
.Replace('/', '\\');
296 m_sBackslashPath
.TrimRight('\\');
297 SanitizeRootPath(m_sBackslashPath
, false);
300 void CTGitPath::SetUTF8FwdslashPath(const CString
& sPath
) const
302 m_sUTF8FwdslashPath
= CUnicodeUtils::GetUTF8(sPath
);
305 void CTGitPath::SanitizeRootPath(CString
& sPath
, bool bIsForwardPath
) const
307 // Make sure to add the trailing slash to root paths such as 'C:'
308 if (sPath
.GetLength() == 2 && sPath
[1] == ':')
310 sPath
+= (bIsForwardPath
) ? _T("/") : _T("\\");
314 bool CTGitPath::IsUrl() const
319 EnsureFwdslashPathSet();
320 if(m_sUTF8FwdslashPath
.IsEmpty())
322 SetUTF8FwdslashPath(m_sFwdslashPath
);
324 m_bIsURL
= !!svn_path_is_url(m_sUTF8FwdslashPath
);
332 bool CTGitPath::IsDirectory() const
334 if(!m_bDirectoryKnown
)
338 return m_bIsDirectory
;
341 bool CTGitPath::Exists() const
350 bool CTGitPath::Delete(bool bTrash
) const
352 EnsureBackslashPathSet();
353 ::SetFileAttributes(m_sBackslashPath
, FILE_ATTRIBUTE_NORMAL
);
357 if ((bTrash
)||(IsDirectory()))
359 TCHAR
* buf
= new TCHAR
[m_sBackslashPath
.GetLength()+2];
360 _tcscpy_s(buf
, m_sBackslashPath
.GetLength()+2, m_sBackslashPath
);
361 buf
[m_sBackslashPath
.GetLength()] = 0;
362 buf
[m_sBackslashPath
.GetLength()+1] = 0;
363 SHFILEOPSTRUCT shop
= {0};
364 shop
.wFunc
= FO_DELETE
;
366 shop
.fFlags
= FOF_NOCONFIRMATION
|FOF_NOERRORUI
|FOF_SILENT
;
368 shop
.fFlags
|= FOF_ALLOWUNDO
;
369 bRet
= (SHFileOperation(&shop
) == 0);
374 bRet
= !!::DeleteFile(m_sBackslashPath
);
378 m_bExistsKnown
= true;
382 __int64
CTGitPath::GetLastWriteTime() const
384 if(!m_bLastWriteTimeKnown
)
388 return m_lastWriteTime
;
391 bool CTGitPath::IsReadOnly() const
393 if(!m_bLastWriteTimeKnown
)
397 return m_bIsReadOnly
;
400 void CTGitPath::UpdateAttributes() const
402 EnsureBackslashPathSet();
403 WIN32_FILE_ATTRIBUTE_DATA attribs
;
404 if(GetFileAttributesEx(m_sBackslashPath
, GetFileExInfoStandard
, &attribs
))
406 m_bIsDirectory
= !!(attribs
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
407 m_lastWriteTime
= *(__int64
*)&attribs
.ftLastWriteTime
;
408 m_bIsReadOnly
= !!(attribs
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
);
413 DWORD err
= GetLastError();
414 if ((err
== ERROR_FILE_NOT_FOUND
)||(err
== ERROR_PATH_NOT_FOUND
)||(err
== ERROR_INVALID_NAME
))
416 m_bIsDirectory
= false;
422 m_bIsDirectory
= false;
428 m_bDirectoryKnown
= true;
429 m_bLastWriteTimeKnown
= true;
430 m_bExistsKnown
= true;
433 CTGitPath
CTGitPath::GetSubPath(CTGitPath
&root
)
437 if(GetWinPathString().Left(root
.GetWinPathString().GetLength()) == root
.GetWinPathString())
439 CString str
=GetWinPathString();
440 path
.SetFromWin(str
.Right(str
.GetLength()-root
.GetWinPathString().GetLength()-1));
445 void CTGitPath::EnsureBackslashPathSet() const
447 if(m_sBackslashPath
.IsEmpty())
449 SetBackslashPath(m_sFwdslashPath
);
450 ATLASSERT(IsEmpty() || !m_sBackslashPath
.IsEmpty());
453 void CTGitPath::EnsureFwdslashPathSet() const
455 if(m_sFwdslashPath
.IsEmpty())
457 SetFwdslashPath(m_sBackslashPath
);
458 ATLASSERT(IsEmpty() || !m_sFwdslashPath
.IsEmpty());
463 // Reset all the caches
464 void CTGitPath::Reset()
466 m_bDirectoryKnown
= false;
468 m_bLastWriteTimeKnown
= false;
469 m_bHasAdminDirKnown
= false;
470 m_bIsValidOnWindowsKnown
= false;
471 m_bIsAdminDirKnown
= false;
472 m_bExistsKnown
= false;
473 m_bIsSpecialDirectoryKnown
= false;
474 m_bIsSpecialDirectory
= false;
476 m_sBackslashPath
.Empty();
477 m_sFwdslashPath
.Empty();
478 m_sUTF8FwdslashPath
.Empty();
480 this->m_StatAdd
=_T("");
481 this->m_StatDel
=_T("");
482 ATLASSERT(IsEmpty());
485 CTGitPath
CTGitPath::GetDirectory() const
487 if ((IsDirectory())||(!Exists()))
491 return GetContainingDirectory();
494 CTGitPath
CTGitPath::GetContainingDirectory() const
496 EnsureBackslashPathSet();
498 CString sDirName
= m_sBackslashPath
.Left(m_sBackslashPath
.ReverseFind('\\'));
499 if(sDirName
.GetLength() == 2 && sDirName
[1] == ':')
501 // This is a root directory, which needs a trailing slash
503 if(sDirName
== m_sBackslashPath
)
505 // We were clearly provided with a root path to start with - we should return nothing now
509 if(sDirName
.GetLength() == 1 && sDirName
[0] == '\\')
511 // We have an UNC path and we already are the root
515 retVal
.SetFromWin(sDirName
);
519 CString
CTGitPath::GetRootPathString() const
521 EnsureBackslashPathSet();
522 CString workingPath
= m_sBackslashPath
;
523 LPTSTR pPath
= workingPath
.GetBuffer(MAX_PATH
); // MAX_PATH ok here.
524 ATLVERIFY(::PathStripToRoot(pPath
));
525 workingPath
.ReleaseBuffer();
530 CString
CTGitPath::GetFilename() const
532 ATLASSERT(!IsDirectory());
533 return GetFileOrDirectoryName();
536 CString
CTGitPath::GetFileOrDirectoryName() const
538 EnsureBackslashPathSet();
539 return m_sBackslashPath
.Mid(m_sBackslashPath
.ReverseFind('\\')+1);
542 CString
CTGitPath::GetUIFileOrDirectoryName() const
545 return m_sUIPath
.Mid(m_sUIPath
.ReverseFind('\\')+1);
548 CString
CTGitPath::GetFileExtension() const
552 EnsureBackslashPathSet();
553 int dotPos
= m_sBackslashPath
.ReverseFind('.');
554 int slashPos
= m_sBackslashPath
.ReverseFind('\\');
555 if (dotPos
> slashPos
)
556 return m_sBackslashPath
.Mid(dotPos
);
560 CString
CTGitPath::GetBaseFilename() const
563 CString filename
=GetFilename();
564 dot
= filename
.ReverseFind(_T('.'));
566 return filename
.Left(dot
);
571 bool CTGitPath::ArePathStringsEqual(const CString
& sP1
, const CString
& sP2
)
573 int length
= sP1
.GetLength();
574 if(length
!= sP2
.GetLength())
579 // We work from the end of the strings, because path differences
580 // are more likely to occur at the far end of a string
581 LPCTSTR pP1Start
= sP1
;
582 LPCTSTR pP1
= pP1Start
+(length
-1);
583 LPCTSTR pP2
= ((LPCTSTR
)sP2
)+(length
-1);
586 if(_totlower(*pP1
--) != _totlower(*pP2
--))
594 bool CTGitPath::ArePathStringsEqualWithCase(const CString
& sP1
, const CString
& sP2
)
596 int length
= sP1
.GetLength();
597 if(length
!= sP2
.GetLength())
602 // We work from the end of the strings, because path differences
603 // are more likely to occur at the far end of a string
604 LPCTSTR pP1Start
= sP1
;
605 LPCTSTR pP1
= pP1Start
+(length
-1);
606 LPCTSTR pP2
= ((LPCTSTR
)sP2
)+(length
-1);
609 if((*pP1
--) != (*pP2
--))
617 bool CTGitPath::IsEmpty() const
619 // Check the backward slash path first, since the chance that this
620 // one is set is higher. In case of a 'false' return value it's a little
622 return m_sBackslashPath
.IsEmpty() && m_sFwdslashPath
.IsEmpty();
625 // Test if both paths refer to the same item
626 // Ignores case and slash direction
627 bool CTGitPath::IsEquivalentTo(const CTGitPath
& rhs
) const
629 // Try and find a slash direction which avoids having to convert
631 if(!m_sBackslashPath
.IsEmpty())
633 // *We've* got a \ path - make sure that the RHS also has a \ path
634 rhs
.EnsureBackslashPathSet();
635 return ArePathStringsEqualWithCase(m_sBackslashPath
, rhs
.m_sBackslashPath
);
639 // Assume we've got a fwdslash path and make sure that the RHS has one
640 rhs
.EnsureFwdslashPathSet();
641 return ArePathStringsEqualWithCase(m_sFwdslashPath
, rhs
.m_sFwdslashPath
);
645 bool CTGitPath::IsEquivalentToWithoutCase(const CTGitPath
& rhs
) const
647 // Try and find a slash direction which avoids having to convert
649 if(!m_sBackslashPath
.IsEmpty())
651 // *We've* got a \ path - make sure that the RHS also has a \ path
652 rhs
.EnsureBackslashPathSet();
653 return ArePathStringsEqual(m_sBackslashPath
, rhs
.m_sBackslashPath
);
657 // Assume we've got a fwdslash path and make sure that the RHS has one
658 rhs
.EnsureFwdslashPathSet();
659 return ArePathStringsEqual(m_sFwdslashPath
, rhs
.m_sFwdslashPath
);
663 bool CTGitPath::IsAncestorOf(const CTGitPath
& possibleDescendant
) const
665 possibleDescendant
.EnsureBackslashPathSet();
666 EnsureBackslashPathSet();
668 bool bPathStringsEqual
= ArePathStringsEqual(m_sBackslashPath
, possibleDescendant
.m_sBackslashPath
.Left(m_sBackslashPath
.GetLength()));
669 if (m_sBackslashPath
.GetLength() >= possibleDescendant
.GetWinPathString().GetLength())
671 return bPathStringsEqual
;
674 return (bPathStringsEqual
&&
675 ((possibleDescendant
.m_sBackslashPath
[m_sBackslashPath
.GetLength()] == '\\')||
676 (m_sBackslashPath
.GetLength()==3 && m_sBackslashPath
[1]==':')));
679 // Get a string representing the file path, optionally with a base
680 // section stripped off the front.
681 CString
CTGitPath::GetDisplayString(const CTGitPath
* pOptionalBasePath
/* = NULL*/) const
683 EnsureFwdslashPathSet();
684 if(pOptionalBasePath
!= NULL
)
686 // Find the length of the base-path without having to do an 'ensure' on it
687 int baseLength
= max(pOptionalBasePath
->m_sBackslashPath
.GetLength(), pOptionalBasePath
->m_sFwdslashPath
.GetLength());
689 // Now, chop that baseLength of the front of the path
690 return m_sFwdslashPath
.Mid(baseLength
).TrimLeft('/');
692 return m_sFwdslashPath
;
695 int CTGitPath::Compare(const CTGitPath
& left
, const CTGitPath
& right
)
697 left
.EnsureBackslashPathSet();
698 right
.EnsureBackslashPathSet();
699 return left
.m_sBackslashPath
.CompareNoCase(right
.m_sBackslashPath
);
702 bool operator<(const CTGitPath
& left
, const CTGitPath
& right
)
704 return CTGitPath::Compare(left
, right
) < 0;
707 bool CTGitPath::PredLeftEquivalentToRight(const CTGitPath
& left
, const CTGitPath
& right
)
709 return left
.IsEquivalentTo(right
);
712 bool CTGitPath::PredLeftSameWCPathAsRight(const CTGitPath
& left
, const CTGitPath
& right
)
714 if (left
.IsAdminDir() && right
.IsAdminDir())
720 l
= l
.GetContainingDirectory();
721 } while(l
.HasAdminDir());
724 r
= r
.GetContainingDirectory();
725 } while(r
.HasAdminDir());
726 return l
.GetContainingDirectory().IsEquivalentTo(r
.GetContainingDirectory());
728 return left
.GetDirectory().IsEquivalentTo(right
.GetDirectory());
731 bool CTGitPath::CheckChild(const CTGitPath
&parent
, const CTGitPath
& child
)
733 return parent
.IsAncestorOf(child
);
736 void CTGitPath::AppendRawString(const CString
& sAppend
)
738 EnsureFwdslashPathSet();
739 CString strCopy
= m_sFwdslashPath
+= sAppend
;
740 SetFromUnknown(strCopy
);
743 void CTGitPath::AppendPathString(const CString
& sAppend
)
745 EnsureBackslashPathSet();
746 CString
cleanAppend(sAppend
);
747 cleanAppend
.Replace('/', '\\');
748 cleanAppend
.TrimLeft('\\');
749 m_sBackslashPath
.TrimRight('\\');
750 CString strCopy
= m_sBackslashPath
+ _T("\\") + cleanAppend
;
754 bool CTGitPath::HasAdminDir() const
756 if (m_bHasAdminDirKnown
)
757 return m_bHasAdminDir
;
759 EnsureBackslashPathSet();
760 m_bHasAdminDir
= g_GitAdminDir
.HasAdminDir(m_sBackslashPath
, IsDirectory(), &m_sProjectRoot
);
761 m_bHasAdminDirKnown
= true;
762 return m_bHasAdminDir
;
765 bool CTGitPath::HasSubmodules() const
767 return !g_GitAdminDir
.GetSuperProjectRoot(GetWinPathString()).IsEmpty();
770 int CTGitPath::GetAdminDirMask() const
774 if(!g_GitAdminDir
.HasAdminDir(GetWinPathString(),&topdir
))
779 status
|= ITEMIS_INSVN
|ITEMIS_FOLDERINSVN
;
783 path
+=g_GitAdminDir
.GetAdminDirName();
784 path
+=_T("\\refs\\stash");
785 if( PathFileExists(path
) )
786 status
|= ITEMIS_STASH
;
790 path
+=g_GitAdminDir
.GetAdminDirName();
792 if( PathFileExists(path
) )
793 status
|= ITEMIS_GITSVN
;
796 path
+=_T("\\.gitmodules");
797 if( PathFileExists(path
) )
798 status
|= ITEMIS_SUBMODULE
;
803 bool CTGitPath::HasStashDir() const
806 if(!g_GitAdminDir
.HasAdminDir(GetWinPathString(),&topdir
))
811 topdir
+=g_GitAdminDir
.GetAdminDirName();
812 topdir
+=_T("\\refs\stash");
813 return PathFileExists(topdir
);
815 bool CTGitPath::HasGitSVNDir() const
818 if(!g_GitAdminDir
.HasAdminDir(GetWinPathString(),&topdir
))
823 topdir
+=g_GitAdminDir
.GetAdminDirName();
825 return PathFileExists(topdir
);
827 bool CTGitPath::HasAdminDir(CString
*ProjectTopDir
) const
829 if (m_bHasAdminDirKnown
)
832 *ProjectTopDir
= m_sProjectRoot
;
833 return m_bHasAdminDir
;
836 EnsureBackslashPathSet();
837 m_bHasAdminDir
= g_GitAdminDir
.HasAdminDir(m_sBackslashPath
, IsDirectory(), &m_sProjectRoot
);
838 m_bHasAdminDirKnown
= true;
840 *ProjectTopDir
= m_sProjectRoot
;
841 return m_bHasAdminDir
;
844 bool CTGitPath::IsAdminDir() const
846 if (m_bIsAdminDirKnown
)
847 return m_bIsAdminDir
;
849 EnsureBackslashPathSet();
850 m_bIsAdminDir
= g_GitAdminDir
.IsAdminDirPath(m_sBackslashPath
);
851 m_bIsAdminDirKnown
= true;
852 return m_bIsAdminDir
;
855 bool CTGitPath::IsValidOnWindows() const
857 if (m_bIsValidOnWindowsKnown
)
858 return m_bIsValidOnWindows
;
860 m_bIsValidOnWindows
= false;
861 EnsureBackslashPathSet();
862 CString sMatch
= m_sBackslashPath
+ _T("\r\n");
864 // the 'file://' URL is just a normal windows path:
865 if (sMatch
.Left(7).CompareNoCase(_T("file:\\\\"))==0)
867 sMatch
= sMatch
.Mid(7);
868 sMatch
.TrimLeft(_T("\\"));
869 sPattern
= _T("^(\\\\\\\\\\?\\\\)?(([a-zA-Z]:|\\\\)\\\\)?(((\\.)|(\\.\\.)|([^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?))\\\\)*[^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?$");
873 sPattern
= _T("^((http|https|svn|svn\\+ssh|file)\\:\\\\+([^\\\\@\\:]+\\:[^\\\\@\\:]+@)?\\\\[^\\\\]+(\\:\\d+)?)?(((\\.)|(\\.\\.)|([^\\\\/:\\*\\?\"\\|<>\\. ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?))\\\\)*[^\\\\/:\\*\\?\"\\|<>\\. ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?$");
877 sPattern
= _T("^(\\\\\\\\\\?\\\\)?(([a-zA-Z]:|\\\\)\\\\)?(((\\.)|(\\.\\.)|([^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?))\\\\)*[^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?$");
882 tr1::wregex
rx(sPattern
, tr1::regex_constants::icase
| tr1::regex_constants::ECMAScript
);
885 wstring rmatch
= wstring((LPCTSTR
)sMatch
);
886 if (tr1::regex_match(rmatch
, match
, rx
))
888 if (wstring(match
[0]).compare(sMatch
)==0)
889 m_bIsValidOnWindows
= true;
891 if (m_bIsValidOnWindows
)
893 // now check for illegal filenames
894 tr1::wregex
rx2(_T("\\\\(lpt\\d|com\\d|aux|nul|prn|con)(\\\\|$)"), tr1::regex_constants::icase
| tr1::regex_constants::ECMAScript
);
895 rmatch
= m_sBackslashPath
;
896 if (tr1::regex_search(rmatch
, rx2
, tr1::regex_constants::match_default
))
897 m_bIsValidOnWindows
= false;
902 m_bIsValidOnWindowsKnown
= true;
903 return m_bIsValidOnWindows
;
906 bool CTGitPath::IsSpecialDirectory() const
908 if (m_bIsSpecialDirectoryKnown
)
909 return m_bIsSpecialDirectory
;
911 static LPCTSTR specialDirectories
[]
912 = { _T("trunk"), _T("tags"), _T("branches") };
914 for (int i
=0 ; i
<(sizeof(specialDirectories
) / sizeof(specialDirectories
[0])) ; ++i
)
916 CString name
= GetFileOrDirectoryName();
917 if (0 == name
.CompareNoCase(specialDirectories
[i
]))
919 m_bIsSpecialDirectory
= true;
924 m_bIsSpecialDirectoryKnown
= true;
926 return m_bIsSpecialDirectory
;
929 //////////////////////////////////////////////////////////////////////////
931 CTGitPathList::CTGitPathList()
936 // A constructor which allows a path list to be easily built which one initial entry in
937 CTGitPathList::CTGitPathList(const CTGitPath
& firstEntry
)
941 int CTGitPathList::ParserFromLsFile(BYTE_VECTOR
&out
,bool staged
)
949 while(pos
>=0 && pos
<out
.size())
954 g_Git
.StringAppend(&one
,&out
[pos
],CP_ACP
);
956 path
.m_Action
=path
.ParserAction(out
[pos
]);
957 one
.Tokenize(_T("\t"),tabstart
);
960 path
.SetFromGit(one
.Right(one
.GetLength()-tabstart
));
964 part
=one
.Tokenize(_T(" "),tabstart
); //Tag
966 part
=one
.Tokenize(_T(" "),tabstart
); //Mode
968 part
=one
.Tokenize(_T(" "),tabstart
); //Hash
970 part
=one
.Tokenize(_T("\t"),tabstart
); //Stage
972 path
.m_Stage
=_ttol(part
);
976 pos
=out
.findNextString(pos
);
980 int CTGitPathList::FillUnRev(int action
,CTGitPathList
*list
)
990 count
=list
->GetCount();
991 for(int i
=0;i
<count
;i
++)
997 if(action
& CTGitPath::LOGACTIONS_IGNORE
)
998 ignored
= _T(" --ignored");
1002 cmd
=_T("git.exe ls-files --exclude-standard --full-name --others -z");
1007 { cmd
.Format(_T("git.exe ls-files --exclude-standard --full-name --others -z %s-- \"%s\""),
1009 (*list
)[i
].GetWinPathString());
1014 g_Git
.Run(cmd
,&out
);
1018 while( pos
>=0 && pos
<out
.size())
1021 g_Git
.StringAppend(&one
,&out
[pos
],CP_ACP
);
1024 //SetFromGit will clear all status
1025 path
.SetFromGit(one
);
1026 path
.m_Action
=action
;
1029 pos
=out
.findNextString(pos
);
1035 int CTGitPathList::ParserFromLog(BYTE_VECTOR
&log
)
1043 while( pos
>=0 && pos
<log
.size())
1045 //one=log.Tokenize(_T("\n"),pos);
1053 if(log
[pos
+1] ==':')
1057 int end
=log
.find(0,pos
);
1060 int file1
=-1,file2
=-1;
1063 actionstart
=log
.find(' ',end
-6);
1070 file1
= log
.find(0,actionstart
);
1076 if( log
[actionstart
] == 'C' || log
[actionstart
] == 'R' )
1080 file1
= log
.find(0,file1
);
1094 g_Git
.StringAppend(&pathname1
,&log
[file1
],CP_ACP
);
1096 g_Git
.StringAppend(&pathname2
,&log
[file2
],CP_ACP
);
1098 CTGitPath
*GitPath
=LookForGitPath(pathname1
);
1102 GitPath
->ParserAction( log
[actionstart
] );
1106 GitPath
->m_Action
|= CTGitPath::LOGACTIONS_MERGED
;
1107 GitPath
->m_Action
&= ~CTGitPath::LOGACTIONS_FORWORD
;
1109 m_Action
|=GitPath
->m_Action
;
1113 int ac
=path
.ParserAction(log
[actionstart
] );
1114 ac
|= merged
?CTGitPath::LOGACTIONS_MERGED
:0;
1116 path
.SetFromGit(pathname1
,&pathname2
);
1118 //action must be set after setfromgit. SetFromGit will clear all status.
1134 tabstart
=log
.find('\t',pos
);
1138 g_Git
.StringAppend(&StatAdd
,&log
[pos
],CP_UTF8
);
1142 tabstart
=log
.find('\t',pos
);
1147 g_Git
.StringAppend(&StatDel
,&log
[pos
],CP_UTF8
);
1151 if(log
[pos
] == 0) //rename
1154 g_Git
.StringAppend(&file2
,&log
[pos
],CP_ACP
);
1155 int sec
=log
.find(0,pos
);
1159 g_Git
.StringAppend(&file1
,&log
[sec
],CP_ACP
);
1165 g_Git
.StringAppend(&file1
,&log
[pos
],CP_ACP
);
1167 path
.SetFromGit(file1
,&file2
);
1169 CTGitPath
*GitPath
=LookForGitPath(path
.GetGitPathString());
1172 GitPath
->m_StatAdd
=StatAdd
;
1173 GitPath
->m_StatDel
=StatDel
;
1176 //path.SetFromGit(pathname);
1177 path
.m_StatAdd
=StatAdd
;
1178 path
.m_StatDel
=StatDel
;
1179 path
.m_Action
|= CTGitPath::LOGACTIONS_FORWORD
;
1184 pos
=log
.findNextString(pos
);
1189 void CTGitPathList::AddPath(const CTGitPath
& newPath
)
1191 m_paths
.push_back(newPath
);
1192 m_commonBaseDirectory
.Reset();
1194 int CTGitPathList::GetCount() const
1196 return (int)m_paths
.size();
1198 void CTGitPathList::Clear()
1201 m_commonBaseDirectory
.Reset();
1204 const CTGitPath
& CTGitPathList::operator[](INT_PTR index
) const
1206 ATLASSERT(index
>= 0 && index
< (INT_PTR
)m_paths
.size());
1207 return m_paths
[index
];
1210 bool CTGitPathList::AreAllPathsFiles() const
1212 // Look through the vector for any directories - if we find them, return false
1213 return std::find_if(m_paths
.begin(), m_paths
.end(), std::mem_fun_ref(&CTGitPath::IsDirectory
)) == m_paths
.end();
1217 #if defined(_MFC_VER)
1219 bool CTGitPathList::LoadFromFile(const CTGitPath
& filename
)
1225 CStdioFile
file(filename
.GetWinPath(), CFile::typeBinary
| CFile::modeRead
| CFile::shareDenyWrite
);
1227 // for every selected file/folder
1229 while (file
.ReadString(strLine
))
1231 path
.SetFromUnknown(strLine
);
1236 catch (CFileException
* pE
)
1238 TRACE("CFileException loading target file list\n");
1239 TCHAR error
[10000] = {0};
1240 pE
->GetErrorMessage(error
, 10000);
1241 // CMessageBox::Show(NULL, error, _T("TortoiseGit"), MB_ICONERROR);
1248 bool CTGitPathList::WriteToFile(const CString
& sFilename
, bool bANSI
/* = false */) const
1254 CStdioFile
file(sFilename
, CFile::typeText
| CFile::modeReadWrite
| CFile::modeCreate
);
1255 PathVector::const_iterator it
;
1256 for(it
= m_paths
.begin(); it
!= m_paths
.end(); ++it
)
1258 CStringA line
= CStringA(it
->GetGitPathString()) + '\n';
1259 file
.Write(line
, line
.GetLength());
1265 CStdioFile
file(sFilename
, CFile::typeBinary
| CFile::modeReadWrite
| CFile::modeCreate
);
1266 PathVector::const_iterator it
;
1267 for(it
= m_paths
.begin(); it
!= m_paths
.end(); ++it
)
1269 file
.WriteString(it
->GetGitPathString()+_T("\n"));
1274 catch (CFileException
* pE
)
1276 TRACE("CFileException in writing temp file\n");
1284 void CTGitPathList::LoadFromAsteriskSeparatedString(const CString
& sPathString
)
1290 temp
= sPathString
.Tokenize(_T("*"),pos
);
1295 AddPath(CTGitPath(CPathUtils::GetLongPathname(temp
)));
1299 CString
CTGitPathList::CreateAsteriskSeparatedString() const
1302 PathVector::const_iterator it
;
1303 for(it
= m_paths
.begin(); it
!= m_paths
.end(); ++it
)
1305 if (!sRet
.IsEmpty())
1307 sRet
+= it
->GetWinPathString();
1314 CTGitPathList::AreAllPathsFilesInOneDirectory() const
1316 // Check if all the paths are files and in the same directory
1317 PathVector::const_iterator it
;
1318 m_commonBaseDirectory
.Reset();
1319 for(it
= m_paths
.begin(); it
!= m_paths
.end(); ++it
)
1321 if(it
->IsDirectory())
1325 const CTGitPath
& baseDirectory
= it
->GetDirectory();
1326 if(m_commonBaseDirectory
.IsEmpty())
1328 m_commonBaseDirectory
= baseDirectory
;
1330 else if(!m_commonBaseDirectory
.IsEquivalentTo(baseDirectory
))
1333 m_commonBaseDirectory
.Reset();
1340 CTGitPath
CTGitPathList::GetCommonDirectory() const
1342 if (m_commonBaseDirectory
.IsEmpty())
1344 PathVector::const_iterator it
;
1345 for(it
= m_paths
.begin(); it
!= m_paths
.end(); ++it
)
1347 const CTGitPath
& baseDirectory
= it
->GetDirectory();
1348 if(m_commonBaseDirectory
.IsEmpty())
1350 m_commonBaseDirectory
= baseDirectory
;
1352 else if(!m_commonBaseDirectory
.IsEquivalentTo(baseDirectory
))
1355 m_commonBaseDirectory
.Reset();
1360 // since we only checked strings, not paths,
1361 // we have to make sure now that we really return a *path* here
1362 PathVector::const_iterator iter
;
1363 for(iter
= m_paths
.begin(); iter
!= m_paths
.end(); ++iter
)
1365 if (!m_commonBaseDirectory
.IsAncestorOf(*iter
))
1367 m_commonBaseDirectory
= m_commonBaseDirectory
.GetContainingDirectory();
1371 return m_commonBaseDirectory
;
1374 CTGitPath
CTGitPathList::GetCommonRoot() const
1376 PathVector::const_iterator it
;
1377 CString sRoot
, sTempRoot
;
1380 if (GetCount() == 1)
1383 int backSlashPos
= 0;
1384 int searchStartPos
= 0;
1387 for (it
= m_paths
.begin(); it
!= m_paths
.end(); ++it
)
1389 if (backSlashPos
== 0)
1391 backSlashPos
= it
->GetWinPathString().Find('\\', searchStartPos
+1);
1392 if ((backSlashPos
< 0)&&(searchStartPos
!= it
->GetWinPathString().GetLength()))
1393 backSlashPos
= it
->GetWinPathString().GetLength();
1395 else if (it
->GetWinPathString().Find('\\', searchStartPos
+1) != backSlashPos
)
1397 if (it
->GetWinPathString().Find('\\', searchStartPos
+1) < 0)
1399 if (it
->GetWinPathString().GetLength() != backSlashPos
)
1411 if (backSlashPos
< 0)
1417 if (bEqual
== false)
1420 sRoot
= m_paths
[0].GetWinPathString().Left(searchStartPos
+1);
1424 searchStartPos
= backSlashPos
;
1429 return CTGitPath(sRoot
.TrimRight('\\'));
1432 void CTGitPathList::SortByPathname(bool bReverse
/*= false*/)
1434 std::sort(m_paths
.begin(), m_paths
.end());
1436 std::reverse(m_paths
.begin(), m_paths
.end());
1439 void CTGitPathList::DeleteAllFiles(bool bTrash
)
1441 PathVector::const_iterator it
;
1446 for (it
= m_paths
.begin(); it
!= m_paths
.end(); ++it
)
1448 if ((it
->Exists())&&(!it
->IsDirectory()))
1450 ::SetFileAttributes(it
->GetWinPath(), FILE_ATTRIBUTE_NORMAL
);
1451 sPaths
+= it
->GetWinPath();
1457 SHFILEOPSTRUCT shop
= {0};
1458 shop
.wFunc
= FO_DELETE
;
1459 shop
.pFrom
= (LPCTSTR
)sPaths
;
1460 shop
.fFlags
= FOF_ALLOWUNDO
|FOF_NOCONFIRMATION
|FOF_NOERRORUI
|FOF_SILENT
;
1461 SHFileOperation(&shop
);
1465 for (it
= m_paths
.begin(); it
!= m_paths
.end(); ++it
)
1467 if (!it
->IsDirectory())
1469 ::SetFileAttributes(it
->GetWinPath(), FILE_ATTRIBUTE_NORMAL
);
1470 ::DeleteFile(it
->GetWinPath());
1477 void CTGitPathList::RemoveDuplicates()
1480 // Remove the duplicates
1481 // (Unique moves them to the end of the vector, then erase chops them off)
1482 m_paths
.erase(std::unique(m_paths
.begin(), m_paths
.end(), &CTGitPath::PredLeftEquivalentToRight
), m_paths
.end());
1485 void CTGitPathList::RemoveAdminPaths()
1487 PathVector::iterator it
;
1488 for(it
= m_paths
.begin(); it
!= m_paths
.end(); )
1490 if (it
->IsAdminDir())
1493 it
= m_paths
.begin();
1500 void CTGitPathList::RemovePath(const CTGitPath
& path
)
1502 PathVector::iterator it
;
1503 for(it
= m_paths
.begin(); it
!= m_paths
.end(); ++it
)
1505 if (it
->IsEquivalentTo(path
))
1513 void CTGitPathList::RemoveItem(CTGitPath
& path
)
1515 PathVector::iterator it
;
1516 for(it
= m_paths
.begin(); it
!= m_paths
.end(); ++it
)
1518 if (it
->GetGitPathString()==path
.GetGitPathString())
1525 void CTGitPathList::RemoveChildren()
1528 m_paths
.erase(std::unique(m_paths
.begin(), m_paths
.end(), &CTGitPath::CheckChild
), m_paths
.end());
1531 bool CTGitPathList::IsEqual(const CTGitPathList
& list
)
1533 if (list
.GetCount() != GetCount())
1535 for (int i
=0; i
<list
.GetCount(); ++i
)
1537 if (!list
[i
].IsEquivalentTo(m_paths
[i
]))
1543 //////////////////////////////////////////////////////////////////////////
1545 apr_array_header_t
* CTGitPathList::MakePathArray (apr_pool_t
*pool
) const
1547 apr_array_header_t
*targets
= apr_array_make (pool
, GetCount(), sizeof(const char *));
1549 for(int nItem
= 0; nItem
< GetCount(); nItem
++)
1551 const char * target
= m_paths
[nItem
].GetGitApiPath(pool
);
1552 (*((const char **) apr_array_push (targets
))) = target
;
1558 //////////////////////////////////////////////////////////////////////////
1562 // Some test cases for these classes
1563 static class CTGitPathTests
1569 pool
= svn_pool_create(NULL
);
1575 RemoveDuplicatesTest();
1576 RemoveChildrenTest();
1577 ContainingDirectoryTest();
1579 SubversionPathTest();
1580 GetCommonRootTest();
1581 #if defined(_MFC_VER)
1582 ValidPathAndUrlTest();
1589 // apr_pool_t * pool;
1590 void GetDirectoryTest()
1592 // Bit tricky, this test, because we need to know something about the file
1593 // layout on the machine which is running the test
1594 TCHAR winDir
[MAX_PATH
+1];
1595 GetWindowsDirectory(winDir
, MAX_PATH
);
1596 CString
sWinDir(winDir
);
1599 // This is a file which we know will always be there
1600 testPath
.SetFromUnknown(sWinDir
+ _T("\\win.ini"));
1601 ATLASSERT(!testPath
.IsDirectory());
1602 ATLASSERT(testPath
.GetDirectory().GetWinPathString() == sWinDir
);
1603 ATLASSERT(testPath
.GetContainingDirectory().GetWinPathString() == sWinDir
);
1605 // Now do the test on the win directory itself - It's hard to be sure about the containing directory
1606 // but we know it must be different to the directory itself
1607 testPath
.SetFromUnknown(sWinDir
);
1608 ATLASSERT(testPath
.IsDirectory());
1609 ATLASSERT(testPath
.GetDirectory().GetWinPathString() == sWinDir
);
1610 ATLASSERT(testPath
.GetContainingDirectory().GetWinPathString() != sWinDir
);
1611 ATLASSERT(testPath
.GetContainingDirectory().GetWinPathString().GetLength() < sWinDir
.GetLength());
1614 testPath
.SetFromUnknown(_T("C:\\"));
1615 ATLASSERT(testPath
.IsDirectory());
1616 ATLASSERT(testPath
.GetDirectory().GetWinPathString().CompareNoCase(_T("C:\\"))==0);
1617 ATLASSERT(testPath
.GetContainingDirectory().IsEmpty());
1618 // Try a root UNC path
1619 testPath
.SetFromUnknown(_T("\\MYSTATION"));
1620 ATLASSERT(testPath
.GetContainingDirectory().IsEmpty());
1626 testPath
.SetFromUnknown(_T("c:\\.svndir"));
1627 ATLASSERT(!testPath
.IsAdminDir());
1628 testPath
.SetFromUnknown(_T("c:\\test.svn"));
1629 ATLASSERT(!testPath
.IsAdminDir());
1630 testPath
.SetFromUnknown(_T("c:\\.svn"));
1631 ATLASSERT(testPath
.IsAdminDir());
1632 testPath
.SetFromUnknown(_T("c:\\.svndir\\test"));
1633 ATLASSERT(!testPath
.IsAdminDir());
1634 testPath
.SetFromUnknown(_T("c:\\.svn\\test"));
1635 ATLASSERT(testPath
.IsAdminDir());
1637 CTGitPathList pathList
;
1638 pathList
.AddPath(CTGitPath(_T("c:\\.svndir")));
1639 pathList
.AddPath(CTGitPath(_T("c:\\.svn")));
1640 pathList
.AddPath(CTGitPath(_T("c:\\.svn\\test")));
1641 pathList
.AddPath(CTGitPath(_T("c:\\test")));
1642 pathList
.RemoveAdminPaths();
1643 ATLASSERT(pathList
.GetCount()==2);
1645 pathList
.AddPath(CTGitPath(_T("c:\\test")));
1646 pathList
.RemoveAdminPaths();
1647 ATLASSERT(pathList
.GetCount()==1);
1652 CTGitPathList testList
;
1654 testPath
.SetFromUnknown(_T("c:/Z"));
1655 testList
.AddPath(testPath
);
1656 testPath
.SetFromUnknown(_T("c:/B"));
1657 testList
.AddPath(testPath
);
1658 testPath
.SetFromUnknown(_T("c:\\a"));
1659 testList
.AddPath(testPath
);
1660 testPath
.SetFromUnknown(_T("c:/Test"));
1661 testList
.AddPath(testPath
);
1663 testList
.SortByPathname();
1665 ATLASSERT(testList
[0].GetWinPathString() == _T("c:\\a"));
1666 ATLASSERT(testList
[1].GetWinPathString() == _T("c:\\B"));
1667 ATLASSERT(testList
[2].GetWinPathString() == _T("c:\\Test"));
1668 ATLASSERT(testList
[3].GetWinPathString() == _T("c:\\Z"));
1671 void RawAppendTest()
1673 CTGitPath
testPath(_T("c:/test/"));
1674 testPath
.AppendRawString(_T("/Hello"));
1675 ATLASSERT(testPath
.GetWinPathString() == _T("c:\\test\\Hello"));
1677 testPath
.AppendRawString(_T("\\T2"));
1678 ATLASSERT(testPath
.GetWinPathString() == _T("c:\\test\\Hello\\T2"));
1680 CTGitPath
testFilePath(_T("C:\\windows\\win.ini"));
1681 CTGitPath
testBasePath(_T("c:/temp/myfile.txt"));
1682 testBasePath
.AppendRawString(testFilePath
.GetFileExtension());
1683 ATLASSERT(testBasePath
.GetWinPathString() == _T("c:\\temp\\myfile.txt.ini"));
1686 void PathAppendTest()
1688 CTGitPath
testPath(_T("c:/test/"));
1689 testPath
.AppendPathString(_T("/Hello"));
1690 ATLASSERT(testPath
.GetWinPathString() == _T("c:\\test\\Hello"));
1692 testPath
.AppendPathString(_T("T2"));
1693 ATLASSERT(testPath
.GetWinPathString() == _T("c:\\test\\Hello\\T2"));
1695 CTGitPath
testFilePath(_T("C:\\windows\\win.ini"));
1696 CTGitPath
testBasePath(_T("c:/temp/myfile.txt"));
1697 // You wouldn't want to do this in real life - you'd use append-raw
1698 testBasePath
.AppendPathString(testFilePath
.GetFileExtension());
1699 ATLASSERT(testBasePath
.GetWinPathString() == _T("c:\\temp\\myfile.txt\\.ini"));
1702 void RemoveDuplicatesTest()
1705 list
.AddPath(CTGitPath(_T("Z")));
1706 list
.AddPath(CTGitPath(_T("A")));
1707 list
.AddPath(CTGitPath(_T("E")));
1708 list
.AddPath(CTGitPath(_T("E")));
1710 ATLASSERT(list
[2].IsEquivalentTo(list
[3]));
1711 ATLASSERT(list
[2]==list
[3]);
1713 ATLASSERT(list
.GetCount() == 4);
1715 list
.RemoveDuplicates();
1717 ATLASSERT(list
.GetCount() == 3);
1719 ATLASSERT(list
[0].GetWinPathString() == _T("A"));
1720 ATLASSERT(list
[1].GetWinPathString().Compare(_T("E")) == 0);
1721 ATLASSERT(list
[2].GetWinPathString() == _T("Z"));
1724 void RemoveChildrenTest()
1727 list
.AddPath(CTGitPath(_T("c:\\test")));
1728 list
.AddPath(CTGitPath(_T("c:\\test\\file")));
1729 list
.AddPath(CTGitPath(_T("c:\\testfile")));
1730 list
.AddPath(CTGitPath(_T("c:\\parent")));
1731 list
.AddPath(CTGitPath(_T("c:\\parent\\child")));
1732 list
.AddPath(CTGitPath(_T("c:\\parent\\child1")));
1733 list
.AddPath(CTGitPath(_T("c:\\parent\\child2")));
1735 ATLASSERT(list
.GetCount() == 7);
1737 list
.RemoveChildren();
1739 ATLTRACE("count = %d\n", list
.GetCount());
1740 ATLASSERT(list
.GetCount() == 3);
1742 list
.SortByPathname();
1744 ATLASSERT(list
[0].GetWinPathString().Compare(_T("c:\\parent")) == 0);
1745 ATLASSERT(list
[1].GetWinPathString().Compare(_T("c:\\test")) == 0);
1746 ATLASSERT(list
[2].GetWinPathString().Compare(_T("c:\\testfile")) == 0);
1749 #if defined(_MFC_VER)
1750 void ListLoadingTest()
1752 TCHAR buf
[MAX_PATH
];
1753 GetCurrentDirectory(MAX_PATH
, buf
);
1754 CString
sPathList(_T("Path1*c:\\path2 with spaces and stuff*\\funnypath\\*"));
1755 CTGitPathList testList
;
1756 testList
.LoadFromAsteriskSeparatedString(sPathList
);
1758 ATLASSERT(testList
.GetCount() == 3);
1759 ATLASSERT(testList
[0].GetWinPathString() == CString(buf
) + _T("\\Path1"));
1760 ATLASSERT(testList
[1].GetWinPathString() == _T("c:\\path2 with spaces and stuff"));
1761 ATLASSERT(testList
[2].GetWinPathString() == _T("\\funnypath"));
1763 ATLASSERT(testList
.GetCommonRoot().GetWinPathString() == _T(""));
1765 sPathList
= _T("c:\\path2 with spaces and stuff*c:\\funnypath\\*");
1766 testList
.LoadFromAsteriskSeparatedString(sPathList
);
1767 ATLASSERT(testList
.GetCommonRoot().GetWinPathString() == _T("c:\\"));
1771 void ContainingDirectoryTest()
1775 testPath
.SetFromWin(_T("c:\\a\\b\\c\\d\\e"));
1777 dir
= testPath
.GetContainingDirectory();
1778 ATLASSERT(dir
.GetWinPathString() == _T("c:\\a\\b\\c\\d"));
1779 dir
= dir
.GetContainingDirectory();
1780 ATLASSERT(dir
.GetWinPathString() == _T("c:\\a\\b\\c"));
1781 dir
= dir
.GetContainingDirectory();
1782 ATLASSERT(dir
.GetWinPathString() == _T("c:\\a\\b"));
1783 dir
= dir
.GetContainingDirectory();
1784 ATLASSERT(dir
.GetWinPathString() == _T("c:\\a"));
1785 dir
= dir
.GetContainingDirectory();
1786 ATLASSERT(dir
.GetWinPathString() == _T("c:\\"));
1787 dir
= dir
.GetContainingDirectory();
1788 ATLASSERT(dir
.IsEmpty());
1789 ATLASSERT(dir
.GetWinPathString() == _T(""));
1795 testPath
.SetFromWin(_T("c:\\windows"));
1796 ATLASSERT(testPath
.IsAncestorOf(CTGitPath(_T("c:\\")))==false);
1797 ATLASSERT(testPath
.IsAncestorOf(CTGitPath(_T("c:\\windows"))));
1798 ATLASSERT(testPath
.IsAncestorOf(CTGitPath(_T("c:\\windowsdummy")))==false);
1799 ATLASSERT(testPath
.IsAncestorOf(CTGitPath(_T("c:\\windows\\test.txt"))));
1800 ATLASSERT(testPath
.IsAncestorOf(CTGitPath(_T("c:\\windows\\system32\\test.txt"))));
1803 void SubversionPathTest()
1806 testPath
.SetFromWin(_T("c:\\"));
1807 ATLASSERT(strcmp(testPath
.GetGitApiPath(pool
), "c:") == 0);
1808 testPath
.SetFromWin(_T("c:\\folder"));
1809 ATLASSERT(strcmp(testPath
.GetGitApiPath(pool
), "c:/folder") == 0);
1810 testPath
.SetFromWin(_T("c:\\a\\b\\c\\d\\e"));
1811 ATLASSERT(strcmp(testPath
.GetGitApiPath(pool
), "c:/a/b/c/d/e") == 0);
1812 testPath
.SetFromUnknown(_T("http://testing/"));
1813 ATLASSERT(strcmp(testPath
.GetGitApiPath(pool
), "http://testing") == 0);
1814 testPath
.SetFromGit(NULL
);
1815 ATLASSERT(strlen(testPath
.GetGitApiPath(pool
))==0);
1816 #if defined(_MFC_VER)
1817 testPath
.SetFromUnknown(_T("http://testing again"));
1818 ATLASSERT(strcmp(testPath
.GetGitApiPath(pool
), "http://testing%20again") == 0);
1819 testPath
.SetFromUnknown(_T("http://testing%20again"));
1820 ATLASSERT(strcmp(testPath
.GetGitApiPath(pool
), "http://testing%20again") == 0);
1821 testPath
.SetFromUnknown(_T("http://testing special chars \344\366\374"));
1822 ATLASSERT(strcmp(testPath
.GetGitApiPath(pool
), "http://testing%20special%20chars%20%c3%a4%c3%b6%c3%bc") == 0);
1826 void GetCommonRootTest()
1828 CTGitPath
pathA (_T("C:\\Development\\LogDlg.cpp"));
1829 CTGitPath
pathB (_T("C:\\Development\\LogDlg.h"));
1830 CTGitPath
pathC (_T("C:\\Development\\SomeDir\\LogDlg.h"));
1833 list
.AddPath(pathA
);
1834 ATLASSERT(list
.GetCommonRoot().GetWinPathString().CompareNoCase(_T("C:\\Development\\LogDlg.cpp"))==0);
1835 list
.AddPath(pathB
);
1836 ATLASSERT(list
.GetCommonRoot().GetWinPathString().CompareNoCase(_T("C:\\Development"))==0);
1837 list
.AddPath(pathC
);
1838 ATLASSERT(list
.GetCommonRoot().GetWinPathString().CompareNoCase(_T("C:\\Development"))==0);
1841 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");
1842 list
.LoadFromAsteriskSeparatedString(sPathList
);
1843 ATLASSERT(list
.GetCommonRoot().GetWinPathString().CompareNoCase(_T("D:\\Development\\StExBar"))==0);
1846 sPathList
= _T("c:\\windows\\explorer.exe*c:\\windows");
1847 list
.LoadFromAsteriskSeparatedString(sPathList
);
1848 ATLASSERT(list
.GetCommonRoot().GetWinPathString().CompareNoCase(_T("c:\\windows"))==0);
1851 sPathList
= _T("c:\\windows\\*c:\\windows");
1852 list
.LoadFromAsteriskSeparatedString(sPathList
);
1853 ATLASSERT(list
.GetCommonRoot().GetWinPathString().CompareNoCase(_T("c:\\windows"))==0);
1856 sPathList
= _T("c:\\windows\\system32*c:\\windows\\system");
1857 list
.LoadFromAsteriskSeparatedString(sPathList
);
1858 ATLASSERT(list
.GetCommonRoot().GetWinPathString().CompareNoCase(_T("c:\\windows"))==0);
1861 sPathList
= _T("c:\\windowsdummy*c:\\windows");
1862 list
.LoadFromAsteriskSeparatedString(sPathList
);
1863 ATLASSERT(list
.GetCommonRoot().GetWinPathString().CompareNoCase(_T("c:\\"))==0);
1867 void ValidPathAndUrlTest()
1870 testPath
.SetFromWin(_T("c:\\a\\b\\c.test.txt"));
1871 ATLASSERT(testPath
.IsValidOnWindows());
1872 testPath
.SetFromWin(_T("c:\\"));
1873 ATLASSERT(testPath
.IsValidOnWindows());
1874 testPath
.SetFromWin(_T("D:\\.Net\\SpindleSearch\\"));
1875 ATLASSERT(testPath
.IsValidOnWindows());
1876 testPath
.SetFromWin(_T("c"));
1877 ATLASSERT(testPath
.IsValidOnWindows());
1878 testPath
.SetFromWin(_T("c:\\test folder\\file"));
1879 ATLASSERT(testPath
.IsValidOnWindows());
1880 testPath
.SetFromWin(_T("c:\\folder\\"));
1881 ATLASSERT(testPath
.IsValidOnWindows());
1882 testPath
.SetFromWin(_T("c:\\ext.ext.ext\\ext.ext.ext.ext"));
1883 ATLASSERT(testPath
.IsValidOnWindows());
1884 testPath
.SetFromWin(_T("c:\\.svn"));
1885 ATLASSERT(testPath
.IsValidOnWindows());
1886 testPath
.SetFromWin(_T("c:\\com\\file"));
1887 ATLASSERT(testPath
.IsValidOnWindows());
1888 testPath
.SetFromWin(_T("c:\\test\\conf"));
1889 ATLASSERT(testPath
.IsValidOnWindows());
1890 testPath
.SetFromWin(_T("c:\\LPT"));
1891 ATLASSERT(testPath
.IsValidOnWindows());
1892 testPath
.SetFromWin(_T("c:\\test\\LPT"));
1893 ATLASSERT(testPath
.IsValidOnWindows());
1894 testPath
.SetFromWin(_T("c:\\com1test"));
1895 ATLASSERT(testPath
.IsValidOnWindows());
1896 testPath
.SetFromWin(_T("\\\\?\\c:\\test\\com1test"));
1897 ATLASSERT(testPath
.IsValidOnWindows());
1899 testPath
.SetFromWin(_T("\\\\Share\\filename"));
1900 ATLASSERT(testPath
.IsValidOnWindows());
1901 testPath
.SetFromWin(_T("\\\\Share\\filename.extension"));
1902 ATLASSERT(testPath
.IsValidOnWindows());
1903 testPath
.SetFromWin(_T("\\\\Share\\.svn"));
1904 ATLASSERT(testPath
.IsValidOnWindows());
1906 // now the negative tests
1907 testPath
.SetFromWin(_T("c:\\test:folder"));
1908 ATLASSERT(!testPath
.IsValidOnWindows());
1909 testPath
.SetFromWin(_T("c:\\file<name"));
1910 ATLASSERT(!testPath
.IsValidOnWindows());
1911 testPath
.SetFromWin(_T("c:\\something*else"));
1912 ATLASSERT(!testPath
.IsValidOnWindows());
1913 testPath
.SetFromWin(_T("c:\\folder\\file?nofile"));
1914 ATLASSERT(!testPath
.IsValidOnWindows());
1915 testPath
.SetFromWin(_T("c:\\ext.>ension"));
1916 ATLASSERT(!testPath
.IsValidOnWindows());
1917 testPath
.SetFromWin(_T("c:\\com1\\filename"));
1918 ATLASSERT(!testPath
.IsValidOnWindows());
1919 testPath
.SetFromWin(_T("c:\\com1"));
1920 ATLASSERT(!testPath
.IsValidOnWindows());
1921 testPath
.SetFromWin(_T("c:\\com1\\AuX"));
1922 ATLASSERT(!testPath
.IsValidOnWindows());
1924 testPath
.SetFromWin(_T("\\\\Share\\lpt9\\filename"));
1925 ATLASSERT(!testPath
.IsValidOnWindows());
1926 testPath
.SetFromWin(_T("\\\\Share\\prn"));
1927 ATLASSERT(!testPath
.IsValidOnWindows());
1928 testPath
.SetFromWin(_T("\\\\Share\\NUL"));
1929 ATLASSERT(!testPath
.IsValidOnWindows());
1931 // now come some URL tests
1932 testPath
.SetFromGit(_T("http://myserver.com/repos/trunk"));
1933 ATLASSERT(testPath
.IsValidOnWindows());
1934 testPath
.SetFromGit(_T("https://myserver.com/repos/trunk/file%20with%20spaces"));
1935 ATLASSERT(testPath
.IsValidOnWindows());
1936 testPath
.SetFromGit(_T("svn://myserver.com/repos/trunk/file with spaces"));
1937 ATLASSERT(testPath
.IsValidOnWindows());
1938 testPath
.SetFromGit(_T("svn+ssh://www.myserver.com/repos/trunk"));
1939 ATLASSERT(testPath
.IsValidOnWindows());
1940 testPath
.SetFromGit(_T("http://localhost:90/repos/trunk"));
1941 ATLASSERT(testPath
.IsValidOnWindows());
1942 testPath
.SetFromGit(_T("file:///C:/GitRepos/Tester/Proj1/tags/t2"));
1943 ATLASSERT(testPath
.IsValidOnWindows());
1944 // and some negative URL tests
1945 testPath
.SetFromGit(_T("httpp://myserver.com/repos/trunk"));
1946 ATLASSERT(!testPath
.IsValidOnWindows());
1947 testPath
.SetFromGit(_T("https://myserver.com/rep:os/trunk/file%20with%20spaces"));
1948 ATLASSERT(!testPath
.IsValidOnWindows());
1949 testPath
.SetFromGit(_T("svn://myserver.com/rep<os/trunk/file with spaces"));
1950 ATLASSERT(!testPath
.IsValidOnWindows());
1951 testPath
.SetFromGit(_T("svn+ssh://www.myserver.com/repos/trunk/prn/"));
1952 ATLASSERT(!testPath
.IsValidOnWindows());
1953 testPath
.SetFromGit(_T("http://localhost:90/repos/trunk/com1"));
1954 ATLASSERT(!testPath
.IsValidOnWindows());
1958 } TGitPathTestobject
;
1962 CTGitPath
* CTGitPathList::LookForGitPath(CString path
)
1965 for(i
=0;i
<this->GetCount();i
++)
1967 if((*this)[i
].GetGitPathString() == path
)
1968 return (CTGitPath
*)&(*this)[i
];
1972 CString
CTGitPath::GetActionName(int action
)
1974 if(action
& CTGitPath::LOGACTIONS_UNMERGED
)
1975 return _T("Conflict");
1976 if(action
& CTGitPath::LOGACTIONS_ADDED
)
1978 if(action
& CTGitPath::LOGACTIONS_DELETED
)
1979 return _T("Deleted");
1980 if(action
& CTGitPath::LOGACTIONS_MERGED
)
1981 return _T("Merged");
1983 if(action
& CTGitPath::LOGACTIONS_MODIFIED
)
1984 return _T("Modified");
1985 if(action
& CTGitPath::LOGACTIONS_REPLACED
)
1986 return _T("Rename");
1987 if(action
& CTGitPath::LOGACTIONS_COPY
)
1990 if(action
& CTGitPath::LOGACTIONS_FORWORD
)
1991 return _T("Forward");
1993 if(action
& CTGitPath::LOGACTIONS_REBASE_EDIT
)
1995 if(action
& CTGitPath::LOGACTIONS_REBASE_SQUASH
)
1996 return _T("Squash");
1997 if(action
& CTGitPath::LOGACTIONS_REBASE_PICK
)
1999 if(action
& CTGitPath::LOGACTIONS_REBASE_SKIP
)
2002 return _T("Unknown");
2004 CString
CTGitPath::GetActionName()
2006 return GetActionName(m_Action
);
2009 int CTGitPathList::GetAction()