管理依赖

当你的代码使用外部包时,这些包(以模块形式分发)就成为依赖项。随着时间的推移,你可能需要升级或替换它们。Go 提供了依赖管理工具,帮助你在引入外部依赖时确保 Go 应用程序的安全。

本主题介绍了如何执行管理代码中依赖项的任务。你可以使用 Go 工具完成大部分任务。本主题还介绍了如何执行其他一些你可能会觉得有用的依赖项相关任务。

另请参阅

  • 如果你是模块化依赖项的新手,请参阅入门教程以获取简要介绍。
  • 使用 go 命令管理依赖项有助于确保你的要求保持一致,并且你的 go.mod 文件内容有效。有关命令的参考,请参阅命令 go。你还可以通过键入 go help command-name(例如 go help mod tidy)从命令行获取帮助。
  • 你用于更改依赖项的 Go 命令会编辑你的 go.mod 文件。有关文件内容的更多信息,请参阅go.mod 文件参考
  • 让你的编辑器或 IDE 了解 Go 模块可以使管理它们的工作变得更容易。有关支持 Go 的编辑器的更多信息,请参阅编辑器插件和 IDE
  • 本主题不描述如何开发、发布和版本化供他人使用的模块。有关这方面的更多信息,请参阅开发和发布模块

使用和管理依赖项的工作流程

你可以使用 Go 工具获取和使用有用的包。在 pkg.go.dev 上,你可以搜索你可能会觉得有用的包,然后使用 go 命令将这些包导入到你自己的代码中以调用它们的函数。

以下列出了最常见的依赖管理步骤。有关每个步骤的更多信息,请参阅本主题中的各部分。

  1. pkg.go.dev定位有用的包
  2. 在你的代码中导入你想要的包
  3. 将你的代码添加到模块中以进行依赖项跟踪(如果它尚未在模块中)。请参阅启用依赖项跟踪
  4. 添加外部包作为依赖项,以便你可以管理它们。
  5. 随着时间的推移,根据需要升级或降级依赖项版本

将依赖项作为模块管理

在 Go 中,你将导入的包作为模块来管理依赖项。此过程由以下各项支持:

  • 一个用于发布模块和检索其代码的去中心化系统。开发人员从自己的存储库中提供他们的模块供其他开发人员使用,并发布带有版本号的模块。
  • 一个包搜索引擎和文档浏览器 (pkg.go.dev),你可以在其中找到模块。请参阅定位和导入有用的包
  • 一个模块版本号约定,可帮助你了解模块的稳定性和向后兼容性保证。请参阅模块版本号
  • Go 工具,让你更容易管理依赖项,包括获取模块源、升级等。有关更多信息,请参阅本主题的各部分。

定位和导入有用的包

你可以搜索 pkg.go.dev 来查找你可能会觉得有用的包。

当你找到一个你想在代码中使用的包时,找到页面顶部的包路径,然后点击“复制路径”按钮将路径复制到剪贴板。在你的代码中,将路径粘贴到 import 语句中,如以下示例所示:

import "rsc.io/quote"

你的代码导入包后,启用依赖跟踪并获取包的代码以进行编译。有关更多信息,请参阅在代码中启用依赖跟踪添加依赖

在代码中启用依赖跟踪

要跟踪和管理你添加的依赖项,首先要将你的代码放入自己的模块中。这会在你的源代码树的根目录下创建一个 go.mod 文件。你添加的依赖项将在此文件中列出。

要将你的代码添加到自己的模块中,请使用go mod init 命令。例如,从命令行中,切换到你的代码的根目录,然后运行命令,如以下示例所示:

$ go mod init example/mymodule

go mod init 命令的参数是你的模块的模块路径。如果可能,模块路径应该是你源代码的仓库位置。

如果你一开始不知道模块最终的仓库位置,请使用安全的替代品。这可能是你拥有的域名或你控制的其他名称(例如你的公司名称),以及模块名称或源目录之后的路径。有关更多信息,请参阅命名模块

当你使用 Go 工具管理依赖项时,这些工具会更新 go.mod 文件,使其保持当前依赖项列表。

当你添加依赖项时,Go 工具还会创建一个 go.sum 文件,其中包含你所依赖模块的校验和。Go 使用它来验证下载的模块文件的完整性,特别是对于在你的项目上工作的其他开发人员。

在你的仓库中包含 go.mod 和 go.sum 文件以及你的代码。

有关更多信息,请参阅go.mod 参考

命名模块

当你运行 go mod init 创建一个用于跟踪依赖项的模块时,你需要指定一个模块路径作为模块的名称。模块路径成为模块中包的导入路径前缀。请务必指定一个不会与其他模块的模块路径冲突的模块路径。

