查看原文
其他

【第2623期】如何在 Monorepo 通过 Git 的 Sparse Checkout 取得部分 Repo 內容

Will 保哥 前端早读课 2022-07-31

前言

上周看到的一篇,应该在实际中是有实际场景的。再次感叹官方文档的重要性,遇到问题先看看官方。今日前端早读课文章由@Will 保哥授权分享。

正文从这开始~~

我们现在有越来越多的项目都是前后端分离的架构,所以在一个 Git Repo 里面同时放前后端代码是很常见的情况。不过,问题来了,如果前后端分离的很干净,若是前端人员不想要看到“后端”的原始代码,那 Git 有办法做到“部分取出”的功能吗?是的,还真的有!这篇文章我就来说说这个好用的功能。

简介 Sparse checkouts 功能

Sparse checkouts 是一个从 Git 2.25 才开始支持的功能,主要用途就是帮助你取得一个 Repo 的部分内容,大幅减少本机硬盘空间的占用,也可以帮助你更加专注在当前的开发项目。这样的机制尤其对目前 Monorepo 正夯的时候,提供一个绝佳的解决方案。

英文的 Sparse 是一个形容词,表示“稀疏的”、“零落的”的意思,代表你仅会从 Repo 取出“零星的”档案!我们从 Git 官网的 git-sparse-checkout 文件可以看到他的定义:

Reduce your working tree to a subset of tracked files

基本使用方式

而 Sparse checkouts 的使用方式,其实也就是两个步骤而已:

1、先通过 git clone 取得一个 Repo 并启用 Sparse checkouts 功能

这个动作是在复制远程储存库内容时,直接启用 Sparse checkouts 功能并套用默认样式清单,默认只会取出根目录下的文件:

git clone --filter=blob:none --sparse https://github.com/doggy8088/MonorepoAspNetCoreWithAngular.git
cd MonorepoAspNetCoreWithAngular

注意: 这个命令依然会下载完整的 Git Repo 内容,只是默认不会取出所有文件到工作目录而已!

2、启用圆锥模式 (Cone mode)

目前 Git 的 Sparse checkouts 支持两种模式,默认为 非圆锥模式 (non-cone mode),但建议改用 圆锥模式 (cone mode) 效能比较好!

git sparse-checkout init --cone

3、接着你可以通过 git sparse-checkout 明确指定你想要取得的文件夹名称

这个命令会指定取得指定文件夹下的所有文件,但在圆锥模式下,默认会包含根目录下的所有文件喔!

git sparse-checkout set "MonorepoAspNetCoreWithAngular/ClientApp"
TIPS: 快速下载超大 Repo 的小技巧

你可以在 git clone 的时候套用 --depth 1 命令,仅取得最近 1 个版本的相关 Blob 物件,这样就可以大幅缩短 git clone 的时间。我以 ASP.NET Core 原始码项目为例,正常的 git clone 在高速网路下也要十几秒才能下载完毕,但通过以下命令就只要 2 秒就可以复制完毕:

git clone --depth 1 --filter=blob:none --sparse https://github.com/dotnet/aspnetcore.git
cd aspnetcore

注意: 使用 --sparse 默认就只会取出 (Checkout) 根目录下的所有文件而已,但沒有任何「子文件夹」喔。

然而,你若只想取回 /docs 文件夹的話,就只要用以下命令即可立即下载这个文件夹下的用到的 Blob 物件,速度極快:

git sparse-checkout set "/docs"

圆锥模式简介

Git 的 Sparse checkouts 支持两种模式, 非圆锥模式 (non-cone mode) 与 圆锥模式 (cone mode),主要差別在于解析 路径样式清单 (Pattern Set) 的格式不同!

事实上,你使用 git sparse-checkout 所做的任何操作,都会记录在一个 .git/info/sparse-checkout 设置文件中,這是一个文本文件 (Text file),內容跟 .gitignore 的格式有点相似,但你要特別注意在不同的模式下,其內容所代表的意义是不同的!

