「译文」Git subtree: Git submodule 的替代品

本文最后更新于:2022年4月27日 下午

👉️URL: https://www.atlassian.com/git/tutorials/git-subtree

✍️Author: Nicola Paolucci

📝Description:

Git subtree 让你把一个仓库嵌套在另一个子目录中。它是 Git 可以管理项目依赖关系的几种方式之一。

git 分支的图解

互联网上有很多关于为什么你不应该使用 Git submodule 的文章。虽然 submodule 在一些使用情况下很有用,但它们确实有一些缺点。

有替代品吗?答案是:有!有(至少)两个工具可以帮助跟踪你项目中的软件依赖历史,同时允许你继续使用 Git。

  • git subtree
  • Google repo

在这篇文章中,我们将看看git subtree,并说明为什么它比 git submodule 有进步–尽管不是很完美。

什么是git subtree,我为什么要使用它?

git subtree让你把一个仓库嵌套在另一个子目录中。它是 Git 项目管理项目依赖关系的几种方式之一。

图中显示了使用 Git Subtree 前后两个仓库之间的互动

为什么你可能要考虑git subtree

  • 简单工作流的管理很容易。
  • 支持老版本的 Git(甚至比 v1.5.2 更老)。
  • 子项目的代码在父级项目的克隆完成后就可以使用。
  • git subtree不需要你的仓库的用户学习任何新东西。他们可以忽略你使用 git subtree 来管理依赖关系的事实。
  • git subtree不会像 git submodule 那样添加新的元数据文件(即.gitmodule)。
  • 模块的内容可以被修改,而不需要在其他地方有单独的依赖关系的仓库副本。

缺点(但在我们看来,这些缺点基本可以接受)。

  • 你必须学习新的合并策略(即 git subtree)。
  • 为子项目向上游贡献代码稍显复杂。
  • 在提交中不混合父级项目和子项目代码的责任在于你。

如何使用 git subtree

git subtree自 2012 年 5 月起在 Git 的库存版本中可用 - v1.7.11 及以上。homebrew 软件在 OSX 上安装的版本已经将 subtree 正确地连接起来,但在某些平台上,你可能需要按照安装说明进行安装。

下面是一个使用git subtree. tracking vim 插件的典型例子。

没有 remote tracking 的快速和肮脏的方法

如果你只想剪切和粘贴几句单句,请阅读这一段。首先在指定的前缀文件夹下添加git subtree

1
git subtree add --prefix .vim/bundle/tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git main --squash

(通常的做法是不把子项目的全部历史保存在主版本库中,但如果你想保存它,只需省略 --squash 标志)。

上述命令产生了这样的输出。

1
2
3
4
5
6
7
8
9
10
git fetch https://bitbucket.org/vim-plugins-mirror/vim-surround.git main
warning: no common commits
remote: Counting objects: 338, done.
remote: Compressing objects: 100% (145/145), done.
remote: Total 338 (delta 101), reused 323 (delta 89)
Receiving objects: 100% (338/338), 71.46 KiB, done.
Resolving deltas: 100% (101/101), done.
From https://bitbucket.org/vim-plugins-mirror/vim-surround.git
* branch main -} FETCH_HEAD
Added dir '.vim/bundle/tpope-vim-surround'

正如你所看到的,这是通过将 vim-surround 版本库的全部历史压缩成一个单一的历史来记录一个合并提交。

1
2
1bda0bd [3 minutes ago] (HEAD, stree) Merge commit 'ca1f4da9f0b93346bba9a430c889a95f75dc0a83' as '.vim/bundle/tpope-vim-surround' [Nicola Paolucci]
ca1f4da [3 minutes ago] Squashed '.vim/bundle/tpope-vim-surround/' content from commit 02199ea [Nicola Paolucci]

如果过了一段时间,你想从上游仓库更新插件的代码,你可以只做一个 git subtree pull。

1
git subtree pull --prefix .vim/bundle/tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git main --squash

这是非常快速和无痛的,但命令略显冗长,难以记忆。我们可以通过添加子项目作为 remote 来使命令更短。

将子项目添加为 remote

将 subtree 作为一个 remote 添加,可以让我们以更短的形式来引用它。

1
git remote add -f tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git

现在我们可以添加 subtree(和以前一样),但现在我们可以用简短的形式来引用远程。

1
git subtree add --prefix .vim/bundle/tpope-vim-surround tpope-vim-surround main --squash

以后更新子项目的命令变成:

1
2
git fetch tpope-vim-surround main
git subtree pull --prefix .vim/bundle/tpope-vim-surround tpope-vim-surround main --squash

回馈上游

现在我们可以自由地提交我们的修正到本地工作目录中的子项目。当需要向上游项目做出贡献时,我们需要分叉该项目并将其作为另一个远程项目。

1
git remote add durdn-vim-surround ssh://git@bitbucket.org/durdn/vim-surround.git

现在我们可以像下面这样使用子树推送命令。

1
2
3
4
5
6
7
8
9
git subtree push --prefix=.vim/bundle/tpope-vim-surround/ durdn-vim-surround main
git push using: durdn-vim-surround main
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 308 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
To ssh://git@bitbucket.org/durdn/vim-surround.git
02199ea..dcacd4b dcacd4b21fe51c9b5824370b3b224c440b3470cb -} main

在这之后,我们就准备好了,我们可以向软件包的维护者发出 pull-request。

我可以不使用 git subtree 命令来做这个吗?

是的!是的,可以。git subtree与 subtree merge 策略不同。即使由于某种原因 git subtree 不可用,你仍然可以使用合并策略。以下是你如何去做的。

将依赖关系作为一个简单的 git remote 添加。

1
git remote add -f tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git

在将依赖关系的内容读入版本库之前,记录一个合并是很重要的,这样我们就可以跟踪到此为止的整个插件树历史。

1
git merge -s ours --no-commit tpope-vim-surround/main

输出为:

1
Automatic merge went well; stopped before committing as requested

然后,我们将最新的 tree-object 的内容读入插件库,进入我们的工作目录,准备提交。

1
git read-tree --prefix=.vim/bundle/tpope-vim-surround/ -u tpope-vim-surround/main

现在我们可以提交了(这将是一个合并提交,将保留我们所读的树的历史)。

1
2
git ci -m"[subtree] adding tpope-vim-surround"
[stree 779b094] [subtree] adding tpope-vim-surround

当我们想更新项目时,我们现在可以使用 git subtree 合并策略进行拉动。

1
git pull -s subtree tpope-vim-surround main

Git subtree是一个很好的选择

在使用了一段时间的 git submodule 之后,你会发现 git subtree 解决了很多 git submodule 的问题。像往常一样,对于所有 Git 的东西,要想充分利用这个功能,都有一个学习曲线。

在 Twitter 上关注我 @durdn,了解更多关于 Git 的事情和东西。如果你正在寻找一个好的工具来管理你的 Git 仓库,请查看 Atlassian Bitbucket。

更新:在发表这篇文章后,我还写了一篇 关于 Git subtree 的 power 的文章


「译文」Git subtree: Git submodule 的替代品
https://ewhisper.cn/posts/9739/
作者
东风微鸣
发布于
2022年4月27日
许可协议