至少,模块路径只需要指示其来源的一些信息,例如公司、作者或所有者名称。但路径也可以更具描述性,说明模块是什么或做什么。

模块路径通常采用以下形式:

<prefix>/<descriptive-text>
  • 前缀通常是部分描述模块的字符串,例如描述其来源的字符串。这可能是

    • Go 工具可以找到模块源代码的存储库位置(如果你要发布模块,则必需)。

      例如,它可能是 github.com/<project-name>/

      如果你认为你可能会发布模块供他人使用,请使用此最佳实践。有关发布的更多信息,请参阅开发和发布模块

    • 你控制的名称。

      如果你不使用仓库名称,请务必选择一个你确信不会被他人使用的前缀。一个不错的选择是你的公司名称。避免使用 widgetsutilitiesapp 等常用术语。

  • 对于描述性文本,一个不错的选择是项目名称。请记住,包名承载了描述功能的大部分重要性。模块路径为这些包名创建了一个命名空间。

保留模块路径前缀

Go 保证以下字符串不会用于包名中。

  • test – 你可以使用 test 作为模块路径前缀,用于其代码旨在本地测试另一个模块中的函数的模块。

    对于作为测试一部分创建的模块,请使用 test 路径前缀。例如,你的测试本身可能会运行 go mod init test,然后以某种特定方式设置该模块,以便使用 Go 源代码分析工具进行测试。

  • example – 在某些 Go 文档中用作模块路径前缀,例如在教程中,你只是创建一个模块来跟踪依赖项。

    请注意,Go 文档也使用 example.com 来说明示例何时可能是已发布的模块。

添加依赖项

一旦你从已发布的模块导入包,你就可以使用go get 命令将该模块添加为要管理的依赖项。

该命令执行以下操作:

  • 如果需要,它会为命令行上命名的包构建所需的模块向你的 go.mod 文件添加 require 指令。require 指令跟踪你的模块所依赖的模块的最低版本。有关更多信息,请参阅go.mod 参考

  • 如果需要,它会下载模块源代码,以便你可以编译依赖它们的包。它可以从诸如 proxy.golang.org 这样的模块代理下载模块,也可以直接从版本控制仓库下载。源代码会本地缓存。

    你可以设置 Go 工具下载模块的位置。有关更多信息,请参阅指定模块代理服务器

以下描述了一些示例。

  • 要添加模块中包的所有依赖项,请运行如下命令(“.”指当前目录中的包)

    $ go get .
    
  • 要添加特定的依赖项,请将其模块路径指定为命令的参数。

    $ go get example.com/theirmodule
    

该命令还会对下载的每个模块进行身份验证。这确保了它与模块发布时没有变化。如果模块自发布以来发生了变化——例如,开发人员更改了提交内容——Go 工具将显示安全错误。此身份验证检查可以保护你免受可能被篡改的模块的影响。

获取特定的依赖项版本

你可以通过在 go get 命令中指定依赖模块的版本来获取特定版本。该命令会更新你的 go.mod 文件中的 require 指令(尽管你也可以手动更新)。

你可能想要这样做,如果:

  • 你想要获取特定预发布版本模块进行尝试。
  • 你发现当前要求的版本对你不起作用,因此你想要获取一个你知道可以依赖的版本。
  • 你想要升级或降级你已经需要的模块。

以下是使用go get 命令的示例

  • 要获取特定编号版本,请在模块路径后附加一个 @ 符号,后跟你想要的版本

    $ go get example.com/theirmodule@v1.3.4
    
  • 要获取最新版本,请在模块路径后附加 @latest

    $ go get example.com/theirmodule@latest
    

以下 go.mod 文件 require 指令示例(有关更多信息,请参阅go.mod 参考)说明了如何要求特定版本号

require example.com/theirmodule v1.3.4

发现可用更新

你可以检查你当前模块中正在使用的依赖项是否有更新版本。使用 go list 命令显示你的模块的依赖项列表,以及该模块可用的最新版本。一旦你发现可用的升级,你就可以用你的代码尝试它们,以决定是否升级到新版本。

有关 go list 命令的更多信息,请参阅go list -m

以下是几个示例。

  • 列出当前模块的所有依赖模块,以及每个模块可用的最新版本

    $ go list -m -u all
    
  • 显示特定模块可用的最新版本

    $ go list -m -u example.com/theirmodule
    

升级或降级依赖项

你可以通过使用 Go 工具发现可用版本,然后添加不同的版本作为依赖项来升级或降级依赖模块。

  1. 要发现新版本,请按照发现可用更新中的说明使用 go list 命令。

  2. 要将特定版本添加为依赖项,请按照获取特定依赖项版本中的说明使用 go get 命令。

同步代码的依赖项

