VisualSvn 的 Repositories 下面,每个库都有hooks目录这里存放9种hook.
我比较关心post-commit,要限制文件扩展名,采用白名单.
hooks支持批处理和EXE,比如post-commit.bat或post-commit.exe放在Repositories\code\hooks下面,在提交后,svn server会自动调用,
如果程序返回0表示成功,返回1表示失败. 可以把错误信息写到stderr,server会把错误信息推送到客户端
批处理语法难懂,喜欢用lua.所以写个exe,转调到lua中,在lua中实现主要判断逻辑.
下面是c++代码
#define CP_GBK (936) // 简体中文code page void Unicode_to_GBK(const wchar_t* in, size_t len, std::string& out) { int bufferlen = (int)::WideCharToMultiByte(CP_GBK,0,in,(int)len,NULL,0,NULL,NULL); char* pBuffer = new char[bufferlen + 4]; if ( NULL == pBuffer ) { return; } int out_len = ::WideCharToMultiByte(CP_GBK,0,in,(int)len,pBuffer,bufferlen+2,NULL,NULL); pBuffer[bufferlen] = '\0'; out.assign( pBuffer, out_len ); delete[] pBuffer; } static int getShortPathName(lua_State* L) { const char* lpFullPath = luaL_checkstring(L, 1); char strShortPath[MAX_PATH] = {0}; GetShortPathNameA(lpFullPath, strShortPath, MAX_PATH); lua_pushstring(L, strShortPath); return 1; } static int pathFindExtension(lua_State* L) { lua_pushstring(L, PathFindExtensionA(luaL_checkstring(L, 1))); return 1; } int MainImpl() { int argc = 0; LPWSTR * argv = CommandLineToArgvW(GetCommandLine(), &argc); wchar_t* lpLuaFile = StrDup(argv[0]); PathRenameExtension(lpLuaFile, L".lua") ; int ret = 0; if (PathFileExists(lpLuaFile) && argv[1] && argv[2]) { std::string strLuaFile; Unicode_to_GBK(lpLuaFile, wcslen(lpLuaFile), strLuaFile); lua_State* L = luaL_newstate(); luaL_openlibs(L); lua_pushcfunction(L, getShortPathName); lua_setglobal(L, "getShortPathName"); lua_pushcfunction(L, pathFindExtension); lua_setglobal(L, "pathFindExtension"); if (luaL_dofile(L, strLuaFile.c_str()) == 0) { lua_getglobal(L, "MainCheck"); if(!lua_isfunction(L, -1)) { fprintf(stderr, "could not find MainCheck function"); return ret; } std::string strDir, strTxn; Unicode_to_GBK(argv[1], wcslen(argv[1]), strDir); Unicode_to_GBK(argv[2], wcslen(argv[2]), strTxn); lua_pushstring(L, strDir.c_str()); lua_pushstring(L, strTxn.c_str()); if (lua_pcall(L, 2, 1, 0) == 0) { ret = lua_tointeger(L, -1); } else { fprintf(stderr, "%s", lua_tostring(L, -1)); } } lua_close(L); } LocalFree(lpLuaFile); LocalFree(argv); return ret; } int main() { return MainImpl(); } int __stdcall WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in_opt LPSTR lpCmdLine, __in int nShowCmd ) { return MainImpl(); }
下面是lua代码
local svn_look = [[C:\Program Files\VisualSVN Server\bin\svnlook.exe]] -- 通过svn_look返回提交的所有文件列表 print = function(...) log = log or io.open([[D:\Repositories\code\hooks\pre-commit.log]], "w") for k, v in ipairs(arg) do log:write(tostring(v), "\t") end log:write("\n") end -- 白名单 local allow_list = { -- c,c++ [".c"] = true, [".cpp"] = true, [".h"] = true, [".cxx"] = true, [".hpp"] = true, [".aps"] = true, [".idl"] = true, -- vs [".sln"] = true, [".vcproj"] = true, [".dsw"] = true, -- other [".html"] = true, [".htm"] = true, [".xml"] = true, [".lua"] = true, [".txt"] = true, [".bat"] = true, -- res [".ico"] = true, [".rc"] = true, -- office [".doc"] = true, [".docx"] = true, [".ppt"] = true, [".pptx"] = true, --image [".jpg"] = true, [".png"] = true, [".gif"] = true, [".bmp"] = true, --xar [".cfg"] = true, } function MainCheck(dir, txn) print("MainCheck", dir, txn) local cmd = string.format("%s changed \"%s\" -t \"%s\"", getShortPathName(svn_look), dir, txn) print(cmd) local p = io.popen(cmd) for line in p:lines() do print(line, line:sub(4), pathFindExtension(line:sub(4))) local ext = pathFindExtension(line:sub(4)) if ext:len() ~= 0 and not allow_list[ext:lower()] then io.stderr:write(ext:lower().. " 不允许提交") return 1 end end log:close() return 0 end
用法:把生成的exe,改名为9中hook的任意一种,然后lua文件与exe同名,放在hooks目录下.
有编译好的文件,依赖vc2005和vc2008的运行时.