非圆锥模式 (non-cone mode)

先说,虽然这是 Git 的默认值,不过不建议使用,主要是效能考量!🔥

在 非圆锥模式 下,这个 .git/info/sparse-checkout 设置文件的内容会采用 FULL PATTERN SET 语法,其内容格式与 .gitignore 完全一样。

那什么是 FULL PATTERN SET 呢?简单来说,就是这些 Patterns 是对整个 Repo 的文件路径进行比对,所以你可以用“正向比对”,也可以用 ! 进行“反向比对”,设置上比较灵活。不过,这层灵活性的唯一的缺点就是效能较差,因为当 Git 在做 Checkout 动作时,每一个文件在取出时,每一条规则都要判断,所以复杂度是 O(M*N) 这么多,这意味着文件越多或规则集越多,取出(Checkout)的速度就越慢!

我举个简单的例子,以下列命令为例,该项目默认使用了非圆锥模式,然后指定了 MonorepoAspNetCoreWithAngular/ClientApp 路径。事实上,这个 MonorepoAspNetCoreWithAngular/ClientApp 所代表的意义,是指“所有目录与子目录下有包含 MonorepoAspNetCoreWithAngular/ClientApp 的路径,都在取出的范围内”。

git sparse-checkout set "MonorepoAspNetCoreWithAngular/ClientApp"

你如果设定 /MonorepoAspNetCoreWithAngular/ClientApp 路径,就代表著只有“位于根目录下的 /MonorepoAspNetCoreWithAngular/ClientApp 路径,才包含在取出范围内”。

git sparse-checkout set "/MonorepoAspNetCoreWithAngular/ClientApp"
圆锥模式 (cone mode)

在 圆锥模式 下,这个 .git/info/sparse-checkout 设置文件的内容会采用 CONE PATTERN SET 语法,这个语法只能说近似于 .gitignore 的格式,但比对上稍微再严格一些,而且有些特殊的规则。

基本上圆锥模式下的 CONE PATTERN SET 规则集有两大特性:

  • Recursive: 你所指定的路径,默认包含所有的子目录下的所有文件路径。

  • Parent: 你所指定的路径所在的目录下的所有文件也都包含在内!(有点绕口,需要思考一下)

我举个简单的例子,以下列命令为例,我将该项目启用了圆锥模式,然后指定了 MonorepoAspNetCoreWithAngular/ClientApp 路径:

git clone --filter=blob:none --sparse https://github.com/doggy8088/MonorepoAspNetCoreWithAngular.git
cd MonorepoAspNetCoreWithAngular
git sparse-checkout init --cone
git sparse-checkout set "MonorepoAspNetCoreWithAngular/ClientApp"

这个 Pattern 将会包含:

