docs(core): turborepo recipe (#19124)
This commit is contained in:
parent
94f71cdbbf
commit
d5f87f79bd
@ -1677,6 +1677,14 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"disableCollapsible": false
|
"disableCollapsible": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Migrate From Turborepo",
|
||||||
|
"path": "/recipes/adopting-nx/from-turborepo",
|
||||||
|
"id": "from-turborepo",
|
||||||
|
"isExternal": false,
|
||||||
|
"children": [],
|
||||||
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Add to any Project",
|
"name": "Add to any Project",
|
||||||
"path": "/recipes/adopting-nx/adding-to-existing-project",
|
"path": "/recipes/adopting-nx/adding-to-existing-project",
|
||||||
@ -2569,6 +2577,14 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"disableCollapsible": false
|
"disableCollapsible": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Migrate From Turborepo",
|
||||||
|
"path": "/recipes/adopting-nx/from-turborepo",
|
||||||
|
"id": "from-turborepo",
|
||||||
|
"isExternal": false,
|
||||||
|
"children": [],
|
||||||
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Add to any Project",
|
"name": "Add to any Project",
|
||||||
"path": "/recipes/adopting-nx/adding-to-existing-project",
|
"path": "/recipes/adopting-nx/adding-to-existing-project",
|
||||||
@ -2612,6 +2628,14 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"disableCollapsible": false
|
"disableCollapsible": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Migrate From Turborepo",
|
||||||
|
"path": "/recipes/adopting-nx/from-turborepo",
|
||||||
|
"id": "from-turborepo",
|
||||||
|
"isExternal": false,
|
||||||
|
"children": [],
|
||||||
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Add to any Project",
|
"name": "Add to any Project",
|
||||||
"path": "/recipes/adopting-nx/adding-to-existing-project",
|
"path": "/recipes/adopting-nx/adding-to-existing-project",
|
||||||
|
|||||||
@ -2091,6 +2091,16 @@
|
|||||||
"path": "/recipes/adopting-nx/adding-to-monorepo",
|
"path": "/recipes/adopting-nx/adding-to-monorepo",
|
||||||
"tags": []
|
"tags": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "from-turborepo",
|
||||||
|
"name": "Migrate From Turborepo",
|
||||||
|
"description": "",
|
||||||
|
"file": "shared/migration/from-turborepo",
|
||||||
|
"itemList": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"path": "/recipes/adopting-nx/from-turborepo",
|
||||||
|
"tags": []
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "adding-to-existing-project",
|
"id": "adding-to-existing-project",
|
||||||
"name": "Add to any Project",
|
"name": "Add to any Project",
|
||||||
@ -3203,6 +3213,16 @@
|
|||||||
"path": "/recipes/adopting-nx/adding-to-monorepo",
|
"path": "/recipes/adopting-nx/adding-to-monorepo",
|
||||||
"tags": []
|
"tags": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "from-turborepo",
|
||||||
|
"name": "Migrate From Turborepo",
|
||||||
|
"description": "",
|
||||||
|
"file": "shared/migration/from-turborepo",
|
||||||
|
"itemList": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"path": "/recipes/adopting-nx/from-turborepo",
|
||||||
|
"tags": []
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "adding-to-existing-project",
|
"id": "adding-to-existing-project",
|
||||||
"name": "Add to any Project",
|
"name": "Add to any Project",
|
||||||
@ -3258,6 +3278,16 @@
|
|||||||
"path": "/recipes/adopting-nx/adding-to-monorepo",
|
"path": "/recipes/adopting-nx/adding-to-monorepo",
|
||||||
"tags": []
|
"tags": []
|
||||||
},
|
},
|
||||||
|
"/recipes/adopting-nx/from-turborepo": {
|
||||||
|
"id": "from-turborepo",
|
||||||
|
"name": "Migrate From Turborepo",
|
||||||
|
"description": "",
|
||||||
|
"file": "shared/migration/from-turborepo",
|
||||||
|
"itemList": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"path": "/recipes/adopting-nx/from-turborepo",
|
||||||
|
"tags": []
|
||||||
|
},
|
||||||
"/recipes/adopting-nx/adding-to-existing-project": {
|
"/recipes/adopting-nx/adding-to-existing-project": {
|
||||||
"id": "adding-to-existing-project",
|
"id": "adding-to-existing-project",
|
||||||
"name": "Add to any Project",
|
"name": "Add to any Project",
|
||||||
|
|||||||
@ -548,6 +548,11 @@
|
|||||||
"id": "adding-to-monorepo",
|
"id": "adding-to-monorepo",
|
||||||
"file": "shared/migration/adding-to-monorepo"
|
"file": "shared/migration/adding-to-monorepo"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Migrate From Turborepo",
|
||||||
|
"id": "from-turborepo",
|
||||||
|
"file": "shared/migration/from-turborepo"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Add to any Project",
|
"name": "Add to any Project",
|
||||||
"id": "adding-to-existing-project",
|
"id": "adding-to-existing-project",
|
||||||
|
|||||||
@ -36,7 +36,7 @@ The starting point of any non-trivial monorepo management tool is to be able to
|
|||||||
|
|
||||||
#### 3. Project graph visualization
|
#### 3. Project graph visualization
|
||||||
|
|
||||||
Being able to visually explore a monorepo workspace can be a deal breaker when it comes to debug and troubleshoot large monorepo workspaces.
|
Being able to visually explore a monorepo workspace can be a deal breaker when you need to debug and troubleshoot large monorepo workspaces.
|
||||||
|
|
||||||
- Nx has a rich, interactive visualiser (watch a video [here](https://www.youtube.com/watch?v=UTB5dOJF43o))
|
- Nx has a rich, interactive visualiser (watch a video [here](https://www.youtube.com/watch?v=UTB5dOJF43o))
|
||||||
- Turborepo has a basic graphviz image export.
|
- Turborepo has a basic graphviz image export.
|
||||||
@ -99,7 +99,7 @@ Learn more [by watching this Egghead lesson](https://egghead.io/lessons/javascri
|
|||||||
Nx has grown over the last 5 years, providing curated presets for common setups, but at the same time focusing on remaining flexible and extensible.
|
Nx has grown over the last 5 years, providing curated presets for common setups, but at the same time focusing on remaining flexible and extensible.
|
||||||
|
|
||||||
- When it comes to Nx core, **the amount of the configuration Nx and Turborepo generate is the same**. Nx and Turborepo both generate a json file at the root of your workspace.
|
- When it comes to Nx core, **the amount of the configuration Nx and Turborepo generate is the same**. Nx and Turborepo both generate a json file at the root of your workspace.
|
||||||
- Turborepo requires you to keep all of your caching configuration in the root `turbo.json` file. This means that if you change a caching setting for a particular project, it will break the cache for every project in the repo. Nx did the same thing 2 years ago, but now you can break out project specific configuration into separate `project.json` files, which makes for less cache misses on large repos.
|
- Both Nx and Turborepo allow you to define project specific configuration in separate files to ensure that changing those settings does not break the cache for the whole repository and to keep the configuration settings close to the related code.
|
||||||
|
|
||||||
Getting started quickly is very easy. Check out some of the examples below:
|
Getting started quickly is very easy. Check out some of the examples below:
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ Nx doesn’t replace any of your tools, and it’s not “all in”. You can sta
|
|||||||
|
|
||||||
## Tech and Performance
|
## Tech and Performance
|
||||||
|
|
||||||
Turborepo is mostly written in Golang. Nx is mostly written in TypeScript, but most of the heavy computation in Nx is done by core Node.js capabilities and node modules written in C++, so performance isn’t affected by this.
|
Turborepo is mostly written in Golang and Rust. Nx is mostly written in TypeScript, but most of the heavy computation in Nx is done by core Node.js capabilities and Rust modules, so performance isn’t affected by this.
|
||||||
|
|
||||||
Benchmarking is hard because a lot depends on what you are trying to run, in what environment, etc. This is one benchmark we use when measuring Nx perf: [Nx and Turbo benchmark](https://github.com/vsavkin/large-monorepo/). It is a repo with 5 Next.js apps. We are measuring how quickly Nx and Turbo can figure out what needs to be restored from cache, and how quickly they can do it.
|
Benchmarking is hard because a lot depends on what you are trying to run, in what environment, etc. This is one benchmark we use when measuring Nx perf: [Nx and Turbo benchmark](https://github.com/vsavkin/large-monorepo/). It is a repo with 5 Next.js apps. We are measuring how quickly Nx and Turbo can figure out what needs to be restored from cache, and how quickly they can do it.
|
||||||
|
|
||||||
@ -175,3 +175,7 @@ Nx was released in 2016. Turborepo was open sourced in December of 2021. Turbore
|
|||||||
- There is a rich ecosystem of [third-party plugins.](https://nx.dev/community)
|
- There is a rich ecosystem of [third-party plugins.](https://nx.dev/community)
|
||||||
|
|
||||||
From day 1 Nx has always been an **MIT-licensed open source project**, and we did everything to make sure companies using Nx won’t end up in the vendor lock-in. We clearly separated Nx the open source project and Nx Cloud the SAAS product. For instance, Nx Cloud is built using the public APIs Nx provides (you can build your own and some companies do). Nx Cloud docs are on a separate domain etc.
|
From day 1 Nx has always been an **MIT-licensed open source project**, and we did everything to make sure companies using Nx won’t end up in the vendor lock-in. We clearly separated Nx the open source project and Nx Cloud the SAAS product. For instance, Nx Cloud is built using the public APIs Nx provides (you can build your own and some companies do). Nx Cloud docs are on a separate domain etc.
|
||||||
|
|
||||||
|
## Switch to Nx
|
||||||
|
|
||||||
|
If you're ready to switch from Turborepo to Nx, the [migration process is fully documented](/recipes/adopting-nx/from-turborepo).
|
||||||
|
|||||||
189
docs/shared/migration/from-turborepo.md
Normal file
189
docs/shared/migration/from-turborepo.md
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
# Migrate from Turborepo to Nx
|
||||||
|
|
||||||
|
If you have an existing monorepo that uses Turborepo, switching to use Nx is a straight-forward process. After switching, you'll have cleaner CLI output, a better graph view and IDE support with the option to incorporate Nx plugins and take advantage of the features of an integrated repository. All this without increasing the complexity of your configuration files.
|
||||||
|
|
||||||
|
For more details, read our [comparison of Nx and Turborepo](/concepts/more-concepts/turbo-and-nx)
|
||||||
|
|
||||||
|
## Initialize Nx
|
||||||
|
|
||||||
|
To switch to Nx, run this command:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
npx nx@latest init
|
||||||
|
```
|
||||||
|
|
||||||
|
The command will ask you three questions.
|
||||||
|
|
||||||
|
1. Which scripts need to be run in order?
|
||||||
|
|
||||||
|
Any scripts you select in this step will be set up so project dependencies will be run first. i.e. `"dependsOn": "^build"`
|
||||||
|
|
||||||
|
2. Which scripts are cacheable?
|
||||||
|
|
||||||
|
Any scripts you select in this step will be added to the `cacheableOperations` in `nx.json`. i.e. `"cacheableOperations": ["build", "test", "lint"]`
|
||||||
|
|
||||||
|
3. For each cacheable script, does it produce output in the file system?
|
||||||
|
|
||||||
|
Any folders identified will be added to the task's `outputs`. i.e. `"outputs": ["{projectRoot}/dist"]`
|
||||||
|
|
||||||
|
This process adds `nx` to your `package.json` at the root of your workspace:
|
||||||
|
|
||||||
|
```json {% fileName="package.json" %}
|
||||||
|
{
|
||||||
|
"name": "my-workspace",
|
||||||
|
...
|
||||||
|
"devDependencies": {
|
||||||
|
...
|
||||||
|
"nx": "16.8.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It also creates an `nx.json` based on the answers given during the setup process. This includes cacheable operations as well as some initial definition of the task pipeline.
|
||||||
|
|
||||||
|
## Convert turbo.json into Nx Configuration
|
||||||
|
|
||||||
|
Most of the settings in your `turbo.json` file can be converted directly into `nx.json` equivalents. The key configuration properties of `dependsOn`, `inputs` and `outputs` have a very similar syntax and can probably be copied over directly from the `turbo.json` `pipeline` into the `nx.json` `targetDefaults`.
|
||||||
|
|
||||||
|
If you have project-specific tasks defined in the root `turbo.json` (i.e. `myreactapp#build`) or in project-level `turbo.json` files (i.e. `/packages/myreactapp/turbo.json`), those settings should go in the `nx` property of the project's `package.json` (i.e. `/packages/myreactapp/package.json`).
|
||||||
|
|
||||||
|
[Specific configuration property conversions](#specific-configuration-property-conversions) are documented below.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Let's say you start with the following `turbo.json` file:
|
||||||
|
|
||||||
|
```json {% fileName="/turbo.json" %}
|
||||||
|
{
|
||||||
|
"$schema": "https://turbo.build/schema.json",
|
||||||
|
"pipeline": {
|
||||||
|
"build": {
|
||||||
|
"dependsOn": ["^build"],
|
||||||
|
"outputs": ["dist/**"]
|
||||||
|
},
|
||||||
|
"docs#build": {
|
||||||
|
"dependsOn": ["^build"],
|
||||||
|
"outputs": ["www/**"]
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"dependsOn": ["build"],
|
||||||
|
"outputs": []
|
||||||
|
},
|
||||||
|
"e2e": {
|
||||||
|
"dependsOn": ["build"],
|
||||||
|
"outputs": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"globalDependencies": ["babel.config.json"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Creating the equivalent configuration with Nx yields the following files:
|
||||||
|
|
||||||
|
```json {% fileName="/nx.json" %}
|
||||||
|
{
|
||||||
|
"$schema": "./node_modules/nx/schemas/nx-schema.json",
|
||||||
|
"namedInputs": {
|
||||||
|
"sharedGlobals": ["babel.config.json"],
|
||||||
|
"default": ["{projectRoot}/**/*", "sharedGlobals"]
|
||||||
|
},
|
||||||
|
"targetDefaults": {
|
||||||
|
"build": {
|
||||||
|
"dependsOn": ["^build"],
|
||||||
|
"inputs": ["default"],
|
||||||
|
"outputs": ["{projectRoot}/dist"]
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"dependsOn": ["build"],
|
||||||
|
"inputs": ["default"]
|
||||||
|
},
|
||||||
|
"e2e": {
|
||||||
|
"dependsOn": ["build"],
|
||||||
|
"inputs": ["default"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tasksRunnerOptions": {
|
||||||
|
"default": {
|
||||||
|
"runner": "nx-cloud",
|
||||||
|
"options": {
|
||||||
|
"cacheableOperations": ["build", "e2e", "test"],
|
||||||
|
"accessToken": "..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```jsonc {% fileName="/packages/docs/package.json" %}
|
||||||
|
{
|
||||||
|
"name": "docs",
|
||||||
|
// etc...
|
||||||
|
"nx": {
|
||||||
|
"targets": {
|
||||||
|
"build": {
|
||||||
|
"outputs": ["www/**"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Specific Configuration Property Conversions
|
||||||
|
|
||||||
|
For each `turbo.json` configuration property, the equivalent Nx property is listed.
|
||||||
|
|
||||||
|
| **Global Configuration:** | |
|
||||||
|
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| `globalDependencies` | add to the [`sharedGlobals` `namedInput`](/concepts/more-concepts/customizing-inputs) |
|
||||||
|
| `globalEnv` | add to the [`sharedGlobals` `namedInput`](/concepts/more-concepts/customizing-inputs) as an [`env` input](/reference/project-configuration#env-variables) |
|
||||||
|
| `globalPassThroughEnv` | N/A. See [Defining Environment Variables](/recipes/tips-n-tricks/define-environment-variables) |
|
||||||
|
| `globalDotEnv` | add to the [`sharedGlobals` `namedInput`](/concepts/more-concepts/customizing-inputs) |
|
||||||
|
|
||||||
|
| **Task Configuration:** | |
|
||||||
|
| ------------------------------- | ------------------------------------------------------------------------------------------------- |
|
||||||
|
| `extends` | N/A. The project configurations will always extend the `targetDefaults` defined in `nx.json`. |
|
||||||
|
| `pipeline[task].dependsOn` | [Same syntax](/reference/project-configuration#dependson). |
|
||||||
|
| `pipeline[task].dotEnv` | Define [file `inputs`](/reference/project-configuration#filesets) |
|
||||||
|
| `pipeline[task].env` | Define [env `inputs`](/reference/project-configuration#env-variables) |
|
||||||
|
| `pipeline[task].passThroughEnv` | N/A. See [Defining Environment Variables](/recipes/tips-n-tricks/define-environment-variables) |
|
||||||
|
| `pipeline[task].outputs` | [Same syntax](/reference/project-configuration#outputs). |
|
||||||
|
| `pipeline[task].cache` | Define in the [`nx.json` `cacheableOperations` property](/reference/nx-json#tasks-runner-options) |
|
||||||
|
| `pipeline[task].inputs` | [Same syntax](/reference/project-configuration#filesets). |
|
||||||
|
| `pipeline[task].outputMode` | Use the [`--output-style` command line flag](/packages/nx/documents/run-many#output-style) |
|
||||||
|
| `pipeline[task].persistent` | N/A. |
|
||||||
|
|
||||||
|
## Command Equivalents
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| `turbo run test lint build` | [`nx run-many -t test lint build`](/packages/nx/documents/run-many) |
|
||||||
|
| `--cache-dir` | Set in [`nx.json` under `tasksRunnerOptions.default.options.cacheDirectory`](/reference/nx-json#tasks-runner-options) |
|
||||||
|
| `--concurrency` | [`--parallel`](/packages/nx/documents/run-many#parallel) |
|
||||||
|
| `--continue` | [Use `--nx-bail`](/packages/nx/documents/run-many#nx-bail) with the inverse value |
|
||||||
|
| `--cwd` | Available when using the [`run-commands` executor](/packages/nx/executors/run-commands#cwd) |
|
||||||
|
| `--dry-run` | N/A. Nx has `--dry-run` for `nx generate` but not for running tasks. |
|
||||||
|
| `--env-mode` | N/A |
|
||||||
|
| `--filter` | Use [`-p admin-*` or `-p tag:api-*`](/packages/nx/documents/run-many#projects). Also see [`nx affected`](/packages/nx/documents/affected). |
|
||||||
|
| `--graph` | [Same syntax](/packages/nx/documents/run-many#graph) or [`nx graph`](/packages/nx/documents/dep-graph) for the entire graph |
|
||||||
|
| `--force` | [`nx reset`](/packages/nx/documents/reset) and then run the command again |
|
||||||
|
| `--global-deps` | Use [`inputs` in the `nx.json`](/concepts/more-concepts/customizing-inputs) or project configuration |
|
||||||
|
| `--framework-inference` | Nx knows if you're using a particular framework if you use an executor for that framework. |
|
||||||
|
| `--ignore` | Use an [`.nxignore` file](/reference/nxignore) (or `.gitignore`) |
|
||||||
|
| `--log-order` | Use [`--output-style`](/packages/nx/documents/run-many#output-style) |
|
||||||
|
| `--no-cache` | Use [`--skip-nx-cache`](/packages/nx/documents/run-many#skip-nx-cache) |
|
||||||
|
| `--no-daemon` | Use [`NX_DAEMON=false` or set `useDaemonProcess: false`](/concepts/more-concepts/nx-daemon#turning-it-off) in `nx.json` |
|
||||||
|
| `--output-logs` | Use [`--output-style`](/packages/nx/documents/run-many#output-style) |
|
||||||
|
| `--only` | N/A |
|
||||||
|
| `--parallel` | N/A |
|
||||||
|
| `--remote-only` | N/A. Can [ignore the remote cache](/core-features/remote-cache#skipping-cloud) with `--no-cloud`. |
|
||||||
|
| `--summarize` | N/A |
|
||||||
|
| `--token` | Set the [Nx Cloud token in `nx.json`](/nx-cloud/account/access-tokens#setting-access-tokens) or as an environment variable (`NX_CLOUD_ACCESS_TOKEN`) |
|
||||||
|
| `--team` | See `--token` for choosing a different Nx Cloud workspace. You can [use `--runner`](/packages/nx/documents/run-many#runner) to choose a different runner defined in the `nx.json` file. |
|
||||||
|
| `--preflight` | N/A |
|
||||||
|
| `--trace` | N/A. [`--verbose`](/packages/nx/documents/run-many#verbose) for more logging. |
|
||||||
|
| `--heap` | N/A. [`--verbose`](/packages/nx/documents/run-many#verbose) for more logging. |
|
||||||
|
| `--cpuprofile` | Use [`NX_PROFILE=profile.json`](/recipes/troubleshooting/performance-profiling). |
|
||||||
|
| `--verbosity` | Use [`--verbose`](/packages/nx/documents/run-many#verbose) |
|
||||||
|
| `turbo gen` | [Use `nx generate`](/packages/nx/documents/generate) |
|
||||||
|
| `turbo login` | No need. [Use `nx connect`](/packages/nx/documents/connect-to-nx-cloud) once to set up Nx Cloud. |
|
||||||
|
| `turbo link` | [Use `nx connect`](/packages/nx/documents/connect-to-nx-cloud) |
|
||||||
@ -95,6 +95,7 @@
|
|||||||
- [Recipes](/recipes)
|
- [Recipes](/recipes)
|
||||||
- [Adopting Nx](/recipes/adopting-nx)
|
- [Adopting Nx](/recipes/adopting-nx)
|
||||||
- [NPM/Yarn/PNPM workspaces](/recipes/adopting-nx/adding-to-monorepo)
|
- [NPM/Yarn/PNPM workspaces](/recipes/adopting-nx/adding-to-monorepo)
|
||||||
|
- [Migrate From Turborepo](/recipes/adopting-nx/from-turborepo)
|
||||||
- [Add to any Project](/recipes/adopting-nx/adding-to-existing-project)
|
- [Add to any Project](/recipes/adopting-nx/adding-to-existing-project)
|
||||||
- [Nx and Lerna](/recipes/adopting-nx/lerna-and-nx)
|
- [Nx and Lerna](/recipes/adopting-nx/lerna-and-nx)
|
||||||
- [Preserving Git Histories](/recipes/adopting-nx/preserving-git-histories)
|
- [Preserving Git Histories](/recipes/adopting-nx/preserving-git-histories)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user