文章目录
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.bat
bool 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 off
ss << "@echo off\r\n";
// rem a1_run_gitea_rep_console.bat
ss << "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
ss << "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.bat
bool 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 off
ss << "@echo off\r\n";
// rem a2_create_rep_user_pwd.bat
ss << "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
ss << "call gitea --work-path ";
ss << g_dir_obj_rep << " ";
// admin user create --admin --username name --password pwd --email me@x.com --access-token
ss << "admin user create --admin --username " << GIT_USER_NAME << " ";
ss << "--password " << GIT_USER_PWD << " ";
ss << "--email " << GIT_USER_EMAIL << " --access-token\r\n";
// PAUSE
ss << "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.bat
bool 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 off
ss << "@echo off" << "\r\n";
// rem b1_list_rep_user.bat
ss << "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 list
ss << "call gitea --work-path " << g_dir_obj_rep << " admin user list" << "\r\n";
// PAUSE
ss << "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.bat
bool 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 off
ss << "@echo off" << "\r\n";
// rem b1_modify_rep_user_pwd.bat
ss << "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
// 修改密码时, 密码的最小长度要为8
ss << "call gitea --work-path " << g_dir_obj_rep << " ";
ss << "admin user change-password -u " << GIT_USER_NAME << " ";
ss << "-p " << " " << GIT_USER_PWD << "\r\n";
// PAUSE
ss << "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.bat
bool 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 off
ss << "@echo off" << "\r\n";
// rem b2_run_gitea_doctor.bat
ss << "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 runing
ss << "echo next, list gitea doctor runing" << "\r\n";
// call gitea --work - path E : \my_git_rep\rep_my_template\my_template doctor check
ss << "call gitea --work-path " << g_dir_obj_rep << " ";
ss << "doctor check" << "\r\n";
// echo END
ss << "echo END" << "\r\n";
// PAUSE
ss << "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.bat
bool 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 off
ss << "@echo off" << "\r\n";
// rem b4_show_git_env_info.bat
ss << "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 --version
ss << "gitea --version" << "\r\n";
// PAUSE
ss << "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.bat
bool 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 off
ss << "@echo off" << "\r\n";
// rem c1_call_cmd_here.bat
ss << "rem " << pszShortFileName << "\r\n";
// call cmd.exe
ss << "call cmd.exe" << "\r\n";
// PAUSE
ss << "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.bat
bool 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 off
ss << "@echo off" << "\r\n";
// rem c2_env_git.bat
ss << "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.exe
ss << "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; // 资源ID
if (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_name
psz = 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_test
g_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(); // 返回写入是否成功
};