跳到主要内容

· 阅读需 5 分钟
绝尘

在项目迭代过程中,随着代码量与模块复杂度不断攀升,将一个大型 Git 工程拆分为多个独立工程,能让项目结构更清晰,便于团队协作与维护。关键是要保留完整的提交历史,这就需要借助 Git 的 filter-branch 工具来实现。

一、准备工作

使用 filter-branch

git filter-branch 是 Git 内置的强大工具,无需额外安装,可以直接使用。虽然相比 filter-repo 效率稍低,但功能完整且稳定可靠。

验证可用性:

git filter-branch --help

备份原仓库

拆分前务必备份原仓库,防止操作失误导致数据丢失:

git clone --mirror 原仓库地址 备份仓库地址

二、拆分示例

假设原仓库有两个独立模块,需拆分为:

  • 工程 A:只保留 module-a/ 目录及相关历史
  • 工程 B:只保留 module-b/ 目录及相关历史

三、具体步骤

拆分工程 A

1. 克隆原仓库

git clone 原仓库地址 工程A
cd 工程A

2. 使用 filter-branch 保留指定目录

git filter-branch --prune-empty --subdirectory-filter module-a master

参数说明:

  • --subdirectory-filter module-a:将 module-a 目录作为新仓库的根目录
  • --prune-empty:删除空提交
  • master:指定要重写历史的分支

3. 清理无关分支(可选)

# 查看所有分支
git branch -a

# 删除不需要的分支
git branch -D 无关分支名
git branch -dr origin/无关分支名

4. 提交到新仓库

# 关联新仓库
git remote add origin 工程A的新仓库地址

# 推送所有分支和标签
git push -u origin --all
git push --tags

拆分工程 B

按照相同步骤操作:

# 重新克隆原仓库
git clone 原仓库地址 工程B
cd 工程B

# 只保留 module-b 目录
git filter-branch --prune-empty --subdirectory-filter module-b master

# 关联工程 B 的远程仓库并推送
git remote add origin 工程B的新仓库地址
git push -u origin --all
git push --tags

四、重要说明

历史完整性

filter-branch 会重写提交历史,只保留与指定目录相关的提交(包括修改、删除、重命名等操作),其他提交会被过滤,最终历史记录"干净"且相关。

处理大型仓库

若仓库体积大(含大量大文件),可先清理超大文件:

# 使用 filter-branch 清理大文件
git filter-branch --tree-filter 'rm -f large_file.zip' HEAD

高级用法

filter-branch 提供了多种过滤方式:

按文件大小过滤:

git filter-branch --tree-filter 'find . -size +10M -delete' HEAD

按文件类型过滤:

git filter-branch --tree-filter 'rm -rf *.log' HEAD

保留多个目录:

git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch module-c module-d' HEAD

五、高级用法

复杂目录结构拆分

如果需要拆分更复杂的目录结构,可使用以下方法:

使用 tree-filter 进行复杂操作:

# 只保留特定文件类型
git filter-branch --tree-filter 'find . -name "*.js" -o -name "*.ts" | xargs rm -f' HEAD

# 排除特定目录
git filter-branch --tree-filter 'rm -rf temp/ build/ node_modules/' HEAD

# 重命名目录结构
git filter-branch --tree-filter 'mkdir -p new_structure && mv module-a/* new_structure/' HEAD

使用 index-filter 提高性能:

# 只保留指定目录(性能更好)
git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch module-b module-c' HEAD

通过以上步骤,两个新工程会分别保留原仓库中与自身模块相关的完整提交历史,且相互独立。这样既保持了代码的连续性,又实现了模块的独立管理。

· 阅读需 3 分钟
绝尘

场景

在 Go 程序中设置并注入版本号是一种常见的做法,借助 -ldflags 参数,可以在编译时动态传递版本信息,使得生成的可执行文件包含明确的版本标识。这种方法可以帮助开发者轻松管理版本发布,并确保每个构建都带有唯一的版本号,方便后续调试、部署及追踪问题。


1. 在 main 包中设置版本号

示例代码

package main

import (
"flag"
"fmt"
"os"
)

var version string // 定义版本变量

func main() {
// 定义命令行参数
versionFlag := flag.Bool("version", false, "显示版本号")
vFlag := flag.Bool("v", false, "显示版本号")

// 解析命令行参数
flag.Parse()

// 检查版本参数并输出
if *versionFlag || *vFlag {
fmt.Printf("版本号: %s\n", version)
os.Exit(0) // 输出后退出
}

// 主程序逻辑
fmt.Println("运行主程序...")
}

打包命令

在打包时使用以下命令注入版本号:

go build -ldflags "-X 'main.version=$(date +%Y.%m.%d-%H.%M.%S)'" -o your_program_name.exe

查看版本号

your_program_name.exe -version // 显示版本号

2. 在模块中设置版本号

monitor

package monitor

var Version string // 定义版本变量

修改 main.go

package main

import (
"flag"
"fmt"
"os"
"your_project/monitor" // 修改为实际路径
)

func main() {
// 定义命令行参数
versionFlag := flag.Bool("version", false, "显示版本号")
vFlag := flag.Bool("v", false, "显示版本号")

// 解析命令行参数
flag.Parse()

// 检查版本参数并输出
if *versionFlag || *vFlag {
fmt.Printf("监控版本: %s\n", monitor.Version)
os.Exit(0) // 输出后退出
}

// 主程序逻辑
fmt.Println("运行主程序...")
}

打包命令

在打包时使用以下命令注入版本号:

go build -ldflags "-X 'your_project/monitor.Version=$(date +%Y.%m.%d-%H.%M.%S)'" -o your_program_name.exe

总结

以上示例展示了如何在 main 包和模块中设置并注入版本号,以便在打包时动态注入版本号;

同样的也可以按照上面方法注入其他需要在打包时动态注入的其他变量。

· 阅读需 2 分钟
绝尘

假设我们有一个可执行程序 app.sh(类比 Windows 下的 app.exe),我们想用 systemd 进行管理,并保证其在任何情况下都能够保活。下面是具体步骤:

  1. 编写 systemd 服务管理配置文件 /opt/app/app.service。 在 [Unit] 段落中编写一个描述性语句,让人们了解你正在管理什么。在 [Service] 段落中定义服务的各个参数:

    • Type=forking 表示当该服务启动时,systemd 假定它会再派生出一个子进程(即父子进程)。这是最常见的 service 类型。
    • ExecStart= 定义将要启动的可执行程序路径。
    • ExecReload= 定义重新加载服务时要运行的命令或脚本。
    • Restart= 当服务失败时自动重启,always 是指总是重启,其他选项为 on-failurenever
    • WantedBy=multi-user.target 将这个服务加入启动所需的目标之一。在多用户系统中,multi-user.target 是把计算机带到用户登录界面的主要目标。

    示例配置如下:

    [Unit]
    Description=app daemon

    [Service]
    Type=forking
    ExecStart=/opt/app/app.sh
    ExecReload=/opt/app/app.sh
    Restart=always

    [Install]
    WantedBy=multi-user.target
  2. 将服务管理配置文件做软链接到 /usr/lib/systemd/system/ 目录下。 通过软链接可以方便地维护服务管理配置文件。示例命令如下:

    ln -s /opt/app/app.service /usr/lib/systemd/system/app.service
  3. app.service 加入系统开机自启动。

    systemctl enable app.service
  4. 启动程序。

    systemctl start app

到此为止,原本的可执行程序 app.sh 就已经被 systemd 管理,并保证其在任何情况下都能够保活。