IProgressDialog::SetAnimation is not supported any more on Windows >= Vista
[TortoiseGit.git] / src / TortoiseProc / Commands / CleanupCommand.cpp
blobcc6d19da75740083089cbbae0b36b2e4da2381a6
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009, 2011-2016 - 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 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;
182 STRING_VECTOR submoduleList;
183 if (bSubmodules)
185 SubmodulePayload payload(submoduleList);
186 payload.basePath = CTGitPath(g_Git.m_CurrentDir).GetGitPathString();
187 if (pathList.GetCount() != 1 || pathList.GetCount() == 1 && !pathList[0].IsEmpty())
189 for (int i = 0; i < pathList.GetCount(); ++i)
191 CString path;
192 if (pathList[i].IsDirectory())
193 payload.prefixList.push_back(pathList[i].GetGitPathString());
194 else
195 payload.prefixList.push_back(pathList[i].GetContainingDirectory().GetGitPathString());
198 if (!GetSubmodulePathList(payload))
199 return false;
200 std::sort(submoduleList.begin(), submoduleList.end());
203 if (bDryRun || bNoRecycleBin)
205 CProgressDlg progress;
206 for (int i = 0; i < pathList.GetCount(); ++i)
208 CString path;
209 if (pathList[i].IsDirectory())
210 path = pathList[i].GetGitPathString();
211 else
212 path = pathList[i].GetContainingDirectory().GetGitPathString();
214 progress.m_GitDirList.push_back(g_Git.m_CurrentDir);
215 progress.m_GitCmdList.push_back(cmd + L" -- \"" + path + L'"');
218 for (CString dir : submoduleList)
220 progress.m_GitDirList.push_back(CTGitPath(dir).GetWinPathString());
221 progress.m_GitCmdList.push_back(cmd);
224 progress.m_PostCmdCallback = [&](DWORD status, PostCmdList& postCmdList)
226 if (status)
227 postCmdList.emplace_back(IDS_MSGBOX_RETRY, [&]{ DoCleanUp(pathList, cleanType, bDir, bSubmodules, bDryRun, bNoRecycleBin); });
229 if (status || !bDryRun)
230 return;
232 if (bNoRecycleBin)
234 postCmdList.emplace_back(IDS_CLEAN_NO_RECYCLEBIN, [&]{ DoCleanUp(pathList, cleanType, bDir, bSubmodules, FALSE, TRUE); });
235 postCmdList.emplace_back(IDS_CLEAN_TO_RECYCLEBIN, [&]{ DoCleanUp(pathList, cleanType, bDir, bSubmodules, FALSE, FALSE); });
237 else
239 postCmdList.emplace_back(IDS_CLEAN_TO_RECYCLEBIN, [&]{ DoCleanUp(pathList, cleanType, bDir, bSubmodules, FALSE, FALSE); });
240 postCmdList.emplace_back(IDS_CLEAN_NO_RECYCLEBIN, [&]{ DoCleanUp(pathList, cleanType, bDir, bSubmodules, FALSE, TRUE); });
244 INT_PTR result = progress.DoModal();
245 return result == IDOK;
247 else
249 CSysProgressDlg sysProgressDlg;
250 sysProgressDlg.SetTitle(CString(MAKEINTRESOURCE(IDS_APPNAME)));
251 sysProgressDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_PROC_CLEANUP_INFO1)));
252 sysProgressDlg.SetLine(2, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
253 sysProgressDlg.SetShowProgressBar(false);
254 sysProgressDlg.ShowModeless((HWND)nullptr, true);
256 bool quotepath = g_Git.GetConfigValueBool(L"core.quotepath");
258 CTGitPathList delList;
259 for (int i = 0; i < pathList.GetCount(); ++i)
261 CString path;
262 if (pathList[i].IsDirectory())
263 path = pathList[i].GetGitPathString();
264 else
265 path = pathList[i].GetContainingDirectory().GetGitPathString();
267 if (!GetFilesToCleanUp(delList, cmd, &g_Git, path, quotepath, sysProgressDlg))
268 return false;
271 for (CString dir : submoduleList)
273 CGit git;
274 git.m_CurrentDir = dir;
275 if (!GetFilesToCleanUp(delList, cmd, &git, L"", quotepath, sysProgressDlg))
276 return false;
279 delList.DeleteAllFiles(true, false, true);
281 sysProgressDlg.Stop();
284 return true;
287 bool CleanupCommand::Execute()
289 bool bRet = false;
291 CCleanTypeDlg dlg;
292 dlg.m_pathList = pathList;
293 if (dlg.DoModal() == IDOK)
295 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);
297 CShellUpdater::Instance().Flush();
299 return bRet;