Fixed issue #2591: Enable accent coloring for search term matches in log messages
[TortoiseGit.git] / src / Git / GitAdminDir.cpp
blob6aeb2d21f5bedcf1cd94850fe17a8ebf74035452
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2017 - 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 "UnicodeUtils.h"
22 #include "GitAdminDir.h"
23 #include "Git.h"
24 #include "SmartHandle.h"
25 #include "PathUtils.h"
27 CString GitAdminDir::GetSuperProjectRoot(const CString& path)
29 CString projectroot=path;
33 if (CGit::GitPathFileExists(projectroot + L"\\.git"))
35 if (CGit::GitPathFileExists(projectroot + L"\\.gitmodules"))
36 return projectroot;
37 else
38 return L"";
41 projectroot.Truncate(max(0, projectroot.ReverseFind(L'\\')));
43 // don't check for \\COMPUTERNAME\.git
44 if (projectroot[0] == L'\\' && projectroot[1] == L'\\' && projectroot.Find(L'\\', 2) < 0)
45 return L"";
46 }while(projectroot.ReverseFind('\\')>0);
48 return L"";
51 bool GitAdminDir::IsWorkingTreeOrBareRepo(const CString& path)
53 return HasAdminDir(path) || IsBareRepo(path);
56 bool GitAdminDir::HasAdminDir(const CString& path)
58 return HasAdminDir(path, !!PathIsDirectory(path));
61 bool GitAdminDir::HasAdminDir(const CString& path,CString* ProjectTopDir)
63 return HasAdminDir(path, !!PathIsDirectory(path),ProjectTopDir);
66 bool GitAdminDir::HasAdminDir(const CString& path, bool bDir, CString* ProjectTopDir, bool* IsAdminDirPath)
68 if (path.IsEmpty())
69 return false;
70 CString sDirName = path;
71 if (!bDir)
73 // e.g "C:\"
74 if (path.GetLength() <= 3)
75 return false;
76 sDirName.Truncate(max(0, sDirName.ReverseFind(L'\\')));
79 // a .git dir or anything inside it should be left out, only interested in working copy files -- Myagi
80 int n = 0;
81 for (;;)
83 n = sDirName.Find(L"\\.git", n);
84 if (n < 0)
85 break;
87 // check for actual .git dir (and not .gitignore or something else), continue search if false match
88 n += 5;
89 if (sDirName[n] == L'\\' || sDirName[n] == 0)
91 if (IsAdminDirPath)
92 *IsAdminDirPath = true;
93 return false;
97 for (;;)
99 if (CGit::GitPathFileExists(sDirName + L"\\.git"))
101 if(ProjectTopDir)
103 *ProjectTopDir=sDirName;
104 // Make sure to add the trailing slash to root paths such as 'C:'
105 if (sDirName.GetLength() == 2 && sDirName[1] == L':')
106 (*ProjectTopDir) += L'\\';
108 return true;
110 else if (IsBareRepo(sDirName))
111 return false;
113 int x = sDirName.ReverseFind(L'\\');
114 if (x < 2)
115 break;
117 sDirName.Truncate(x);
118 // don't check for \\COMPUTERNAME\.git
119 if (sDirName[0] == L'\\' && sDirName[1] == L'\\' && sDirName.Find(L'\\', 2) < 0)
120 break;
123 return false;
126 * Returns the .git-path (if .git is a file, read the repository path and return it)
127 * adminDir always ends with "\"
129 bool GitAdminDir::GetAdminDirPath(const CString& projectTopDir, CString& adminDir, bool* isWorktree)
131 CString wtAdminDir;
132 if (!GetWorktreeAdminDirPath(projectTopDir, wtAdminDir))
133 return false;
135 CString pathToCommonDir = wtAdminDir + L"commondir";
136 if (!PathFileExists(pathToCommonDir))
138 adminDir = wtAdminDir;
139 if (isWorktree)
140 *isWorktree = false;
141 return true;
144 CAutoFILE pFile = _wfsopen(pathToCommonDir, L"rb", SH_DENYWR);
145 if (!pFile)
146 return false;
148 int size = 65536;
149 CStringA commonDirA;
150 int length = (int)fread(commonDirA.GetBufferSetLength(size), sizeof(char), size, pFile);
151 commonDirA.ReleaseBuffer(length);
152 CString commonDir = CUnicodeUtils::GetUnicode(commonDirA);
153 commonDir.TrimRight(L"\r\n");
154 commonDir.Replace(L'/', L'\\');
155 if (PathIsRelative(commonDir))
156 adminDir = CPathUtils::BuildPathWithPathDelimiter(wtAdminDir + commonDir);
157 else
158 adminDir = CPathUtils::BuildPathWithPathDelimiter(commonDir);
159 if (isWorktree)
160 *isWorktree = true;
161 return true;
164 bool GitAdminDir::GetWorktreeAdminDirPath(const CString& projectTopDir, CString& adminDir)
166 if (IsBareRepo(projectTopDir))
168 adminDir = CPathUtils::BuildPathWithPathDelimiter(projectTopDir);
169 return true;
172 CString sDotGitPath = CPathUtils::BuildPathWithPathDelimiter(projectTopDir) + GetAdminDirName();
173 if (CTGitPath(sDotGitPath).IsDirectory())
175 adminDir = CPathUtils::BuildPathWithPathDelimiter(sDotGitPath);
176 return true;
178 else
180 CString result = ReadGitLink(projectTopDir, sDotGitPath);
181 if (result.IsEmpty())
182 return false;
183 adminDir = CPathUtils::BuildPathWithPathDelimiter(result);
184 return true;
188 CString GitAdminDir::ReadGitLink(const CString& topDir, const CString& dotGitPath)
190 CAutoFILE pFile = _wfsopen(dotGitPath, L"r", SH_DENYWR);
192 if (!pFile)
193 return L"";
195 int size = 65536;
196 auto buffer = std::make_unique<char[]>(size);
197 int length = (int)fread(buffer.get(), sizeof(char), size, pFile);
198 CStringA gitPathA(buffer.get(), length);
199 if (length < 8 || !CStringUtils::StartsWith(gitPathA, "gitdir: "))
200 return L"";
201 CString gitPath = CUnicodeUtils::GetUnicode(gitPathA);
202 // trim after converting to UTF-16, because CStringA trim does not work when having UTF-8 chars
203 gitPath = gitPath.Trim().Mid((int)wcslen(L"gitdir: "));
204 gitPath.Replace('/', '\\');
205 if (!gitPath.IsEmpty() && gitPath[0] == L'.')
207 gitPath = CPathUtils::BuildPathWithPathDelimiter(topDir) + gitPath;
208 CString adminDir;
209 PathCanonicalize(CStrBuf(adminDir, MAX_PATH), gitPath);
210 return adminDir;
212 CPathUtils::TrimTrailingPathDelimiter(gitPath);
213 return gitPath;
216 bool GitAdminDir::IsAdminDirPath(const CString& path)
218 if (path.IsEmpty())
219 return false;
220 bool bIsAdminDir = false;
221 CString lowerpath = path;
222 lowerpath.MakeLower();
223 int ind1 = 0;
224 while ((ind1 = lowerpath.Find(L"\\.git", ind1)) >= 0)
226 int ind = ind1++;
227 if (ind == (lowerpath.GetLength() - 5))
229 bIsAdminDir = true;
230 break;
232 else if (lowerpath.Find(L"\\.git\\", ind) >= 0)
234 bIsAdminDir = true;
235 break;
239 return bIsAdminDir;
242 bool GitAdminDir::IsBareRepo(const CString& path)
244 if (path.IsEmpty())
245 return false;
247 if (IsAdminDirPath(path))
248 return false;
250 // don't check for \\COMPUTERNAME\HEAD
251 if (path[0] == L'\\' && path[1] == L'\\')
253 if (path.Find(L'\\', 2) < 0)
254 return false;
257 if (!PathFileExists(path + L"\\HEAD") || !PathFileExists(path + L"\\config"))
258 return false;
260 if (!PathFileExists(path + L"\\objects\\") || !PathFileExists(path + L"\\refs\\"))
261 return false;
263 return true;