Fixed issue #914: unifiled diff always show wrong changes (base files compare with...
[TortoiseGit.git] / src / Git / TGitPath.cpp
blobf3f67041f3ed38b6e2bc054a013df8dcbe659516
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2011 - TortoiseGit
4 // Copyright (C) 2010-2011 Sven Strickroth <email@cs-ware.de>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "StdAfx.h"
21 #include "TGitPath.h"
22 #include "UnicodeUtils.h"
23 #include "GitAdminDir.h"
24 #include "PathUtils.h"
25 #include <regex>
26 #include "git.h"
27 #include "Globals.h"
29 #if defined(_MFC_VER)
30 //#include "MessageBox.h"
31 //#include "AppUtils.h"
32 #endif
34 #ifndef ASSERT
35 #define ASSERT()
36 #endif
37 using namespace std;
38 extern CGit g_Git;
40 CTGitPath::CTGitPath(void) :
41 m_bDirectoryKnown(false),
42 m_bIsDirectory(false),
43 m_bIsURL(false),
44 m_bURLKnown(false),
45 m_bHasAdminDirKnown(false),
46 m_bHasAdminDir(false),
47 m_bIsValidOnWindowsKnown(false),
48 m_bIsReadOnly(false),
49 m_bIsAdminDirKnown(false),
50 m_bIsAdminDir(false),
51 m_bExists(false),
52 m_bExistsKnown(false),
53 m_bLastWriteTimeKnown(0),
54 m_lastWriteTime(0),
55 m_customData(NULL),
56 m_bIsSpecialDirectoryKnown(false),
57 m_bIsSpecialDirectory(false)
59 m_Action=0;
60 m_ParentNo=0;
63 CTGitPath::~CTGitPath(void)
66 // Create a TGitPath object from an unknown path type (same as using SetFromUnknown)
67 CTGitPath::CTGitPath(const CString& sUnknownPath) :
68 m_bDirectoryKnown(false),
69 m_bIsDirectory(false),
70 m_bIsURL(false),
71 m_bURLKnown(false),
72 m_bHasAdminDirKnown(false),
73 m_bHasAdminDir(false),
74 m_bIsValidOnWindowsKnown(false),
75 m_bIsReadOnly(false),
76 m_bIsAdminDirKnown(false),
77 m_bIsAdminDir(false),
78 m_bExists(false),
79 m_bExistsKnown(false),
80 m_bLastWriteTimeKnown(0),
81 m_lastWriteTime(0),
82 m_customData(NULL),
83 m_bIsSpecialDirectoryKnown(false),
84 m_bIsSpecialDirectory(false)
86 SetFromUnknown(sUnknownPath);
87 m_Action=0;
88 m_Stage=0;
89 m_ParentNo=0;
92 int CTGitPath::ParserAction(BYTE action)
94 //action=action.TrimLeft();
95 //TCHAR c=action.GetAt(0);
96 if(action == 'M')
97 m_Action|= LOGACTIONS_MODIFIED;
98 if(action == 'R')
99 m_Action|= LOGACTIONS_REPLACED;
100 if(action == 'A')
101 m_Action|= LOGACTIONS_ADDED;
102 if(action == 'D')
103 m_Action|= LOGACTIONS_DELETED;
104 if(action == 'U')
105 m_Action|= LOGACTIONS_UNMERGED;
106 if(action == 'K')
107 m_Action|= LOGACTIONS_DELETED;
108 if(action == 'H')
109 m_Action|= LOGACTIONS_CACHE;
110 if(action == 'C' )
111 m_Action|= LOGACTIONS_COPY;
113 return m_Action;
115 void CTGitPath::SetFromGit(const char* pPath)
117 Reset();
118 if (pPath == NULL)
119 return;
120 int len = MultiByteToWideChar(CP_UTF8, 0, pPath, -1, NULL, 0);
121 if (len)
123 len = MultiByteToWideChar(CP_UTF8, 0, pPath, -1, m_sFwdslashPath.GetBuffer(len+1), len+1);
124 m_sFwdslashPath.ReleaseBuffer(len-1);
126 SanitizeRootPath(m_sFwdslashPath, true);
129 void CTGitPath::SetFromGit(const char* pPath, bool bIsDirectory)
131 SetFromGit(pPath);
132 m_bDirectoryKnown = true;
133 m_bIsDirectory = bIsDirectory;
136 void CTGitPath::SetFromGit(const TCHAR* pPath, bool bIsDirectory)
138 Reset();
139 if (pPath)
141 m_sFwdslashPath = pPath;
142 SanitizeRootPath(m_sFwdslashPath, true);
144 m_bDirectoryKnown = true;
145 m_bIsDirectory = bIsDirectory;
148 void CTGitPath::SetFromGit(const CString& sPath,CString *oldpath)
150 Reset();
151 m_sFwdslashPath = sPath;
152 SanitizeRootPath(m_sFwdslashPath, true);
153 if(oldpath)
154 m_sOldFwdslashPath = *oldpath;
157 void CTGitPath::SetFromWin(LPCTSTR pPath)
159 Reset();
160 m_sBackslashPath = pPath;
161 SanitizeRootPath(m_sBackslashPath, false);
162 ATLASSERT(m_sBackslashPath.Find('/')<0);
164 void CTGitPath::SetFromWin(const CString& sPath)
166 Reset();
167 m_sBackslashPath = sPath;
168 SanitizeRootPath(m_sBackslashPath, false);
170 void CTGitPath::SetFromWin(const CString& sPath, bool bIsDirectory)
172 Reset();
173 m_sBackslashPath = sPath;
174 m_bIsDirectory = bIsDirectory;
175 m_bDirectoryKnown = true;
176 SanitizeRootPath(m_sBackslashPath, false);
178 void CTGitPath::SetFromUnknown(const CString& sPath)
180 Reset();
181 // Just set whichever path we think is most likely to be used
182 // GitAdminDir admin;
183 // CString p;
184 // if(admin.HasAdminDir(sPath,&p))
185 // SetFwdslashPath(sPath.Right(sPath.GetLength()-p.GetLength()));
186 // else
187 SetFwdslashPath(sPath);
190 LPCTSTR CTGitPath::GetWinPath() const
192 if(IsEmpty())
194 return _T("");
196 if(m_sBackslashPath.IsEmpty())
198 SetBackslashPath(m_sFwdslashPath);
200 return m_sBackslashPath;
202 // This is a temporary function, to be used during the migration to
203 // the path class. Ultimately, functions consuming paths should take a CTGitPath&, not a CString
204 const CString& CTGitPath::GetWinPathString() const
206 if(m_sBackslashPath.IsEmpty())
208 SetBackslashPath(m_sFwdslashPath);
210 return m_sBackslashPath;
213 const CString& CTGitPath::GetGitPathString() const
215 if(m_sFwdslashPath.IsEmpty())
217 SetFwdslashPath(m_sBackslashPath);
219 return m_sFwdslashPath;
222 const CString &CTGitPath::GetGitOldPathString() const
224 return m_sOldFwdslashPath;
226 #if 0
227 const char* CTGitPath::GetGitApiPath(apr_pool_t *pool) const
229 // This funny-looking 'if' is to avoid a subtle problem with empty paths, whereby
230 // each call to GetGitApiPath returns a different pointer value.
231 // If you made multiple calls to GetGitApiPath on the same string, only the last
232 // one would give you a valid pointer to an empty string, because each
233 // call would invalidate the previous call's return.
234 if(IsEmpty())
236 return "";
238 if(m_sFwdslashPath.IsEmpty())
240 SetFwdslashPath(m_sBackslashPath);
242 if(m_sUTF8FwdslashPath.IsEmpty())
244 SetUTF8FwdslashPath(m_sFwdslashPath);
246 if (svn_path_is_url(m_sUTF8FwdslashPath))
248 m_sUTF8FwdslashPathEscaped = CPathUtils::PathEscape(m_sUTF8FwdslashPath);
249 m_sUTF8FwdslashPathEscaped.Replace("file:////", "file:///\\");
250 m_sUTF8FwdslashPathEscaped = svn_path_canonicalize(m_sUTF8FwdslashPathEscaped, pool);
251 return m_sUTF8FwdslashPathEscaped;
253 m_sUTF8FwdslashPath = svn_path_canonicalize(m_sUTF8FwdslashPath, pool);
255 return m_sUTF8FwdslashPath;
257 #endif
259 const CString& CTGitPath::GetUIPathString() const
261 if (m_sUIPath.IsEmpty())
263 #if defined(_MFC_VER)
264 //BUGBUG HORRIBLE!!! - CPathUtils::IsEscaped doesn't need to be MFC-only
265 if (IsUrl())
267 m_sUIPath = CPathUtils::PathUnescape(GetGitPathString());
268 m_sUIPath.Replace(_T("file:////"), _T("file:///\\"));
271 else
272 #endif
274 m_sUIPath = GetWinPathString();
277 return m_sUIPath;
280 void CTGitPath::SetFwdslashPath(const CString& sPath) const
282 m_sFwdslashPath = sPath;
283 m_sFwdslashPath.Replace('\\', '/');
285 // We don't leave a trailing /
286 m_sFwdslashPath.TrimRight('/');
288 SanitizeRootPath(m_sFwdslashPath, true);
290 m_sFwdslashPath.Replace(_T("file:////"), _T("file:///\\"));
292 m_sUTF8FwdslashPath.Empty();
295 void CTGitPath::SetBackslashPath(const CString& sPath) const
297 m_sBackslashPath = sPath;
298 m_sBackslashPath.Replace('/', '\\');
299 m_sBackslashPath.TrimRight('\\');
300 SanitizeRootPath(m_sBackslashPath, false);
303 void CTGitPath::SetUTF8FwdslashPath(const CString& sPath) const
305 m_sUTF8FwdslashPath = CUnicodeUtils::GetUTF8(sPath);
308 void CTGitPath::SanitizeRootPath(CString& sPath, bool bIsForwardPath) const
310 // Make sure to add the trailing slash to root paths such as 'C:'
311 if (sPath.GetLength() == 2 && sPath[1] == ':')
313 sPath += (bIsForwardPath) ? _T("/") : _T("\\");
317 bool CTGitPath::IsUrl() const
319 #if 0
320 if (!m_bURLKnown)
322 EnsureFwdslashPathSet();
323 if(m_sUTF8FwdslashPath.IsEmpty())
325 SetUTF8FwdslashPath(m_sFwdslashPath);
327 m_bIsURL = !!svn_path_is_url(m_sUTF8FwdslashPath);
328 m_bURLKnown = true;
330 return m_bIsURL;
331 #endif
332 return false;
335 bool CTGitPath::IsDirectory() const
337 if(!m_bDirectoryKnown)
339 UpdateAttributes();
341 return m_bIsDirectory;
344 bool CTGitPath::Exists() const
346 if (!m_bExistsKnown)
348 UpdateAttributes();
350 return m_bExists;
353 bool CTGitPath::Delete(bool bTrash) const
355 EnsureBackslashPathSet();
356 ::SetFileAttributes(m_sBackslashPath, FILE_ATTRIBUTE_NORMAL);
357 bool bRet = false;
358 if (Exists())
360 if ((bTrash)||(IsDirectory()))
362 TCHAR * buf = new TCHAR[m_sBackslashPath.GetLength()+2];
363 _tcscpy_s(buf, m_sBackslashPath.GetLength()+2, m_sBackslashPath);
364 buf[m_sBackslashPath.GetLength()] = 0;
365 buf[m_sBackslashPath.GetLength()+1] = 0;
366 SHFILEOPSTRUCT shop = {0};
367 shop.wFunc = FO_DELETE;
368 shop.pFrom = buf;
369 shop.fFlags = FOF_NOCONFIRMATION|FOF_NOERRORUI|FOF_SILENT;
370 if (bTrash)
371 shop.fFlags |= FOF_ALLOWUNDO;
372 bRet = (SHFileOperation(&shop) == 0);
373 delete [] buf;
375 else
377 bRet = !!::DeleteFile(m_sBackslashPath);
380 m_bExists = false;
381 m_bExistsKnown = true;
382 return bRet;
385 __int64 CTGitPath::GetLastWriteTime() const
387 if(!m_bLastWriteTimeKnown)
389 UpdateAttributes();
391 return m_lastWriteTime;
394 bool CTGitPath::IsReadOnly() const
396 if(!m_bLastWriteTimeKnown)
398 UpdateAttributes();
400 return m_bIsReadOnly;
403 void CTGitPath::UpdateAttributes() const
405 EnsureBackslashPathSet();
406 WIN32_FILE_ATTRIBUTE_DATA attribs;
407 if(GetFileAttributesEx(m_sBackslashPath, GetFileExInfoStandard, &attribs))
409 m_bIsDirectory = !!(attribs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
410 m_lastWriteTime = *(__int64*)&attribs.ftLastWriteTime;
411 m_bIsReadOnly = !!(attribs.dwFileAttributes & FILE_ATTRIBUTE_READONLY);
412 m_bExists = true;
414 else
416 DWORD err = GetLastError();
417 if ((err == ERROR_FILE_NOT_FOUND)||(err == ERROR_PATH_NOT_FOUND)||(err == ERROR_INVALID_NAME))
419 m_bIsDirectory = false;
420 m_lastWriteTime = 0;
421 m_bExists = false;
423 else
425 m_bIsDirectory = false;
426 m_lastWriteTime = 0;
427 m_bExists = true;
428 return;
431 m_bDirectoryKnown = true;
432 m_bLastWriteTimeKnown = true;
433 m_bExistsKnown = true;
436 CTGitPath CTGitPath::GetSubPath(const CTGitPath &root)
438 CTGitPath path;
440 if(GetWinPathString().Left(root.GetWinPathString().GetLength()) == root.GetWinPathString())
442 CString str=GetWinPathString();
443 path.SetFromWin(str.Right(str.GetLength()-root.GetWinPathString().GetLength()-1));
445 return path;
448 void CTGitPath::EnsureBackslashPathSet() const
450 if(m_sBackslashPath.IsEmpty())
452 SetBackslashPath(m_sFwdslashPath);
453 ATLASSERT(IsEmpty() || !m_sBackslashPath.IsEmpty());
456 void CTGitPath::EnsureFwdslashPathSet() const
458 if(m_sFwdslashPath.IsEmpty())
460 SetFwdslashPath(m_sBackslashPath);
461 ATLASSERT(IsEmpty() || !m_sFwdslashPath.IsEmpty());
466 // Reset all the caches
467 void CTGitPath::Reset()
469 m_bDirectoryKnown = false;
470 m_bURLKnown = false;
471 m_bLastWriteTimeKnown = false;
472 m_bHasAdminDirKnown = false;
473 m_bIsValidOnWindowsKnown = false;
474 m_bIsAdminDirKnown = false;
475 m_bExistsKnown = false;
476 m_bIsSpecialDirectoryKnown = false;
477 m_bIsSpecialDirectory = false;
479 m_sBackslashPath.Empty();
480 m_sFwdslashPath.Empty();
481 m_sUTF8FwdslashPath.Empty();
482 this->m_Action=0;
483 this->m_StatAdd=_T("");
484 this->m_StatDel=_T("");
485 m_ParentNo=0;
486 ATLASSERT(IsEmpty());
489 CTGitPath CTGitPath::GetDirectory() const
491 if ((IsDirectory())||(!Exists()))
493 return *this;
495 return GetContainingDirectory();
498 CTGitPath CTGitPath::GetContainingDirectory() const
500 EnsureBackslashPathSet();
502 CString sDirName = m_sBackslashPath.Left(m_sBackslashPath.ReverseFind('\\'));
503 if(sDirName.GetLength() == 2 && sDirName[1] == ':')
505 // This is a root directory, which needs a trailing slash
506 sDirName += '\\';
507 if(sDirName == m_sBackslashPath)
509 // We were clearly provided with a root path to start with - we should return nothing now
510 sDirName.Empty();
513 if(sDirName.GetLength() == 1 && sDirName[0] == '\\')
515 // We have an UNC path and we already are the root
516 sDirName.Empty();
518 CTGitPath retVal;
519 retVal.SetFromWin(sDirName);
520 return retVal;
523 CString CTGitPath::GetRootPathString() const
525 EnsureBackslashPathSet();
526 CString workingPath = m_sBackslashPath;
527 LPTSTR pPath = workingPath.GetBuffer(MAX_PATH); // MAX_PATH ok here.
528 ATLVERIFY(::PathStripToRoot(pPath));
529 workingPath.ReleaseBuffer();
530 return workingPath;
534 CString CTGitPath::GetFilename() const
536 //ATLASSERT(!IsDirectory());
537 return GetFileOrDirectoryName();
540 CString CTGitPath::GetFileOrDirectoryName() const
542 EnsureBackslashPathSet();
543 return m_sBackslashPath.Mid(m_sBackslashPath.ReverseFind('\\')+1);
546 CString CTGitPath::GetUIFileOrDirectoryName() const
548 GetUIPathString();
549 return m_sUIPath.Mid(m_sUIPath.ReverseFind('\\')+1);
552 CString CTGitPath::GetFileExtension() const
554 if(!IsDirectory())
556 EnsureBackslashPathSet();
557 int dotPos = m_sBackslashPath.ReverseFind('.');
558 int slashPos = m_sBackslashPath.ReverseFind('\\');
559 if (dotPos > slashPos)
560 return m_sBackslashPath.Mid(dotPos);
562 return CString();
564 CString CTGitPath::GetBaseFilename() const
566 int dot;
567 CString filename=GetFilename();
568 dot = filename.ReverseFind(_T('.'));
569 if(dot>0)
570 return filename.Left(dot);
571 else
572 return filename;
575 bool CTGitPath::ArePathStringsEqual(const CString& sP1, const CString& sP2)
577 int length = sP1.GetLength();
578 if(length != sP2.GetLength())
580 // Different lengths
581 return false;
583 // We work from the end of the strings, because path differences
584 // are more likely to occur at the far end of a string
585 LPCTSTR pP1Start = sP1;
586 LPCTSTR pP1 = pP1Start+(length-1);
587 LPCTSTR pP2 = ((LPCTSTR)sP2)+(length-1);
588 while(length-- > 0)
590 if(_totlower(*pP1--) != _totlower(*pP2--))
592 return false;
595 return true;
598 bool CTGitPath::ArePathStringsEqualWithCase(const CString& sP1, const CString& sP2)
600 int length = sP1.GetLength();
601 if(length != sP2.GetLength())
603 // Different lengths
604 return false;
606 // We work from the end of the strings, because path differences
607 // are more likely to occur at the far end of a string
608 LPCTSTR pP1Start = sP1;
609 LPCTSTR pP1 = pP1Start+(length-1);
610 LPCTSTR pP2 = ((LPCTSTR)sP2)+(length-1);
611 while(length-- > 0)
613 if((*pP1--) != (*pP2--))
615 return false;
618 return true;
621 bool CTGitPath::IsEmpty() const
623 // Check the backward slash path first, since the chance that this
624 // one is set is higher. In case of a 'false' return value it's a little
625 // bit faster.
626 return m_sBackslashPath.IsEmpty() && m_sFwdslashPath.IsEmpty();
629 // Test if both paths refer to the same item
630 // Ignores case and slash direction
631 bool CTGitPath::IsEquivalentTo(const CTGitPath& rhs) const
633 // Try and find a slash direction which avoids having to convert
634 // both filenames
635 if(!m_sBackslashPath.IsEmpty())
637 // *We've* got a \ path - make sure that the RHS also has a \ path
638 rhs.EnsureBackslashPathSet();
639 return ArePathStringsEqualWithCase(m_sBackslashPath, rhs.m_sBackslashPath);
641 else
643 // Assume we've got a fwdslash path and make sure that the RHS has one
644 rhs.EnsureFwdslashPathSet();
645 return ArePathStringsEqualWithCase(m_sFwdslashPath, rhs.m_sFwdslashPath);
649 bool CTGitPath::IsEquivalentToWithoutCase(const CTGitPath& rhs) const
651 // Try and find a slash direction which avoids having to convert
652 // both filenames
653 if(!m_sBackslashPath.IsEmpty())
655 // *We've* got a \ path - make sure that the RHS also has a \ path
656 rhs.EnsureBackslashPathSet();
657 return ArePathStringsEqual(m_sBackslashPath, rhs.m_sBackslashPath);
659 else
661 // Assume we've got a fwdslash path and make sure that the RHS has one
662 rhs.EnsureFwdslashPathSet();
663 return ArePathStringsEqual(m_sFwdslashPath, rhs.m_sFwdslashPath);
667 bool CTGitPath::IsAncestorOf(const CTGitPath& possibleDescendant) const
669 possibleDescendant.EnsureBackslashPathSet();
670 EnsureBackslashPathSet();
672 bool bPathStringsEqual = ArePathStringsEqual(m_sBackslashPath, possibleDescendant.m_sBackslashPath.Left(m_sBackslashPath.GetLength()));
673 if (m_sBackslashPath.GetLength() >= possibleDescendant.GetWinPathString().GetLength())
675 return bPathStringsEqual;
678 return (bPathStringsEqual &&
679 ((possibleDescendant.m_sBackslashPath[m_sBackslashPath.GetLength()] == '\\')||
680 (m_sBackslashPath.GetLength()==3 && m_sBackslashPath[1]==':')));
683 // Get a string representing the file path, optionally with a base
684 // section stripped off the front.
685 CString CTGitPath::GetDisplayString(const CTGitPath* pOptionalBasePath /* = NULL*/) const
687 EnsureFwdslashPathSet();
688 if(pOptionalBasePath != NULL)
690 // Find the length of the base-path without having to do an 'ensure' on it
691 int baseLength = max(pOptionalBasePath->m_sBackslashPath.GetLength(), pOptionalBasePath->m_sFwdslashPath.GetLength());
693 // Now, chop that baseLength of the front of the path
694 return m_sFwdslashPath.Mid(baseLength).TrimLeft('/');
696 return m_sFwdslashPath;
699 int CTGitPath::Compare(const CTGitPath& left, const CTGitPath& right)
701 left.EnsureBackslashPathSet();
702 right.EnsureBackslashPathSet();
703 return left.m_sBackslashPath.CompareNoCase(right.m_sBackslashPath);
706 bool operator<(const CTGitPath& left, const CTGitPath& right)
708 return CTGitPath::Compare(left, right) < 0;
711 bool CTGitPath::PredLeftEquivalentToRight(const CTGitPath& left, const CTGitPath& right)
713 return left.IsEquivalentTo(right);
716 bool CTGitPath::PredLeftSameWCPathAsRight(const CTGitPath& left, const CTGitPath& right)
718 if (left.IsAdminDir() && right.IsAdminDir())
720 CTGitPath l = left;
721 CTGitPath r = right;
724 l = l.GetContainingDirectory();
725 } while(l.HasAdminDir());
728 r = r.GetContainingDirectory();
729 } while(r.HasAdminDir());
730 return l.GetContainingDirectory().IsEquivalentTo(r.GetContainingDirectory());
732 return left.GetDirectory().IsEquivalentTo(right.GetDirectory());
735 bool CTGitPath::CheckChild(const CTGitPath &parent, const CTGitPath& child)
737 return parent.IsAncestorOf(child);
740 void CTGitPath::AppendRawString(const CString& sAppend)
742 EnsureFwdslashPathSet();
743 CString strCopy = m_sFwdslashPath += sAppend;
744 SetFromUnknown(strCopy);
747 void CTGitPath::AppendPathString(const CString& sAppend)
749 EnsureBackslashPathSet();
750 CString cleanAppend(sAppend);
751 cleanAppend.Replace('/', '\\');
752 cleanAppend.TrimLeft('\\');
753 m_sBackslashPath.TrimRight('\\');
754 CString strCopy = m_sBackslashPath + _T("\\") + cleanAppend;
755 SetFromWin(strCopy);
758 bool CTGitPath::HasAdminDir() const
760 if (m_bHasAdminDirKnown)
761 return m_bHasAdminDir;
763 EnsureBackslashPathSet();
764 m_bHasAdminDir = g_GitAdminDir.HasAdminDir(m_sBackslashPath, IsDirectory(), &m_sProjectRoot);
765 m_bHasAdminDirKnown = true;
766 return m_bHasAdminDir;
769 bool CTGitPath::HasSubmodules() const
771 return !g_GitAdminDir.GetSuperProjectRoot(GetWinPathString()).IsEmpty();
774 int CTGitPath::GetAdminDirMask() const
776 int status = 0;
777 CString topdir,path;
778 if(!g_GitAdminDir.HasAdminDir(GetWinPathString(),&topdir))
780 return status;
783 status |= ITEMIS_INSVN|ITEMIS_FOLDERINSVN|ITEMIS_INVERSIONEDFOLDER;
785 path=topdir;
786 path+=_T("\\");
787 path+=g_GitAdminDir.GetAdminDirName();
788 path+=_T("\\refs\\stash");
789 if( PathFileExists(path) )
790 status |= ITEMIS_STASH;
792 path=topdir;
793 path+=_T("\\");
794 path+=g_GitAdminDir.GetAdminDirName();
795 path+=_T("\\svn");
796 if( PathFileExists(path) )
797 status |= ITEMIS_GITSVN;
799 path=topdir;
800 path+=_T("\\.gitmodules");
801 if( PathFileExists(path) )
802 status |= ITEMIS_SUBMODULE;
804 return status;
807 bool CTGitPath::HasStashDir() const
809 CString topdir;
810 if(!g_GitAdminDir.HasAdminDir(GetWinPathString(),&topdir))
812 return false;
814 topdir+=_T("\\");
815 topdir+=g_GitAdminDir.GetAdminDirName();
816 topdir+=_T("\\refs\\stash");
817 return !!PathFileExists(topdir);
819 bool CTGitPath::HasGitSVNDir() const
821 CString topdir;
822 if(!g_GitAdminDir.HasAdminDir(GetWinPathString(),&topdir))
824 return false;
826 topdir+=_T("\\");
827 topdir+=g_GitAdminDir.GetAdminDirName();
828 topdir+=_T("\\svn");
829 return !!PathFileExists(topdir);
832 bool CTGitPath::HasRebaseApply() const
834 CString topdir;
835 if(!g_GitAdminDir.HasAdminDir(GetWinPathString(),&topdir))
837 return false;
839 topdir+=_T("\\");
840 topdir+=g_GitAdminDir.GetAdminDirName();
841 topdir+=_T("\\rebase-apply");
842 return !!PathFileExists(topdir);
845 bool CTGitPath::HasAdminDir(CString *ProjectTopDir) const
847 if (m_bHasAdminDirKnown)
849 if (ProjectTopDir)
850 *ProjectTopDir = m_sProjectRoot;
851 return m_bHasAdminDir;
854 EnsureBackslashPathSet();
855 m_bHasAdminDir = g_GitAdminDir.HasAdminDir(m_sBackslashPath, IsDirectory(), &m_sProjectRoot);
856 m_bHasAdminDirKnown = true;
857 if (ProjectTopDir)
858 *ProjectTopDir = m_sProjectRoot;
859 return m_bHasAdminDir;
862 bool CTGitPath::IsAdminDir() const
864 if (m_bIsAdminDirKnown)
865 return m_bIsAdminDir;
867 EnsureBackslashPathSet();
868 m_bIsAdminDir = g_GitAdminDir.IsAdminDirPath(m_sBackslashPath);
869 m_bIsAdminDirKnown = true;
870 return m_bIsAdminDir;
873 bool CTGitPath::IsValidOnWindows() const
875 if (m_bIsValidOnWindowsKnown)
876 return m_bIsValidOnWindows;
878 m_bIsValidOnWindows = false;
879 EnsureBackslashPathSet();
880 CString sMatch = m_sBackslashPath + _T("\r\n");
881 wstring sPattern;
882 // the 'file://' URL is just a normal windows path:
883 if (sMatch.Left(7).CompareNoCase(_T("file:\\\\"))==0)
885 sMatch = sMatch.Mid(7);
886 sMatch.TrimLeft(_T("\\"));
887 sPattern = _T("^(\\\\\\\\\\?\\\\)?(([a-zA-Z]:|\\\\)\\\\)?(((\\.)|(\\.\\.)|([^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?))\\\\)*[^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?$");
889 else if (IsUrl())
891 sPattern = _T("^((http|https|svn|svn\\+ssh|file)\\:\\\\+([^\\\\@\\:]+\\:[^\\\\@\\:]+@)?\\\\[^\\\\]+(\\:\\d+)?)?(((\\.)|(\\.\\.)|([^\\\\/:\\*\\?\"\\|<>\\. ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?))\\\\)*[^\\\\/:\\*\\?\"\\|<>\\. ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?$");
893 else
895 sPattern = _T("^(\\\\\\\\\\?\\\\)?(([a-zA-Z]:|\\\\)\\\\)?(((\\.)|(\\.\\.)|([^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?))\\\\)*[^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?$");
900 tr1::wregex rx(sPattern, tr1::regex_constants::icase | tr1::regex_constants::ECMAScript);
901 tr1::wsmatch match;
903 wstring rmatch = wstring((LPCTSTR)sMatch);
904 if (tr1::regex_match(rmatch, match, rx))
906 if (wstring(match[0]).compare(sMatch)==0)
907 m_bIsValidOnWindows = true;
909 if (m_bIsValidOnWindows)
911 // now check for illegal filenames
912 tr1::wregex rx2(_T("\\\\(lpt\\d|com\\d|aux|nul|prn|con)(\\\\|$)"), tr1::regex_constants::icase | tr1::regex_constants::ECMAScript);
913 rmatch = m_sBackslashPath;
914 if (tr1::regex_search(rmatch, rx2, tr1::regex_constants::match_default))
915 m_bIsValidOnWindows = false;
918 catch (exception) {}
920 m_bIsValidOnWindowsKnown = true;
921 return m_bIsValidOnWindows;
924 bool CTGitPath::IsSpecialDirectory() const
926 if (m_bIsSpecialDirectoryKnown)
927 return m_bIsSpecialDirectory;
929 static LPCTSTR specialDirectories[]
930 = { _T("trunk"), _T("tags"), _T("branches") };
932 for (int i=0 ; i<(sizeof(specialDirectories) / sizeof(specialDirectories[0])) ; ++i)
934 CString name = GetFileOrDirectoryName();
935 if (0 == name.CompareNoCase(specialDirectories[i]))
937 m_bIsSpecialDirectory = true;
938 break;
942 m_bIsSpecialDirectoryKnown = true;
944 return m_bIsSpecialDirectory;
947 //////////////////////////////////////////////////////////////////////////
949 CTGitPathList::CTGitPathList()
954 // A constructor which allows a path list to be easily built which one initial entry in
955 CTGitPathList::CTGitPathList(const CTGitPath& firstEntry)
957 AddPath(firstEntry);
959 int CTGitPathList::ParserFromLsFile(BYTE_VECTOR &out,bool /*staged*/)
961 unsigned int pos=0;
962 CString one;
963 CTGitPath path;
964 CString part;
965 this->Clear();
967 while(pos>=0 && pos<out.size())
969 one.Empty();
970 path.Reset();
972 g_Git.StringAppend(&one,&out[pos],CP_ACP);
973 int tabstart=0;
974 path.m_Action=path.ParserAction(out[pos]);
975 one.Tokenize(_T("\t"),tabstart);
977 if(tabstart>=0)
978 path.SetFromGit(one.Right(one.GetLength()-tabstart));
980 tabstart=0;
982 part=one.Tokenize(_T(" "),tabstart); //Tag
984 part=one.Tokenize(_T(" "),tabstart); //Mode
986 part=one.Tokenize(_T(" "),tabstart); //Hash
988 part=one.Tokenize(_T("\t"),tabstart); //Stage
990 path.m_Stage=_ttol(part);
992 this->AddPath(path);
994 pos=out.findNextString(pos);
996 return pos;
998 int CTGitPathList::FillUnRev(unsigned int action,CTGitPathList *list)
1000 int pos=0;
1001 this->Clear();
1002 CTGitPath path;
1004 int count;
1005 if(list==NULL)
1006 count=1;
1007 else
1008 count=list->GetCount();
1009 for(int i=0;i<count;i++)
1011 CString cmd;
1012 pos=0;
1014 CString ignored;
1015 if(action & CTGitPath::LOGACTIONS_IGNORE)
1016 ignored= _T(" -i");
1018 if(list==NULL)
1020 cmd=_T("git.exe ls-files --exclude-standard --full-name --others -z");
1021 cmd+=ignored;
1024 else
1025 { cmd.Format(_T("git.exe ls-files --exclude-standard --full-name --others -z%s -- \"%s\""),
1026 ignored,
1027 (*list)[i].GetWinPathString());
1030 BYTE_VECTOR out;
1031 out.clear();
1032 g_Git.Run(cmd,&out);
1034 pos=0;
1035 CString one;
1036 while( pos>=0 && pos<out.size())
1038 one.Empty();
1039 g_Git.StringAppend(&one,&out[pos],CP_ACP);
1040 if(!one.IsEmpty())
1042 //SetFromGit will clear all status
1043 path.SetFromGit(one);
1044 path.m_Action=action;
1045 AddPath(path);
1047 pos=out.findNextString(pos);
1051 return 0;
1053 int CTGitPathList::ParserFromLog(BYTE_VECTOR &log, bool parseDeletes /*false*/)
1055 this->Clear();
1056 int pos=0;
1057 //BYTE *p=&log[0];
1058 //CString one;
1059 CTGitPath path;
1060 m_Action=0;
1061 while( pos>=0 && pos<log.size())
1063 //one=log.Tokenize(_T("\n"),pos);
1064 path.Reset();
1065 if(log[pos]=='\n')
1066 pos++;
1068 if(log[pos]==':')
1070 bool merged=false;
1071 if(log[pos+1] ==':')
1073 merged=true;
1075 int end=log.find(0,pos);
1076 int actionstart=-1;
1077 int numfile=1;
1078 int file1=-1,file2=-1;
1079 if( end>0 )
1081 actionstart=log.find(' ',end-6);
1082 pos=actionstart;
1084 if( actionstart>0 )
1086 actionstart++;
1088 file1 = log.find(0,actionstart);
1089 if( file1>=0 )
1091 file1++;
1092 pos=file1;
1094 if( log[actionstart] == 'C' || log[actionstart] == 'R' )
1096 file2=file1;
1097 numfile=2;
1098 file1 = log.find(0,file1);
1099 if(file1>=0 )
1101 file1++;
1102 pos=file1;
1108 CString pathname1;
1109 CString pathname2;
1111 if( file1>=0 )
1112 g_Git.StringAppend(&pathname1,&log[file1],CP_ACP);
1113 if( file2>=0 )
1114 g_Git.StringAppend(&pathname2,&log[file2],CP_ACP);
1116 CTGitPath *GitPath=LookForGitPath(pathname1);
1118 if(GitPath)
1120 GitPath->ParserAction( log[actionstart] );
1122 if(merged)
1124 GitPath->m_Action |= CTGitPath::LOGACTIONS_MERGED;
1125 GitPath->m_Action &= ~CTGitPath::LOGACTIONS_FORWORD;
1127 m_Action |=GitPath->m_Action;
1129 }else
1131 int ac=path.ParserAction(log[actionstart] );
1132 ac |= merged?CTGitPath::LOGACTIONS_MERGED:0;
1134 path.SetFromGit(pathname1,&pathname2);
1135 path.m_Action=ac;
1136 //action must be set after setfromgit. SetFromGit will clear all status.
1137 this->m_Action|=ac;
1139 AddPath(path);
1143 }else
1145 int tabstart=0;
1146 path.Reset();
1147 CString StatAdd;
1148 CString StatDel;
1149 CString file1;
1150 CString file2;
1152 tabstart=log.find('\t',pos);
1153 if(tabstart >=0)
1155 log[tabstart]=0;
1156 g_Git.StringAppend(&StatAdd,&log[pos],CP_UTF8);
1157 pos=tabstart+1;
1160 tabstart=log.find('\t',pos);
1161 if(tabstart >=0)
1163 log[tabstart]=0;
1165 g_Git.StringAppend(&StatDel,&log[pos],CP_UTF8);
1166 pos=tabstart+1;
1169 if(log[pos] == 0) //rename
1171 pos++;
1172 g_Git.StringAppend(&file2,&log[pos],CP_ACP);
1173 int sec=log.find(0,pos);
1174 if(sec>=0)
1176 sec++;
1177 g_Git.StringAppend(&file1,&log[sec],CP_ACP);
1179 pos=sec;
1181 }else
1183 g_Git.StringAppend(&file1,&log[pos],CP_ACP);
1185 path.SetFromGit(file1,&file2);
1187 CTGitPath *GitPath=LookForGitPath(path.GetGitPathString());
1188 if(GitPath)
1190 GitPath->m_StatAdd=StatAdd;
1191 GitPath->m_StatDel=StatDel;
1192 }else
1194 //path.SetFromGit(pathname);
1195 if (parseDeletes) {
1196 path.m_StatAdd=_T("0");
1197 path.m_StatDel=_T("0");
1198 path.m_Action |= CTGitPath::LOGACTIONS_DELETED;
1199 } else {
1200 path.m_StatAdd=StatAdd;
1201 path.m_StatDel=StatDel;
1202 path.m_Action |= CTGitPath::LOGACTIONS_FORWORD;
1204 AddPath(path);
1208 pos=log.findNextString(pos);
1210 return pos;
1213 void CTGitPathList::AddPath(const CTGitPath& newPath)
1215 m_paths.push_back(newPath);
1216 m_commonBaseDirectory.Reset();
1218 int CTGitPathList::GetCount() const
1220 return (int)m_paths.size();
1222 void CTGitPathList::Clear()
1224 m_paths.clear();
1225 m_commonBaseDirectory.Reset();
1228 const CTGitPath& CTGitPathList::operator[](INT_PTR index) const
1230 ATLASSERT(index >= 0 && index < (INT_PTR)m_paths.size());
1231 return m_paths[index];
1234 bool CTGitPathList::AreAllPathsFiles() const
1236 // Look through the vector for any directories - if we find them, return false
1237 return std::find_if(m_paths.begin(), m_paths.end(), std::mem_fun_ref(&CTGitPath::IsDirectory)) == m_paths.end();
1241 #if defined(_MFC_VER)
1243 bool CTGitPathList::LoadFromFile(const CTGitPath& filename)
1245 Clear();
1248 CString strLine;
1249 CStdioFile file(filename.GetWinPath(), CFile::typeBinary | CFile::modeRead | CFile::shareDenyWrite);
1251 // for every selected file/folder
1252 CTGitPath path;
1253 while (file.ReadString(strLine))
1255 path.SetFromUnknown(strLine);
1256 AddPath(path);
1258 file.Close();
1260 catch (CFileException* pE)
1262 TRACE("CFileException loading target file list\n");
1263 TCHAR error[10000] = {0};
1264 pE->GetErrorMessage(error, 10000);
1265 // CMessageBox::Show(NULL, error, _T("TortoiseGit"), MB_ICONERROR);
1266 pE->Delete();
1267 return false;
1269 return true;
1272 bool CTGitPathList::WriteToFile(const CString& sFilename, bool bANSI /* = false */) const
1276 if (bANSI)
1278 CStdioFile file(sFilename, CFile::typeText | CFile::modeReadWrite | CFile::modeCreate);
1279 PathVector::const_iterator it;
1280 for(it = m_paths.begin(); it != m_paths.end(); ++it)
1282 CStringA line = CStringA(it->GetGitPathString()) + '\n';
1283 file.Write(line, line.GetLength());
1285 file.Close();
1287 else
1289 CStdioFile file(sFilename, CFile::typeBinary | CFile::modeReadWrite | CFile::modeCreate);
1290 PathVector::const_iterator it;
1291 for(it = m_paths.begin(); it != m_paths.end(); ++it)
1293 file.WriteString(it->GetGitPathString()+_T("\n"));
1295 file.Close();
1298 catch (CFileException* pE)
1300 TRACE("CFileException in writing temp file\n");
1301 pE->Delete();
1302 return false;
1304 return true;
1308 void CTGitPathList::LoadFromAsteriskSeparatedString(const CString& sPathString)
1310 int pos = 0;
1311 CString temp;
1312 for(;;)
1314 temp = sPathString.Tokenize(_T("*"),pos);
1315 if(temp.IsEmpty())
1317 break;
1319 AddPath(CTGitPath(CPathUtils::GetLongPathname(temp)));
1323 CString CTGitPathList::CreateAsteriskSeparatedString() const
1325 CString sRet;
1326 PathVector::const_iterator it;
1327 for(it = m_paths.begin(); it != m_paths.end(); ++it)
1329 if (!sRet.IsEmpty())
1330 sRet += _T("*");
1331 sRet += it->GetWinPathString();
1333 return sRet;
1335 #endif // _MFC_VER
1337 bool
1338 CTGitPathList::AreAllPathsFilesInOneDirectory() const
1340 // Check if all the paths are files and in the same directory
1341 PathVector::const_iterator it;
1342 m_commonBaseDirectory.Reset();
1343 for(it = m_paths.begin(); it != m_paths.end(); ++it)
1345 if(it->IsDirectory())
1347 return false;
1349 const CTGitPath& baseDirectory = it->GetDirectory();
1350 if(m_commonBaseDirectory.IsEmpty())
1352 m_commonBaseDirectory = baseDirectory;
1354 else if(!m_commonBaseDirectory.IsEquivalentTo(baseDirectory))
1356 // Different path
1357 m_commonBaseDirectory.Reset();
1358 return false;
1361 return true;
1364 CTGitPath CTGitPathList::GetCommonDirectory() const
1366 if (m_commonBaseDirectory.IsEmpty())
1368 PathVector::const_iterator it;
1369 for(it = m_paths.begin(); it != m_paths.end(); ++it)
1371 const CTGitPath& baseDirectory = it->GetDirectory();
1372 if(m_commonBaseDirectory.IsEmpty())
1374 m_commonBaseDirectory = baseDirectory;
1376 else if(!m_commonBaseDirectory.IsEquivalentTo(baseDirectory))
1378 // Different path
1379 m_commonBaseDirectory.Reset();
1380 break;
1384 // since we only checked strings, not paths,
1385 // we have to make sure now that we really return a *path* here
1386 PathVector::const_iterator iter;
1387 for(iter = m_paths.begin(); iter != m_paths.end(); ++iter)
1389 if (!m_commonBaseDirectory.IsAncestorOf(*iter))
1391 m_commonBaseDirectory = m_commonBaseDirectory.GetContainingDirectory();
1392 break;
1395 return m_commonBaseDirectory;
1398 CTGitPath CTGitPathList::GetCommonRoot() const
1400 PathVector::const_iterator it;
1401 CString sRoot, sTempRoot;
1402 bool bEqual = true;
1404 if (GetCount() == 1)
1405 return m_paths[0];
1407 int backSlashPos = 0;
1408 int searchStartPos = 0;
1409 while (bEqual)
1411 if(m_paths.empty())
1412 break;
1414 for (it = m_paths.begin(); it != m_paths.end(); ++it)
1416 if (backSlashPos == 0)
1418 backSlashPos = it->GetWinPathString().Find('\\', searchStartPos+1);
1419 if ((backSlashPos < 0)&&(searchStartPos != it->GetWinPathString().GetLength()))
1420 backSlashPos = it->GetWinPathString().GetLength();
1421 sTempRoot = it->GetWinPathString().Left(backSlashPos+1);
1423 else if (it->GetWinPathString().Find('\\', searchStartPos+1) != backSlashPos || it->GetWinPathString().Left(backSlashPos+1) != sTempRoot.Left(backSlashPos+1))
1425 if (it->GetWinPathString().Find('\\', searchStartPos+1) < 0)
1427 if (it->GetWinPathString().GetLength() != backSlashPos || it->GetWinPathString().Left(backSlashPos+1) != sTempRoot.Left(backSlashPos+1))
1429 bEqual = false;
1430 break;
1433 else
1435 bEqual = false;
1436 break;
1439 if (backSlashPos < 0)
1441 bEqual = false;
1442 break;
1445 if (bEqual == false)
1447 if (searchStartPos)
1448 sRoot = m_paths[0].GetWinPathString().Left(searchStartPos+1);
1450 else
1452 searchStartPos = backSlashPos;
1454 backSlashPos = 0;
1457 return CTGitPath(sRoot.TrimRight('\\'));
1460 void CTGitPathList::SortByPathname(bool bReverse /*= false*/)
1462 std::sort(m_paths.begin(), m_paths.end());
1463 if (bReverse)
1464 std::reverse(m_paths.begin(), m_paths.end());
1467 void CTGitPathList::DeleteAllFiles(bool bTrash)
1469 PathVector::const_iterator it;
1470 if (bTrash)
1472 SortByPathname();
1473 CString sPaths;
1474 for (it = m_paths.begin(); it != m_paths.end(); ++it)
1476 if ((it->Exists())&&(!it->IsDirectory()))
1478 ::SetFileAttributes(it->GetWinPath(), FILE_ATTRIBUTE_NORMAL);
1479 sPaths += it->GetWinPath();
1480 sPaths += '\0';
1483 sPaths += '\0';
1484 sPaths += '\0';
1485 SHFILEOPSTRUCT shop = {0};
1486 shop.wFunc = FO_DELETE;
1487 shop.pFrom = (LPCTSTR)sPaths;
1488 shop.fFlags = FOF_ALLOWUNDO|FOF_NOCONFIRMATION|FOF_NOERRORUI|FOF_SILENT;
1489 SHFileOperation(&shop);
1491 else
1493 for (it = m_paths.begin(); it != m_paths.end(); ++it)
1495 if (!it->IsDirectory())
1497 ::SetFileAttributes(it->GetWinPath(), FILE_ATTRIBUTE_NORMAL);
1498 ::DeleteFile(it->GetWinPath());
1502 Clear();
1505 void CTGitPathList::RemoveDuplicates()
1507 SortByPathname();
1508 // Remove the duplicates
1509 // (Unique moves them to the end of the vector, then erase chops them off)
1510 m_paths.erase(std::unique(m_paths.begin(), m_paths.end(), &CTGitPath::PredLeftEquivalentToRight), m_paths.end());
1513 void CTGitPathList::RemoveAdminPaths()
1515 PathVector::iterator it;
1516 for(it = m_paths.begin(); it != m_paths.end(); )
1518 if (it->IsAdminDir())
1520 m_paths.erase(it);
1521 it = m_paths.begin();
1523 else
1524 ++it;
1528 void CTGitPathList::RemovePath(const CTGitPath& path)
1530 PathVector::iterator it;
1531 for(it = m_paths.begin(); it != m_paths.end(); ++it)
1533 if (it->IsEquivalentTo(path))
1535 m_paths.erase(it);
1536 return;
1541 void CTGitPathList::RemoveItem(CTGitPath & path)
1543 PathVector::iterator it;
1544 for(it = m_paths.begin(); it != m_paths.end(); ++it)
1546 if (it->GetGitPathString()==path.GetGitPathString())
1548 m_paths.erase(it);
1549 return;
1553 void CTGitPathList::RemoveChildren()
1555 SortByPathname();
1556 m_paths.erase(std::unique(m_paths.begin(), m_paths.end(), &CTGitPath::CheckChild), m_paths.end());
1559 bool CTGitPathList::IsEqual(const CTGitPathList& list)
1561 if (list.GetCount() != GetCount())
1562 return false;
1563 for (int i=0; i<list.GetCount(); ++i)
1565 if (!list[i].IsEquivalentTo(m_paths[i]))
1566 return false;
1568 return true;
1571 //////////////////////////////////////////////////////////////////////////
1572 #if 0
1573 apr_array_header_t * CTGitPathList::MakePathArray (apr_pool_t *pool) const
1575 apr_array_header_t *targets = apr_array_make (pool, GetCount(), sizeof(const char *));
1577 for(int nItem = 0; nItem < GetCount(); nItem++)
1579 const char * target = m_paths[nItem].GetGitApiPath(pool);
1580 (*((const char **) apr_array_push (targets))) = target;
1583 return targets;
1585 #endif
1586 //////////////////////////////////////////////////////////////////////////
1588 #if 0
1589 #if defined(_DEBUG)
1590 // Some test cases for these classes
1591 static class CTGitPathTests
1593 public:
1594 CTGitPathTests()
1596 apr_initialize();
1597 pool = svn_pool_create(NULL);
1598 GetDirectoryTest();
1599 AdminDirTest();
1600 SortTest();
1601 RawAppendTest();
1602 PathAppendTest();
1603 RemoveDuplicatesTest();
1604 RemoveChildrenTest();
1605 ContainingDirectoryTest();
1606 AncestorTest();
1607 SubversionPathTest();
1608 GetCommonRootTest();
1609 #if defined(_MFC_VER)
1610 ValidPathAndUrlTest();
1611 ListLoadingTest();
1612 #endif
1613 apr_terminate();
1616 private:
1617 // apr_pool_t * pool;
1618 void GetDirectoryTest()
1620 // Bit tricky, this test, because we need to know something about the file
1621 // layout on the machine which is running the test
1622 TCHAR winDir[MAX_PATH+1];
1623 GetWindowsDirectory(winDir, MAX_PATH);
1624 CString sWinDir(winDir);
1626 CTGitPath testPath;
1627 // This is a file which we know will always be there
1628 testPath.SetFromUnknown(sWinDir + _T("\\win.ini"));
1629 ATLASSERT(!testPath.IsDirectory());
1630 ATLASSERT(testPath.GetDirectory().GetWinPathString() == sWinDir);
1631 ATLASSERT(testPath.GetContainingDirectory().GetWinPathString() == sWinDir);
1633 // Now do the test on the win directory itself - It's hard to be sure about the containing directory
1634 // but we know it must be different to the directory itself
1635 testPath.SetFromUnknown(sWinDir);
1636 ATLASSERT(testPath.IsDirectory());
1637 ATLASSERT(testPath.GetDirectory().GetWinPathString() == sWinDir);
1638 ATLASSERT(testPath.GetContainingDirectory().GetWinPathString() != sWinDir);
1639 ATLASSERT(testPath.GetContainingDirectory().GetWinPathString().GetLength() < sWinDir.GetLength());
1641 // Try a root path
1642 testPath.SetFromUnknown(_T("C:\\"));
1643 ATLASSERT(testPath.IsDirectory());
1644 ATLASSERT(testPath.GetDirectory().GetWinPathString().CompareNoCase(_T("C:\\"))==0);
1645 ATLASSERT(testPath.GetContainingDirectory().IsEmpty());
1646 // Try a root UNC path
1647 testPath.SetFromUnknown(_T("\\MYSTATION"));
1648 ATLASSERT(testPath.GetContainingDirectory().IsEmpty());
1651 void AdminDirTest()
1653 CTGitPath testPath;
1654 testPath.SetFromUnknown(_T("c:\\.svndir"));
1655 ATLASSERT(!testPath.IsAdminDir());
1656 testPath.SetFromUnknown(_T("c:\\test.svn"));
1657 ATLASSERT(!testPath.IsAdminDir());
1658 testPath.SetFromUnknown(_T("c:\\.svn"));
1659 ATLASSERT(testPath.IsAdminDir());
1660 testPath.SetFromUnknown(_T("c:\\.svndir\\test"));
1661 ATLASSERT(!testPath.IsAdminDir());
1662 testPath.SetFromUnknown(_T("c:\\.svn\\test"));
1663 ATLASSERT(testPath.IsAdminDir());
1665 CTGitPathList pathList;
1666 pathList.AddPath(CTGitPath(_T("c:\\.svndir")));
1667 pathList.AddPath(CTGitPath(_T("c:\\.svn")));
1668 pathList.AddPath(CTGitPath(_T("c:\\.svn\\test")));
1669 pathList.AddPath(CTGitPath(_T("c:\\test")));
1670 pathList.RemoveAdminPaths();
1671 ATLASSERT(pathList.GetCount()==2);
1672 pathList.Clear();
1673 pathList.AddPath(CTGitPath(_T("c:\\test")));
1674 pathList.RemoveAdminPaths();
1675 ATLASSERT(pathList.GetCount()==1);
1678 void SortTest()
1680 CTGitPathList testList;
1681 CTGitPath testPath;
1682 testPath.SetFromUnknown(_T("c:/Z"));
1683 testList.AddPath(testPath);
1684 testPath.SetFromUnknown(_T("c:/B"));
1685 testList.AddPath(testPath);
1686 testPath.SetFromUnknown(_T("c:\\a"));
1687 testList.AddPath(testPath);
1688 testPath.SetFromUnknown(_T("c:/Test"));
1689 testList.AddPath(testPath);
1691 testList.SortByPathname();
1693 ATLASSERT(testList[0].GetWinPathString() == _T("c:\\a"));
1694 ATLASSERT(testList[1].GetWinPathString() == _T("c:\\B"));
1695 ATLASSERT(testList[2].GetWinPathString() == _T("c:\\Test"));
1696 ATLASSERT(testList[3].GetWinPathString() == _T("c:\\Z"));
1699 void RawAppendTest()
1701 CTGitPath testPath(_T("c:/test/"));
1702 testPath.AppendRawString(_T("/Hello"));
1703 ATLASSERT(testPath.GetWinPathString() == _T("c:\\test\\Hello"));
1705 testPath.AppendRawString(_T("\\T2"));
1706 ATLASSERT(testPath.GetWinPathString() == _T("c:\\test\\Hello\\T2"));
1708 CTGitPath testFilePath(_T("C:\\windows\\win.ini"));
1709 CTGitPath testBasePath(_T("c:/temp/myfile.txt"));
1710 testBasePath.AppendRawString(testFilePath.GetFileExtension());
1711 ATLASSERT(testBasePath.GetWinPathString() == _T("c:\\temp\\myfile.txt.ini"));
1714 void PathAppendTest()
1716 CTGitPath testPath(_T("c:/test/"));
1717 testPath.AppendPathString(_T("/Hello"));
1718 ATLASSERT(testPath.GetWinPathString() == _T("c:\\test\\Hello"));
1720 testPath.AppendPathString(_T("T2"));
1721 ATLASSERT(testPath.GetWinPathString() == _T("c:\\test\\Hello\\T2"));
1723 CTGitPath testFilePath(_T("C:\\windows\\win.ini"));
1724 CTGitPath testBasePath(_T("c:/temp/myfile.txt"));
1725 // You wouldn't want to do this in real life - you'd use append-raw
1726 testBasePath.AppendPathString(testFilePath.GetFileExtension());
1727 ATLASSERT(testBasePath.GetWinPathString() == _T("c:\\temp\\myfile.txt\\.ini"));
1730 void RemoveDuplicatesTest()
1732 CTGitPathList list;
1733 list.AddPath(CTGitPath(_T("Z")));
1734 list.AddPath(CTGitPath(_T("A")));
1735 list.AddPath(CTGitPath(_T("E")));
1736 list.AddPath(CTGitPath(_T("E")));
1738 ATLASSERT(list[2].IsEquivalentTo(list[3]));
1739 ATLASSERT(list[2]==list[3]);
1741 ATLASSERT(list.GetCount() == 4);
1743 list.RemoveDuplicates();
1745 ATLASSERT(list.GetCount() == 3);
1747 ATLASSERT(list[0].GetWinPathString() == _T("A"));
1748 ATLASSERT(list[1].GetWinPathString().Compare(_T("E")) == 0);
1749 ATLASSERT(list[2].GetWinPathString() == _T("Z"));
1752 void RemoveChildrenTest()
1754 CTGitPathList list;
1755 list.AddPath(CTGitPath(_T("c:\\test")));
1756 list.AddPath(CTGitPath(_T("c:\\test\\file")));
1757 list.AddPath(CTGitPath(_T("c:\\testfile")));
1758 list.AddPath(CTGitPath(_T("c:\\parent")));
1759 list.AddPath(CTGitPath(_T("c:\\parent\\child")));
1760 list.AddPath(CTGitPath(_T("c:\\parent\\child1")));
1761 list.AddPath(CTGitPath(_T("c:\\parent\\child2")));
1763 ATLASSERT(list.GetCount() == 7);
1765 list.RemoveChildren();
1767 ATLTRACE("count = %d\n", list.GetCount());
1768 ATLASSERT(list.GetCount() == 3);
1770 list.SortByPathname();
1772 ATLASSERT(list[0].GetWinPathString().Compare(_T("c:\\parent")) == 0);
1773 ATLASSERT(list[1].GetWinPathString().Compare(_T("c:\\test")) == 0);
1774 ATLASSERT(list[2].GetWinPathString().Compare(_T("c:\\testfile")) == 0);
1777 #if defined(_MFC_VER)
1778 void ListLoadingTest()
1780 TCHAR buf[MAX_PATH];
1781 GetCurrentDirectory(MAX_PATH, buf);
1782 CString sPathList(_T("Path1*c:\\path2 with spaces and stuff*\\funnypath\\*"));
1783 CTGitPathList testList;
1784 testList.LoadFromAsteriskSeparatedString(sPathList);
1786 ATLASSERT(testList.GetCount() == 3);
1787 ATLASSERT(testList[0].GetWinPathString() == CString(buf) + _T("\\Path1"));
1788 ATLASSERT(testList[1].GetWinPathString() == _T("c:\\path2 with spaces and stuff"));
1789 ATLASSERT(testList[2].GetWinPathString() == _T("\\funnypath"));
1791 ATLASSERT(testList.GetCommonRoot().GetWinPathString() == _T(""));
1792 testList.Clear();
1793 sPathList = _T("c:\\path2 with spaces and stuff*c:\\funnypath\\*");
1794 testList.LoadFromAsteriskSeparatedString(sPathList);
1795 ATLASSERT(testList.GetCommonRoot().GetWinPathString() == _T("c:\\"));
1797 #endif
1799 void ContainingDirectoryTest()
1802 CTGitPath testPath;
1803 testPath.SetFromWin(_T("c:\\a\\b\\c\\d\\e"));
1804 CTGitPath dir;
1805 dir = testPath.GetContainingDirectory();
1806 ATLASSERT(dir.GetWinPathString() == _T("c:\\a\\b\\c\\d"));
1807 dir = dir.GetContainingDirectory();
1808 ATLASSERT(dir.GetWinPathString() == _T("c:\\a\\b\\c"));
1809 dir = dir.GetContainingDirectory();
1810 ATLASSERT(dir.GetWinPathString() == _T("c:\\a\\b"));
1811 dir = dir.GetContainingDirectory();
1812 ATLASSERT(dir.GetWinPathString() == _T("c:\\a"));
1813 dir = dir.GetContainingDirectory();
1814 ATLASSERT(dir.GetWinPathString() == _T("c:\\"));
1815 dir = dir.GetContainingDirectory();
1816 ATLASSERT(dir.IsEmpty());
1817 ATLASSERT(dir.GetWinPathString() == _T(""));
1820 void AncestorTest()
1822 CTGitPath testPath;
1823 testPath.SetFromWin(_T("c:\\windows"));
1824 ATLASSERT(testPath.IsAncestorOf(CTGitPath(_T("c:\\")))==false);
1825 ATLASSERT(testPath.IsAncestorOf(CTGitPath(_T("c:\\windows"))));
1826 ATLASSERT(testPath.IsAncestorOf(CTGitPath(_T("c:\\windowsdummy")))==false);
1827 ATLASSERT(testPath.IsAncestorOf(CTGitPath(_T("c:\\windows\\test.txt"))));
1828 ATLASSERT(testPath.IsAncestorOf(CTGitPath(_T("c:\\windows\\system32\\test.txt"))));
1831 void SubversionPathTest()
1833 CTGitPath testPath;
1834 testPath.SetFromWin(_T("c:\\"));
1835 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "c:") == 0);
1836 testPath.SetFromWin(_T("c:\\folder"));
1837 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "c:/folder") == 0);
1838 testPath.SetFromWin(_T("c:\\a\\b\\c\\d\\e"));
1839 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "c:/a/b/c/d/e") == 0);
1840 testPath.SetFromUnknown(_T("http://testing/"));
1841 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "http://testing") == 0);
1842 testPath.SetFromGit(NULL);
1843 ATLASSERT(strlen(testPath.GetGitApiPath(pool))==0);
1844 #if defined(_MFC_VER)
1845 testPath.SetFromUnknown(_T("http://testing again"));
1846 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "http://testing%20again") == 0);
1847 testPath.SetFromUnknown(_T("http://testing%20again"));
1848 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "http://testing%20again") == 0);
1849 testPath.SetFromUnknown(_T("http://testing special chars \344\366\374"));
1850 ATLASSERT(strcmp(testPath.GetGitApiPath(pool), "http://testing%20special%20chars%20%c3%a4%c3%b6%c3%bc") == 0);
1851 #endif
1854 void GetCommonRootTest()
1856 CTGitPath pathA (_T("C:\\Development\\LogDlg.cpp"));
1857 CTGitPath pathB (_T("C:\\Development\\LogDlg.h"));
1858 CTGitPath pathC (_T("C:\\Development\\SomeDir\\LogDlg.h"));
1860 CTGitPathList list;
1861 list.AddPath(pathA);
1862 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("C:\\Development\\LogDlg.cpp"))==0);
1863 list.AddPath(pathB);
1864 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("C:\\Development"))==0);
1865 list.AddPath(pathC);
1866 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("C:\\Development"))==0);
1867 #ifdef _MFC_VER
1868 list.Clear();
1869 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");
1870 list.LoadFromAsteriskSeparatedString(sPathList);
1871 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("D:\\Development\\StExBar"))==0);
1873 list.Clear();
1874 sPathList = _T("c:\\windows\\explorer.exe*c:\\windows");
1875 list.LoadFromAsteriskSeparatedString(sPathList);
1876 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("c:\\windows"))==0);
1878 list.Clear();
1879 sPathList = _T("c:\\windows\\*c:\\windows");
1880 list.LoadFromAsteriskSeparatedString(sPathList);
1881 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("c:\\windows"))==0);
1883 list.Clear();
1884 sPathList = _T("c:\\windows\\system32*c:\\windows\\system");
1885 list.LoadFromAsteriskSeparatedString(sPathList);
1886 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("c:\\windows"))==0);
1888 list.Clear();
1889 sPathList = _T("c:\\windowsdummy*c:\\windows");
1890 list.LoadFromAsteriskSeparatedString(sPathList);
1891 ATLASSERT(list.GetCommonRoot().GetWinPathString().CompareNoCase(_T("c:\\"))==0);
1892 #endif
1895 void ValidPathAndUrlTest()
1897 CTGitPath testPath;
1898 testPath.SetFromWin(_T("c:\\a\\b\\c.test.txt"));
1899 ATLASSERT(testPath.IsValidOnWindows());
1900 testPath.SetFromWin(_T("c:\\"));
1901 ATLASSERT(testPath.IsValidOnWindows());
1902 testPath.SetFromWin(_T("D:\\.Net\\SpindleSearch\\"));
1903 ATLASSERT(testPath.IsValidOnWindows());
1904 testPath.SetFromWin(_T("c"));
1905 ATLASSERT(testPath.IsValidOnWindows());
1906 testPath.SetFromWin(_T("c:\\test folder\\file"));
1907 ATLASSERT(testPath.IsValidOnWindows());
1908 testPath.SetFromWin(_T("c:\\folder\\"));
1909 ATLASSERT(testPath.IsValidOnWindows());
1910 testPath.SetFromWin(_T("c:\\ext.ext.ext\\ext.ext.ext.ext"));
1911 ATLASSERT(testPath.IsValidOnWindows());
1912 testPath.SetFromWin(_T("c:\\.svn"));
1913 ATLASSERT(testPath.IsValidOnWindows());
1914 testPath.SetFromWin(_T("c:\\com\\file"));
1915 ATLASSERT(testPath.IsValidOnWindows());
1916 testPath.SetFromWin(_T("c:\\test\\conf"));
1917 ATLASSERT(testPath.IsValidOnWindows());
1918 testPath.SetFromWin(_T("c:\\LPT"));
1919 ATLASSERT(testPath.IsValidOnWindows());
1920 testPath.SetFromWin(_T("c:\\test\\LPT"));
1921 ATLASSERT(testPath.IsValidOnWindows());
1922 testPath.SetFromWin(_T("c:\\com1test"));
1923 ATLASSERT(testPath.IsValidOnWindows());
1924 testPath.SetFromWin(_T("\\\\?\\c:\\test\\com1test"));
1925 ATLASSERT(testPath.IsValidOnWindows());
1927 testPath.SetFromWin(_T("\\\\Share\\filename"));
1928 ATLASSERT(testPath.IsValidOnWindows());
1929 testPath.SetFromWin(_T("\\\\Share\\filename.extension"));
1930 ATLASSERT(testPath.IsValidOnWindows());
1931 testPath.SetFromWin(_T("\\\\Share\\.svn"));
1932 ATLASSERT(testPath.IsValidOnWindows());
1934 // now the negative tests
1935 testPath.SetFromWin(_T("c:\\test:folder"));
1936 ATLASSERT(!testPath.IsValidOnWindows());
1937 testPath.SetFromWin(_T("c:\\file<name"));
1938 ATLASSERT(!testPath.IsValidOnWindows());
1939 testPath.SetFromWin(_T("c:\\something*else"));
1940 ATLASSERT(!testPath.IsValidOnWindows());
1941 testPath.SetFromWin(_T("c:\\folder\\file?nofile"));
1942 ATLASSERT(!testPath.IsValidOnWindows());
1943 testPath.SetFromWin(_T("c:\\ext.>ension"));
1944 ATLASSERT(!testPath.IsValidOnWindows());
1945 testPath.SetFromWin(_T("c:\\com1\\filename"));
1946 ATLASSERT(!testPath.IsValidOnWindows());
1947 testPath.SetFromWin(_T("c:\\com1"));
1948 ATLASSERT(!testPath.IsValidOnWindows());
1949 testPath.SetFromWin(_T("c:\\com1\\AuX"));
1950 ATLASSERT(!testPath.IsValidOnWindows());
1952 testPath.SetFromWin(_T("\\\\Share\\lpt9\\filename"));
1953 ATLASSERT(!testPath.IsValidOnWindows());
1954 testPath.SetFromWin(_T("\\\\Share\\prn"));
1955 ATLASSERT(!testPath.IsValidOnWindows());
1956 testPath.SetFromWin(_T("\\\\Share\\NUL"));
1957 ATLASSERT(!testPath.IsValidOnWindows());
1959 // now come some URL tests
1960 testPath.SetFromGit(_T("http://myserver.com/repos/trunk"));
1961 ATLASSERT(testPath.IsValidOnWindows());
1962 testPath.SetFromGit(_T("https://myserver.com/repos/trunk/file%20with%20spaces"));
1963 ATLASSERT(testPath.IsValidOnWindows());
1964 testPath.SetFromGit(_T("svn://myserver.com/repos/trunk/file with spaces"));
1965 ATLASSERT(testPath.IsValidOnWindows());
1966 testPath.SetFromGit(_T("svn+ssh://www.myserver.com/repos/trunk"));
1967 ATLASSERT(testPath.IsValidOnWindows());
1968 testPath.SetFromGit(_T("http://localhost:90/repos/trunk"));
1969 ATLASSERT(testPath.IsValidOnWindows());
1970 testPath.SetFromGit(_T("file:///C:/GitRepos/Tester/Proj1/tags/t2"));
1971 ATLASSERT(testPath.IsValidOnWindows());
1972 // and some negative URL tests
1973 testPath.SetFromGit(_T("httpp://myserver.com/repos/trunk"));
1974 ATLASSERT(!testPath.IsValidOnWindows());
1975 testPath.SetFromGit(_T("https://myserver.com/rep:os/trunk/file%20with%20spaces"));
1976 ATLASSERT(!testPath.IsValidOnWindows());
1977 testPath.SetFromGit(_T("svn://myserver.com/rep<os/trunk/file with spaces"));
1978 ATLASSERT(!testPath.IsValidOnWindows());
1979 testPath.SetFromGit(_T("svn+ssh://www.myserver.com/repos/trunk/prn/"));
1980 ATLASSERT(!testPath.IsValidOnWindows());
1981 testPath.SetFromGit(_T("http://localhost:90/repos/trunk/com1"));
1982 ATLASSERT(!testPath.IsValidOnWindows());
1986 } TGitPathTestobject;
1987 #endif
1988 #endif
1990 CTGitPath * CTGitPathList::LookForGitPath(CString path)
1992 int i=0;
1993 for(i=0;i<this->GetCount();i++)
1995 if((*this)[i].GetGitPathString() == path )
1996 return (CTGitPath*)&(*this)[i];
1998 return NULL;
2000 CString CTGitPath::GetActionName(int action)
2002 if(action & CTGitPath::LOGACTIONS_UNMERGED)
2003 return _T("Conflict");
2004 if(action & CTGitPath::LOGACTIONS_ADDED)
2005 return _T("Added");
2006 if(action & CTGitPath::LOGACTIONS_DELETED)
2007 return _T("Deleted");
2008 if(action & CTGitPath::LOGACTIONS_MERGED )
2009 return _T("Merged");
2011 if(action & CTGitPath::LOGACTIONS_MODIFIED)
2012 return _T("Modified");
2013 if(action & CTGitPath::LOGACTIONS_REPLACED)
2014 return _T("Rename");
2015 if(action & CTGitPath::LOGACTIONS_COPY)
2016 return _T("Copy");
2018 if(action & CTGitPath::LOGACTIONS_FORWORD )
2019 return _T("Forward");
2021 if(action & CTGitPath::LOGACTIONS_REBASE_EDIT)
2022 return _T("Edit");
2023 if(action & CTGitPath::LOGACTIONS_REBASE_SQUASH)
2024 return _T("Squash");
2025 if(action & CTGitPath::LOGACTIONS_REBASE_PICK)
2026 return _T("Pick");
2027 if(action & CTGitPath::LOGACTIONS_REBASE_SKIP)
2028 return _T("Skip");
2030 return _T("Unknown");
2032 CString CTGitPath::GetActionName()
2034 return GetActionName(m_Action);
2037 int CTGitPathList::GetAction()
2039 return m_Action;