关于svn日志提交的简介请参考 点击打开链接
下面直接上代码,如下:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <vector>
#include <windows.h>
#define BUFF_SIZE 2048
const char* commitfmtlogs = "\
请按照如下格式填写提交修改记录信息:\n\
【修改内容】\n\
此处说明你的修改内容,不少于20个字。\n\
【修改原因】\n\
此处说明你为什么要做出这样的修改,不少于20个字。\n\
【影响范围】\n\
此处说明你的修改会对其它什么地方产生影响,不少于10个字。\n\
【TODO列表】\n\
此处说明针对这次提交,还有什么事情没有做,如可能想到一些功能缺陷,以后要进行修改。 字数不限,如没有内容请填写 无。\n\
【问题列表】\n\
fixes #Redmine上的问题编号。(已解决某问题)\n\
或\n\
refs #Redmine上的问题编号。(关联到某问题)\n";
//HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\VisualSVN\VisualSVN Server\InstallDir
bool getSvnBinPath(std::string& svnBinPath)
{
HKEY hKey = NULL;
LONG retVal = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\VisualSVN\\VisualSVN Server", 0, KEY_ALL_ACCESS, &hKey);
if (ERROR_SUCCESS != retVal)
{
OutputDebugStringA("打开svn注册表失败");
return false;
}
char buff[MAX_PATH] = { 0 };
DWORD len = MAX_PATH;
DWORD type = REG_SZ;
retVal = RegQueryValueExA(hKey, "InstallDir", NULL, &type, (unsigned char*)buff, &len);
if (ERROR_SUCCESS != retVal)
{
OutputDebugStringA("读取svn注册表项InstallDir失败");
return false;
}
svnBinPath = buff;
svnBinPath += "bin\\";
return true;
}
/**
* @brief 通过svnlook获取提交的日志信息。
* 函数功能详述:
* @param
* @return
* @note no
* @warning no
* @see no
*/
void getSvnLog(const char* cmd, std::string& commitLogs)
{
//创建匿名管道从svnlook进程中获取提交的日志信息
HANDLE hReadPipe = NULL;
HANDLE hWritePipe = NULL;
SECURITY_ATTRIBUTES stSecurity;
stSecurity.nLength = sizeof(SECURITY_ATTRIBUTES);
stSecurity.lpSecurityDescriptor = NULL;
stSecurity.bInheritHandle = TRUE;//继承性
CreatePipe(&hReadPipe, &hWritePipe, &stSecurity, 0);
STARTUPINFOA siStartInfo;
GetStartupInfo(&siStartInfo);
siStartInfo.hStdInput = hReadPipe;
siStartInfo.hStdOutput = hWritePipe;
siStartInfo.hStdError = hWritePipe;
siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
siStartInfo.wShowWindow = SW_HIDE;
PROCESS_INFORMATION procInfo;
if (CreateProcessA(NULL, (char*)cmd, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &siStartInfo, &procInfo))
{
char buff[BUFF_SIZE + 1] = { 0 };
DWORD bytes_read = BUFF_SIZE;
while (true)
{
PeekNamedPipe(hReadPipe, buff, BUFF_SIZE, &bytes_read, NULL, NULL);
if (0 != bytes_read)
{
ReadFile(hReadPipe, buff, bytes_read, &bytes_read, NULL);
OutputDebugStringA(buff);
break;
}
}
commitLogs = buff;
}
}
/**
* @brief 按照指定规则进行格式化字符串。
* 函数功能详述:
* @param
* @return
* @note no
* @warning no
* @see no
*/
bool splitString(const std::string& src,
std::vector<std::string>& fields,
const std::string& pattern)
{
int size = src.size();
int pos = 0;
int i = 0;
for (; i < size; ++i)
{
pos = src.find(pattern, i);
if (std::string::npos == pos)
{
break;
}
fields.push_back(src.substr(i, pos - i));
i = pos + pattern.size() - 1;
}
//判断最后是否是以pattern结尾
if (i != size)
{
fields.push_back(src.substr(i, size - i));
}
return true;
}
/**
* @brief 判断问题号是否为数字。
* 函数功能详述:
* @param
* @return
* @note no
* @warning no
* @see no
*/
bool isDigit(std::string issue)
{
int len = issue.size();
for (int i = 0; i < len; ++i)
{
if (issue[i] >= '0' && issue[i] <= '9')
{
return true;
}
}
return false;
}
/**
* @brief 检查日志规则。
* 函数功能详述:
* @param
* @return
* @note no
* @warning no
* @see no
*/
bool checkSvnLog(const std::string& commitLogs)
{
std::string subLogs;
std::vector<std::string> fields;
splitString(commitLogs, fields, "\r\n");
int flag = 10; //至少需要10行内容,才能匹配规则
int cnts = fields.size();
if (cnts < flag)
{
OutputDebugStringA("cnts < flag");
return false;
}
//第一项 修改内容检查
if (std::string::npos == fields[0].find("修改内容"))
{
OutputDebugStringA("缺少【修改内容】");
return false;
}
if (fields[1].size() < 20)
{
OutputDebugStringA("【修改内容】字数不够");
return false;
}
//第二项 修改原因检查
if (std::string::npos == fields[2].find("修改原因"))
{
OutputDebugStringA("缺少【修改原因】");
return false;
}
if (fields[3].size() < 20)
{
OutputDebugStringA("【修改原因】字数不够");
return false;
}
//第三项 影响范围检查
if (std::string::npos == fields[4].find("影响范围"))
{
OutputDebugStringA("缺少【影响范围】");
return false;
}
if (fields[5].size() < 10)
{
OutputDebugStringA("【影响范围】字数不够");
return false;
}
/*if (fields[1] == fields[3] ||
fields[2] == fields[3] ||
fields[1] == fields[2])
{
OutputDebugStringA("【修改内容】【修改原因】【影响范围】三处内容不能一样");
return false;
}*/
//第四项 TODO列表检查
if (std::string::npos == fields[6].find("TODO列表"))
{
OutputDebugStringA("缺少【TODO列表】");
return false;
}
//第五项 问题列表检查
if (std::string::npos == fields[8].find("问题列表"))
{
OutputDebugStringA("缺少【问题列表】");
return false;
}
if (std::string::npos == fields[9].find("fixes #") &&
std::string::npos == fields[9].find("refs #"))
{
OutputDebugStringA("【问题列表】内容不对");
return false;
}
std::string issueId = fields[9].substr(fields[9].find("#"));
if (!isDigit(issueId))
{
OutputDebugStringA("问题号为非数字");
return false;
}
return true;
}
int main(int argc, char** argv)
{
if (argc < 3)
{
return 0;
}
std::string svnlookpath = "";
getSvnBinPath(svnlookpath);
std::string cmd = "\"" + svnlookpath + "svnlook.exe\" log \"" + argv[1] + "\" -t " + argv[2];
std::string commitLogs = "";
getSvnLog(cmd.c_str(), commitLogs);
if (!checkSvnLog(commitLogs))
{
std::cerr << commitfmtlogs;
return 1;
}
return 0;
}