Show subpath in Clean Dialog title
[TortoiseGit.git] / src / TortoiseProc / Commands / CleanupCommand.cpp
blob61d80bf734e8b8ce90e965ab185da7364fefc2d6
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009,2011-2015 - 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
43 t += s[i];
47 return CUnicodeUtils::GetUnicode(t);
50 struct SubmodulePayload
52 STRING_VECTOR &list;
53 CString basePath;
54 STRING_VECTOR prefixList;
55 SubmodulePayload(STRING_VECTOR &alist, CString abasePath = _T(""), STRING_VECTOR aprefixList = STRING_VECTOR())
56 : list(alist)
57 , basePath(abasePath)
58 , prefixList(aprefixList)
63 static bool GetSubmodulePathList(SubmodulePayload &payload);
65 static int SubmoduleCallback(git_submodule *sm, const char * /*name*/, void *payload)
67 auto spayload = (SubmodulePayload *)payload;
68 CString path = CUnicodeUtils::GetUnicode(git_submodule_path(sm));
69 if (spayload->prefixList.empty())
71 CTGitPath subPath(spayload->basePath);
72 subPath.AppendPathString(path);
73 spayload->list.push_back(subPath.GetGitPathString());
74 SubmodulePayload tpayload(spayload->list, subPath.GetGitPathString());
75 GetSubmodulePathList(tpayload);
77 else
79 for (size_t i = 0; i < spayload->prefixList.size(); ++i)
81 CString prefix = spayload->prefixList.at(i) + _T("/");
82 if (path.Left(prefix.GetLength()) == prefix)
84 CTGitPath subPath(spayload->basePath);
85 subPath.AppendPathString(path);
86 spayload->list.push_back(subPath.GetGitPathString());
87 SubmodulePayload tpayload(spayload->list, subPath.GetGitPathString());
88 GetSubmodulePathList(tpayload);
92 return 0;
95 static bool GetSubmodulePathList(SubmodulePayload &payload)
97 CAutoRepository repo;
98 if (repo.Open(payload.basePath))
100 // Silence the warning message, submodule may not be initialized yet.
101 return false;
104 if (git_submodule_foreach(repo, SubmoduleCallback, &payload))
106 MessageBox(nullptr, CGit::GetLibGit2LastErr(_T("Could not get submodule list.")), _T("TortoiseGit"), MB_ICONERROR);
107 return false;
110 return true;
113 static bool GetFilesToCleanUp(CTGitPathList& delList, const CString& baseCmd, CGit *pGit, const CString& path, const boolean quotepath, CSysProgressDlg& sysProgressDlg)
115 CString cmd(baseCmd);
116 if (!path.IsEmpty())
117 cmd += _T(" -- \"") + path + _T("\"");
119 CString cmdout, cmdouterr;
120 if (pGit->Run(cmd, &cmdout, &cmdouterr, CP_UTF8))
122 MessageBox(nullptr, cmdouterr, _T("TortoiseGit"), MB_ICONERROR);
123 return false;
126 if (sysProgressDlg.HasUserCancelled())
128 CMessageBox::Show(nullptr, IDS_SVN_USERCANCELLED, IDS_APPNAME, MB_OK);
129 return false;
132 int pos = 0;
133 CString token = cmdout.Tokenize(_T("\n"), pos);
134 while (!token.IsEmpty())
136 if (token.Mid(0, 13) == _T("Would remove "))
138 CString tempPath = token.Mid(13).TrimRight();
139 if (quotepath)
140 tempPath = UnescapeQuotePath(tempPath.Trim(_T('"')));
141 delList.AddPath(pGit->CombinePath(tempPath));
144 token = cmdout.Tokenize(_T("\n"), pos);
147 if (sysProgressDlg.HasUserCancelled())
149 CMessageBox::Show(nullptr, IDS_SVN_USERCANCELLED, IDS_APPNAME, MB_OK);
150 return false;
153 return true;
156 static bool DoCleanUp(const CTGitPathList& pathList, int cleanType, bool bDir, bool bSubmodules, bool bDryRun, bool bNoRecycleBin)
158 CString cmd;
159 cmd.Format(_T("git.exe clean"));
160 if (bDryRun || !bNoRecycleBin)
161 cmd += _T(" -n ");
162 if (bDir)
163 cmd += _T(" -d ");
164 switch (cleanType)
166 case 0:
167 cmd += _T(" -fx");
168 break;
169 case 1:
170 cmd += _T(" -f");
171 break;
172 case 2:
173 cmd += _T(" -fX");
174 break;
177 STRING_VECTOR submoduleList;
178 if (bSubmodules)
180 SubmodulePayload payload(submoduleList);
181 payload.basePath = CTGitPath(g_Git.m_CurrentDir).GetGitPathString();
182 if (pathList.GetCount() != 1 || pathList.GetCount() == 1 && !pathList[0].IsEmpty())
184 for (int i = 0; i < pathList.GetCount(); ++i)
186 CString path;
187 if (pathList[i].IsDirectory())
188 payload.prefixList.push_back(pathList[i].GetGitPathString());
189 else
190 payload.prefixList.push_back(pathList[i].GetContainingDirectory().GetGitPathString());
193 if (!GetSubmodulePathList(payload))
194 return false;
195 std::sort(submoduleList.begin(), submoduleList.end());
198 if (bDryRun || bNoRecycleBin)
200 CProgressDlg progress;
201 for (int i = 0; i < pathList.GetCount(); ++i)
203 CString path;
204 if (pathList[i].IsDirectory())
205 path = pathList[i].GetGitPathString();
206 else
207 path = pathList[i].GetContainingDirectory().GetGitPathString();
209 progress.m_GitDirList.push_back(g_Git.m_CurrentDir);
210 progress.m_GitCmdList.push_back(cmd + _T(" -- \"") + path + _T("\""));
213 for (CString dir : submoduleList)
215 progress.m_GitDirList.push_back(CTGitPath(dir).GetWinPathString());
216 progress.m_GitCmdList.push_back(cmd);
219 progress.m_PostCmdCallback = [&](DWORD status, PostCmdList& postCmdList)
221 if (status)
222 postCmdList.push_back(PostCmd(IDS_MSGBOX_RETRY, [&]{ DoCleanUp(pathList, cleanType, bDir, bSubmodules, bDryRun, bNoRecycleBin); }));
224 if (status || !bDryRun)
225 return;
227 if (bNoRecycleBin)
229 postCmdList.push_back(PostCmd(IDS_CLEAN_NO_RECYCLEBIN, [&]{ DoCleanUp(pathList, cleanType, bDir, bSubmodules, FALSE, TRUE); }));
230 postCmdList.push_back(PostCmd(IDS_CLEAN_TO_RECYCLEBIN, [&]{ DoCleanUp(pathList, cleanType, bDir, bSubmodules, FALSE, FALSE); }));
232 else
234 postCmdList.push_back(PostCmd(IDS_CLEAN_TO_RECYCLEBIN, [&]{ DoCleanUp(pathList, cleanType, bDir, bSubmodules, FALSE, FALSE); }));
235 postCmdList.push_back(PostCmd(IDS_CLEAN_NO_RECYCLEBIN, [&]{ DoCleanUp(pathList, cleanType, bDir, bSubmodules, FALSE, TRUE); }));
239 INT_PTR result = progress.DoModal();
240 return result == IDOK;
242 else
244 CSysProgressDlg sysProgressDlg;
245 sysProgressDlg.SetAnimation(IDR_CLEANUPANI);
246 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
247 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_PROC_CLEANUP_INFO1)));
248 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
249 sysProgressDlg.SetShowProgressBar(false);
250 sysProgressDlg.ShowModeless((HWND)NULL, true);
252 bool quotepath = g_Git.GetConfigValueBool(_T("core.quotepath"));
254 CTGitPathList delList;
255 for (int i = 0; i < pathList.GetCount(); ++i)
257 CString path;
258 if (pathList[i].IsDirectory())
259 path = pathList[i].GetGitPathString();
260 else
261 path = pathList[i].GetContainingDirectory().GetGitPathString();
263 if (!GetFilesToCleanUp(delList, cmd, &g_Git, path, quotepath, sysProgressDlg))
264 return false;
267 for (CString dir : submoduleList)
269 CGit git;
270 git.m_CurrentDir = dir;
271 if (!GetFilesToCleanUp(delList, cmd, &git, _T(""), quotepath, sysProgressDlg))
272 return false;
275 delList.DeleteAllFiles(true, false);
277 sysProgressDlg.Stop();
280 return true;
283 bool CleanupCommand::Execute()
285 bool bRet = false;
287 CCleanTypeDlg dlg;
288 dlg.m_pathList = pathList;
289 if (dlg.DoModal() == IDOK)
291 bRet = DoCleanUp(pathList, dlg.m_CleanType, dlg.m_bDir == BST_CHECKED, dlg.m_bSubmodules == BST_CHECKED, dlg.m_bDryRun == BST_CHECKED, dlg.m_bNoRecycleBin == BST_CHECKED);
293 CShellUpdater::Instance().Flush();
295 return bRet;