使用 Copybara 在 GIT 存储库之间移动代码
Copybara是一个在 git 存储库之间自动移动源代码的工具。
你什么时候会使用这样的工具?
-
当您有一个内部存储库但想要它的开源部分时。
-
当您有多个存储库并且需要一次将代码更改传播到所有存储库时。
了解 Copybara 如何保持 GIT 存储库同步
Copybara 是一个声明性工具,您可以在其中描述源和目标存储库以及要应用于代码的任何转换。
让我们看一个简单的例子。
假设您有两个存储库:一个monorepo和一个public-repo。
monorepo/
├─ internal/
│ ├─ do-not-share.js
├─ external/
│ ├─ library.js
├─ README.md
public-repo/
├─ library.js
此时monorepo中的external文件夹和public-repo是同步的。
它们具有相同内容的相同文件 (library.js)。
虽然monorepo仅在内部可用,但公共存储库可以接收贡献。
让我们假设有一个包含README.md文件和对library.js的改进的拉取请求。
合并后,两个存储库不同步:
monorepo/
├─ internal/
│ ├─ do-not-share.js
├─ external/
│ ├─ library.js
├─ README.md
public-repo/
├─ library.js* <-- modified
├─ README.md <-- added
这两个存储库不同步。
这就是 Copybara 的用武之地。
您可以定义一种机制来将更改从一个存储库复制到另一个存储库。
我们来看一下。
sourceUrl = "ssh://git@github.com/danielepolencic/public-repo.git"
destinationUrl = "ssh://git@github.com/danielepolencic/monorepo.git"
core.workflow(
name = "default",
origin = git.origin(
url = sourceUrl,
ref = "master",
),
destination = git.destination(
url = destinationUrl,
fetch = "master",
push = "master",
),
destination_files = glob(["external/**"]),
authoring = authoring.pass_thru("Copybara <copybara@example.com>"),
transformations = [
core.move("", "external"),
],
)
您可以将此文件另存为copy.bara.sky。
什么是.sky文件?!
Copybara 使用 Starlark 来定义如何移动代码。
Starlark 是 Python 的一个子集,没有副作用(执行相同的 Starlark 两次应该会产生相同的输出)。
该文件使用名为.workflow的方法来定义转换。
-
git.origin包含源存储库的详细信息。在这种情况下,它指向外部存储库。 -
git.destination包含将接收更改的存储库的详细信息。在这种情况下,它是 monorepo。 -
destination_files是应该存储文件的位置。在这种情况下,应该将 public-repo 中的所有文件复制到 monorepo 中的external文件夹。 -
authoring是进行更改的默认作者。 -
[`transformation zwz100032 zwz100030 is a list of transformation. In this case, all files are moved to the
external文件夹。
您可以使用以下命令运行该文件:
$ java -jar bazel-bin/java/com/google/copybara/copybara_deploy.jar copy.bara.sky
INFO: Setting up LogManager
Copybara source mover (Version: Unknown version)
Task: Git Destination: Fetching: ssh://git@github.com/danielepolencic/monorepo.git refs/heads/master
ERROR: Cannot find last imported revision. Use --force if you really want to proceed with the migration use, or use '--last-rev' to override the revision.
失败了!
Copybara 使用您的 GIT 存储库上的GitOrigin-RevId标签来跟踪已迁移的更改。
由于这是您第一次运行该工具并且没有标签,因此 Copybara 失败。
您可以通过附加--force标志来强制 Copybara 重新开始。
$ java -jar bazel-bin/java/com/google/copybara/copybara_deploy.jar copy.bara.sky --force
Copybara 完成后,两个仓库的新结构如下:
monorepo/
├─ internal/
│ ├─ do-not-share.js
├─ external/
│ ├─ library.js* <-- updated
│ ├─ README.md <-- added
├─ README.md
public-repo/
├─ library.js
├─ README.md
伟大的!
现在这两个存储库是同步的。
但是,如果您不想直接将更改提交给 master 并提出 Pull Request 怎么办?
使用 Copybara 在 GitHub 上提出拉取请求
Copybara 可以使用更改创建一个不同的分支,并在 GitHub 上打开一个拉取请求。
让我们修改前面的示例,在monorepo上创建一个 Pull Request,而不是直接将更改提交给 master。
修改copy.bara.sky文件以获得这个新代码:
sourceUrl = "ssh://git@github.com/danielepolencic/public-repo.git"
destinationUrl = "ssh://git@github.com/danielepolencic/monorepo.git"
core.workflow(
name = "default",
origin = git.origin(
url = sourceUrl,
ref = "master",
),
destination = git.github_pr_destination(
url = destinationUrl,
destination_ref = "master",
pr_branch = "from_public_repo",
title = "pr from external public repo",
body = "this is a sample pull request",
integrates = [],
),
destination_files = glob(["external/**"]),
authoring = authoring.pass_thru("Copybara <copybara@example.com>"),
transformations = [
core.move("", "external"),
],
)
这次,您将git.destination方法替换为git.github_pr_destination。
新方法接受更多参数,您可以在其中指定接收更新的目标分支 (destination ref) 以及 PR 的标题 (title) 和分支的名称 (pr_branch)。
在执行迁移之前,让我们对公共 repo 做一个微小的更改;否则,Copybara 将抱怨未检测到任何更改。
monorepo/
├─ internal/
│ ├─ do-not-share.js
├─ external/
│ ├─ library.js
│ ├─ README.md
├─ README.md
public-repo/
├─ library.js
├─ README.md <-- updated
让我们运行 Copybara:
$ java -jar bazel-bin/java/com/google/copybara/copybara_deploy.jar copy.bara.sky
Task: Git Destination: Fetching: ssh://git@github.com/danielepolencic/monorepo.git refs/heads/master
Task: Git Destination: Pushing to ssh://git@github.com/danielepolencic/monorepo.git refs/heads/from_public_repo
INFO: GitHub credentials not found in ~/.git-credentials. Assuming the repository is public.
ERROR: Project not found: GitHub API call failed with code 404 The request was GET repos/danielepolencic/monorepo/pulls?per_page=100&head=danielepolencic:from_public_repo
又失败了!
到目前为止,Copybara 使用您计算机上的配置连接到 GitHub。
换句话说,如果您设置 SSH 私钥来连接到您的 GitHub 公有或私有存储库,Copybara 可以使用它们来创建提交、添加标签等。
但在提出拉取请求和 GitHub 特定功能时,Copybara 必须调用 GitHub API 才能使这些工作正常进行。
默认情况下,它会查找存储在~/.git-credentials中的凭据。
因为,在这种情况下,没有,请求失败。
您可以在此处找到有关如何添加凭据的说明。
如果你重新运行之前的命令,它应该会通过,并且会在 monorepo 上创建一个 Pull Request。
跨存储库推送和拉取更改的端到端工作流程
到目前为止,对存储库的所有更改都是单向的——我们总是将所有更改从public-repo移动到monorepo。
但是将变更从 monorepo 推送到 public-repo 呢?
我们可以扩充我们的设计,以便:
-
来自
public-repo的所有更改都作为拉取请求迁移到monorepo。 -
来自
monorepo的所有更改都作为另一个拉取请求迁移到public-repo。
为此,我们可以在copy.bara.sky文件中创建多个工作流:
sourceUrl = "ssh://git@github.com/danielepolencic/public-repo.git"
destinationUrl = "ssh://git@github.com/danielepolencic/monorepo.git"
core.workflow(
name = "pull", # <- renamed to pull
origin = git.origin(
url = sourceUrl,
ref = "master",
),
destination = git.github_pr_destination(
url = destinationUrl,
destination_ref = "master",
pr_branch = "from_public_repo",
title = "pr from external public repo",
body = "this is a sample pull request",
integrates = [],
),
destination_files = glob(["external/**"]),
authoring = authoring.pass_thru("Copybara <copybara@example.com>"),
transformations = [
core.move("", "external"),
],
)
core.workflow(
name = "push", # <- created
origin = git.origin(
url = destinationUrl,
ref = "master",
),
destination = git.github_pr_destination(
url = sourceUrl,
destination_ref = "master",
pr_branch = "from_monorepo",
title = "pr from monorepo",
body = "this is a sample pull request",
integrates = [],
),
origin_files = glob(["external/**"]), # pay attention!
authoring = authoring.pass_thru("Copybara <copybara@example.com>"),
transformations = [
core.move("external", ""),
],
)
在这个文件中,我们有两个工作流程:
-
一个
pull的工作流程,和之前的一样。 -
一个新的
push工作流程,将更改从monorepo复制到public-repo。
值得注意的是,这两个工作流程非常相似,但有一些值得注意的区别:
-
交换源和目标存储库 URL。
-
在
pull工作流程中,您使用destination_files将所有文件复制到特定文件夹中,在push工作流程中,您使用origin_files仅导出对该文件夹中文件的更改。
3、core.move方法在pull工作流中添加前缀,在push中去掉前缀。
通过这些更改,您可以使用以下命令将更改拉取和推送到两个存储库:
$ java -jar bazel-bin/java/com/google/copybara/copybara_deploy.jar copy.bara.sky push
$ java -jar bazel-bin/java/com/google/copybara/copybara_deploy.jar copy.bara.sky pull
这种工作流组合与熟悉的git pull和git push命令非常相似,但它可以跨存储库工作。
但是还有另一个方便的功能可以使该过程更加无缝。
使用 Copybara 镜像对 Pull Requests 的更改
您可以配置您的主体存储库(在我们的示例中为monorepo)以镜像另一个(外部)存储库上的拉取请求。
以下是此类工作流程的示例:
+--------------------+ +--------------------+
| | | |
| External Repo | | External PR +<---+ OSS contributor
| | | | opens a PR
| | | |
+--------^-----------+ +--------+-----------+
| |
New commits are Changes shadowed as an
pushed via copybara internal PR via copybara
| |
+--------+-----------+ +--------v-----------+
| | | |
| Internal Repo +<------------+ Internal PR |
| | CI runs | |
| | & +--------------------+
+--------------------+ Team member reviews and merges
整个过程可以通过 CI/CD 管道自动化,以便您始终拥有最新的更改。
总结
如果你不想使用 GIT 子模块但仍需要在 GIT 中管理依赖项目,你应该考虑给 Copybara 一个机会。
Copybara 是一个可靠的工具,可以在存储库之间自动进行 GIT 更改,并且可以轻松地与 GitHub 集成。
我希望你发现这个使用 Copybara 的笔记集很有用。
如果你喜欢这篇文章,你可能会喜欢我在 Twitter 上发布的线程。
附件:如何安装Copybara
Copybara 没有打包为单个二进制文件。你应该先构建它。
您应该检查存储库并使用以下命令构建 jar:
$ brew install bazelisk
$ git clone https://github.com/google/copybara
$ bazel build java/com/google/copybara:copybara_deploy.jar
您可能会遇到以下错误:
没有为类型@bazel_tools//tools/cpp:toolchain_type 找到匹配的工具链。可能 --incompatible_use_cc_configure_from_rules_cc 已经翻转,WORKSPACE 文件中没有添加默认的 C++ 工具链?有关详细信息和迁移说明,请参阅github.com/bazelbuild/bazel/issues/10134。
问题是 Bazel 和您的 M1 的版本。我再也找不到 GitHub 问题了,但修复在5.0.0中实现,在5.2.0中“丢失”。
要修复它,您可以在项目的根目录下创建一个.bazelversion文件并将5.0.0添加为内容。
如果您遇到以下错误:
获取存储库“JCommander”期间发生错误:
您应该将您的 repo 版本降级到此提交之前的版本。你可以在这里找到更多信息。
您最终可以使用以下命令运行 copybara:
$ java -jar bazel-bin/java/com/google/copybara/copybara_deploy.jar
Jun 24, 2022 10:46:51 AM com.google.copybara.Main configureLog
INFO: Setting up LogManager
Copybara source mover (Version: Unknown version)
Task: Running migrate
ERROR: Configuration file missing for 'migrate' subcommand.
ERROR: Try 'copybara help'.
在Hashnode、Twitter和Linkedin上关注 Kubesimplify。加入我们的Discord服务器与我们一起学习。
更多推荐


所有评论(0)