你可以确保为代码的所有导入包管理依赖项,同时移除不再导入的包的依赖项。

当你在代码和依赖项上进行更改时,这会很有用,可能会创建一组受管依赖项和下载模块,这些依赖项和模块不再与代码中导入的包专门所需的一组匹配。

为了保持托管依赖项集的整洁,请使用 go mod tidy 命令。此命令使用代码中导入的包集来编辑 go.mod 文件,以添加必要但缺少的模块。它还会移除不提供任何相关包的未使用模块。

该命令除了一个标志 -v(打印有关已删除模块的信息)之外,没有其他参数。

$ go mod tidy

开发和测试未发布的模块代码

你可以指定你的代码应该使用可能未发布的依赖模块。这些模块的代码可能位于其各自的仓库中,这些仓库的一个分支中,或者在与使用它们的当前模块相同的驱动器上。

你可能希望这样做,当:

  • 你想要对外部模块的代码进行自己的更改,例如在分支和/或克隆它之后。例如,你可能想要准备对模块的修复,然后将其作为拉取请求发送给模块的开发人员。
  • 你正在构建一个新模块,但尚未发布它,因此它在 go get 命令无法访问的仓库上不可用。

需要本地目录中的模块代码

你可以指定所需模块的代码位于与需要它的代码相同的本地驱动器上。当你进行以下操作时,你可能会发现这很有用:

  • 开发你自己的独立模块并希望从当前模块进行测试。
  • 修复外部模块中的问题或添加功能,并希望从当前模块进行测试。(请注意,你也可以从你自己的仓库分支中引用外部模块。有关更多信息,请参阅从你自己的仓库分支引用外部模块代码。)

要告诉 Go 命令使用模块代码的本地副本,请在 go.mod 文件中使用 replace 指令替换 require 指令中给定的模块路径。有关指令的更多信息,请参阅go.mod 参考

在以下 go.mod 文件示例中,当前模块需要外部模块 example.com/theirmodule,使用不存在的版本号(v0.0.0-unpublished)以确保替换正常工作。然后 replace 指令将原始模块路径替换为 ../theirmodule,这是一个与当前模块目录处于同一级别的目录。

module example.com/mymodule

go 1.23.0

require example.com/theirmodule v0.0.0-unpublished

replace example.com/theirmodule v0.0.0-unpublished => ../theirmodule

设置 require/replace 对时,使用 go mod editgo get 命令以确保文件中描述的需求保持一致

$ go mod edit -replace=example.com/theirmodule@v0.0.0-unpublished=../theirmodule
$ go get example.com/theirmodule@v0.0.0-unpublished

注意:当你使用 replace 指令时,Go 工具不会对外部模块进行身份验证,如添加依赖项中所述。

有关版本号的更多信息,请参阅模块版本号

从你自己的仓库分支引用外部模块代码

当你分支了一个外部模块的仓库(例如为了修复模块代码中的问题或添加功能)时,你可以让 Go 工具使用你的分支作为模块的源。这对于从你自己的代码测试更改很有用。(请注意,你也可以在与引用它的模块位于同一本地驱动器上的目录中引用模块代码。有关更多信息,请参阅引用本地目录中的模块代码。)

你可以通过在 go.mod 文件中使用 replace 指令将外部模块的原始模块路径替换为仓库中分支的路径来完成此操作。这指示 Go 工具在编译时使用替换路径(分支的位置),例如,同时允许你保持 import 语句与原始模块路径不变。

有关 replace 指令的更多信息,请参阅go.mod 文件参考

在以下 go.mod 文件示例中,当前模块需要外部模块 example.com/theirmodule。然后 replace 指令将原始模块路径替换为 example.com/myfork/theirmodule,这是该模块自身仓库的一个分支。

module example.com/mymodule

go 1.23.0

require example.com/theirmodule v1.2.3

replace example.com/theirmodule v1.2.3 => example.com/myfork/theirmodule v1.2.3-fixed

设置 require/replace 对时,请使用 Go 工具命令来确保文件中描述的需求保持一致。使用 go list 命令获取当前模块正在使用的版本。然后使用 go mod edit 命令用分支替换所需的模块。

$ go list -m example.com/theirmodule
example.com/theirmodule v1.2.3
$ go mod edit -replace=example.com/theirmodule@v1.2.3=example.com/myfork/theirmodule@v1.2.3-fixed

注意:当你使用 replace 指令时,Go 工具不会像添加依赖项中所述那样对外部模块进行身份验证。

有关版本号的更多信息,请参阅模块版本号

使用仓库标识符获取特定提交

你可以使用 go get 命令从其仓库中的特定提交添加未发布的模块代码。

为此,你需要使用 go get 命令,并用 @ 符号指定你想要的代码。当你使用 go get 时,该命令将向你的 go.mod 文件添加一个 require 指令,该指令要求外部模块,并使用基于提交详细信息的伪版本号。

