Drop LOGACTIONS_CACHE
[TortoiseGit.git] / src / Git / TGitPath.cpp
blob56434ccbbdaa183083d739e4fa05619b87129d63
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2015 - TortoiseGit
4 // Copyright (C) 2003-2008 - TortoiseSVN
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 "../TortoiseShell/Globals.h"
28 #include "StringUtils.h"
29 #include "../Resources/LoglistCommonResource.h"
30 #include <memory>
32 extern CGit g_Git;
34 CTGitPath::CTGitPath(void)
35 : m_bDirectoryKnown(false)
36 , m_bIsDirectory(false)
37 , m_bURLKnown(false)
38 , m_bHasAdminDirKnown(false)
39 , m_bHasAdminDir(false)
40 , m_bIsValidOnWindowsKnown(false)
41 , m_bIsValidOnWindows(false)
42 , m_bIsReadOnly(false)
43 , m_bIsAdminDirKnown(false)
44 , m_bIsAdminDir(false)
45 , m_bExists(false)
46 , m_bExistsKnown(false)
47 , m_bLastWriteTimeKnown(0)
48 , m_lastWriteTime(0)
49 , m_customData(NULL)
50 , m_bIsSpecialDirectoryKnown(false)
51 , m_bIsSpecialDirectory(false)
52 , m_bIsWCRootKnown(false)
53 , m_bIsWCRoot(false)
54 , m_fileSize(0)
55 , m_Checked(false)
57 m_Action=0;
58 m_ParentNo=0;
59 m_Stage = 0;
62 CTGitPath::~CTGitPath(void)
65 // Create a TGitPath object from an unknown path type (same as using SetFromUnknown)
66 CTGitPath::CTGitPath(const CString& sUnknownPath) :
67 m_bDirectoryKnown(false)
68 , m_bIsDirectory(false)
69 , m_bURLKnown(false)
70 , m_bHasAdminDirKnown(false)
71 , m_bHasAdminDir(false)
72 , m_bIsValidOnWindowsKnown(false)
73 , m_bIsValidOnWindows(false)
74 , m_bIsReadOnly(false)
75 , m_bIsAdminDirKnown(false)
76 , m_bIsAdminDir(false)
77 , m_bExists(false)
78 , m_bExistsKnown(false)
79 , m_bLastWriteTimeKnown(0)
80 , m_lastWriteTime(0)
81 , m_customData(NULL)
82 , m_bIsSpecialDirectoryKnown(false)
83 , m_bIsSpecialDirectory(false)
84 , m_bIsWCRootKnown(false)
85 , m_bIsWCRoot(false)
86 , m_fileSize(0)
87 , m_Checked(false)
89 SetFromUnknown(sUnknownPath);
90 m_Action=0;
91 m_Stage=0;
92 m_ParentNo=0;
95 int CTGitPath::ParserAction(BYTE action)
97 //action=action.TrimLeft();
98 //TCHAR c=action.GetAt(0);
99 if(action == 'M')
100 m_Action|= LOGACTIONS_MODIFIED;
101 if(action == 'R')
102 m_Action|= LOGACTIONS_REPLACED;
103 if(action == 'A')
104 m_Action|= LOGACTIONS_ADDED;
105 if(action == 'D')
106 m_Action|= LOGACTIONS_DELETED;
107 if(action == 'U')
108 m_Action|= LOGACTIONS_UNMERGED;
109 if(action == 'K')
110 m_Action|= LOGACTIONS_DELETED;
111 if(action == 'C' )
112 m_Action|= LOGACTIONS_COPY;
113 if(action == 'T')
114 m_Action|= LOGACTIONS_MODIFIED;
116 return m_Action;
118 void CTGitPath::SetFromGit(const char* pPath)
120 Reset();
121 if (pPath == NULL)
122 return;
123 int len = MultiByteToWideChar(CP_UTF8, 0, pPath, -1, NULL, 0);
124 if (len)
126 len = MultiByteToWideChar(CP_UTF8, 0, pPath, -1, m_sFwdslashPath.GetBuffer(len+1), len+1);
127 m_sFwdslashPath.ReleaseBuffer(len-1);
129 SanitizeRootPath(m_sFwdslashPath, true);
132 void CTGitPath::SetFromGit(const char* pPath, bool bIsDirectory)
134 SetFromGit(pPath);
135 m_bDirectoryKnown = true;
136 m_bIsDirectory = bIsDirectory;
139 void CTGitPath::SetFromGit(const TCHAR* pPath, bool bIsDirectory)
141 Reset();
142 if (pPath)
144 m_sFwdslashPath = pPath;
145 SanitizeRootPath(m_sFwdslashPath, true);
147 m_bDirectoryKnown = true;
148 m_bIsDirectory = bIsDirectory;
151 void CTGitPath::SetFromGit(const CString& sPath,CString *oldpath)
153 Reset();
154 m_sFwdslashPath = sPath;
155 SanitizeRootPath(m_sFwdslashPath, true);
156 if(oldpath)
157 m_sOldFwdslashPath = *oldpath;
160 void CTGitPath::SetFromWin(LPCTSTR pPath)
162 Reset();
163 m_sBackslashPath = pPath;
164 m_sBackslashPath.Replace(L"\\\\?\\", L"");
165 SanitizeRootPath(m_sBackslashPath, false);
166 ATLASSERT(m_sBackslashPath.Find('/')<0);
168 void CTGitPath::SetFromWin(const CString& sPath)
170 Reset();
171 m_sBackslashPath = sPath;
172 m_sBackslashPath.Replace(L"\\\\?\\", L"");
173 SanitizeRootPath(m_sBackslashPath, false);
175 void CTGitPath::SetFromWin(LPCTSTR pPath, bool bIsDirectory)
177 Reset();
178 m_sBackslashPath = pPath;
179 m_bIsDirectory = bIsDirectory;
180 m_bDirectoryKnown = true;
181 SanitizeRootPath(m_sBackslashPath, false);
183 void CTGitPath::SetFromWin(const CString& sPath, bool bIsDirectory)
185 Reset();
186 m_sBackslashPath = sPath;
187 m_bIsDirectory = bIsDirectory;
188 m_bDirectoryKnown = true;
189 SanitizeRootPath(m_sBackslashPath, false);
191 void CTGitPath::SetFromUnknown(const CString& sPath)
193 Reset();
194 // Just set whichever path we think is most likely to be used
195 // GitAdminDir admin;
196 // CString p;
197 // if(admin.HasAdminDir(sPath,&p))
198 // SetFwdslashPath(sPath.Right(sPath.GetLength()-p.GetLength()));
199 // else
200 SetFwdslashPath(sPath);
203 LPCTSTR CTGitPath::GetWinPath() const
205 if(IsEmpty())
207 return _T("");
209 if(m_sBackslashPath.IsEmpty())
211 SetBackslashPath(m_sFwdslashPath);
213 return m_sBackslashPath;
215 // This is a temporary function, to be used during the migration to
216 // the path class. Ultimately, functions consuming paths should take a CTGitPath&, not a CString
217 const CString& CTGitPath::GetWinPathString() const
219 if(m_sBackslashPath.IsEmpty())
221 SetBackslashPath(m_sFwdslashPath);
223 return m_sBackslashPath;
226 const CString& CTGitPath::GetGitPathString() const
228 if(m_sFwdslashPath.IsEmpty())
230 SetFwdslashPath(m_sBackslashPath);
232 return m_sFwdslashPath;
235 const CString &CTGitPath::GetGitOldPathString() const
237 return m_sOldFwdslashPath;
240 const CString& CTGitPath::GetUIPathString() const
242 if (m_sUIPath.IsEmpty())
244 m_sUIPath = GetWinPathString();
246 return m_sUIPath;
249 void CTGitPath::SetFwdslashPath(const CString& sPath) const
251 CString path = sPath;
252 path.Replace('\\', '/');
254 // We don't leave a trailing /
255 path.TrimRight('/');
256 path.Replace(L"//?/", L"");
258 SanitizeRootPath(path, true);
260 path.Replace(_T("file:////"), _T("file://"));
261 m_sFwdslashPath = path;
263 m_sUTF8FwdslashPath.Empty();
266 void CTGitPath::SetBackslashPath(const CString& sPath) const
268 CString path = sPath;
269 path.Replace('/', '\\');
270 path.TrimRight('\\');
271 SanitizeRootPath(path, false);
272 m_sBackslashPath = path;
275 void CTGitPath::SetUTF8FwdslashPath(const CString& sPath) const
277 m_sUTF8FwdslashPath = CUnicodeUtils::GetUTF8(sPath);
280 void CTGitPath::SanitizeRootPath(CString& sPath, bool bIsForwardPath) const
282 // Make sure to add the trailing slash to root paths such as 'C:'
283 if (sPath.GetLength() == 2 && sPath[1] == ':')
285 sPath += (bIsForwardPath) ? _T("/") : _T("\\");
289 bool CTGitPath::IsDirectory() const
291 if(!m_bDirectoryKnown)
293 UpdateAttributes();
295 return m_bIsDirectory;
298 bool CTGitPath::Exists() const
300 if (!m_bExistsKnown)
302 UpdateAttributes();
304 return m_bExists;
307 bool CTGitPath::Delete(bool bTrash) const
309 EnsureBackslashPathSet();
310 ::SetFileAttributes(m_sBackslashPath, FILE_ATTRIBUTE_NORMAL);
311 bool bRet = false;
312 if (Exists())
314 if ((bTrash)||(IsDirectory()))
316 std::unique_ptr<TCHAR[]> buf(new TCHAR[m_sBackslashPath.GetLength() + 2]);
317 _tcscpy_s(buf.get(), m_sBackslashPath.GetLength() + 2, m_sBackslashPath);
318 buf[m_sBackslashPath.GetLength()] = 0;
319 buf[m_sBackslashPath.GetLength()+1] = 0;
320 bRet = CTGitPathList::DeleteViaShell(buf.get(), bTrash);
322 else
324 bRet = !!::DeleteFile(m_sBackslashPath);
327 m_bExists = false;
328 m_bExistsKnown = true;
329 return bRet;
332 __int64 CTGitPath::GetLastWriteTime() const
334 if(!m_bLastWriteTimeKnown)
336 UpdateAttributes();
338 return m_lastWriteTime;
341 __int64 CTGitPath::GetFileSize() const
343 if(!m_bDirectoryKnown)
345 UpdateAttributes();
347 return m_fileSize;
350 bool CTGitPath::IsReadOnly() const
352 if(!m_bLastWriteTimeKnown)
354 UpdateAttributes();
356 return m_bIsReadOnly;
359 void CTGitPath::UpdateAttributes() const
361 EnsureBackslashPathSet();
362 WIN32_FILE_ATTRIBUTE_DATA attribs;
363 if (m_sBackslashPath.GetLength() >= 248)
364 m_sLongBackslashPath = _T("\\\\?\\") + m_sBackslashPath;
365 if(GetFileAttributesEx(m_sBackslashPath.GetLength() >= 248 ? m_sLongBackslashPath : m_sBackslashPath, GetFileExInfoStandard, &attribs))
367 m_bIsDirectory = !!(attribs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
368 // don't cast directly to an __int64:
369 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms724284%28v=vs.85%29.aspx
370 // "Do not cast a pointer to a FILETIME structure to either a ULARGE_INTEGER* or __int64* value
371 // because it can cause alignment faults on 64-bit Windows."
372 m_lastWriteTime = static_cast<__int64>(attribs.ftLastWriteTime.dwHighDateTime) << 32 | attribs.ftLastWriteTime.dwLowDateTime;
373 if (m_bIsDirectory)
375 m_fileSize = 0;
377 else
379 m_fileSize = ((INT64)( (DWORD)(attribs.nFileSizeLow) ) | ( (INT64)( (DWORD)(attribs.nFileSizeHigh) )<<32 ));
381 m_bIsReadOnly = !!(attribs.dwFileAttributes & FILE_ATTRIBUTE_READONLY);
382 m_bExists = true;
384 else
386 m_bIsDirectory = false;
387 m_lastWriteTime = 0;
388 m_fileSize = 0;
389 DWORD err = GetLastError();
390 if ((err == ERROR_FILE_NOT_FOUND)||(err == ERROR_PATH_NOT_FOUND)||(err == ERROR_INVALID_NAME))
392 m_bExists = false;
394 else
396 m_bExists = true;
397 return;
400 m_bDirectoryKnown = true;
401 m_bLastWriteTimeKnown = true;
402 m_bExistsKnown = true;
405 CTGitPath CTGitPath::GetSubPath(const CTGitPath &root)
407 CTGitPath path;
409 if(GetWinPathString().Left(root.GetWinPathString().GetLength()) == root.GetWinPathString())
411 CString str=GetWinPathString();
412 path.SetFromWin(str.Right(str.GetLength()-root.GetWinPathString().GetLength()-1));
414 return path;
417 void CTGitPath::EnsureBackslashPathSet() const
419 if(m_sBackslashPath.IsEmpty())
421 SetBackslashPath(m_sFwdslashPath);
422 ATLASSERT(IsEmpty() || !m_sBackslashPath.IsEmpty());
425 void CTGitPath::EnsureFwdslashPathSet() const
427 if(m_sFwdslashPath.IsEmpty())
429 SetFwdslashPath(m_sBackslashPath);
430 ATLASSERT(IsEmpty() || !m_sFwdslashPath.IsEmpty());
435 // Reset all the caches
436 void CTGitPath::Reset()
438 m_bDirectoryKnown = false;
439 m_bURLKnown = false;
440 m_bLastWriteTimeKnown = false;
441 m_bHasAdminDirKnown = false;
442 m_bIsValidOnWindowsKnown = false;
443 m_bIsAdminDirKnown = false;
444 m_bExistsKnown = false;
445 m_bIsSpecialDirectoryKnown = false;
446 m_bIsSpecialDirectory = false;
448 m_sBackslashPath.Empty();
449 m_sFwdslashPath.Empty();
450 m_sUTF8FwdslashPath.Empty();
451 this->m_Action=0;
452 this->m_StatAdd=_T("");
453 this->m_StatDel=_T("");
454 m_ParentNo=0;
455 ATLASSERT(IsEmpty());
458 CTGitPath CTGitPath::GetDirectory() const
460 if ((IsDirectory())||(!Exists()))
462 return *this;
464 return GetContainingDirectory();
467 CTGitPath CTGitPath::GetContainingDirectory() const
469 EnsureBackslashPathSet();
471 CString sDirName = m_sBackslashPath.Left(m_sBackslashPath.ReverseFind('\\'));
472 if(sDirName.GetLength() == 2 && sDirName[1] == ':')
474 // This is a root directory, which needs a trailing slash
475 sDirName += '\\';
476 if(sDirName == m_sBackslashPath)
478 // We were clearly provided with a root path to start with - we should return nothing now
479 sDirName.Empty();
482 if(sDirName.GetLength() == 1 && sDirName[0] == '\\')
484 // We have an UNC path and we already are the root
485 sDirName.Empty();
487 CTGitPath retVal;
488 retVal.SetFromWin(sDirName);
489 return retVal;
492 CString CTGitPath::GetRootPathString() const
494 EnsureBackslashPathSet();
495 CString workingPath = m_sBackslashPath;
496 LPTSTR pPath = workingPath.GetBuffer(MAX_PATH); // MAX_PATH ok here.
497 ATLVERIFY(::PathStripToRoot(pPath));
498 workingPath.ReleaseBuffer();
499 return workingPath;
503 CString CTGitPath::GetFilename() const
505 //ATLASSERT(!IsDirectory());
506 return GetFileOrDirectoryName();
509 CString CTGitPath::GetFileOrDirectoryName() const
511 EnsureBackslashPathSet();
512 return m_sBackslashPath.Mid(m_sBackslashPath.ReverseFind('\\')+1);
515 CString CTGitPath::GetUIFileOrDirectoryName() const
517 GetUIPathString();
518 return m_sUIPath.Mid(m_sUIPath.ReverseFind('\\')+1);
521 CString CTGitPath::GetFileExtension() const
523 if(!IsDirectory())
525 EnsureBackslashPathSet();
526 int dotPos = m_sBackslashPath.ReverseFind('.');
527 int slashPos = m_sBackslashPath.ReverseFind('\\');
528 if (dotPos > slashPos)
529 return m_sBackslashPath.Mid(dotPos);
531 return CString();
533 CString CTGitPath::GetBaseFilename() const
535 int dot;
536 CString filename=GetFilename();
537 dot = filename.ReverseFind(_T('.'));
538 if(dot>0)
539 return filename.Left(dot);
540 else
541 return filename;
544 bool CTGitPath::ArePathStringsEqual(const CString& sP1, const CString& sP2)
546 int length = sP1.GetLength();
547 if(length != sP2.GetLength())
549 // Different lengths
550 return false;
552 // We work from the end of the strings, because path differences
553 // are more likely to occur at the far end of a string
554 LPCTSTR pP1Start = sP1;
555 LPCTSTR pP1 = pP1Start+(length-1);
556 LPCTSTR pP2 = ((LPCTSTR)sP2)+(length-1);
557 while(length-- > 0)
559 if(_totlower(*pP1--) != _totlower(*pP2--))
561 return false;
564 return true;
567 bool CTGitPath::ArePathStringsEqualWithCase(const CString& sP1, const CString& sP2)
569 int length = sP1.GetLength();
570 if(length != sP2.GetLength())
572 // Different lengths
573 return false;
575 // We work from the end of the strings, because path differences
576 // are more likely to occur at the far end of a string
577 LPCTSTR pP1Start = sP1;
578 LPCTSTR pP1 = pP1Start+(length-1);
579 LPCTSTR pP2 = ((LPCTSTR)sP2)+(length-1);
580 while(length-- > 0)
582 if((*pP1--) != (*pP2--))
584 return false;
587 return true;
590 bool CTGitPath::IsEmpty() const
592 // Check the backward slash path first, since the chance that this
593 // one is set is higher. In case of a 'false' return value it's a little
594 // bit faster.
595 return m_sBackslashPath.IsEmpty() && m_sFwdslashPath.IsEmpty();
598 // Test if both paths refer to the same item
599 // Ignores case and slash direction
600 bool CTGitPath::IsEquivalentTo(const CTGitPath& rhs) const
602 // Try and find a slash direction which avoids having to convert
603 // both filenames
604 if(!m_sBackslashPath.IsEmpty())
606 // *We've* got a \ path - make sure that the RHS also has a \ path
607 rhs.EnsureBackslashPathSet();
608 return ArePathStringsEqualWithCase(m_sBackslashPath, rhs.m_sBackslashPath);
610 else
612 // Assume we've got a fwdslash path and make sure that the RHS has one
613 rhs.EnsureFwdslashPathSet();
614 return ArePathStringsEqualWithCase(m_sFwdslashPath, rhs.m_sFwdslashPath);
618 bool CTGitPath::IsEquivalentToWithoutCase(const CTGitPath& rhs) const
620 // Try and find a slash direction which avoids having to convert
621 // both filenames
622 if(!m_sBackslashPath.IsEmpty())
624 // *We've* got a \ path - make sure that the RHS also has a \ path
625 rhs.EnsureBackslashPathSet();
626 return ArePathStringsEqual(m_sBackslashPath, rhs.m_sBackslashPath);
628 else
630 // Assume we've got a fwdslash path and make sure that the RHS has one
631 rhs.EnsureFwdslashPathSet();
632 return ArePathStringsEqual(m_sFwdslashPath, rhs.m_sFwdslashPath);
636 bool CTGitPath::IsAncestorOf(const CTGitPath& possibleDescendant) const
638 possibleDescendant.EnsureBackslashPathSet();
639 EnsureBackslashPathSet();
641 bool bPathStringsEqual = ArePathStringsEqual(m_sBackslashPath, possibleDescendant.m_sBackslashPath.Left(m_sBackslashPath.GetLength()));
642 if (m_sBackslashPath.GetLength() >= possibleDescendant.GetWinPathString().GetLength())
644 return bPathStringsEqual;
647 return (bPathStringsEqual &&
648 ((possibleDescendant.m_sBackslashPath[m_sBackslashPath.GetLength()] == '\\')||
649 (m_sBackslashPath.GetLength()==3 && m_sBackslashPath[1]==':')));
652 // Get a string representing the file path, optionally with a base
653 // section stripped off the front.
654 CString CTGitPath::GetDisplayString(const CTGitPath* pOptionalBasePath /* = NULL*/) const
656 EnsureFwdslashPathSet();
657 if(pOptionalBasePath != NULL)
659 // Find the length of the base-path without having to do an 'ensure' on it
660 int baseLength = max(pOptionalBasePath->m_sBackslashPath.GetLength(), pOptionalBasePath->m_sFwdslashPath.GetLength());
662 // Now, chop that baseLength of the front of the path
663 return m_sFwdslashPath.Mid(baseLength).TrimLeft('/');
665 return m_sFwdslashPath;
668 int CTGitPath::Compare(const CTGitPath& left, const CTGitPath& right)
670 left.EnsureBackslashPathSet();
671 right.EnsureBackslashPathSet();
672 return left.m_sBackslashPath.CompareNoCase(right.m_sBackslashPath);
675 bool operator<(const CTGitPath& left, const CTGitPath& right)
677 return CTGitPath::Compare(left, right) < 0;
680 bool CTGitPath::PredLeftEquivalentToRight(const CTGitPath& left, const CTGitPath& right)
682 return left.IsEquivalentTo(right);
685 bool CTGitPath::PredLeftSameWCPathAsRight(const CTGitPath& left, const CTGitPath& right)
687 if (left.IsAdminDir() && right.IsAdminDir())
689 CTGitPath l = left;
690 CTGitPath r = right;
693 l = l.GetContainingDirectory();
694 } while(l.HasAdminDir());
697 r = r.GetContainingDirectory();
698 } while(r.HasAdminDir());
699 return l.GetContainingDirectory().IsEquivalentTo(r.GetContainingDirectory());
701 return left.GetDirectory().IsEquivalentTo(right.GetDirectory());
704 bool CTGitPath::CheckChild(const CTGitPath &parent, const CTGitPath& child)
706 return parent.IsAncestorOf(child);
709 void CTGitPath::AppendRawString(const CString& sAppend)
711 EnsureFwdslashPathSet();
712 CString strCopy = m_sFwdslashPath += sAppend;
713 SetFromUnknown(strCopy);
716 void CTGitPath::AppendPathString(const CString& sAppend)
718 EnsureBackslashPathSet();
719 CString cleanAppend(sAppend);
720 cleanAppend.Replace('/', '\\');
721 cleanAppend.TrimLeft('\\');
722 m_sBackslashPath.TrimRight('\\');
723 CString strCopy = m_sBackslashPath + _T("\\") + cleanAppend;
724 SetFromWin(strCopy);
727 bool CTGitPath::IsWCRoot() const
729 if (m_bIsWCRootKnown)
730 return m_bIsWCRoot;
732 m_bIsWCRootKnown = true;
733 m_bIsWCRoot = false;
735 CString topDirectory;
736 if (!IsDirectory() || !HasAdminDir(&topDirectory))
738 return m_bIsWCRoot;
741 if (IsEquivalentToWithoutCase(topDirectory))
743 m_bIsWCRoot = true;
746 return m_bIsWCRoot;
749 bool CTGitPath::HasAdminDir() const
751 if (m_bHasAdminDirKnown)
752 return m_bHasAdminDir;
754 EnsureBackslashPathSet();
755 m_bHasAdminDir = GitAdminDir::HasAdminDir(m_sBackslashPath, IsDirectory(), &m_sProjectRoot);
756 m_bHasAdminDirKnown = true;
757 return m_bHasAdminDir;
760 bool CTGitPath::HasSubmodules() const
762 if (HasAdminDir())
764 CString path = m_sProjectRoot;
765 path += _T("\\.gitmodules");
766 if( PathFileExists(path) )
767 return true;
769 return false;
772 int CTGitPath::GetAdminDirMask() const
774 int status = 0;
775 CString topdir;
776 if (!GitAdminDir::HasAdminDir(GetWinPathString(), &topdir))
778 return status;
781 // ITEMIS_INGIT will be revoked if necessary in TortoiseShell/ContextMenu.cpp
782 status |= ITEMIS_INGIT|ITEMIS_INVERSIONEDFOLDER;
784 if (IsDirectory())
786 status |= ITEMIS_FOLDERINGIT;
787 if (IsWCRoot())
789 status |= ITEMIS_WCROOT;
791 CString topProjectDir;
792 if (GitAdminDir::HasAdminDir(GetWinPathString(), false, &topProjectDir))
794 if (PathFileExists(topProjectDir + _T("\\.gitmodules")))
796 CAutoConfig config(true);
797 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(topProjectDir + _T("\\.gitmodules")), GIT_CONFIG_LEVEL_APP, FALSE);
798 CString relativePath = GetWinPathString().Mid(topProjectDir.GetLength());
799 relativePath.Replace(_T("\\"), _T("/"));
800 relativePath.Trim(_T("/"));
801 CStringA submodulePath = CUnicodeUtils::GetUTF8(relativePath);
802 if (git_config_foreach_match(config, "submodule\\..*\\.path",
803 [](const git_config_entry *entry, void *data) { return entry->value == *(CStringA *)data ? GIT_EUSER : 0; }, &submodulePath) == GIT_EUSER)
804 status |= ITEMIS_SUBMODULE;
810 CString dotGitPath;
811 GitAdminDir::GetAdminDirPath(topdir, dotGitPath);
813 if (PathFileExists(dotGitPath + _T("BISECT_START")))
814 status |= ITEMIS_BISECT;
816 if (PathFileExists(dotGitPath + _T("MERGE_HEAD")))
817 status |= ITEMIS_MERGEACTIVE;
819 if (PathFileExists(dotGitPath + _T("refs\\stash")))
820 status |= ITEMIS_STASH;
822 if (PathFileExists(dotGitPath + _T("svn")))
823 status |= ITEMIS_GITSVN;
825 if (PathFileExists(topdir + _T("\\.gitmodules")))
826 status |= ITEMIS_SUBMODULECONTAINER;
828 return status;
831 bool CTGitPath::HasStashDir() const
833 CString topdir;
834 if(!GitAdminDir::HasAdminDir(GetWinPathString(),&topdir))
836 return false;
839 CString dotGitPath;
840 GitAdminDir::GetAdminDirPath(topdir, dotGitPath);
842 return !!PathFileExists(dotGitPath + _T("\\refs\\stash"));
844 bool CTGitPath::HasGitSVNDir() const
846 CString topdir;
847 if (!GitAdminDir::HasAdminDir(GetWinPathString(), &topdir))
849 return false;
852 CString dotGitPath;
853 GitAdminDir::GetAdminDirPath(topdir, dotGitPath);
855 return !!PathFileExists(dotGitPath + _T("svn"));
857 bool CTGitPath::IsBisectActive() const
859 CString topdir;
860 if (!GitAdminDir::HasAdminDir(GetWinPathString(), &topdir))
862 return false;
865 CString dotGitPath;
866 GitAdminDir::GetAdminDirPath(topdir, dotGitPath);
868 return !!PathFileExists(dotGitPath + _T("BISECT_START"));
870 bool CTGitPath::IsMergeActive() const
872 CString topdir;
873 if (!GitAdminDir::HasAdminDir(GetWinPathString(), &topdir))
875 return false;
878 CString dotGitPath;
879 GitAdminDir::GetAdminDirPath(topdir, dotGitPath);
881 return !!PathFileExists(dotGitPath + _T("MERGE_HEAD"));
883 bool CTGitPath::HasRebaseApply() const
885 CString topdir;
886 if (!GitAdminDir::HasAdminDir(GetWinPathString(), &topdir))
888 return false;
891 CString dotGitPath;
892 GitAdminDir::GetAdminDirPath(topdir, dotGitPath);
894 return !!PathFileExists(dotGitPath + _T("rebase-apply"));
897 bool CTGitPath::HasAdminDir(CString *ProjectTopDir) const
899 if (m_bHasAdminDirKnown)
901 if (ProjectTopDir)
902 *ProjectTopDir = m_sProjectRoot;
903 return m_bHasAdminDir;
906 EnsureBackslashPathSet();
907 m_bHasAdminDir = GitAdminDir::HasAdminDir(m_sBackslashPath, IsDirectory(), &m_sProjectRoot);
908 m_bHasAdminDirKnown = true;
909 if (ProjectTopDir)
910 *ProjectTopDir = m_sProjectRoot;
911 return m_bHasAdminDir;
914 bool CTGitPath::IsAdminDir() const
916 if (m_bIsAdminDirKnown)
917 return m_bIsAdminDir;
919 EnsureBackslashPathSet();
920 m_bIsAdminDir = GitAdminDir::IsAdminDirPath(m_sBackslashPath);
921 m_bIsAdminDirKnown = true;
922 return m_bIsAdminDir;
925 bool CTGitPath::IsValidOnWindows() const
927 if (m_bIsValidOnWindowsKnown)
928 return m_bIsValidOnWindows;
930 m_bIsValidOnWindows = false;
931 EnsureBackslashPathSet();
932 CString sMatch = m_sBackslashPath + _T("\r\n");
933 std::wstring sPattern;
934 // the 'file://' URL is just a normal windows path:
935 if (sMatch.Left(7).CompareNoCase(_T("file:\\\\"))==0)
937 sMatch = sMatch.Mid(7);
938 sMatch.TrimLeft(_T("\\"));
939 sPattern = _T("^(\\\\\\\\\\?\\\\)?(([a-zA-Z]:|\\\\)\\\\)?(((\\.)|(\\.\\.)|([^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?))\\\\)*[^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?$");
941 else
943 sPattern = _T("^(\\\\\\\\\\?\\\\)?(([a-zA-Z]:|\\\\)\\\\)?(((\\.)|(\\.\\.)|([^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?))\\\\)*[^\\\\/:\\*\\?\"\\|<> ](([^\\\\/:\\*\\?\"\\|<>\\. ])|([^\\\\/:\\*\\?\"\\|<>]*[^\\\\/:\\*\\?\"\\|<>\\. ]))?$");
948 std::tr1::wregex rx(sPattern, std::tr1::regex_constants::icase | std::tr1::regex_constants::ECMAScript);
949 std::tr1::wsmatch match;
951 std::wstring rmatch = std::wstring((LPCTSTR)sMatch);
952 if (std::tr1::regex_match(rmatch, match, rx))
954 if (std::wstring(match[0]).compare(sMatch)==0)
955 m_bIsValidOnWindows = true;
957 if (m_bIsValidOnWindows)
959 // now check for illegal filenames
960 std::tr1::wregex rx2(_T("\\\\(lpt\\d|com\\d|aux|nul|prn|con)(\\\\|$)"), std::tr1::regex_constants::icase | std::tr1::regex_constants::ECMAScript);
961 rmatch = m_sBackslashPath;
962 if (std::tr1::regex_search(rmatch, rx2, std::tr1::regex_constants::match_default))
963 m_bIsValidOnWindows = false;
966 catch (std::exception) {}
968 m_bIsValidOnWindowsKnown = true;
969 return m_bIsValidOnWindows;
972 //////////////////////////////////////////////////////////////////////////
974 CTGitPathList::CTGitPathList()
976 m_Action = 0;
979 // A constructor which allows a path list to be easily built which one initial entry in
980 CTGitPathList::CTGitPathList(const CTGitPath& firstEntry)
982 m_Action = 0;
983 AddPath(firstEntry);
985 int CTGitPathList::ParserFromLsFile(BYTE_VECTOR &out,bool /*staged*/)
987 int pos=0;
988 CString one;
989 CTGitPath path;
990 CString part;
991 this->Clear();
993 while (pos >= 0 && pos < (int)out.size())
995 one.Empty();
996 path.Reset();
998 CGit::StringAppend(&one, &out[pos], CP_UTF8);
999 int tabstart=0;
1000 // m_Action is never used and propably never worked (needs to be set after path.SetFromGit)
1001 // also dropped LOGACTIONS_CACHE for 'H'
1002 // path.m_Action=path.ParserAction(out[pos]);
1003 one.Tokenize(_T("\t"),tabstart);
1005 if(tabstart>=0)
1006 path.SetFromGit(one.Right(one.GetLength()-tabstart));
1007 else
1008 return -1;
1010 tabstart=0;
1012 part=one.Tokenize(_T(" "),tabstart); //Tag
1013 if (tabstart < 0)
1014 return -1;
1016 part=one.Tokenize(_T(" "),tabstart); //Mode
1017 if (tabstart < 0)
1018 return -1;
1020 part=one.Tokenize(_T(" "),tabstart); //Hash
1021 if (tabstart < 0)
1022 return -1;
1024 part=one.Tokenize(_T("\t"),tabstart); //Stage
1025 if (tabstart < 0)
1026 return -1;
1028 path.m_Stage=_ttol(part);
1030 this->AddPath(path);
1032 pos=out.findNextString(pos);
1034 return 0;
1036 int CTGitPathList::FillUnRev(unsigned int action, CTGitPathList *list, CString *err)
1038 this->Clear();
1039 CTGitPath path;
1041 int count;
1042 if(list==NULL)
1043 count=1;
1044 else
1045 count=list->GetCount();
1046 for (int i = 0; i < count; ++i)
1048 CString cmd;
1049 int pos = 0;
1051 CString ignored;
1052 if(action & CTGitPath::LOGACTIONS_IGNORE)
1053 ignored= _T(" -i");
1055 if(list==NULL)
1057 cmd=_T("git.exe ls-files --exclude-standard --full-name --others -z");
1058 cmd+=ignored;
1061 else
1062 { cmd.Format(_T("git.exe ls-files --exclude-standard --full-name --others -z%s -- \"%s\""),
1063 ignored,
1064 (*list)[i].GetWinPathString());
1067 BYTE_VECTOR out, errb;
1068 out.clear();
1069 if (g_Git.Run(cmd, &out, &errb))
1071 if (err != nullptr)
1072 CGit::StringAppend(err, &errb[0], CP_UTF8, (int)errb.size());
1073 return -1;
1076 pos=0;
1077 CString one;
1078 while (pos >= 0 && pos < (int)out.size())
1080 one.Empty();
1081 CGit::StringAppend(&one, &out[pos], CP_UTF8);
1082 if(!one.IsEmpty())
1084 //SetFromGit will clear all status
1085 path.SetFromGit(one);
1086 path.m_Action=action;
1087 AddPath(path);
1089 pos=out.findNextString(pos);
1093 return 0;
1095 int CTGitPathList::FillBasedOnIndexFlags(unsigned short flag, CTGitPathList* list /*nullptr*/)
1097 Clear();
1098 CTGitPath path;
1100 CAutoRepository repository(g_Git.GetGitRepository());
1101 if (!repository)
1102 return -1;
1104 CAutoIndex index;
1105 if (git_repository_index(index.GetPointer(), repository))
1106 return -1;
1108 int count;
1109 if (list == nullptr)
1110 count = 1;
1111 else
1112 count = list->GetCount();
1113 for (int j = 0; j < count; ++j)
1115 for (size_t i = 0, ecount = git_index_entrycount(index); i < ecount; ++i)
1117 const git_index_entry *e = git_index_get_byindex(index, i);
1119 if (!e || !((e->flags | e->flags_extended) & flag) || !e->path)
1120 continue;
1122 CString one = CUnicodeUtils::GetUnicode(e->path);
1124 if (!(!list || (*list)[j].GetWinPathString().IsEmpty() || one == (*list)[j].GetGitPathString() || (PathIsDirectory(g_Git.CombinePath((*list)[j].GetWinPathString())) && one.Find((*list)[j].GetGitPathString() + _T("/")) == 0)))
1125 continue;
1127 //SetFromGit will clear all status
1128 path.SetFromGit(one);
1129 if ((e->flags | e->flags_extended) & GIT_IDXENTRY_SKIP_WORKTREE)
1130 path.m_Action = CTGitPath::LOGACTIONS_SKIPWORKTREE;
1131 else if ((e->flags | e->flags_extended) & GIT_IDXENTRY_VALID)
1132 path.m_Action = CTGitPath::LOGACTIONS_ASSUMEVALID;
1133 AddPath(path);
1136 RemoveDuplicates();
1137 return 0;
1139 int CTGitPathList::ParserFromLog(BYTE_VECTOR &log, bool parseDeletes /*false*/)
1141 this->Clear();
1142 int pos=0;
1143 CTGitPath path;
1144 m_Action=0;
1145 while (pos >= 0 && pos < (int)log.size())
1147 path.Reset();
1148 if(log[pos]=='\n')
1149 ++pos;
1151 if(log[pos]==':')
1153 bool merged=false;
1154 if(log[pos+1] ==':')
1156 merged=true;
1158 int end=log.find(0,pos);
1159 int actionstart=-1;
1160 int file1=-1,file2=-1;
1161 if( end>0 )
1163 actionstart=log.find(' ',end-6);
1164 pos=actionstart;
1166 if( actionstart>0 )
1168 ++actionstart;
1170 file1 = log.find(0,actionstart);
1171 if( file1>=0 )
1173 ++file1;
1174 pos=file1;
1176 if( log[actionstart] == 'C' || log[actionstart] == 'R' )
1178 file2=file1;
1179 file1 = log.find(0,file1);
1180 if(file1>=0 )
1182 ++file1;
1183 pos=file1;
1189 CString pathname1;
1190 CString pathname2;
1192 if( file1>=0 )
1193 CGit::StringAppend(&pathname1, &log[file1], CP_UTF8);
1194 if( file2>=0 )
1195 CGit::StringAppend(&pathname2, &log[file2], CP_UTF8);
1197 CTGitPath *GitPath=LookForGitPath(pathname1);
1199 if(GitPath)
1201 GitPath->ParserAction( log[actionstart] );
1203 if(merged)
1205 GitPath->m_Action |= CTGitPath::LOGACTIONS_MERGED;
1206 GitPath->m_Action &= ~CTGitPath::LOGACTIONS_FORWORD;
1208 m_Action |=GitPath->m_Action;
1211 else
1213 int ac=path.ParserAction(log[actionstart] );
1214 ac |= merged?CTGitPath::LOGACTIONS_MERGED:0;
1216 path.SetFromGit(pathname1,&pathname2);
1217 path.m_Action=ac;
1218 //action must be set after setfromgit. SetFromGit will clear all status.
1219 this->m_Action|=ac;
1221 AddPath(path);
1226 else
1228 int tabstart=0;
1229 path.Reset();
1230 CString StatAdd;
1231 CString StatDel;
1232 CString file1;
1233 CString file2;
1235 tabstart=log.find('\t',pos);
1236 if(tabstart >=0)
1238 log[tabstart]=0;
1239 CGit::StringAppend(&StatAdd, &log[pos], CP_UTF8);
1240 pos=tabstart+1;
1243 tabstart=log.find('\t',pos);
1244 if(tabstart >=0)
1246 log[tabstart]=0;
1248 CGit::StringAppend(&StatDel, &log[pos], CP_UTF8);
1249 pos=tabstart+1;
1252 if(log[pos] == 0) //rename
1254 ++pos;
1255 CGit::StringAppend(&file2, &log[pos], CP_UTF8);
1256 int sec=log.find(0,pos);
1257 if(sec>=0)
1259 ++sec;
1260 CGit::StringAppend(&file1, &log[sec], CP_UTF8);
1262 pos=sec;
1265 else
1267 CGit::StringAppend(&file1, &log[pos], CP_UTF8);
1269 path.SetFromGit(file1,&file2);
1271 CTGitPath *GitPath=LookForGitPath(path.GetGitPathString());
1272 if(GitPath)
1274 GitPath->m_StatAdd=StatAdd;
1275 GitPath->m_StatDel=StatDel;
1277 else
1279 //path.SetFromGit(pathname);
1280 if (parseDeletes)
1282 path.m_StatAdd=_T("0");
1283 path.m_StatDel=_T("0");
1284 path.m_Action |= CTGitPath::LOGACTIONS_DELETED;
1286 else
1288 path.m_StatAdd=StatAdd;
1289 path.m_StatDel=StatDel;
1290 path.m_Action |= CTGitPath::LOGACTIONS_FORWORD;
1292 AddPath(path);
1296 pos=log.findNextString(pos);
1298 return pos;
1301 void CTGitPathList::AddPath(const CTGitPath& newPath)
1303 m_paths.push_back(newPath);
1304 m_commonBaseDirectory.Reset();
1306 int CTGitPathList::GetCount() const
1308 return (int)m_paths.size();
1310 bool CTGitPathList::IsEmpty() const
1312 return m_paths.empty();
1314 void CTGitPathList::Clear()
1316 m_paths.clear();
1317 m_commonBaseDirectory.Reset();
1320 const CTGitPath& CTGitPathList::operator[](INT_PTR index) const
1322 ATLASSERT(index >= 0 && index < (INT_PTR)m_paths.size());
1323 return m_paths[index];
1326 bool CTGitPathList::AreAllPathsFiles() const
1328 // Look through the vector for any directories - if we find them, return false
1329 return std::find_if(m_paths.begin(), m_paths.end(), std::mem_fun_ref(&CTGitPath::IsDirectory)) == m_paths.end();
1333 #if defined(_MFC_VER)
1335 bool CTGitPathList::LoadFromFile(const CTGitPath& filename)
1337 Clear();
1340 CString strLine;
1341 CStdioFile file(filename.GetWinPath(), CFile::typeBinary | CFile::modeRead | CFile::shareDenyWrite);
1343 // for every selected file/folder
1344 CTGitPath path;
1345 while (file.ReadString(strLine))
1347 path.SetFromUnknown(strLine);
1348 AddPath(path);
1350 file.Close();
1352 catch (CFileException* pE)
1354 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": CFileException loading target file list\n");
1355 TCHAR error[10000] = {0};
1356 pE->GetErrorMessage(error, 10000);
1357 // CMessageBox::Show(NULL, error, _T("TortoiseGit"), MB_ICONERROR);
1358 pE->Delete();
1359 return false;
1361 return true;
1364 bool CTGitPathList::WriteToFile(const CString& sFilename, bool bANSI /* = false */) const
1368 if (bANSI)
1370 CStdioFile file(sFilename, CFile::typeText | CFile::modeReadWrite | CFile::modeCreate);
1371 PathVector::const_iterator it;
1372 for(it = m_paths.begin(); it != m_paths.end(); ++it)
1374 CStringA line = CStringA(it->GetGitPathString()) + '\n';
1375 file.Write(line, line.GetLength());
1377 file.Close();
1379 else
1381 CStdioFile file(sFilename, CFile::typeBinary | CFile::modeReadWrite | CFile::modeCreate);
1382 PathVector::const_iterator it;
1383 for(it = m_paths.begin(); it != m_paths.end(); ++it)
1385 file.WriteString(it->GetGitPathString()+_T("\n"));
1387 file.Close();
1390 catch (CFileException* pE)
1392 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": CFileException in writing temp file\n");
1393 pE->Delete();
1394 return false;
1396 return true;
1400 void CTGitPathList::LoadFromAsteriskSeparatedString(const CString& sPathString)
1402 int pos = 0;
1403 CString temp;
1404 for(;;)
1406 temp = sPathString.Tokenize(_T("*"),pos);
1407 if(temp.IsEmpty())
1409 break;
1411 AddPath(CTGitPath(CPathUtils::GetLongPathname(temp)));
1415 CString CTGitPathList::CreateAsteriskSeparatedString() const
1417 CString sRet;
1418 PathVector::const_iterator it;
1419 for(it = m_paths.begin(); it != m_paths.end(); ++it)
1421 if (!sRet.IsEmpty())
1422 sRet += _T("*");
1423 sRet += it->GetWinPathString();
1425 return sRet;
1427 #endif // _MFC_VER
1429 bool
1430 CTGitPathList::AreAllPathsFilesInOneDirectory() const
1432 // Check if all the paths are files and in the same directory
1433 PathVector::const_iterator it;
1434 m_commonBaseDirectory.Reset();
1435 for(it = m_paths.begin(); it != m_paths.end(); ++it)
1437 if(it->IsDirectory())
1439 return false;
1441 const CTGitPath& baseDirectory = it->GetDirectory();
1442 if(m_commonBaseDirectory.IsEmpty())
1444 m_commonBaseDirectory = baseDirectory;
1446 else if(!m_commonBaseDirectory.IsEquivalentTo(baseDirectory))
1448 // Different path
1449 m_commonBaseDirectory.Reset();
1450 return false;
1453 return true;
1456 CTGitPath CTGitPathList::GetCommonDirectory() const
1458 if (m_commonBaseDirectory.IsEmpty())
1460 PathVector::const_iterator it;
1461 for(it = m_paths.begin(); it != m_paths.end(); ++it)
1463 const CTGitPath& baseDirectory = it->GetDirectory();
1464 if(m_commonBaseDirectory.IsEmpty())
1466 m_commonBaseDirectory = baseDirectory;
1468 else if(!m_commonBaseDirectory.IsEquivalentTo(baseDirectory))
1470 // Different path
1471 m_commonBaseDirectory.Reset();
1472 break;
1476 // since we only checked strings, not paths,
1477 // we have to make sure now that we really return a *path* here
1478 PathVector::const_iterator iter;
1479 for(iter = m_paths.begin(); iter != m_paths.end(); ++iter)
1481 if (!m_commonBaseDirectory.IsAncestorOf(*iter))
1483 m_commonBaseDirectory = m_commonBaseDirectory.GetContainingDirectory();
1484 break;
1487 return m_commonBaseDirectory;
1490 CTGitPath CTGitPathList::GetCommonRoot() const
1492 if (IsEmpty())
1493 return CTGitPath();
1495 if (GetCount() == 1)
1496 return m_paths[0];
1498 // first entry is common root for itself
1499 // (add trailing '\\' to detect partial matches of the last path element)
1500 CString root = m_paths[0].GetWinPathString() + _T('\\');
1501 int rootLength = root.GetLength();
1503 // determine common path string prefix
1504 for (PathVector::const_iterator it = m_paths.begin() + 1; it != m_paths.end(); ++it)
1506 CString path = it->GetWinPathString() + _T('\\');
1508 int newLength = CStringUtils::GetMatchingLength(root, path);
1509 if (newLength != rootLength)
1511 root.Delete(newLength, rootLength);
1512 rootLength = newLength;
1516 // remove the last (partial) path element
1517 if (rootLength > 0)
1518 root.Delete(root.ReverseFind(_T('\\')), rootLength);
1520 // done
1521 return CTGitPath(root);
1524 void CTGitPathList::SortByPathname(bool bReverse /*= false*/)
1526 std::sort(m_paths.begin(), m_paths.end());
1527 if (bReverse)
1528 std::reverse(m_paths.begin(), m_paths.end());
1531 void CTGitPathList::DeleteAllFiles(bool bTrash, bool bFilesOnly)
1533 PathVector::const_iterator it;
1534 SortByPathname(true); // nested ones first
1536 CString sPaths;
1537 for (it = m_paths.begin(); it != m_paths.end(); ++it)
1539 if ((it->Exists()) && ((it->IsDirectory() != bFilesOnly) || !bFilesOnly))
1541 if (!it->IsDirectory())
1542 ::SetFileAttributes(it->GetWinPath(), FILE_ATTRIBUTE_NORMAL);
1544 sPaths += it->GetWinPath();
1545 sPaths += '\0';
1548 sPaths += '\0';
1549 sPaths += '\0';
1550 DeleteViaShell((LPCTSTR)sPaths, bTrash);
1551 Clear();
1554 bool CTGitPathList::DeleteViaShell(LPCTSTR path, bool bTrash)
1556 SHFILEOPSTRUCT shop = {0};
1557 shop.wFunc = FO_DELETE;
1558 shop.pFrom = path;
1559 shop.fFlags = FOF_NOCONFIRMATION|FOF_NOERRORUI|FOF_SILENT|FOF_NO_CONNECTED_ELEMENTS;
1560 if (bTrash)
1561 shop.fFlags |= FOF_ALLOWUNDO;
1562 const bool bRet = (SHFileOperation(&shop) == 0);
1563 return bRet;
1566 void CTGitPathList::RemoveDuplicates()
1568 SortByPathname();
1569 // Remove the duplicates
1570 // (Unique moves them to the end of the vector, then erase chops them off)
1571 m_paths.erase(std::unique(m_paths.begin(), m_paths.end(), &CTGitPath::PredLeftEquivalentToRight), m_paths.end());
1574 void CTGitPathList::RemoveAdminPaths()
1576 PathVector::iterator it;
1577 for(it = m_paths.begin(); it != m_paths.end(); )
1579 if (it->IsAdminDir())
1581 m_paths.erase(it);
1582 it = m_paths.begin();
1584 else
1585 ++it;
1589 void CTGitPathList::RemovePath(const CTGitPath& path)
1591 PathVector::iterator it;
1592 for(it = m_paths.begin(); it != m_paths.end(); ++it)
1594 if (it->IsEquivalentTo(path))
1596 m_paths.erase(it);
1597 return;
1602 void CTGitPathList::RemoveItem(CTGitPath & path)
1604 PathVector::iterator it;
1605 for(it = m_paths.begin(); it != m_paths.end(); ++it)
1607 if (it->GetGitPathString()==path.GetGitPathString())
1609 m_paths.erase(it);
1610 return;
1614 void CTGitPathList::RemoveChildren()
1616 SortByPathname();
1617 m_paths.erase(std::unique(m_paths.begin(), m_paths.end(), &CTGitPath::CheckChild), m_paths.end());
1620 bool CTGitPathList::IsEqual(const CTGitPathList& list)
1622 if (list.GetCount() != GetCount())
1623 return false;
1624 for (int i=0; i<list.GetCount(); ++i)
1626 if (!list[i].IsEquivalentTo(m_paths[i]))
1627 return false;
1629 return true;
1632 CTGitPath * CTGitPathList::LookForGitPath(CString path)
1634 int i=0;
1635 for (i = 0; i < this->GetCount(); ++i)
1637 if((*this)[i].GetGitPathString() == path )
1638 return (CTGitPath*)&(*this)[i];
1640 return NULL;
1642 CString CTGitPath::GetActionName(int action)
1644 if(action & CTGitPath::LOGACTIONS_UNMERGED)
1645 return MAKEINTRESOURCE(IDS_PATHACTIONS_CONFLICT);
1646 if(action & CTGitPath::LOGACTIONS_ADDED)
1647 return MAKEINTRESOURCE(IDS_PATHACTIONS_ADD);
1648 if(action & CTGitPath::LOGACTIONS_DELETED)
1649 return MAKEINTRESOURCE(IDS_PATHACTIONS_DELETE);
1650 if(action & CTGitPath::LOGACTIONS_MERGED )
1651 return MAKEINTRESOURCE(IDS_PATHACTIONS_MERGED);
1653 if(action & CTGitPath::LOGACTIONS_MODIFIED)
1654 return MAKEINTRESOURCE(IDS_PATHACTIONS_MODIFIED);
1655 if(action & CTGitPath::LOGACTIONS_REPLACED)
1656 return MAKEINTRESOURCE(IDS_PATHACTIONS_RENAME);
1657 if(action & CTGitPath::LOGACTIONS_COPY)
1658 return MAKEINTRESOURCE(IDS_PATHACTIONS_COPY);
1660 if(action & CTGitPath::LOGACTIONS_FORWORD )
1661 return MAKEINTRESOURCE(IDS_PATHACTIONS_FORWARD);
1663 if (action & CTGitPath::LOGACTIONS_ASSUMEVALID)
1664 return MAKEINTRESOURCE(IDS_PATHACTIONS_ASSUMEUNCHANGED);
1665 if (action & CTGitPath::LOGACTIONS_SKIPWORKTREE)
1666 return MAKEINTRESOURCE(IDS_PATHACTIONS_SKIPWORKTREE);
1668 return MAKEINTRESOURCE(IDS_PATHACTIONS_UNKNOWN);
1670 CString CTGitPath::GetActionName()
1672 return GetActionName(m_Action);
1675 int CTGitPathList::GetAction()
1677 return m_Action;