Make more use of CStringUtils::StartsWith
[TortoiseGit.git] / src / Git / GitAdminDir.cpp
blob83c1382a0c45762ccfdd9a672ad3124077be2cfb
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2016 - 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"
26 CString GitAdminDir::GetSuperProjectRoot(const CString& path)
28 CString projectroot=path;
32 if (CGit::GitPathFileExists(projectroot + _T("\\.git")))
34 if (CGit::GitPathFileExists(projectroot + _T("\\.gitmodules")))
35 return projectroot;
36 else
37 return _T("");
40 projectroot.Truncate(max(0, projectroot.ReverseFind(L'\\')));
42 // don't check for \\COMPUTERNAME\.git
43 if (projectroot[0] == _T('\\') && projectroot[1] == _T('\\') && projectroot.Find(_T('\\'), 2) < 0)
44 return _T("");
45 }while(projectroot.ReverseFind('\\')>0);
47 return _T("");
50 CString GitAdminDir::GetGitTopDir(const CString& path)
52 CString str;
53 HasAdminDir(path,!!PathIsDirectory(path),&str);
54 return str;
57 bool GitAdminDir::IsWorkingTreeOrBareRepo(const CString& path)
59 return HasAdminDir(path) || IsBareRepo(path);
62 bool GitAdminDir::HasAdminDir(const CString& path)
64 return HasAdminDir(path, !!PathIsDirectory(path));
67 bool GitAdminDir::HasAdminDir(const CString& path,CString* ProjectTopDir)
69 return HasAdminDir(path, !!PathIsDirectory(path),ProjectTopDir);
72 bool GitAdminDir::HasAdminDir(const CString& path, bool bDir, CString* ProjectTopDir)
74 if (path.IsEmpty())
75 return false;
76 CString sDirName = path;
77 if (!bDir)
79 // e.g "C:\"
80 if (path.GetLength() <= 3)
81 return false;
82 sDirName.Truncate(max(0, sDirName.ReverseFind(L'\\')));
85 // a .git dir or anything inside it should be left out, only interested in working copy files -- Myagi
86 int n = 0;
87 for (;;)
89 n = sDirName.Find(_T("\\.git"), n);
90 if (n < 0)
91 break;
93 // check for actual .git dir (and not .gitignore or something else), continue search if false match
94 n += 5;
95 if (sDirName[n] == _T('\\') || sDirName[n] == 0)
96 return false;
99 for (;;)
101 if(CGit::GitPathFileExists(sDirName + _T("\\.git")))
103 if(ProjectTopDir)
105 *ProjectTopDir=sDirName;
106 // Make sure to add the trailing slash to root paths such as 'C:'
107 if (sDirName.GetLength() == 2 && sDirName[1] == _T(':'))
108 (*ProjectTopDir) += _T("\\");
110 return true;
112 else if (IsBareRepo(sDirName))
113 return false;
115 int x = sDirName.ReverseFind(_T('\\'));
116 if (x < 2)
117 break;
119 sDirName.Truncate(x);
120 // don't check for \\COMPUTERNAME\.git
121 if (sDirName[0] == _T('\\') && sDirName[1] == _T('\\') && sDirName.Find(_T('\\'), 2) < 0)
122 break;
125 return false;
128 * Returns the .git-path (if .git is a file, read the repository path and return it)
129 * adminDir always ends with "\"
131 bool GitAdminDir::GetAdminDirPath(const CString &projectTopDir, CString& adminDir)
133 if (IsBareRepo(projectTopDir))
135 adminDir = projectTopDir;
136 adminDir.TrimRight('\\');
137 adminDir.Append(_T("\\"));
138 return true;
141 CString sDotGitPath = projectTopDir + _T("\\") + GetAdminDirName();
142 if (CTGitPath(sDotGitPath).IsDirectory())
144 sDotGitPath.TrimRight('\\');
145 sDotGitPath.Append(_T("\\"));
146 adminDir = sDotGitPath;
147 return true;
149 else
151 CString result = ReadGitLink(projectTopDir, sDotGitPath);
152 if (result.IsEmpty())
153 return false;
154 adminDir = result + _T("\\");
155 return true;
159 CString GitAdminDir::ReadGitLink(const CString& topDir, const CString& dotGitPath)
161 CAutoFILE pFile = _tfsopen(dotGitPath, _T("r"), SH_DENYWR);
163 if (!pFile)
164 return _T("");
166 int size = 65536;
167 auto buffer = std::make_unique<char[]>(size);
168 int length = (int)fread(buffer.get(), sizeof(char), size, pFile);
169 CStringA gitPathA(buffer.get(), length);
170 if (length < 8 || !CStringUtils::StartsWith(gitPathA, "gitdir: "))
171 return _T("");
172 CString gitPath = CUnicodeUtils::GetUnicode(gitPathA);
173 // trim after converting to UTF-16, because CStringA trim does not work when having UTF-8 chars
174 gitPath = gitPath.Trim().Mid(8); // 8 = len("gitdir: ")
175 gitPath.Replace('/', '\\');
176 gitPath.TrimRight('\\');
177 if (!gitPath.IsEmpty() && gitPath[0] == _T('.'))
179 gitPath = topDir + _T("\\") + gitPath;
180 CString adminDir;
181 PathCanonicalize(CStrBuf(adminDir, MAX_PATH), gitPath);
182 return adminDir;
184 return gitPath;
187 bool GitAdminDir::IsAdminDirPath(const CString& path)
189 if (path.IsEmpty())
190 return false;
191 bool bIsAdminDir = false;
192 CString lowerpath = path;
193 lowerpath.MakeLower();
194 int ind1 = 0;
195 while ((ind1 = lowerpath.Find(_T("\\.git"), ind1))>=0)
197 int ind = ind1++;
198 if (ind == (lowerpath.GetLength() - 5))
200 bIsAdminDir = true;
201 break;
203 else if (lowerpath.Find(_T("\\.git\\"), ind)>=0)
205 bIsAdminDir = true;
206 break;
210 return bIsAdminDir;
213 bool GitAdminDir::IsBareRepo(const CString& path)
215 if (path.IsEmpty())
216 return false;
218 if (IsAdminDirPath(path))
219 return false;
221 // don't check for \\COMPUTERNAME\HEAD
222 if (path[0] == _T('\\') && path[1] == _T('\\'))
224 if (path.Find(_T('\\'), 2) < 0)
225 return false;
228 if (!PathFileExists(path + _T("\\HEAD")) || !PathFileExists(path + _T("\\config")))
229 return false;
231 if (!PathFileExists(path + _T("\\objects\\")) || !PathFileExists(path + _T("\\refs\\")))
232 return false;
234 return true;