Extend static functions in CAppUtils with a window handle parameter
[TortoiseGit.git] / src / TortoiseProc / Commands / CleanupCommand.cpp
blobb79d8c9721b45e83ece87d50329ee5d8946b59e6
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009, 2011-2017 - 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 static CString UnescapeQuotePath(CString s)
32 CStringA t;
33 for (int i = 0; i < s.GetLength(); ++i)
35 if (s[i] == '\\' && i + 3 < s.GetLength())
37 char c = (char)((s[i + 1] - '0') * 64 + (s[i + 2] - '0') * 8 + (s[i + 3] - '0'));
38 t += c;
39 i += 3;
41 else
42 t += s[i];
45 return CUnicodeUtils::GetUnicode(t);
48 struct SubmodulePayload
50 STRING_VECTOR &list;
51 CString basePath;
52 STRING_VECTOR prefixList;
53 SubmodulePayload(STRING_VECTOR &alist, CString abasePath = L"", STRING_VECTOR aprefixList = STRING_VECTOR())
54 : list(alist)
55 , basePath(abasePath)
56 , prefixList(aprefixList)
61 static bool GetSubmodulePathList(SubmodulePayload &payload);
63 static int SubmoduleCallback(git_submodule *sm, const char * /*name*/, void *payload)
65 auto spayload = reinterpret_cast<SubmodulePayload*>(payload);
66 CString path = CUnicodeUtils::GetUnicode(git_submodule_path(sm));
67 CString fullPath(spayload->basePath);
68 fullPath += L'\\';
69 fullPath += path;
70 if (!PathIsDirectory(fullPath))
71 return 0;
72 if (spayload->prefixList.empty())
74 CTGitPath subPath(spayload->basePath);
75 subPath.AppendPathString(path);
76 spayload->list.push_back(subPath.GetGitPathString());
77 SubmodulePayload tpayload(spayload->list, subPath.GetGitPathString());
78 GetSubmodulePathList(tpayload);
80 else
82 for (size_t i = 0; i < spayload->prefixList.size(); ++i)
84 CString prefix = spayload->prefixList.at(i) + L'/';
85 if (CStringUtils::StartsWith(path, prefix))
87 CTGitPath subPath(spayload->basePath);
88 subPath.AppendPathString(path);
89 spayload->list.push_back(subPath.GetGitPathString());
90 SubmodulePayload tpayload(spayload->list, subPath.GetGitPathString());
91 GetSubmodulePathList(tpayload);
95 return 0;
98 static bool GetSubmodulePathList(SubmodulePayload &payload)
100 CAutoRepository repo(payload.basePath);
101 if (!repo)
103 // Silence the warning message, submodule may not be initialized yet.
104 return false;
107 if (git_submodule_foreach(repo, SubmoduleCallback, &payload))
109 MessageBox(hWndExplorer, CGit::GetLibGit2LastErr(L"Could not get submodule list."), L"TortoiseGit", MB_ICONERROR);
110 return false;
113 return true;
116 static bool GetFilesToCleanUp(CTGitPathList& delList, const CString& baseCmd, CGit *pGit, const CString& path, const boolean quotepath, CSysProgressDlg& sysProgressDlg)
118 CString cmd(baseCmd);
119 if (!path.IsEmpty())
120 cmd += L" -- \"" + path + L'"';
122 CString cmdout, cmdouterr;
123 if (pGit->Run(cmd, &cmdout, &cmdouterr, CP_UTF8))
125 if (cmdouterr.IsEmpty())
126 cmdouterr.Format(IDS_GITEXEERROR_NOMESSAGE, (LPCTSTR)cmdout);
127 MessageBox(hWndExplorer, cmdouterr, L"TortoiseGit", MB_ICONERROR);
128 return false;
131 if (sysProgressDlg.HasUserCancelled())
133 CMessageBox::Show(hWndExplorer, IDS_USERCANCELLED, IDS_APPNAME, MB_OK);
134 return false;
137 int pos = 0;
138 CString token = cmdout.Tokenize(L"\n", pos);
139 while (!token.IsEmpty())
141 if (token.Mid(0, 13) == L"Would remove ")
143 CString tempPath = token.Mid(13).TrimRight();
144 if (quotepath)
145 tempPath = UnescapeQuotePath(tempPath.Trim(L'"'));
146 delList.AddPath(pGit->CombinePath(tempPath));
149 token = cmdout.Tokenize(L"\n", pos);
152 if (sysProgressDlg.HasUserCancelled())
154 CMessageBox::Show(hWndExplorer, IDS_USERCANCELLED, IDS_APPNAME, MB_OK);
155 return false;
158 return true;
161 static bool DoCleanUp(const CTGitPathList& pathList, int cleanType, bool bDir, bool bDirUnmanagedRepos, bool bSubmodules, bool bDryRun, bool bNoRecycleBin)
163 CString cmd;
164 cmd.Format(L"git.exe clean");
165 if (bDryRun || !bNoRecycleBin)
166 cmd += L" -n ";
167 if (bDir)
168 cmd += L" -d ";
169 switch (cleanType)
171 case 0:
172 cmd += L" -fx";
173 break;
174 case 1:
175 cmd += L" -f";
176 break;
177 case 2:
178 cmd += L" -fX";
179 break;
181 if (bDirUnmanagedRepos)
182 cmd += L" -f";
184 STRING_VECTOR submoduleList;
185 if (bSubmodules)
187 SubmodulePayload payload(submoduleList);
188 payload.basePath = CTGitPath(g_Git.m_CurrentDir).GetGitPathString();
189 if (pathList.GetCount() != 1 || pathList.GetCount() == 1 && !pathList[0].IsEmpty())
191 for (int i = 0; i < pathList.GetCount(); ++i)
193 CString path;
194 if (pathList[i].IsDirectory())
195 payload.prefixList.push_back(pathList[i].GetGitPathString());
196 else
197 payload.prefixList.push_back(pathList[i].GetContainingDirectory().GetGitPathString());
200 if (!GetSubmodulePathList(payload))
201 return false;
202 std::sort(submoduleList.begin(), submoduleList.end());
205 if (bDryRun || bNoRecycleBin)
207 CProgressDlg progress;
208 for (int i = 0; i < pathList.GetCount(); ++i)
210 CString path;
211 if (pathList[i].IsDirectory())
212 path = pathList[i].GetGitPathString();
213 else
214 path = pathList[i].GetContainingDirectory().GetGitPathString();
216 progress.m_GitDirList.push_back(g_Git.m_CurrentDir);
217 progress.m_GitCmdList.push_back(cmd + (path.IsEmpty() ? L"" : (L" -- \"" + path + L'"')));
220 for (CString dir : submoduleList)
222 progress.m_GitDirList.push_back(CTGitPath(dir).GetWinPathString());
223 progress.m_GitCmdList.push_back(cmd);
226 progress.m_PostCmdCallback = [&](DWORD status, PostCmdList& postCmdList)
228 if (status)
229 postCmdList.emplace_back(IDS_MSGBOX_RETRY, [&]{ DoCleanUp(pathList, cleanType, bDir, bDirUnmanagedRepos, bSubmodules, bDryRun, bNoRecycleBin); });
231 if (status || !bDryRun)
232 return;
234 if (bNoRecycleBin)
236 postCmdList.emplace_back(IDS_CLEAN_NO_RECYCLEBIN, [&]{ DoCleanUp(pathList, cleanType, bDir, bDirUnmanagedRepos, bSubmodules, FALSE, TRUE); });
237 postCmdList.emplace_back(IDS_CLEAN_TO_RECYCLEBIN, [&]{ DoCleanUp(pathList, cleanType, bDir, bDirUnmanagedRepos, bSubmodules, FALSE, FALSE); });
239 else
241 postCmdList.emplace_back(IDS_CLEAN_TO_RECYCLEBIN, [&]{ DoCleanUp(pathList, cleanType, bDir, bDirUnmanagedRepos, bSubmodules, FALSE, FALSE); });
242 postCmdList.emplace_back(IDS_CLEAN_NO_RECYCLEBIN, [&]{ DoCleanUp(pathList, cleanType, bDir, bDirUnmanagedRepos, bSubmodules, FALSE, TRUE); });
246 INT_PTR result = progress.DoModal();
247 return result == IDOK;
249 else
251 CSysProgressDlg sysProgressDlg;
252 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
253 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_PROC_CLEANUP_INFO1)));
254 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
255 sysProgressDlg.SetShowProgressBar(false);
256 sysProgressDlg.ShowModeless((HWND)nullptr, true);
258 bool quotepath = g_Git.GetConfigValueBool(L"core.quotepath");
260 CTGitPathList delList;
261 for (int i = 0; i < pathList.GetCount(); ++i)
263 CString path;
264 if (pathList[i].IsDirectory())
265 path = pathList[i].GetGitPathString();
266 else
267 path = pathList[i].GetContainingDirectory().GetGitPathString();
269 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 bool bRet = false;
293 CCleanTypeDlg dlg;
294 dlg.m_pathList = pathList;
295 if (dlg.DoModal() == IDOK)
297 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);
299 CShellUpdater::Instance().Flush();
301 return bRet;