☰
degit 使用指南:从 CLI 到 ESM API 再到 degit.json 动作的项目脚手架全解析
2026/9/27 9:18:29 网站建设 项目流程
  • 开发工具
  • CLI

【免费下载链接】degit

Straightforward project scaffolding

项目地址:https://gitcode.com/gh_mirrors/de/degit
点击查看免费下载

degit是一个"直接了当的项目脚手架"工具:给定user/repo,它会解析该仓库的最新提交、下载对应的 tar 快照并解压到目标目录,全程不拉取完整 git 历史,也不在你的新项目里留下模板的.git目录。本文以官方使用文档 docs/USAGE.md 为主体,结合仓库源码(src/domain/repo.ts、src/core/orchestrator.ts、src/bin.ts 等)逐层讲解 CLI 全部命令与选项、ESM 编程接口、degit.json后置动作,并说明它与git clone --depth 1的本质差异。读完本文,你将能熟练地用 degit 拉取任意公开仓库模板、按需过滤文件、使用别名与缓存,并把它嵌入到自己的 Node.js 工具链中。

快速开始

把某个 GitHub 仓库的默认分支下载到当前目录:

degit user/repo

下载到一个新文件夹:

degit user/repo my-new-project

只下载指定文件(用逗号分隔):

degit user/repo my-project --files README.md,src/index.ts