以下示例提供了一些说明。这些示例基于其源代码位于 Git 仓库中的模块。

  • 要获取特定提交的模块,请附加 @commithash 形式

    $ go get example.com/theirmodule@4cf76c2
    
  • 要获取特定分支的模块,请附加 @branchname 形式

    $ go get example.com/theirmodule@bugfixes
    

移除依赖项

当你的代码不再使用模块中的任何包时,你可以停止将该模块作为依赖项进行跟踪。

要停止跟踪所有未使用的模块,请运行go mod tidy 命令。此命令还可能添加构建模块中包所需的缺失依赖项。

$ go mod tidy

要删除特定的依赖项,请使用go get 命令,指定模块的模块路径并附加 @none,如以下示例所示

$ go get example.com/theirmodule@none

go get 命令还会降级或移除依赖于已移除模块的其他依赖项。

工具依赖

工具依赖允许你管理用 Go 编写并在处理你的模块时使用的开发工具。例如,你可能将 stringergo generate 一起使用,或者使用特定的 linter 或格式化工具作为准备提交更改的一部分。

在 Go 1.24 及更高版本中,你可以添加工具依赖项,使用

$ go get -tool golang.org/x/tools/cmd/stringer

这将向你的 go.mod 文件添加一个 tool 指令,并确保存在必要的 require 指令。添加此指令后,你可以通过将工具导入路径的最后一个非主要版本组件传递给 go tool 来运行该工具

$ go tool stringer

如果多个工具共享最后一个路径片段,或者路径片段与 Go 发行版中附带的工具之一匹配,则你必须传递完整的包路径

$ go tool golang.org/x/tools/cmd/stringer

要查看所有当前可用工具的列表,请不带参数运行 go tool

$ go tool

你可以手动向 go.mod 添加 tool 指令,但你必须确保存在定义该工具的模块的 require 指令。添加任何缺失的 require 指令最简单的方法是运行

$ go mod tidy

满足工具依赖项所需的需求与模块图中的任何其他需求行为相同。它们参与最小版本选择并遵守 requirereplaceexclude 指令。由于模块剪枝,当你依赖一个自身具有工具依赖项的模块时,仅为满足该工具依赖项而存在的求通常不会成为你的模块的需求。

tool 元模式提供了一种同时对所有工具执行操作的方法。例如,你可以使用 go get -u tool 升级所有工具,或者使用 go install tool 将它们全部安装到 $GOBIN。

在 Go 1.24 之前的版本中,你可以通过将一个空白导入添加到模块中一个使用构建约束从构建中排除的 Go 文件中来实现类似于 tool 指令的效果。如果这样做,你就可以使用 go run 和完整的包路径来运行该工具。

指定模块代理服务器

当你使用 Go 工具处理模块时,工具默认从 proxy.golang.org(由 Google 运营的公共模块镜像)或直接从模块的仓库下载模块。你可以指定 Go 工具应改用另一个代理服务器来下载和验证模块。

如果你(或你的团队)已经设置或选择了要使用的不同模块代理服务器,你可能希望这样做。例如,有些设置模块代理服务器是为了更好地控制依赖项的使用方式。

要为 Go 工具指定另一个模块代理服务器,请将 GOPROXY 环境变量设置为一个或多个服务器的 URL。Go 工具将按你指定的顺序尝试每个 URL。默认情况下,GOPROXY 首先指定一个由 Google 运营的公共模块代理,然后直接从模块的仓库下载(如其模块路径中指定)

GOPROXY="https://proxy.golang.org,direct"

有关 GOPROXY 环境变量的更多信息,包括支持其他行为的值,请参阅go 命令参考

你可以将变量设置为其他模块代理服务器的 URL,用逗号或竖线分隔 URL。

  • 当你使用逗号时,Go 工具只有在当前 URL 返回 HTTP 404 或 410 时才会尝试列表中的下一个 URL。

    GOPROXY="https://proxy.example.com,https://proxy2.example.com"
    
  • 当你使用竖线时,Go 工具无论 HTTP 错误代码如何都会尝试列表中的下一个 URL。

    GOPROXY="https://proxy.example.com|https://proxy2.example.com"
    

Go 模块通常在版本控制服务器和公共互联网上不可用的模块代理上开发和分发。你可以设置 GOPRIVATE 环境变量来配置 go 命令,以从私有源下载和构建模块。然后 go 命令可以从私有源下载和构建模块。

GOPRIVATEGONOPROXY 环境变量可以设置为与私有且不应从任何代理请求的模块前缀匹配的全局模式列表。例如

GOPRIVATE=*.corp.example.com,*.research.example.com