Fix typos
[TortoiseGit.git] / src / TortoiseProc / Commands / CleanupCommand.cpp
blob304fbb9b413a84250ad3f6e9229dc9b84831c509
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009, 2011-2021, 2023 - TortoiseGit
4 // Copyright (C) 2007-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 "CleanupCommand.h"
23 #include "MessageBox.h"
24 #include "ProgressDlg.h"
25 #include "ShellUpdater.h"
26 #include "CleanTypeDlg.h"
27 #include "../Utils/UnicodeUtils.h"
28 #include "SysProgressDlg.h"
30 struct SubmodulePayload
32 STRING_VECTOR &list;
33 CString basePath;
34 STRING_VECTOR prefixList;
35 SubmodulePayload(STRING_VECTOR& alist, CString abasePath = L"", const STRING_VECTOR& aprefixList = STRING_VECTOR())
36 : list(alist)
37 , basePath(abasePath)
38 , prefixList(aprefixList)
43 static bool GetSubmodulePathList(SubmodulePayload &payload);
45 static int SubmoduleCallback(git_submodule *sm, const char * /*name*/, void *payload)
47 auto spayload = reinterpret_cast<SubmodulePayload*>(payload);
48 CString path = CUnicodeUtils::GetUnicode(git_submodule_path(sm));
49 CString fullPath(spayload->basePath);
50 fullPath += L'\\';
51 fullPath += path;
52 if (!PathIsDirectory(fullPath))
53 return 0;
54 if (spayload->prefixList.empty())
56 CTGitPath subPath(spayload->basePath);
57 subPath.AppendPathString(path);
58 spayload->list.push_back(subPath.GetGitPathString());
59 SubmodulePayload tpayload(spayload->list, subPath.GetGitPathString());
60 GetSubmodulePathList(tpayload);
62 else
64 for (size_t i = 0; i < spayload->prefixList.size(); ++i)
66 CString prefix = spayload->prefixList.at(i) + L'/';
67 if (CStringUtils::StartsWith(path, prefix))
69 CTGitPath subPath(spayload->basePath);
70 subPath.AppendPathString(path);
71 spayload->list.push_back(subPath.GetGitPathString());
72 SubmodulePayload tpayload(spayload->list, subPath.GetGitPathString());
73 GetSubmodulePathList(tpayload);
77 return 0;
80 static bool GetSubmodulePathList(SubmodulePayload &payload)
82 CAutoRepository repo(payload.basePath);
83 if (!repo)
85 // Silence the warning message, submodule may not be initialized yet.
86 return false;
89 if (git_submodule_foreach(repo, SubmoduleCallback, &payload))
91 MessageBox(GetExplorerHWND(), CGit::GetLibGit2LastErr(L"Could not get submodule list."), L"TortoiseGit", MB_ICONERROR);
92 return false;
95 return true;
98 static bool GetFilesToCleanUp(CTGitPathList& delList, const CString& baseCmd, CGit *pGit, const CString& path, const boolean quotepath, CSysProgressDlg& sysProgressDlg)
100 CString cmd(baseCmd);
101 if (!path.IsEmpty())
102 cmd += L" -- \"" + path + L'"';
104 CString cmdout, cmdouterr;
105 if (pGit->Run(cmd, &cmdout, &cmdouterr, CP_UTF8))
107 if (cmdouterr.IsEmpty())
108 cmdouterr.Format(IDS_GITEXEERROR_NOMESSAGE, static_cast<LPCWSTR>(cmdout));
109 MessageBox(GetExplorerHWND(), cmdouterr, L"TortoiseGit", MB_ICONERROR);
110 return false;
113 if (sysProgressDlg.HasUserCancelled())
115 CMessageBox::Show(GetExplorerHWND(), IDS_USERCANCELLED, IDS_APPNAME, MB_OK);
116 return false;
119 int pos = 0;
120 CString token = cmdout.Tokenize(L"\n", pos);
121 while (!token.IsEmpty())
123 if (CStringUtils::StartsWith(token, L"Would remove "))
125 CString tempPath = token.Mid(static_cast<int>(wcslen(L"Would remove "))).TrimRight();
126 if (quotepath)
127 tempPath = CStringUtils::UnescapeGitQuotePath(tempPath.Trim(L'"'));
128 delList.AddPath(pGit->CombinePath(tempPath));
131 token = cmdout.Tokenize(L"\n", pos);
134 if (sysProgressDlg.HasUserCancelled())
136 CMessageBox::Show(GetExplorerHWND(), IDS_USERCANCELLED, IDS_APPNAME, MB_OK);
137 return false;
140 return true;
143 static bool DoCleanUp(const CTGitPathList& pathList, int cleanType, bool bDir, bool bDirUnmanagedRepos, bool bSubmodules, bool bDryRun, bool bNoRecycleBin)
145 CString cmd;
146 cmd.Format(L"git.exe clean");
147 if (bDryRun || !bNoRecycleBin)
148 cmd += L" -n ";
149 if (bDir)
150 cmd += L" -d ";
151 switch (cleanType)
153 case 0:
154 cmd += L" -fx";
155 break;
156 case 1:
157 cmd += L" -f";
158 break;
159 case 2:
160 cmd += L" -fX";
161 break;
163 if (bDirUnmanagedRepos)
164 cmd += L" -f";
166 STRING_VECTOR submoduleList;
167 if (bSubmodules)
169 SubmodulePayload payload(submoduleList);
170 payload.basePath = CTGitPath(g_Git.m_CurrentDir).GetGitPathString();
171 if (pathList.GetCount() != 1 || pathList.GetCount() == 1 && !pathList[0].IsEmpty())
173 for (int i = 0; i < pathList.GetCount(); ++i)
175 CString path;
176 if (pathList[i].IsDirectory())
177 payload.prefixList.push_back(pathList[i].GetGitPathString());
178 else
179 payload.prefixList.push_back(pathList[i].GetContainingDirectory().GetGitPathString());
182 if (!GetSubmodulePathList(payload))
183 return false;
184 std::sort(submoduleList.begin(), submoduleList.end());
187 if (bDryRun || bNoRecycleBin)
189 CProgressDlg progress;
190 for (int i = 0; i < pathList.GetCount(); ++i)
192 CString path;
193 if (pathList[i].IsDirectory() && !pathList[i].IsWCRoot())
194 path = pathList[i].GetGitPathString();
195 else
196 path = pathList[i].GetContainingDirectory().GetGitPathString();
198 if (pathList[i].IsWCRoot() && pathList[i].GetWinPathString() != g_Git.m_CurrentDir)
200 if (PathIsRelative(pathList[i].GetWinPathString()))
201 progress.m_GitDirList.push_back(g_Git.CombinePath(pathList[i].GetWinPathString()));
202 else
203 progress.m_GitDirList.push_back(pathList[i].GetWinPathString());
205 else
206 progress.m_GitDirList.push_back(g_Git.m_CurrentDir);
207 progress.m_GitCmdList.push_back(cmd + (path.IsEmpty() ? CString() : (L" -- \"" + path + L'"')));
210 for (CString dir : submoduleList)
212 progress.m_GitDirList.push_back(CTGitPath(dir).GetWinPathString());
213 progress.m_GitCmdList.push_back(cmd);
216 progress.m_PostCmdCallback = [&](DWORD status, PostCmdList& postCmdList)
218 if (status)
219 postCmdList.emplace_back(IDS_MSGBOX_RETRY, [&]{ DoCleanUp(pathList, cleanType, bDir, bDirUnmanagedRepos, bSubmodules, bDryRun, bNoRecycleBin); });
221 if (status || !bDryRun)
222 return;
224 if (bNoRecycleBin)
226 postCmdList.emplace_back(IDS_CLEAN_NO_RECYCLEBIN, [&]{ DoCleanUp(pathList, cleanType, bDir, bDirUnmanagedRepos, bSubmodules, FALSE, TRUE); });
227 postCmdList.emplace_back(IDS_CLEAN_TO_RECYCLEBIN, [&]{ DoCleanUp(pathList, cleanType, bDir, bDirUnmanagedRepos, bSubmodules, FALSE, FALSE); });
229 else
231 postCmdList.emplace_back(IDS_CLEAN_TO_RECYCLEBIN, [&]{ DoCleanUp(pathList, cleanType, bDir, bDirUnmanagedRepos, bSubmodules, FALSE, FALSE); });
232 postCmdList.emplace_back(IDS_CLEAN_NO_RECYCLEBIN, [&]{ DoCleanUp(pathList, cleanType, bDir, bDirUnmanagedRepos, bSubmodules, FALSE, TRUE); });
236 INT_PTR result = progress.DoModal();
237 return result == IDOK;
239 else
241 CSysProgressDlg sysProgressDlg;
242 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
243 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_PROC_CLEANUP_INFO1)));
244 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
245 sysProgressDlg.SetShowProgressBar(false);
246 sysProgressDlg.ShowModeless(static_cast<HWND>(nullptr), true);
248 bool quotepath = g_Git.GetConfigValueBool(L"core.quotepath", true);
250 CTGitPathList delList;
251 for (int i = 0; i < pathList.GetCount(); ++i)
253 CString path;
254 if (pathList[i].IsDirectory() && !pathList[i].IsWCRoot())
255 path = pathList[i].GetGitPathString();
256 else
257 path = pathList[i].GetContainingDirectory().GetGitPathString();
259 if (pathList[i].IsWCRoot() && pathList[i].GetWinPathString() != g_Git.m_CurrentDir)
261 CGit git;
262 if (PathIsRelative(pathList[i].GetWinPathString()))
263 git.m_CurrentDir = g_Git.CombinePath(pathList[i].GetWinPathString());
264 else
265 git.m_CurrentDir = pathList[i].GetWinPathString();
266 if (!GetFilesToCleanUp(delList, cmd, &git, path, quotepath, sysProgressDlg))
267 return false;
269 else if (!GetFilesToCleanUp(delList, cmd, &g_Git, path, quotepath, sysProgressDlg))
270 return false;
273 for (CString dir : submoduleList)
275 CGit git;
276 git.m_CurrentDir = dir;
277 if (!GetFilesToCleanUp(delList, cmd, &git, L"", quotepath, sysProgressDlg))
278 return false;
281 delList.DeleteAllFiles(true, false, true);
283 sysProgressDlg.Stop();
286 return true;
289 bool CleanupCommand::Execute()
291 if (!GitAdminDir::HasAdminDir(g_Git.m_CurrentDir))
293 CMessageBox::Show(GetExplorerHWND(), IDS_NOWORKINGCOPY, IDS_APPNAME, MB_ICONERROR);
294 return false;
297 bool bRet = false;
299 CCleanTypeDlg dlg;
300 dlg.m_pathList = pathList;
301 if (dlg.DoModal() == IDOK)
303 bRet = DoCleanUp(pathList, dlg.m_CleanType, dlg.m_bDir == BST_CHECKED, dlg.m_bDirUnmanagedRepo == BST_CHECKED, dlg.m_bSubmodules == BST_CHECKED, dlg.m_bDryRun == BST_CHECKED, dlg.m_bNoRecycleBin == BST_CHECKED);
305 CShellUpdater::Instance().Flush();
307 return bRet;