修改vendored代码的正确方式:Golang开发中的最佳实践

修改vendored代码的正确方式:Golang开发中的最佳实践 我有一个使用 google/gopacket 的项目(该项目已通过 vendor 方式引入)。 我们需要对 gopacket 进行一些小的修改,因此我们修改了 vendor 中的代码,以便让程序正常工作。

我猜想修改 vendor 中的代码可能不是一个好主意? 是否有大家通常采用的最佳实践?

我知道提交拉取请求可能是理想的解决方案,但我想了解在无法提交拉取请求时应该怎么做。

2 回复

您可以修改供应商代码,但工具可能会覆盖您的更改。在这种情况下,我的做法是使用代码库的本地副本和替换指令,至少用于开发目的。如果需要长期使用,我会分叉该项目,并在上游考虑合并请求时,使用替换指令指向我的分叉版本。

更多关于修改vendored代码的正确方式:Golang开发中的最佳实践的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


修改vendor目录中的代码确实不是推荐的做法,这会导致代码库与上游版本脱节,给未来的更新和维护带来困难。以下是几种更合适的替代方案:

1. 使用本地replace指令(推荐)

在go.mod文件中使用replace指令,将依赖重定向到本地修改后的副本。这样可以保持vendor目录的原始状态。

module example.com/myproject

go 1.21

require github.com/google/gopacket v1.1.19

replace github.com/google/gopacket => ../my-gopacket-fork

然后在../my-gopacket-fork目录中维护你的修改版本,这个目录可以是原仓库的一个fork。

2. 创建私有分支并引用

将修改提交到自己的代码仓库分支,然后通过git引用该分支。

module example.com/myproject

go 1.21

require github.com/google/gopacket v1.1.20

replace github.com/google/gopacket => github.com/yourusername/gopacket v1.1.20-myfix

或者直接引用特定提交:

replace github.com/google/gopacket => github.com/yourusername/gopacket v0.0.0-20240101000000-abcdef123456

3. 使用patch文件

如果修改很小,可以考虑使用patch文件。虽然Go没有内置patch支持,但可以通过构建脚本实现:

# 应用patch的示例脚本
cd vendor/github.com/google/gopacket
patch -p1 < ../../patches/gopacket-fix.patch

然后在构建流程中加入这个步骤。

4. 包装模式

创建本地包装类型,而不是修改原始代码:

package mygopacket

import (
    "github.com/google/gopacket"
    "github.com/google/gopacket/layers"
)

type MyPacketSource struct {
    gopacket.PacketSource
}

func (m *MyPacketSource) MyCustomMethod() {
    // 实现你的自定义逻辑
    // 可以组合使用原始包的功能
}

// 或者通过嵌入和重写方法
type MyDecoder struct {
    layers.Decoder
}

func (d *MyDecoder) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
    // 先进行自定义处理
    if customCondition {
        // 自定义逻辑
    }
    // 调用原始方法
    return d.Decoder.DecodeFromBytes(data, df)
}

5. 维护独立的vendor分支

如果你必须修改vendor代码,可以创建一个独立的分支来管理这些修改:

# 创建专门的分支管理vendor修改
git checkout -b vendor/gopacket-modifications
# 修改vendor代码
# 提交修改
git add vendor/github.com/google/gopacket
git commit -m "modify gopacket for specific needs"

这样至少可以将修改隔离在特定分支中。

实际示例

假设你需要修改gopacket的某个解码函数:

// 原始vendor代码(不要直接修改)
// vendor/github.com/google/gopacket/layers/tcp.go

// 更好的方式:创建本地包装
package network

import (
    "github.com/google/gopacket"
    "github.com/google/gopacket/layers"
)

type CustomTCP struct {
    layers.TCP
}

func (t *CustomTCP) CustomDecode() error {
    // 在这里实现你的自定义解码逻辑
    // 可以调用原始方法,也可以完全重写
    if t.SrcPort == 8080 {
        // 特殊处理逻辑
    }
    return nil
}

// 在项目中使用自定义类型
func processPacket(data []byte) {
    packet := gopacket.NewPacket(data, layers.LayerTypeTCP, gopacket.Default)
    if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
        tcp, _ := tcpLayer.(*layers.TCP)
        customTCP := &CustomTCP{TCP: *tcp}
        customTCP.CustomDecode()
    }
}

这些方法都能在保持上游依赖可更新的同时,实现你的定制需求。replace指令方案是目前最常用且维护性最好的做法。

回到顶部