C++ 小工具一键解决SVN Clean Up 失败的问题
参考文章:
1.http://blog.csdn.net/luochao_tj/article/details/46358145
2.http://blog.csdn.net/segen_jaa/article/details/7938959
可执行文件地址
http://pan.baidu.com/s/1nvi7kW5
使用方法:
下载压缩包,解压出可执行文件 SvnCleanUp.exe (Debug 和 Release 均可),放到SVN目录下面,双击运行即可.
完整VS工程地址:http://pan.baidu.com/s/1skECVDF 提取码 : lk5e
原理:
TortoiseSVN 把项目的cleanup信息和lock信息放在了.svn目录下的wd.db文件里面,这是一个sqlite数据库文件,如果手动CleanUp失败,我们只需要杀掉占用这个文件的SVN进程,然后把这个文件中的 WC_LOCK 和 WORK_QUEUE 表清空就好了.
sqlite3源码文件官网地址:https://www.sqlite.org/2017/sqlite-amalgamation-3160100.zip
主要逻辑代码:
#include <stdint.h> #include <stdio.h> #include <vector> #include <string> #include <io.h> #include <windows.h> #include <tlhelp32.h> #include "sqlite3/sqlite3.h" struct ProcessData { std::string name; int32_t id; }; void ScanWCDBFiles(const std::string &startPath, std::vector<std::string> &files, const std::string &dbFileName); int32_t CleanOneDB(const std::string &dbFile); int32_t DeleteCallback(void *NotUsed, int argc, char **argv, char **azColName); void GetAllProcess(std::vector<ProcessData> &processes); bool KillProcessByID(int32_t pid); int32_t main(int32_t argc, char *argv[]) { int32_t ret = 0; // kill all TSVN* processes const char *svnProcesses = "TSVN"; std::vector<ProcessData> processes; GetAllProcess(processes); for (const ProcessData &p : processes) { ret = p.name.find(svnProcesses); if (ret >= 0) { printf("kill %s\n", p.name.c_str()); if (!KillProcessByID(p.id)) { printf("\tfailed!\n"); } } } int32_t succCount = 0, failCount = 0; std::vector<std::string> files; printf("scanning...\n"); ScanWCDBFiles(".", files, "wc.db"); for (const std::string &file : files) { printf("cleaning [%s]\n", file.c_str()); ret = CleanOneDB(file); if (ret == S_OK) { ++succCount; } else { printf("clean [%s] failed!!!\n", file.c_str()); ++failCount; } } printf("clean finished.succ(s) %d, fail(s) %d.\n", succCount, failCount); getchar(); return 0; } void ScanWCDBFiles(const std::string &startPath, std::vector<std::string> &files, const std::string &dbFileName) { struct _finddata_t fileinfo; long hFile = 0; char tmpPath[MAX_PATH] = { 0 }; sprintf_s(tmpPath, "%s\\*", startPath.c_str()); if ((hFile = _findfirst(tmpPath, &fileinfo)) == -1){ return; } do { if ((fileinfo.attrib & _A_SUBDIR)) { if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0) { sprintf_s(tmpPath, "%s\\%s", startPath.c_str(), fileinfo.name); ScanWCDBFiles(tmpPath, files, dbFileName); } } else { if (dbFileName == fileinfo.name) { sprintf_s(tmpPath, "%s\\%s", startPath.c_str(), fileinfo.name); files.push_back(tmpPath); } } } while (_findnext(hFile, &fileinfo) == 0); _findclose(hFile); } int32_t CleanOneDB(const std::string &dbFile) { const char* tables[] = { "WC_LOCK", "WORK_QUEUE", "LOCK" }; sqlite3 * pDB = NULL; int32_t ret = sqlite3_open(dbFile.c_str(), &pDB); if (ret != SQLITE_OK) { printf("\topen db %s failed.\n", dbFile.c_str()); return ret; } for (int32_t i = 0; i < sizeof(tables) / sizeof(tables[0]); ++i) { printf("\tdelete [%s]...\n", tables[i]); char sql[256] = { 0 }; sprintf_s(sql, "DELETE FROM %s;", tables[i]); char* errorMsg = NULL; ret = sqlite3_exec(pDB, sql, DeleteCallback, 0, &errorMsg); if (ret != SQLITE_OK) { printf("\tselect %s error![%s]\n", tables[i], errorMsg); return ret; } } return 0; } int32_t DeleteCallback(void *NotUsed, int argc, char **argv, char **azColName) { return 0; } void GetAllProcess(std::vector<ProcessData> &processes) { HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (INVALID_HANDLE_VALUE == hSnapshot) { return; } PROCESSENTRY32 pe = { sizeof(pe) }; for (BOOL ret = Process32First(hSnapshot, &pe); ret; ret = Process32Next(hSnapshot, &pe)) { ProcessData data; data.name = pe.szExeFile; data.id = pe.th32ProcessID; processes.push_back(data); //printf("%-6d %s\n", pe.th32ProcessID, pe.szExeFile); } CloseHandle(hSnapshot); } bool KillProcessByID(int32_t pid) { HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); if (hProcess == NULL) { return FALSE; } return TerminateProcess(hProcess, 0) != 0; }
运行效果: