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.
21 #include "UnicodeUtils.h"
22 #include "GitAdminDir.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")))
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)
45 }while(projectroot
.ReverseFind('\\')>0);
50 CString
GitAdminDir::GetGitTopDir(const CString
& path
)
53 HasAdminDir(path
,!!PathIsDirectory(path
),&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
)
76 CString sDirName
= path
;
80 if (path
.GetLength() <= 3)
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
89 n
= sDirName
.Find(_T("\\.git"), n
);
93 // check for actual .git dir (and not .gitignore or something else), continue search if false match
95 if (sDirName
[n
] == _T('\\') || sDirName
[n
] == 0)
101 if(CGit::GitPathFileExists(sDirName
+ _T("\\.git")))
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("\\");
112 else if (IsBareRepo(sDirName
))
115 int x
= sDirName
.ReverseFind(_T('\\'));
119 sDirName
.Truncate(x
);
120 // don't check for \\COMPUTERNAME\.git
121 if (sDirName
[0] == _T('\\') && sDirName
[1] == _T('\\') && sDirName
.Find(_T('\\'), 2) < 0)
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("\\"));
141 CString sDotGitPath
= projectTopDir
+ _T("\\") + GetAdminDirName();
142 if (CTGitPath(sDotGitPath
).IsDirectory())
144 sDotGitPath
.TrimRight('\\');
145 sDotGitPath
.Append(_T("\\"));
146 adminDir
= sDotGitPath
;
151 CString result
= ReadGitLink(projectTopDir
, sDotGitPath
);
152 if (result
.IsEmpty())
154 adminDir
= result
+ _T("\\");
159 CString
GitAdminDir::ReadGitLink(const CString
& topDir
, const CString
& dotGitPath
)
161 CAutoFILE pFile
= _tfsopen(dotGitPath
, _T("r"), SH_DENYWR
);
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: "))
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
;
181 PathCanonicalize(CStrBuf(adminDir
, MAX_PATH
), gitPath
);
187 bool GitAdminDir::IsAdminDirPath(const CString
& path
)
191 bool bIsAdminDir
= false;
192 CString lowerpath
= path
;
193 lowerpath
.MakeLower();
195 while ((ind1
= lowerpath
.Find(_T("\\.git"), ind1
))>=0)
198 if (ind
== (lowerpath
.GetLength() - 5))
203 else if (lowerpath
.Find(_T("\\.git\\"), ind
)>=0)
213 bool GitAdminDir::IsBareRepo(const CString
& path
)
218 if (IsAdminDirPath(path
))
221 // don't check for \\COMPUTERNAME\HEAD
222 if (path
[0] == _T('\\') && path
[1] == _T('\\'))
224 if (path
.Find(_T('\\'), 2) < 0)
228 if (!PathFileExists(path
+ _T("\\HEAD")) || !PathFileExists(path
+ _T("\\config")))
231 if (!PathFileExists(path
+ _T("\\objects\\")) || !PathFileExists(path
+ _T("\\refs\\")))