使用指定 tag、分支或 commit(#ref语法):

degit user/repo#v1.0.0

以上四条命令涵盖了 degit 最核心的使用形态:源仓库、目标目录、文件过滤、引用锁定。完整的参数语义在下面"CLI 参考"一节展开。

安装与运行环境

npm install -g degit

degit要求Node.js 20 或更高版本(见 package.json 中的engines字段)。安装完成后直接运行degit即可。

CLI 参考

基本语法

degit <src>[#ref] [<dest>] [options]
  • src:要复制的仓库,支持多种写法(见下文"支持的源")。
  • dest:解压目标目录;省略时使用当前目录。
  • options:可选参数,见下方选项表。

从源码看,参数解析由 src/bin.ts 中的parseCliArgs完成:它使用mri解析process.argv,并把短选项c/f/F/m/r/v/V分别映射为cache/force/files/mode/repo-name/verbose/version。

支持的源

degit支持 GitHub、GitLab、Bitbucket 和 Sourcehut 四种托管平台。以 src/domain/repo.ts 中的providerDomains映射为准,各平台域名分别为github.com、gitlab.com、bitbucket.org、git.sr.ht。

GitHub
degit user/repo degit github:user/repo degit https://github.com/user/repo degit git@github.com:user/repo
GitLab
degit gitlab:user/repo degit https://gitlab.com/user/repo degit git@gitlab.com:user/repo

对于自托管 GitLab 实例,使用gitlab://协议:

degit gitlab://git.example.com/user/repo

gitlab://的解析逻辑见 src/domain/repo.ts:gitlab://后的第一段被当作自定义域名(customDomain),其余部分作为项目路径。

Bitbucket
degit bitbucket:user/repo degit https://bitbucket.org/user/repo degit git@bitbucket.org:user/repo
Sourcehut
degit git.sr.ht/user/repo degit https://git.sr.ht/user/repo degit git@git.sr.ht:user/repo

指定 tag、分支或 commit

在任意源后面追加#ref:

degit user/repo#dev # 分支 degit user/repo#v1.2.3 # 发布 tag degit user/repo#1234abcd # commit 哈希

省略#ref时,degit 解析仓库的默认分支。其底层实现在 src/core/orchestrator.ts:

  • getHash()先通过 git 客户端fetchRefs()拉取远端引用列表;
  • selectRef()优先精确匹配 ref 名,若选择器长度 ≥ 8 且无法精确匹配,则退化为按 commit 哈希前缀匹配;
  • selectHead()用于HEAD解析:优先查找类型为HEAD的引用,其次回退到main/master分支,最后兜底取第一个branch类型的引用。

创建新文件夹

省略dest时,degit 解压到当前目录。目标目录必须为空,除非使用--force。使用--repo-name(短选项-r)可以按仓库名自动创建目录:

degit user/repo my-new-project degit -r user/repo

在 src/bin.ts 中可以看到dest的推导逻辑:dest = positionalDest ?? (args['repo-name'] ? parse(resolvedSrc).name : '.'),即显式传入目录优先;否则在-r时取解析后的仓库名,其余情况取当前目录.。

目录非空的检查实现在 src/operations/filesystem.ts 的checkDirIsEmpty():非空且未传force时抛出DEST_NOT_EMPTY错误,并提示"Use options.force to override"。

克隆子目录

把子目录直接拼到源路径末尾:

degit user/repo/subdirectory

也可以直接粘贴完整的 GitHub 网页 URL:

degit https://github.com/user/repo/tree/main/subdirectory

GitHub 网页 URL 中的/tree/<ref>/<subdir>(或/blob/<ref>/<subdir>)会被解析为 ref 与子目录,见 src/domain/repo.ts 的parseWebPath();GitLab 使用/-/tree/<ref>/<subdir>标记,Bitbucket 使用/src/<ref>/<subdir>,Sourcehut 使用/tree/<ref>/<subdir>。

对于GitLab 嵌套组,degit 会先尝试user/repo两段式解读,失败后把整个路径当作嵌套组项目处理。该逻辑由 src/core/orchestrator.ts 的tryGitlabProject()与 src/domain/repo.ts 的generateGitlabRepoCandidates()共同实现:后者按段切分路径生成一组候选 Repo(user、name、subdir各不相同),前者依次尝试克隆,只有MISSING_REF或COULD_NOT_FETCH这类可重试错误才继续尝试下一个候选。

克隆指定文件

只保留特定文件或目录时,使用--files(短选项-F)。路径之间用逗号分隔,或重复使用该选项:

degit user/repo my-project --files README.md,src/index.ts degit user/repo my-project -F README.md -F src/index.ts

CLI 侧的-F支持逗号分隔与重复传参,其规范化逻辑见 src/bin.ts 的normalizeFiles()。执行时的文件保留实现在 src/operations/filesystem.ts 的keepFiles():

  • 不存在的路径或解析后超出目标目录的路径会被跳过并发出警告;
  • 如果没有任何请求的路径被解析到,则保留整个目标目录(警告NO_FILES_MATCHED);
  • 递归剪枝时,请求的目录会连同其下所有内容一起保留,空目录会被清理。

选项一览

选项短选项说明
--help-h显示帮助文本。
--version-V显示版本号。
--cache-c只使用本地缓存,不访问网络。
--force-f允许克隆到非空目标目录。
--files <paths>-F <paths>只保留列出的文件或目录。
--repo-name-r克隆到以仓库名命名的目录。
--verbose-v打印额外的进度信息。
--mode <mode>-mtar(默认)或git。--mode=git为兼容而保留,但会打印弃用提示。

运行degit --help可以查看发布的完整帮助文本(仓库中对应 assets/help.md)。mode的取值在 src/domain/types.ts 中被限定为tar或git二选一,非法值会在 src/core/orchestrator.ts 抛出Valid modes are tar, git错误。

缓存机制

degit 会把下载的 tar 快照缓存到平台对应的目录:

  • Linux/BSD:$XDG_CACHE_HOME/degit或~/.cache/degit
  • macOS:~/Library/Caches/degit
  • Windows:%LOCALAPPDATA%\degit或~/AppData/Local/degit

缓存目录的解析实现在 src/shared/utils.ts 的resolveBase()中,缓存根路径由base常量导出。缓存结构上,每个仓库对应一个目录,内含:

  • map.json:ref 到 commit 哈希的映射;
  • access.json:各 ref 最近访问时间戳(供交互模式按最近使用排序);
  • <hash>.tar.gz:按 commit 哈希命名的归档文件。

读写逻辑见 src/transports/tar/cache.ts:readCachedRefs()读取map.json,updateCache()更新access.json、map.json,并在哈希变化时清理旧的.tar.gz文件。

默认情况下,degit 会先从网络解析最新 ref,若网络不可达则回退到缓存版本;使用--cache则完全跳过网络请求,只用本地缓存。这一点在 src/transports/tar/archive.ts 的resolveArchiveHash()中体现得最清楚:cache为真时直接调用getHashFromCache(),否则走getHash()在线解析。

私有仓库

私有仓库会被自动处理。degit默认尝试 HTTPS tarball 路径,当无法获取或解压快照时回退到 SSH 克隆。SSH/私有仓库仍然要求本地PATH中存在git。回退逻辑见 src/core/orchestrator.ts 的shouldFallbackToGit():只有当错误码为COULD_NOT_DOWNLOAD且未开启--cache时才触发回退;src/transports/tar/archive.ts 中,当源的传输方式为 SSH 时,tar 查询失败也会直接回退到 git 克隆。

HTTPS 代理

如果设置了https_proxy环境变量,degit 在拉取 tar 归档时会使用该代理。实现上,src/core/orchestrator.ts 在构造时读取process.env.https_proxy存入this.proxy,下载时传给FetchFn;src/shared/utils.ts 的默认fetch在存在代理时通过https-proxy-agent建立请求,并自动处理 3xx 重定向与 4xx/5xx 错误响应。

别名(Aliases)

保存一个别名:

degit alias github:user/repo myRepo

使用别名:

degit myRepo

管理别名:

degit unalias myRepo degit ls # 列出已保存的别名

别名存储在 degit 缓存目录下的aliases.json中。其实现见 src/aliases.ts:saveAlias()/removeAlias()负责读写aliases.json,loadAliases()返回全部别名,resolveAlias()用于在解析源之前做替换。CLI 入口 src/bin.ts 中,alias、unalias、ls三个子命令会被最先识别并分发到对应处理函数;普通克隆前会先loadAliases()+resolveAlias()完成替换,ESM 侧则在 src/core/orchestrator.ts 的构造函数中解析。

交互模式

不带任何参数运行degit会启动交互式选择器:依次提示输入源仓库、目标目录、是否使用缓存;如果目标目录非空,还会询问是否覆盖。交互实现见 src/bin.ts:源仓库的候选列表来自缓存中所有map.json的条目,并按access.json记录的最近访问时间排序,支持模糊搜索(fuzzysearch);覆盖确认通过 enquirer 的 toggle 完成,用户拒绝时输出! Directory not empty — aborting。

ESM API

degit 也可以在 Node.js 脚本中以编程方式使用。

基础示例

import degit from 'degit'; const emitter = degit('user/repo', { cache: true, force: true, verbose: true, }); emitter.on('info', (info) => { console.log(info.message); }); emitter.on('warn', (info) => { console.warn(info.message); }); await emitter.clone('path/to/dest'); console.log('done');

degit的默认导出在 src/index.ts 中定义:export default function degit(src, opts) { return new Degit(src, opts); },即每次调用都会创建一个 src/core/orchestrator.ts 中定义的Degit实例(继承自EventEmitter)。clone()的完整流程(src/core/orchestrator.ts)为:检查目标目录是否为空 → 克隆到目标目录 → 按需保留文件 → 发出SUCCESS事件 → 执行degit.json动作。

构造参数

参数类型说明
aliasesRecord<string, string>解析src时使用的别名映射表。
cacheboolean只使用本地缓存,不访问网络。
fetchFetchFn自定义(url, dest, proxy?) => Promise<void>下载函数。
filesstring[]只保留列出的文件或目录。
forceboolean允许克隆到非空目标目录。
gitGitClient自定义 git 客户端,用于 ref 解析与回退克隆。
mode'tar' \| 'git'克隆模式,tar为默认。
verboseboolean打印额外的进度信息。

各选项的类型定义见 src/domain/types.ts 的ConstructorOptions。其中fetch允许你完全替换网络下载逻辑(默认实现是 src/shared/utils.ts 的fetch),git允许替换 ref 解析与克隆的 git 客户端(默认实现是 src/transports/git/client.ts),这在测试与离线场景中尤其有用。

事件

Degit实例(emitter)暴露两个事件通道:

  • info—— 进度与成功消息(如cloned user/repo#ref to dest、using cached commit hash ...);
  • warn—— 非致命问题(如跳过的路径、回退提示、未定义的替换环境变量)。

事件对象至少包含message,还可能包含code、dest、repo、url、ref、subdir等字段。事件对象的类型定义见 src/domain/types.ts 的EventInfo,可用的code值包括SUCCESS、USING_CACHE、DEST_NOT_EMPTY、REMOVED、NO_FILES_MATCHED、GLOB_NOT_ALLOWED等(src/domain/types.ts)。

degit.json 动作

初始克隆完成后,degit 会在目标目录顶层查找degit.json文件并执行其中定义的动作。该文件在 src/shared/utils.ts 中定义为常量degitConfigName = 'degit.json',读取逻辑在 src/operations/filesystem.ts 的getDirectives():文件必须存在且内容为数组,读取后会立即删除该文件(fs.unlinkSync),再交由 src/operations/directives.ts 的applyDirectives()逐个执行。

针对编辑器自动补全与校验,仓库提供了一份 JSON Schema:schemas/degit.schema.json。

clone

克隆另一个仓库到目标目录,保留已有文件:

[ { "action": "clone", "src": "user/another-repo" }, { "action": "clone", "src": "user/another-repo", "files": ["README.md", "src/index.ts"] } ]

clone 动作还支持cache(使用缓存版本)与verbose(输出额外信息)两个可选字段,见 Schema 中clone的定义。被克隆的仓库也可以定义自己的degit.json动作(嵌套执行)。实现上,src/operations/directives.ts 的cloneDirective()会先用stashFiles()把目标目录现有内容暂存到缓存目录下的临时位置,再以force: true创建子Degit实例克隆新仓库,全部动作结束后由unstashFiles()恢复原有文件——这正是"保留已有文件"的实现原理(src/shared/utils.ts)。

search_replace

在列出的文件中,把正则表达式的每一次匹配替换为指定内容。replacement字段是环境变量的名字,其值被用作替换字符串:

[ { "action": "search_replace", "files": ["package.json", "README.md"], "pattern": "\\{\\{project_name\\}\\}", "replacement": "PROJECT_NAME" } ]

执行时(src/operations/directives.ts):

  • 从process.env[action.replacement]读取替换值;若该环境变量未定义,动作被跳过并发出警告;
  • 使用new RegExp(pattern, 'gu')编译模式(全局 + Unicode 模式);
  • 若环境变量定义了但目标文件不存在、是目录或解析后超出目标目录,均跳过并警告;
  • 实际内容替换发生在replaceFile(),替换后文件内容有变化才计入统计,并通过info事件报告替换的文件数量与文件名。

files可以是单个路径或路径数组;路径相对于目标目录解析,超出目标目录的路径会被跳过。

remove

删除一个或多个文件:

[ { "action": "remove", "files": ["LICENSE"] } ]

files条目支持 glob 模式,这样无需逐个列出即可删除整类文件。由于 glob 可能匹配到比预期更多的文件,只有显式设置allowGlobs: true时才会处理 glob 模式(src/operations/filesystem.ts 的isGlobPattern()用于检测* ? { } [ ]等字符,src/operations/filesystem.ts 的removeFiles()在allowGlobs未开启时会发出GLOB_NOT_ALLOWED警告并跳过):

[ { "action": "remove", "files": [".github/**/*.md"], "allowGlobs": true } ]

removeFiles()会校验每个待删路径都解析在目标目录之内(safeResolve()+ 符号链接真实路径检查),超出目标目录的路径会被跳过并警告;删除目录时按深度优先排序,确保子目录先于父目录删除。

为什么不用git clone --depth 1?

几个关键差异:

  • git clone会在你的新项目里留下属于模板的.git目录,很容易忘记重新初始化仓库;degit 只解压快照,不会留下.git。
  • degit 会缓存归档文件,首次下载后可以离线使用。
  • 输入更少(degit user/repo对比git clone --depth 1 ssh://git@github.com/user/repo)。
  • 通过degit.json支持可组合的后置动作(clone、search_replace、remove)。
  • 内置对子目录、文件过滤和别名的支持。

更多参考

  • README.md —— 项目概览与快速开始
  • docs/ARCHITECTURE.md —— 仓库架构与数据流
  • docs/CONTRIBUTING.md —— 贡献与开发工作流
  • assets/help.md —— 发布的 CLI 帮助文本
  • schemas/degit.schema.json ——degit.json动作的 JSON Schema
  • 开发工具
  • CLI

【免费下载链接】degit

Straightforward project scaffolding

项目地址:https://gitcode.com/gh_mirrors/de/degit
点击查看免费下载

相关推荐

上一篇:游戏自动化革命:从时间消耗者到效率掌控者
下一篇:BACnet4J:纯Java实现的智能建筑通信协议完整指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询