degit: scaffolding from a tar snapshot instead of a git history
Straightforward project scaffolding
At a glance
- What is it?
- degit copies a repository by fetching its tar snapshot into a cache rather than cloning, which is why it is faster than a shallow clone and why no .git directory is left behind. It fetches public sources over HTTPS without needing a git binary, falls back to SSH when that fails, and its agent skill exists partly to avoid overwriting a non-empty destination without confirmation.
- Who is it for?
- Use degit when you are scaffolding from a template and that template's history is dead weight, because it fetches a tar snapshot, caches it for offline reuse and leaves no .git directory behind. Do not treat it as a general git client, since private and SSH sources still need a git binary on your PATH and the tarball path only covers public HTTPS.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 24 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- Rich-Harris/degit7,969 ★ · +28
| Repository | 2026-09-22 | 2026-11-14 |
|---|---|---|
| Rich-Harris/degit | 7.9k | 8k |
28 Stars in the last 53 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Three fetch paths, and only one needs a git binary
The core operation is small. Running `degit some-user/some-repo` finds the latest commit on the GitHub repository, downloads the associated tar file into a platform-appropriate cache directory if it is not already there, and extracts it. The README is explicit about why that is faster than the obvious alternative: you are not downloading the entire git history.
What is less obvious is that there are three paths, and the README describes all of them in one sentence. Refs are resolved through an internal git backend, tar snapshots are downloaded by default, and it falls back to SSH cloning when tarball fetches or extraction fail.
The consequence is a split in what you need installed. Public HTTPS sources do not need a local `git` binary on your PATH. SSH and private repositories still do, because the fallback path is a real clone.
That internal git backend is not a black box. The development dependencies include a pure-JavaScript git implementation as a named package, which is how ref resolution works without shelling out to git for the common case. So the design is: resolve the ref in-process, fetch a tarball over HTTPS, and only reach for SSH when one of the first two fails.
It overwrites the destination, which is why the agent skill exists
The quick start is a handful of invocations, and one of them is worth pausing on.
degit user/repoThat downloads the default branch into the current directory. The variants are: a destination folder, the short `-r` form for the same, a file filter, and a ref.
degit user/repo my-new-project
degit -r user/repodegit user/repo my-project --files README.md,src/index.tsdegit user/repo#v1.0.0None of these quick-start forms says what happens when the destination is not empty.
That is where the Agent skill section becomes relevant rather than decorative. The project ships a skill file for agents that support the SKILL.md convention, installable per project or globally.
# project-level install
npx skills add Rich-Harris/degit --skill degit
# global install
npx skills add Rich-Harris/degit --skill degit -gAnd the README says what the skill is for: it helps the agent choose a source, ref and destination, handle private repositories and aliases, and avoid overwriting a non-empty destination without confirmation.
So the project considers overwrite-without-asking a hazard serious enough to write a guardrail document for automated callers.
The consequence is that if you are invoking this yourself, keep pointing it at empty directories, and if you are wiring it into an agent, that last clause is the reason to install the skill rather than just describing the tool.
Exact pins for every dev dependency, and a five percent duplication gate
The manifest's dependency section is short and unusually strict, and every entry is pinned to an exact version with no range operator at all.
"@types/node": "25.6.0",
"@vitest/coverage-v8": "5.0.4",
"enquirer": "2.3.6",
"eslint-plugin-security": "4.0.0",
"fuzzysearch": "1.0.3",
"https-proxy-agent": "5.0.0",
"husky": "9.1.7",
"isomorphic-git": "1.38.1",
"jscpd": "4.0.9",
"knip": "6.34.0",Ten entries, all exact. For a library with no runtime dependencies that is a reasonable way to make a build reproducible, and it means a maintenance commit is required to move anything.
Some of those names hint at behaviour: a pure-JavaScript git implementation, `isomorphic-git`, sits alongside the README's mention of an internal git backend.
The scripts are equally specific about quality gates.
"lint": "oxlint --fix .",
"lint:ci": "oxlint .",
"typecheck": "tsc --noEmit",
"audit": "nub audit --ignore-unfixable",
"knip:ci": "knip",
"perf": "nub perf/bench.ts",
"prepare": "husky",
"format": "oxfmt --write .",
"format:ci": "oxfmt --check .",
"duplicates:ci": "jscpd --gitignore --reporters console --threshold 5 tsdown.config.ts vitest.config.ts src test",The last line is the interesting one. It runs a copy-paste detector across the build configs, the source and the tests, and fails at a five percent duplication threshold.
The consequence is that this repository optimises for having as little repeated code as possible, which is consistent with a tool whose entire job is to remove boilerplate.
The default test run skips the private-repository tests
There is a filter in the test script that explains the architecture better than any diagram would.
"test": "vitest run --exclude test/integration/private.test.ts",
"test:integration": "vitest run test/integration",
"test:integration:release": "nub test/integration/release.ts",
"test:coverage": "vitest run --coverage",
"pretest": "nub run build",
"prepublishOnly": "nub run test"Three things in six lines.
The default run excludes one file by name, the private repository test, while a separate script runs the whole integration directory. That split only makes sense if the private test needs credentials or a git binary, which is exactly the caveat the README attaches to SSH and private sources.
`pretest` runs the build first. And `prepublishOnly` runs the tests, so nothing is published without a passing run.
The consequence is that a passing default run tells you the public HTTPS tarball path works and says nothing about SSH, which is the path you need for private templates. If your use depends on private repositories, run the integration script yourself with credentials before you rely on it.
Bun builds it, npm installs it
The build tooling and the install path are separate in this repository.
The top level carries an npm lockfile, `package-lock.json`. The benchmark, the audit, the release integration test, and both the pretest and prepublish hooks go through a `nub` command, while the dev and build scripts call tsdown.
"perf": "nub perf/bench.ts",
"pretest": "nub run build",
"prepublishOnly": "nub run test"Meanwhile the install instructions a user follows are npm.
npm install -g degitAnd the Requirements section states Node.js 20 or later, pointing at the engines field for the authoritative number.
So the tool you install is an npm global package with an ES module entry point and a declaration file, built with tsdown. The requirement for a consumer is Node.
Two other files make the tool's shape visible. There is an `llms.txt` at the root, the convention file for describing a project to language models. And the bin entry points at an extensionless file also named `degit` at the repository root.
"bin": {
"degit": "degit"
},The consequence is that the CLI is a single checked-in script rather than a compiled entry point, which is a plausible design for a tool this size and means the bin file is maintained in the repository rather than generated.
The published tarball is six entries, JSON schemas among them
The files array is the complete contents of what npm receives.
"files": [
"assets/help.md",
"dist",
"docs/CHANGELOG.md",
"docs/USAGE.md",
"LICENSE.md",
"README.md",
"schemas"
],Seven entries, and two of them are worth explaining.
`schemas` ships, which is the JSON Schema directory from the repository root.
`assets/help.md` ships too, so the help text is a shipped asset rather than something assembled at runtime. Combined with the extensionless bin script at the root, the CLI's user-facing strings are files in the repository.
The other five are the build output and four documents. Note which documents: the changelog and the usage reference travel with the package, while contributing, security, architecture and the code of conduct do not.
The consequence is that a consumer installing degit gets the reference documentation and the schemas but not the project's contribution or security policy. If you are auditing what you are about to run, those four files are in the repository rather than in your node_modules.
Three releases in three days, and the tag naming is not stable
The release record is the last thing worth reading, because it tells you how fast this moves and how carefully to pin.
The three most recent releases are v3.10.1 published on 2026-10-21, v3.10.0 on 2026-09-06 and v3.9.0 on 2026-09-04, while package.json already reads 3.11.0.
Two observations follow. First, v3.9.0 and v3.10.0 landed two days apart, and v3.10.1 followed about six weeks later.
Second, the naming is not consistent. The tag for 3.9.0 is `v3.9.0` but its release name is recorded without the prefix, `3.9.0`. The newer v3.10.0 and v3.10.1 have the prefix in both the tag and the name.
So a script that assumes the release name matches the tag will work for the two most recent releases and break for 3.9.0.
The repository publishes a changelog, and it is shipped in the npm package alongside the usage reference, which is the place to look rather than reconstructing changes from tag names.
The consequence is that if you depend on degit, pin the version you tested rather than tracking latest, and read the changelog file from the package rather than the releases page, because the release list is where the naming drifts.
What degit cannot do, and what it leaves you owning
Five limits, and they are the mirror image of the five reasons the README gives for using it over a shallow clone.
It cannot reach private repositories without git installed. The tarball path covers public HTTPS sources, and SSH or private sources fall back to a real clone, which means a git binary on your PATH.
It cannot preserve history, which is the feature rather than a limit, but it does mean the result is not a repository. There is no .git directory from the template, so you cannot commit on top of what you fetched without initialising your own repository.
It cannot be trusted with an occupied directory. It can overwrite a non-empty destination, and the project's own agent skill exists to avoid doing so without confirmation. That is a sharp edge for any script that passes a computed path.
It relies on its cache. It finds the latest commit and downloads that commit's tar file only if it is not already cached locally, which is what makes offline reuse possible.
And its default test run does not exercise the path most private users depend on. The private repository test is excluded by name, so the SSH fallback is verified only by the separate integration script and the release script.
Editorial conclusion
Use degit when you are scaffolding from a template and that template's history is dead weight, because it fetches a tar snapshot, caches it for offline reuse and leaves no .git directory behind. Do not treat it as a general git client, since private and SSH sources still need a git binary on your PATH and the tarball path only covers public HTTPS. Before you adopt it, check three things. It can overwrite a non-empty destination, and the agent skill that ships with the project exists specifically to stop an automated caller doing that without confirmation. The default test run excludes the private-repository integration tests, so a green run tells you nothing about whether the SSH path works. And every development dependency is pinned to an exact version while the project shipped v3.9.0 and v3.10.0 two days apart, so pin what you depend on and expect the tag naming to vary between releases.
Frequently asked questions
How do I install degit?
Globally with npm install -g degit. The Requirements section states Node.js 20 or later, pointing at the engines field in package.json for the authoritative number. An agent skill is also installable per project or globally with npx skills add.
how to use degit
Give it a user and repository, optionally a destination folder, optionally a ref after a hash, and optionally a file filter. So degit user/repo, degit user/repo my-new-project, degit user/repo#v1.0.0 and degit user/repo my-project --files README.md,src/index.ts are all valid forms.
degit vs git clone
The README gives five reasons in its own words: no leftover .git folder from the template, cached tar archives for offline reuse, less to type, composable post-clone actions via degit.json, and built-in support for subdirectories, file filtering and aliases. The speed argument is that you avoid downloading the entire git history.
degit alternative
The README names three under a See also heading: zel by Vu Tran, gittar by Luke Edwards, and gitpick by Neeraj Dalal. It offers no comparison against any of them, and describes itself only as a straightforward project scaffolding tool.
Official sources
Where developers are discussing it
Posts on Hacker News, dev.to and Lobsters that link to this repository, found by our scan of those communities. 0 in the last 30 days; newest first.
- Degit – Project ScaffoldingHacker News · Sep 18, 2026 · 3 points · 0 comments
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/rich-harris-degit)Star history chart for your README
[](https://hysenlabs.com/projects/rich-harris-degit)Was this useful?