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.
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
)
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'));
47 return CUnicodeUtils::GetUnicode(t
);
50 struct SubmodulePayload
54 STRING_VECTOR prefixList
;
55 SubmodulePayload(STRING_VECTOR
&alist
, CString abasePath
= _T(""), STRING_VECTOR aprefixList
= STRING_VECTOR())
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
);
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
);
95 static bool GetSubmodulePathList(SubmodulePayload
&payload
)
98 if (repo
.Open(payload
.basePath
))
100 // Silence the warning message, submodule may not be initialized yet.
104 if (git_submodule_foreach(repo
, SubmoduleCallback
, &payload
))
106 MessageBox(nullptr, CGit::GetLibGit2LastErr(_T("Could not get submodule list.")), _T("TortoiseGit"), MB_ICONERROR
);
113 static bool GetFilesToCleanUp(CTGitPathList
& delList
, const CString
& baseCmd
, CGit
*pGit
, const CString
& path
, const boolean quotepath
, CSysProgressDlg
& sysProgressDlg
)
115 CString
cmd(baseCmd
);
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
);
126 if (sysProgressDlg
.HasUserCancelled())
128 CMessageBox::Show(nullptr, IDS_SVN_USERCANCELLED
, IDS_APPNAME
, MB_OK
);
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();
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
);
156 static bool DoCleanUp(const CTGitPathList
& pathList
, int cleanType
, bool bDir
, bool bSubmodules
, bool bDryRun
, bool bNoRecycleBin
)
159 cmd
.Format(_T("git.exe clean"));
160 if (bDryRun
|| !bNoRecycleBin
)
177 STRING_VECTOR submoduleList
;
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
)
187 if (pathList
[i
].IsDirectory())
188 payload
.prefixList
.push_back(pathList
[i
].GetGitPathString());
190 payload
.prefixList
.push_back(pathList
[i
].GetContainingDirectory().GetGitPathString());
193 if (!GetSubmodulePathList(payload
))
195 std::sort(submoduleList
.begin(), submoduleList
.end());
198 if (bDryRun
|| bNoRecycleBin
)
200 CProgressDlg progress
;
201 for (int i
= 0; i
< pathList
.GetCount(); ++i
)
204 if (pathList
[i
].IsDirectory())
205 path
= pathList
[i
].GetGitPathString();
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
)
222 postCmdList
.push_back(PostCmd(IDS_MSGBOX_RETRY
, [&]{ DoCleanUp(pathList
, cleanType
, bDir
, bSubmodules
, bDryRun
, bNoRecycleBin
); }));
224 if (status
|| !bDryRun
)
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
); }));
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
;
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
)
258 if (pathList
[i
].IsDirectory())
259 path
= pathList
[i
].GetGitPathString();
261 path
= pathList
[i
].GetContainingDirectory().GetGitPathString();
263 if (!GetFilesToCleanUp(delList
, cmd
, &g_Git
, path
, quotepath
, sysProgressDlg
))
267 for (CString dir
: submoduleList
)
270 git
.m_CurrentDir
= dir
;
271 if (!GetFilesToCleanUp(delList
, cmd
, &git
, _T(""), quotepath
, sysProgressDlg
))
275 delList
.DeleteAllFiles(true, false);
277 sysProgressDlg
.Stop();
283 bool CleanupCommand::Execute()
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();