文章目录
- git,gitea - tool - creat Gitea Empty Rep Dir
- 概述
- 笔记
- 编译输出
- 使用方式
- 生成后的子库目录
- 使用说明
- 用写好的bat来建立gitea库的步骤
- step1
- step2
- step3
- step4
- step5
- step6
- other
- 工具源码
- END
git,gitea - tool - creat Gitea Empty Rep Dir
概述
现在想采用小库的方式来使用gitea.
这样就涉及到开小库时,要用gitea命令行来做新的小库的目录。
以前做了实验,这样的脚本已经有了。
但是以这些脚本为模板,建立新的gitea库的目录时,需要改多个脚本中的绝对路径,很繁琐,容易错。
如果用git bash, 写一个脚本(e.g. sed), 来替换.bat中的绝对路径,控制的不细致。如果想控制的细致,对bash编程也没有那么熟悉。
那就用C来手搓一个工具,花了1天,挺好使的。
笔记
编译输出
就一个 creatGiteaEmptyRepDir.exe,拷贝到自己的gitea库的根目录
使用方式
creatGiteaEmptyRepDir.exe <新的小库名称 e.g. my_test>
因为操作的是gitea小库的初始目录,为了安全,如果库目录中已经有预期的文件夹,不会再操作,会提示并退出。没啥风险。
生成完的目录在./creatGiteaEmptyRepDir_out中的子目录中(e.g. ./creatGiteaEmptyRepDir_out/rep_my_test), 由用户(me)手工拷贝到gitea库的根目录,规避风险。
生成后的子库目录
使用说明
用写好的bat来建立gitea库的步骤
step1
关掉其他gitea控制台(因为端口都是3000)
运行 a1_run_gitea_rep_console.bat, 等待服务启动完成
step2
在浏览器中运行网页 http://localhost:3000/
选择sqlite3作为数据库, 将网页拉到底部,点击"立即安装"
等待安装完成,出现登录页面为止。
step3
建立库的用户
运行 a2_create_rep_user_pwd.bat 建立库用户
运行 b1_list_rep_user.bat 查看用户是否建立成功
step4
登录gitea库网页
在网页右上角的用户头像处点击下拉列表,选择设置 => SSH密钥 => 增加密钥,将生成在 C:\Users\me\.ssh 中的公钥id_rsa.pub内容增加未SSH密钥
创建仓库(只需要给出仓库名), 网页拉到尾部,点击"创建仓库"
step5
复制仓库地址, 好像本地ssh库地址不好使,只能复制http的库地址。
step6
迁出库地址。
首次迁出时,有警告,说库是空的,这是正常的,不用理会。
正常git提交。
other
如果要修改用户名和口令, 执行 b2_modify_rep_user_pwd.bat
如果要检查库的健康度, 执行 b3_run_gitea_doctor.bat
如果要查看gitea版本,执行 b4_show_git_env_info.bat
如果要打开一个cmd窗口, 执行 c1_call_cmd_here.bat
如果要手工执行git命令, 执行 c2_env_git.bat
工具源码
VS2019 c++ console
800行代码
// @file creatGiteaEmptyRepDir.cpp
// 生成gitea用的库目录脚本, 用户自己拷贝生成的子目录(e.g. <./creatGiteaEmptyRepDir_out/sub_rep_dir>)到总的库目录(e.g. E:\my_git_rep), 防止意外操作或程序bug#include <windows.h>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <fstream>#include "resource.h"#define MY_VER "1.0.0.0"
#define DIR_OUT "creatGiteaEmptyRepDir_out"// 目录默认都要带尾巴上的\\
#define SET_GIT_GITEA_PATH "set PATH=C:\\Program Files\\Git\\bin;C:\\Program Files\\Git\\cmd;C:\\soft\\gitea;%PATH%"
#define MY_DEFAULT_GITEA_REP_ROOT_DIR "E:\\my_git_rep\\"
#define GIT_USER_NAME "name"
#define GIT_USER_PWD "password"
#define GIT_USER_EMAIL "me@x.com"void usage();
void show_error(const char* psz_reason);
bool check_param(int argc, char** argv);
bool is_dir_exist(const char* psz_path_name);
std::string get_cur_dir_path_name();
bool create_dir(const char* pszPath);
std::string Wstring2String(std::wstring strW);
bool string_find(const std::string& strA, char c);bool write_res_file(const char* psz_write_to);
bool write_ascii_file(const std::string& strFileName, const std::string& strText);bool DoTask();
bool DoTask_a1_run_gitea_rep_console_bat();// a1_run_gitea_rep_console.bat
bool DoTask_a2_create_rep_user_pwd_bat();// a2_create_rep_user_pwd.bat
bool DoTask_b1_modify_rep_user_pwd_bat();// b1_list_rep_user.bat
bool DoTask_b2_modify_rep_user_pwd_bat();// b2_modify_rep_user_pwd.bat
bool DoTask_b3_run_gitea_doctor_bat();// b3_run_gitea_doctor.bat
bool DoTask_b4_show_git_env_info_bat();// b4_show_git_env_info.bat
bool DoTask_c1_call_cmd_here_bat();// c1_call_cmd_here.bat
bool DoTask_c2_env_git_bat();// c2_env_git.bat
bool DoTask_Readme_md(); // readme.md(在资源中, BIN_RES/IDR_BIN_RES_README_MD)std::string g_new_sub_rep_name;
std::string g_cur_dir;
std::string g_dir_out; // 当前目录下的输出目录
std::string g_dir_out_obj; // 在当前输出目录中的目标目录, 需要用户手工拷贝到总库目录下 e.g. debug/out/rep_my_test
std::string g_dir_out_obj_rep; // 在当前输出目录中的目标目录中的库目录中的实际库目录 e.g. debug/out/rep_my_test/my_test
std::string g_dir_obj; // 要拷贝到的总目录下的子库目录
std::string g_dir_obj_rep; // 要拷贝到的总目录下的子库目录中的gitea库目录int main(int argc, char** argv)
{do {if (!check_param(argc, argv)){usage();break;}if (!DoTask()){break;}printf("do task ok\r\n");} while (false);printf("\r\n");printf("END\r\n");system("pause");return 0;
}bool DoTask()
{bool b_rc = false;do {if (!DoTask_a1_run_gitea_rep_console_bat()){break;}if (!DoTask_a2_create_rep_user_pwd_bat()){break;}if (!DoTask_b1_modify_rep_user_pwd_bat()){break;}if (!DoTask_b2_modify_rep_user_pwd_bat()){break;}if (!DoTask_b3_run_gitea_doctor_bat()){break;}if (!DoTask_b4_show_git_env_info_bat()){break;}if (!DoTask_c1_call_cmd_here_bat()){break;}if (!DoTask_c2_env_git_bat()){break;}if (!DoTask_Readme_md()){break;}b_rc = true;} while (false);return b_rc;
}bool DoTask_a1_run_gitea_rep_console_bat()
{// a1_run_gitea_rep_console.batbool b_rc = false;std::string strFileName;std::string strText;const char* pszShortFileName = "a1_run_gitea_rep_console.bat";do {strFileName = g_dir_out_obj;strFileName += pszShortFileName;std::stringstream ss;// @echo offss << "@echo off\r\n";// rem a1_run_gitea_rep_console.batss << "rem " << pszShortFileName << "\r\n";// set PATH=C:\\Program Files\\Git\\bin;C:\\Program Files\\Git\\cmd;C:\\soft\\gitea;%PATH%ss << SET_GIT_GITEA_PATH << "\r\n";// call gitea --work - path E : \my_git_rep\rep_my_template\my_templatess << "call gitea --work-path ";ss << g_dir_obj_rep << "\r\n";strText = ss.str();if (!write_ascii_file(strFileName, strText)){break;}b_rc = true;} while (false);return b_rc;
}bool DoTask_a2_create_rep_user_pwd_bat()
{// a2_create_rep_user_pwd.batbool b_rc = false;std::string strFileName;std::string strText;const char* pszShortFileName = "a2_create_rep_user_pwd.bat";do {strFileName = g_dir_out_obj;strFileName += pszShortFileName;std::stringstream ss;// @echo offss << "@echo off\r\n";// rem a2_create_rep_user_pwd.batss << "rem " << pszShortFileName << "\r\n";// set PATH = C:\Program Files\Git\bin; C:\Program Files\Git\cmd; C:\soft\gitea; % PATH%ss << SET_GIT_GITEA_PATH << "\r\n";// call gitea --work - path E : \my_git_rep\rep_my_template\my_templatess << "call gitea --work-path ";ss << g_dir_obj_rep << " ";// admin user create --admin --username name --password pwd --email me@x.com --access-tokenss << "admin user create --admin --username " << GIT_USER_NAME << " ";ss << "--password " << GIT_USER_PWD << " ";ss << "--email " << GIT_USER_EMAIL << " --access-token\r\n";// PAUSEss << "PAUSE" << "\r\n";strText = ss.str();if (!write_ascii_file(strFileName, strText)){break;}b_rc = true;} while (false);return b_rc;
}bool DoTask_b1_modify_rep_user_pwd_bat()
{// b1_list_rep_user.batbool b_rc = false;std::string strFileName;std::string strText;const char* pszShortFileName = "b1_list_rep_user.bat";do {strFileName = g_dir_out_obj;strFileName += pszShortFileName;std::stringstream ss;//@echo offss << "@echo off" << "\r\n";// rem b1_list_rep_user.batss << "rem b1_list_rep_user.bat" << "\r\n";// set PATH = C:\Program Files\Git\bin; C:\Program Files\Git\cmd; C:\soft\gitea; % PATH%ss << SET_GIT_GITEA_PATH << "\r\n";// call gitea --work - path E : \my_git_rep\rep_my_tools\my_tools admin user listss << "call gitea --work-path " << g_dir_obj_rep << " admin user list" << "\r\n";// PAUSEss << "PAUSE" << "\r\n";strText = ss.str();if (!write_ascii_file(strFileName, strText)){break;}b_rc = true;} while (false);return b_rc;
}bool DoTask_b2_modify_rep_user_pwd_bat()
{// b2_modify_rep_user_pwd.batbool b_rc = false;std::string strFileName;std::string strText;const char* pszShortFileName = "b2_modify_rep_user_pwd.bat";do {strFileName = g_dir_out_obj;strFileName += pszShortFileName;std::stringstream ss;//@echo offss << "@echo off" << "\r\n";// rem b1_modify_rep_user_pwd.batss << "rem " << pszShortFileName << "\r\n";// set PATH = C:\Program Files\Git\bin; C:\Program Files\Git\cmd; C:\soft\gitea; % PATH%ss << SET_GIT_GITEA_PATH << "\r\n";// call gitea --work - path E : \my_git_rep\rep_my_template\my_template admin user change-password -u name -p pwd11111// 修改密码时, 密码的最小长度要为8ss << "call gitea --work-path " << g_dir_obj_rep << " ";ss << "admin user change-password -u " << GIT_USER_NAME << " ";ss << "-p " << " " << GIT_USER_PWD << "\r\n";// PAUSEss << "PAUSE" << "\r\n";strText = ss.str();if (!write_ascii_file(strFileName, strText)){break;}b_rc = true;} while (false);return b_rc;
}bool DoTask_b3_run_gitea_doctor_bat()
{// b3_run_gitea_doctor.batbool b_rc = false;std::string strFileName;std::string strText;const char* pszShortFileName = "b3_run_gitea_doctor.bat";do {strFileName = g_dir_out_obj;strFileName += pszShortFileName;std::stringstream ss;//@echo offss << "@echo off" << "\r\n";// rem b2_run_gitea_doctor.batss << "rem " << pszShortFileName << "\r\n";// set PATH = C:\Program Files\Git\bin; C:\Program Files\Git\cmd; C:\soft\gitea; % PATH%ss << SET_GIT_GITEA_PATH << "\r\n";// echo next, list gitea doctor runingss << "echo next, list gitea doctor runing" << "\r\n";// call gitea --work - path E : \my_git_rep\rep_my_template\my_template doctor checkss << "call gitea --work-path " << g_dir_obj_rep << " ";ss << "doctor check" << "\r\n";// echo ENDss << "echo END" << "\r\n";// PAUSEss << "PAUSE" << "\r\n";strText = ss.str();if (!write_ascii_file(strFileName, strText)){break;}b_rc = true;} while (false);return b_rc;
}bool DoTask_b4_show_git_env_info_bat()
{// b4_show_git_env_info.batbool b_rc = false;std::string strFileName;std::string strText;const char* pszShortFileName = "b4_show_git_env_info.bat";do {strFileName = g_dir_out_obj;strFileName += pszShortFileName;std::stringstream ss;//@echo offss << "@echo off" << "\r\n";// rem b4_show_git_env_info.batss << "rem " << pszShortFileName << "\r\n";// set PATH = C:\Program Files\Git\bin; C:\Program Files\Git\cmd; C:\soft\gitea; % PATH%ss << SET_GIT_GITEA_PATH << "\r\n";// gitea --versionss << "gitea --version" << "\r\n";// PAUSEss << "PAUSE" << "\r\n";strText = ss.str();if (!write_ascii_file(strFileName, strText)){break;}b_rc = true;} while (false);return b_rc;
}bool DoTask_c1_call_cmd_here_bat()
{// c1_call_cmd_here.batbool b_rc = false;std::string strFileName;std::string strText;const char* pszShortFileName = "c1_call_cmd_here.bat";do {strFileName = g_dir_out_obj;strFileName += pszShortFileName;std::stringstream ss;//@echo offss << "@echo off" << "\r\n";// rem c1_call_cmd_here.batss << "rem " << pszShortFileName << "\r\n";// call cmd.exess << "call cmd.exe" << "\r\n";// PAUSEss << "PAUSE" << "\r\n";strText = ss.str();if (!write_ascii_file(strFileName, strText)){break;}b_rc = true;} while (false);return b_rc;
}bool DoTask_c2_env_git_bat()
{// c2_env_git.batbool b_rc = false;std::string strFileName;std::string strText;const char* pszShortFileName = "c2_env_git.bat";do {strFileName = g_dir_out_obj;strFileName += pszShortFileName;std::stringstream ss;//@echo offss << "@echo off" << "\r\n";// rem c2_env_git.batss << "rem " << pszShortFileName << "\r\n";// set PATH = C:\Program Files\Git\bin; C:\Program Files\Git\cmd; C:\soft\gitea; % PATH%ss << SET_GIT_GITEA_PATH << "\r\n";// call cmd.exess << "call cmd.exe" << "\r\n";strText = ss.str();if (!write_ascii_file(strFileName, strText)){break;}b_rc = true;} while (false);return b_rc;
}bool DoTask_Readme_md()
{// readme.md(在资源中, BIN_RES/IDR_BIN_RES_README_MD)bool b_rc = false;std::string strFileName;std::string strText;const char* pszShortFileName = "readme.md";do {// 将嵌入资源中的readme.md写入输出文件夹中的库目录中strFileName = g_dir_out_obj;strFileName += pszShortFileName;if (!write_res_file(strFileName.c_str())){break;}b_rc = true;} while (false);return b_rc;
}bool write_res_file(const char* psz_write_to)
{// 定义资源标识符(根据实际资源头文件中的定义)const TCHAR* lpType = TEXT("BIN_RES"); // 自定义资源类型const int resId = IDR_BIN_RES_README_MD; // 资源IDif (NULL == psz_write_to) return false;// 获取模块句柄HMODULE hModule = GetModuleHandle(NULL);if (!hModule) return false;// 查找资源HRSRC hRes = FindResource(hModule, MAKEINTRESOURCE(resId), lpType);if (!hRes) return false;// 获取资源大小DWORD dwSize = SizeofResource(hModule, hRes);if (dwSize == 0) return false;// 加载资源HGLOBAL hGlobal = LoadResource(hModule, hRes);if (!hGlobal) return false;// 锁定资源获取指针LPVOID lpResData = LockResource(hGlobal);if (!lpResData) return false;// 创建输出文件HANDLE hFile = CreateFileA(psz_write_to, GENERIC_WRITE, 0, NULL,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);if (hFile == INVALID_HANDLE_VALUE) return false;// 写入文件DWORD dwWritten = 0;BOOL bResult = WriteFile(hFile, lpResData, dwSize, &dwWritten, NULL);// 清理资源CloseHandle(hFile);FreeResource(hGlobal);return (bResult && (dwWritten == dwSize));
}bool check_param(int argc, char** argv)
{bool b_rc = false;std::string str_msg;char* psz = NULL;do {if (2 != argc){break;}// g_new_sub_rep_namepsz = argv[1];if ((NULL == psz) || (strlen(psz) <= 0)){std::stringstream ss;ss << "参数1是子库名称, 不能为空";str_msg = ss.str();show_error(str_msg.c_str());break;}g_new_sub_rep_name = psz;if (string_find(g_new_sub_rep_name, '/') || string_find(g_new_sub_rep_name, '\\')){std::stringstream ss;ss << "新建的子库名称不能包含路径字符";str_msg = ss.str();show_error(str_msg.c_str());break;}g_cur_dir = get_cur_dir_path_name();if (!is_dir_exist(g_cur_dir.c_str())){break;}// param1 = for_testg_dir_obj = MY_DEFAULT_GITEA_REP_ROOT_DIR;g_dir_obj += "rep_";g_dir_obj += g_new_sub_rep_name;g_dir_obj += "\\"; // E:\my_git_rep\rep_for_test\
g_dir_obj_rep = g_dir_obj;g_dir_obj_rep += g_new_sub_rep_name;g_dir_obj_rep += "\\"; // E:\my_git_rep\rep_for_test\for_test\
if (is_dir_exist(g_dir_obj.c_str())){std::stringstream ss;ss << "总库下的子目录[" << g_dir_obj << "]已经存在, 为了安全, 请重新给定子库名称";str_msg = ss.str();show_error(str_msg.c_str());break;}g_dir_out = g_cur_dir;g_dir_out += DIR_OUT;g_dir_out += "\\";// 如果输出目录不存在,就建立目录if (!is_dir_exist(g_dir_out.c_str())){// 建立输出目录if (!create_dir(g_dir_out.c_str())){std::stringstream ss;ss << "目录[" << g_dir_out << "]建立失败";str_msg = ss.str();show_error(str_msg.c_str());break;}}// 建立输出目录中的子库目录g_dir_out_obj = g_dir_out;g_dir_out_obj += "rep_";g_dir_out_obj += g_new_sub_rep_name;g_dir_out_obj += "\\";if (!is_dir_exist(g_dir_out_obj.c_str())){// 建立输出目录if (!create_dir(g_dir_out_obj.c_str())){std::stringstream ss;ss << "目录[" << g_dir_out << "]建立失败";str_msg = ss.str();show_error(str_msg.c_str());break;}}else {// 如果目录存在,就不干活了, 必须用户移走了目录,才能干活std::stringstream ss;ss << "目录[" << g_dir_out_obj << "]已经存在,请移动目录到总库目录 或者 删除输出目录中的子库目录再运行程序";str_msg = ss.str();show_error(str_msg.c_str());break;}b_rc = true;} while (false);if (!b_rc){show_error("命令行参数错误");}return b_rc;
}std::string get_cur_dir_path_name() {// 使用MAX_PATH+1确保足够缓冲区char buffer[MAX_PATH + 1] = { 0 };// 获取EXE完整路径(ANSI版本)DWORD ret = GetModuleFileNameA(nullptr, buffer, MAX_PATH);if (ret == 0 || ret == MAX_PATH) { // 失败或路径过长return "";}// 反向查找最后一个路径分隔符char* last_sep = strrchr(buffer, '\\');if (last_sep == nullptr) { // 异常情况处理return "";}// 截断到目录部分(包含结尾反斜杠)*(last_sep + 1) = '\0';// 转换为标准字符串并返回return std::string(buffer);
}bool is_dir_exist(const char* psz_path_name) {if (!psz_path_name) return false;const char* path = psz_path_name;struct _stat fileStat;if (_stat(path, &fileStat) == 0) {return (fileStat.st_mode & _S_IFDIR) != 0;}return false;
}void show_error(const char* psz_reason)
{printf("error");if ((NULL != psz_reason) && (strlen(psz_reason) > 0)){printf(" : %s\r\n", psz_reason);}else {printf("\r\n");}printf("\r\n");
}void usage()
{printf("-------- creatGiteaEmptyRepDir v%s --------\r\n", MY_VER);printf("usage :\r\n");printf("\t<THE_EXE> <gitea_rep_name>\r\n");printf("\tgitea_rep_name e.g. like my_gitea_study_rep\r\n");printf("function :\r\n");printf("\t生成gitea用的库目录脚本\r\n"); printf("\t用户自己拷贝生成的子目录(e.g. <./creatGiteaEmptyRepDir_out/sub_rep_dir>)\r\n"); printf("\t到总的库目录(e.g.E:/my_git_rep), 防止意外操作或程序bug\r\n");
}bool create_dir(const char* pszPath) {if (!pszPath || *pszPath == '\0') {SetLastError(ERROR_INVALID_PARAMETER);return false;}// 转换UTF-8到宽字符(支持中文路径)const int bufferSize = MultiByteToWideChar(CP_UTF8, 0, pszPath, -1, nullptr, 0);if (bufferSize == 0) return false;std::wstring wPath(bufferSize, L'\0');if (MultiByteToWideChar(CP_UTF8, 0, pszPath, -1, &wPath[0], bufferSize) == 0) {return false;}// 检查路径有效性DWORD attrs = GetFileAttributesW(wPath.c_str());if (attrs != INVALID_FILE_ATTRIBUTES) {if (attrs & FILE_ATTRIBUTE_DIRECTORY) {SetLastError(ERROR_ALREADY_EXISTS);return false;}SetLastError(ERROR_FILE_EXISTS);return false;}// 创建目录(含基础权限)SECURITY_ATTRIBUTES sa = { sizeof(sa), nullptr, TRUE };if (CreateDirectoryW(wPath.c_str(), &sa)) {return true;}// 错误处理(可扩展记录日志)const DWORD err = GetLastError();if (err != ERROR_PATH_NOT_FOUND) {return false;}// 尝试创建父目录(可选递归)const std::wstring parentDir = wPath.substr(0, wPath.find_last_of(L'\\'));std::string parentDirA = Wstring2String(parentDir);if (!parentDir.empty() && create_dir(parentDirA.c_str())) {return CreateDirectoryW(wPath.c_str(), &sa) != 0;}return false;
}std::string Wstring2String(std::wstring strW) {if (strW.empty()) return std::string();// 计算转换后需要的缓冲区大小int len = WideCharToMultiByte(CP_ACP, // 使用系统默认 ANSI 编码(如中文系统下为 GBK)0, // 无特殊标志strW.c_str(), // 输入宽字符串(int)strW.size(), // 输入字符串长度(不含终止符)NULL, 0, NULL, NULL // 输出缓冲区和长度置空以计算所需空间);if (len == 0) return std::string();// 直接操作 std::string 的缓冲区std::string result;result.resize(len);WideCharToMultiByte(CP_ACP, 0,strW.c_str(), (int)strW.size(),&result[0], len, // 输出到 result 的缓冲区NULL, NULL);return result;
}bool string_find(const std::string& strA, char c) {return strA.find(c) != std::string::npos;
}bool write_ascii_file(const std::string& strFileName, const std::string& strText) {std::ofstream outFile;outFile.open(strFileName, std::ios::out | std::ios::trunc); // 覆盖模式写入if (!outFile.is_open()) { // 检查文件是否成功打开return false;}outFile << strText; // 写入内容outFile.close(); // 显式关闭文件return !outFile.fail(); // 返回写入是否成功
};