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.
21 #include "UnicodeUtils.h"
22 #include "GitAdminDir.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"))
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)
46 }while(projectroot
.ReverseFind('\\')>0);
51 CString
GitAdminDir::GetGitTopDir(const CString
& path
)
54 HasAdminDir(path
,!!PathIsDirectory(path
),&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
)
77 CString sDirName
= path
;
81 if (path
.GetLength() <= 3)
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
90 n
= sDirName
.Find(L
"\\.git", n
);
94 // check for actual .git dir (and not .gitignore or something else), continue search if false match
96 if (sDirName
[n
] == L
'\\' || sDirName
[n
] == 0)
99 *IsAdminDirPath
= true;
106 if (CGit::GitPathFileExists(sDirName
+ L
"\\.git"))
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
'\\';
117 else if (IsBareRepo(sDirName
))
120 int x
= sDirName
.ReverseFind(L
'\\');
124 sDirName
.Truncate(x
);
125 // don't check for \\COMPUTERNAME\.git
126 if (sDirName
[0] == L
'\\' && sDirName
[1] == L
'\\' && sDirName
.Find(L
'\\', 2) < 0)
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
)
139 if (!GetWorktreeAdminDirPath(projectTopDir
, wtAdminDir
))
142 CString pathToCommonDir
= wtAdminDir
+ L
"commondir";
143 if (!PathFileExists(pathToCommonDir
))
145 adminDir
= wtAdminDir
;
151 CAutoFILE pFile
= _wfsopen(pathToCommonDir
, L
"rb", SH_DENYWR
);
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
);
165 adminDir
= CPathUtils::BuildPathWithPathDelimiter(commonDir
);
171 bool GitAdminDir::GetWorktreeAdminDirPath(const CString
& projectTopDir
, CString
& adminDir
)
173 if (IsBareRepo(projectTopDir
))
175 adminDir
= CPathUtils::BuildPathWithPathDelimiter(projectTopDir
);
179 CString sDotGitPath
= CPathUtils::BuildPathWithPathDelimiter(projectTopDir
) + GetAdminDirName();
180 if (CTGitPath(sDotGitPath
).IsDirectory())
182 adminDir
= CPathUtils::BuildPathWithPathDelimiter(sDotGitPath
);
187 CString result
= ReadGitLink(projectTopDir
, sDotGitPath
);
188 if (result
.IsEmpty())
190 adminDir
= CPathUtils::BuildPathWithPathDelimiter(result
);
195 CString
GitAdminDir::ReadGitLink(const CString
& topDir
, const CString
& dotGitPath
)
197 CAutoFILE pFile
= _wfsopen(dotGitPath
, L
"r", SH_DENYWR
);
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: "))
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
;
216 PathCanonicalize(CStrBuf(adminDir
, MAX_PATH
), gitPath
);
219 CPathUtils::TrimTrailingPathDelimiter(gitPath
);
223 bool GitAdminDir::IsAdminDirPath(const CString
& path
)
227 bool bIsAdminDir
= false;
228 CString lowerpath
= path
;
229 lowerpath
.MakeLower();
231 while ((ind1
= lowerpath
.Find(L
"\\.git", ind1
)) >= 0)
234 if (ind
== (lowerpath
.GetLength() - 5))
239 else if (lowerpath
.Find(L
"\\.git\\", ind
) >= 0)
249 bool GitAdminDir::IsBareRepo(const CString
& path
)
254 if (IsAdminDirPath(path
))
257 // don't check for \\COMPUTERNAME\HEAD
258 if (path
[0] == L
'\\' && path
[1] == L
'\\')
260 if (path
.Find(L
'\\', 2) < 0)
264 if (!PathFileExists(path
+ L
"\\HEAD") || !PathFileExists(path
+ L
"\\config"))
267 if (!PathFileExists(path
+ L
"\\objects\\") || !PathFileExists(path
+ L
"\\refs\\"))