Recursive: 包含所有子目录所有路径 (MonorepoAspNetCoreWithAngular/ClientApp/**)
Parent: 包含上层的 /MonorepoAspNetCoreWithAngular/*
 与 /* 目录下的所有文件

所以我们可以说,以下圆锥模式的样式集(CONE PATTERN SET):

MonorepoAspNetCoreWithAngular/ClientApp

会等同于以下非圆锥模式的样式集(FULL PATTERN SET):

/* 根目录所有档案
!/*/
排除根目录下的所子目录 (没有排除档案)
/MonorepoAspNetCoreWithAngular/ 包含这个子目录以及底下所有路径(目录+档案)
!/MonorepoAspNetCoreWithAngular/*/ 排除这个子目录下的所有子目录 (没有排除档案)
/MonorepoAspNetCoreWithAngular/ClientApp/ 包含这个子目录以及底下所有路径(目录+档案)

在圆锥模式下,你无法排除已经包含在这些 Pattern Set 在内的所有文件。

如果你真的想要精准的包含与排除文件,只能使用非圆锥模式 (non-cone mode)!

为什么要取名叫圆锥模式?

我从 Bring your monorepo down to size with sparse-checkout 文章中撷取两张图片来说明。

假设你的 Monorepo 有以下目录结构:

若你只想取出 /client/android 目录下的源码,那么你若用圆锥模式的话,就只要用一条规则就可以取出你要的档案:

git sparse-checkout set "client/android"

你从这个文件取出的模式来看,整个文件夹结构就好像一个“圆锥体”,而取得文件的方式是从上到下以“圆锥状”的方式取出,圆锥的顶端就是项目的“根目录”,而之后每一层的档案也都需要取出,一直取到某一层之后,就会取出以下的所有档案。

我只能说取名是一种艺术,大家要多发挥想像力!😄

常用 Sparse checkouts 命令与参数

1、启用 Sparse checkouts 功能

# 启用非圆锥模式 (non-cone mode)
git sparse-checkout init

# 启用圆锥模式 (cone mode)
git sparse-checkout init --cone

2、取得 Sparse checkouts 的路径样式清单

git sparse-checkout list

注意: 如果你的 Repo 并没有启用过 Sparse checkouts,第一次使用会出现 fatal: this worktree is not sparse 错误信息。

3、设定 Sparse checkouts 的路径样式清单 (.git/info/sparse-checkout)

使用 git sparse-checkout set 命令会直接覆盖现有的路径清单:

git sparse-checkout set "/MonorepoAspNetCoreWithAngular/ClientApp"
git sparse-checkout list

执行完 git sparse-checkout set 命令之后,你的工作目录下所有文件就会立即反应出结果,执行前请先确认拥有一个干净的工作目录!

你可以使用以下命令调整 Sparse checkouts 回覆到默认值:

git sparse-checkout set ""
git sparse-checkout list

无论你使用哪种 Sparse 模式,执行完之后预设都只会剩根目录下的档案!

4、一次设定多组 Sparse checkouts 的路径样式清单

设定多组 Sparse checkouts 路径样式时,套用顺序很重要!

git sparse-checkout set "!/*" "/MonorepoAspNetCoreWithAngular/ClientApp" ".git*"
git sparse-checkout list

此范例仅适用于非圆锥模式!(FULL PATTERN SET)

5、加入新路径到 Sparse checkouts 的路径样式清单

git sparse-checkout set "!/*" "/MonorepoAspNetCoreWithAngular/ClientApp"
git sparse-checkout add ".git*"
git sparse-checkout list

此范例仅适用于非圆锥模式!(FULL PATTERN SET)

6、重新套用 Sparse checkouts 的路径样式清单

这个命令仅用于你手动调整 .git/info/sparse-checkout 设定档内容之后执行。

git sparse-checkout reapply

7、停用 Sparse checkouts 功能

这个命令会将所有工作目录中的档案完整取出:

git sparse-checkout disable

注意: 此命令不会更动 .git/info/sparse-checkout 设定档中的路径样式清单。

8、将 Sparse checkouts 功能切换至圆锥模式 (cone mode)

git sparse-checkout disable
git sparse-checkout init --cone
#git sparse-checkout set
git sparse-checkout list

由于圆锥模式与非圆锥模式的 Patterns 并不相容,切换过程记得要重新检视过!

将 Sparse checkouts 功能切换至非圆锥模式 (non-cone mode)

git sparse-checkout disable
git sparse-checkout init
#git sparse-checkout set
git sparse-checkout list

由于圆锥模式与非圆锥模式的 Patterns 并不相容,切换过程记得要重新检视过!

相关连结

  • Git - git-sparse-checkout Documentation

  • Bring your monorepo down to size with sparse-checkout | The GitHub Blog

  • Highlights from Git 2.25 | The GitHub Blog

  • Partial clone | GitLab

  • Monorepo vs. Polyrepo

关于本文
作者:@Will 保哥
原文:https://blog.miniasp.com/post/2022/05/17/Down-size-your-Monorepo-with-Git-Sparse-checkouts

相关阅读,欢迎自荐投稿,前端早读课等你来。


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存