Extend static functions in CAppUtils with a window handle parameter
[TortoiseGit.git] / src / Git / GitAdminDir.cpp
blob397119cb297f1bd4520e13dd28fbc13a20c39ade
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 CString GitAdminDir::GetGitTopDir(const CString& path)
53 CString str;
54 HasAdminDir(path,!!PathIsDirectory(path),&str);
55 return str;
58 bool GitAdminDir::IsWorkingTreeOrBareRepo(const CString& path)
60 return HasAdminDir(path) || IsBareRepo(path);
63 bool GitAdminDir::HasAdminDir(const CString& path)
65 return HasAdminDir(path, !!PathIsDirectory(path));
68 bool GitAdminDir::HasAdminDir(const CString& path,CString* ProjectTopDir)
70 return HasAdminDir(path, !!PathIsDirectory(path),ProjectTopDir);
73 bool GitAdminDir::HasAdminDir(const CString& path, bool bDir, CString* ProjectTopDir, bool* IsAdminDirPath)
75 if (path.IsEmpty())
76 return false;
77 CString sDirName = path;
78 if (!bDir)
80 // e.g "C:\"
81 if (path.GetLength() <= 3)
82 return false;
83 sDirName.Truncate(max(0, sDirName.ReverseFind(L'\\')));
86 // a .git dir or anything inside it should be left out, only interested in working copy files -- Myagi
87 int n = 0;
88 for (;;)
90 n = sDirName.Find(L"\\.git", n);
91 if (n < 0)
92 break;
94 // check for actual .git dir (and not .gitignore or something else), continue search if false match
95 n += 5;
96 if (sDirName[n] == L'\\' || sDirName[n] == 0)
98 if (IsAdminDirPath)
99 *IsAdminDirPath = true;
100 return false;
104 for (;;)
106 if (CGit::GitPathFileExists(sDirName + L"\\.git"))
108 if(ProjectTopDir)
110 *ProjectTopDir=sDirName;
111 // Make sure to add the trailing slash to root paths such as 'C:'
112 if (sDirName.GetLength() == 2 && sDirName[1] == L':')
113 (*ProjectTopDir) += L'\\';
115 return true;
117 else if (IsBareRepo(sDirName))
118 return false;
120 int x = sDirName.ReverseFind(L'\\');
121 if (x < 2)
122 break;
124 sDirName.Truncate(x);
125 // don't check for \\COMPUTERNAME\.git
126 if (sDirName[0] == L'\\' && sDirName[1] == L'\\' && sDirName.Find(L'\\', 2) < 0)
127 break;
130 return false;
133 * Returns the .git-path (if .git is a file, read the repository path and return it)
134 * adminDir always ends with "\"
136 bool GitAdminDir::GetAdminDirPath(const CString& projectTopDir, CString& adminDir, bool* isWorktree)
138 CString wtAdminDir;
139 if (!GetWorktreeAdminDirPath(projectTopDir, wtAdminDir))
140 return false;
142 CString pathToCommonDir = wtAdminDir + L"commondir";
143 if (!PathFileExists(pathToCommonDir))
145 adminDir = wtAdminDir;
146 if (isWorktree)
147 *isWorktree = false;
148 return true;
151 CAutoFILE pFile = _wfsopen(pathToCommonDir, L"rb", SH_DENYWR);
152 if (!pFile)
153 return false;
155 int size = 65536;
156 CStringA commonDirA;
157 int length = (int)fread(commonDirA.GetBufferSetLength(size), sizeof(char), size, pFile);
158 commonDirA.ReleaseBuffer(length);
159 CString commonDir = CUnicodeUtils::GetUnicode(commonDirA);
160 commonDir.TrimRight(L"\r\n");
161 commonDir.Replace(L'/', L'\\');
162 if (PathIsRelative(commonDir))
163 adminDir = CPathUtils::BuildPathWithPathDelimiter(wtAdminDir + commonDir);
164 else
165 adminDir = CPathUtils::BuildPathWithPathDelimiter(commonDir);
166 if (isWorktree)
167 *isWorktree = true;
168 return true;
171 bool GitAdminDir::GetWorktreeAdminDirPath(const CString& projectTopDir, CString& adminDir)
173 if (IsBareRepo(projectTopDir))
175 adminDir = CPathUtils::BuildPathWithPathDelimiter(projectTopDir);
176 return true;
179 CString sDotGitPath = CPathUtils::BuildPathWithPathDelimiter(projectTopDir) + GetAdminDirName();
180 if (CTGitPath(sDotGitPath).IsDirectory())
182 adminDir = CPathUtils::BuildPathWithPathDelimiter(sDotGitPath);
183 return true;
185 else
187 CString result = ReadGitLink(projectTopDir, sDotGitPath);
188 if (result.IsEmpty())
189 return false;
190 adminDir = CPathUtils::BuildPathWithPathDelimiter(result);
191 return true;
195 CString GitAdminDir::ReadGitLink(const CString& topDir, const CString& dotGitPath)
197 CAutoFILE pFile = _wfsopen(dotGitPath, L"r", SH_DENYWR);
199 if (!pFile)
200 return L"";
202 int size = 65536;
203 auto buffer = std::make_unique<char[]>(size);
204 int length = (int)fread(buffer.get(), sizeof(char), size, pFile);
205 CStringA gitPathA(buffer.get(), length);
206 if (length < 8 || !CStringUtils::StartsWith(gitPathA, "gitdir: "))
207 return L"";
208 CString gitPath = CUnicodeUtils::GetUnicode(gitPathA);
209 // trim after converting to UTF-16, because CStringA trim does not work when having UTF-8 chars
210 gitPath = gitPath.Trim().Mid((int)wcslen(L"gitdir: "));
211 gitPath.Replace('/', '\\');
212 if (!gitPath.IsEmpty() && gitPath[0] == L'.')
214 gitPath = CPathUtils::BuildPathWithPathDelimiter(topDir) + gitPath;
215 CString adminDir;
216 PathCanonicalize(CStrBuf(adminDir, MAX_PATH), gitPath);
217 return adminDir;
219 CPathUtils::TrimTrailingPathDelimiter(gitPath);
220 return gitPath;
223 bool GitAdminDir::IsAdminDirPath(const CString& path)
225 if (path.IsEmpty())
226 return false;
227 bool bIsAdminDir = false;
228 CString lowerpath = path;
229 lowerpath.MakeLower();
230 int ind1 = 0;
231 while ((ind1 = lowerpath.Find(L"\\.git", ind1)) >= 0)
233 int ind = ind1++;
234 if (ind == (lowerpath.GetLength() - 5))
236 bIsAdminDir = true;
237 break;
239 else if (lowerpath.Find(L"\\.git\\", ind) >= 0)
241 bIsAdminDir = true;
242 break;
246 return bIsAdminDir;
249 bool GitAdminDir::IsBareRepo(const CString& path)
251 if (path.IsEmpty())
252 return false;
254 if (IsAdminDirPath(path))
255 return false;
257 // don't check for \\COMPUTERNAME\HEAD
258 if (path[0] == L'\\' && path[1] == L'\\')
260 if (path.Find(L'\\', 2) < 0)
261 return false;
264 if (!PathFileExists(path + L"\\HEAD") || !PathFileExists(path + L"\\config"))
265 return false;
267 if (!PathFileExists(path + L"\\objects\\") || !PathFileExists(path + L"\\refs\\"))
268 return false;
270 return true;