diff --git a/packages/website/src/docs.ts b/packages/website/src/docs.ts index 1325cd933f..896d86daf9 100644 --- a/packages/website/src/docs.ts +++ b/packages/website/src/docs.ts @@ -186,7 +186,13 @@ function insertDocByPath(nodes: DocsNavNode[], doc: Doc, stripTopDirectory = fal // A section's top-level directory and its category name the same thing, so // only directories below it become collapsible groups. - if (stripTopDirectory) directories.shift(); + if ( + stripTopDirectory && + directories[0] && + doc.frontmatter.category?.toLowerCase().endsWith(directories[0].toLowerCase()) + ) { + directories.shift(); + } let current = nodes; let parent: Extract | undefined; diff --git a/packages/website/src/plugin-docs-navigation.test.ts b/packages/website/src/plugin-docs-navigation.test.ts index 93f0cf7736..a3e537620a 100644 --- a/packages/website/src/plugin-docs-navigation.test.ts +++ b/packages/website/src/plugin-docs-navigation.test.ts @@ -2,6 +2,25 @@ import { describe, expect, it } from "vitest"; import { buildDocsNavTree, getDoc, getDocs, getLegacyDocsRedirect, parseHeadings } from "./docs"; describe("plugin documentation", () => { + it.each(["en", "zh-CN"] as const)( + "nests local file copying under Git worktrees in %s", + (locale) => { + const workspaces = buildDocsNavTree(getDocs(locale)).find( + (node) => node.type === "category" && node.label === "Workspaces", + ); + expect(workspaces).toMatchObject({ + children: expect.arrayContaining([ + expect.objectContaining({ + type: "group", + href: "/docs/worktrees", + children: [expect.objectContaining({ href: "/docs/worktrees/worktreeinclude" })], + }), + ]), + }); + expect(getDoc("worktrees/worktreeinclude", locale)).toBeDefined(); + }, + ); + it("groups current guides by task without version navigation", () => { const plugins = buildDocsNavTree(getDocs("en")).find( (node) => node.type === "category" && node.label === "Plugins", diff --git a/public-docs/worktrees.md b/public-docs/worktrees/index.md similarity index 91% rename from public-docs/worktrees.md rename to public-docs/worktrees/index.md index baaea2e177..faabb2d81f 100644 --- a/public-docs/worktrees.md +++ b/public-docs/worktrees/index.md @@ -84,16 +84,7 @@ Add `--forge ` when Paseo cannot infer the forge from the source checkout. ## Copy local files with .worktreeinclude -Create `.worktreeinclude` at the root of your source checkout to copy selected gitignored files into new worktrees: - -```gitignore -.env -config/*.local.json -``` - -Patterns use gitignore syntax. A file must match `.worktreeinclude` and be gitignored to be copied. Tracked files are never copied, and existing destination files are never overwritten. Without `.worktreeinclude`, no local files are copied. Symlinked directories and nested repositories inside an included path cause the copy to fail and abort worktree creation. - -The built-in `worktree-include` plugin copies from the checkout you create the worktree from, including when that checkout is another worktree. Copying finishes before setup, terminals, and the first agent. A copy failure aborts creation and removes the new worktree. Copies use copy-on-write clones on APFS and btrfs when supported, with a regular copy elsewhere. +Use [`.worktreeinclude`](/docs/worktrees/worktreeinclude) to copy selected gitignored files, such as `.env`, from your source checkout before setup runs. ## paseo.json diff --git a/public-docs/worktrees.zh-CN.md b/public-docs/worktrees/index.zh-CN.md similarity index 92% rename from public-docs/worktrees.zh-CN.md rename to public-docs/worktrees/index.zh-CN.md index e84d0a8a25..cce65fc090 100644 --- a/public-docs/worktrees.zh-CN.md +++ b/public-docs/worktrees/index.zh-CN.md @@ -82,16 +82,7 @@ paseo workspace create \ ## 使用 .worktreeinclude 复制本地文件 {#copy-local-files-with-worktreeinclude} -在源检出的根目录创建 `.worktreeinclude`,将选定的 Git 忽略文件复制到新的 worktree: - -```gitignore -.env -config/*.local.json -``` - -模式使用 gitignore 语法。只有同时匹配 `.worktreeinclude` 且被 Git 忽略的文件才会复制。已跟踪的文件不会被复制,目标中已存在的文件不会被覆盖。没有 `.worktreeinclude` 时,不会复制任何本地文件。包含路径中的符号链接目录和嵌套仓库会导致复制失败,并中止 worktree 创建。 - -内置的 `worktree-include` 插件从你创建 worktree 时所选的检出复制文件,即使源检出本身也是 worktree。复制会在 setup、终端和第一个 Agent 运行之前完成。复制失败会中止创建并删除新的 worktree。支持时会在 APFS 和 btrfs 上使用写时复制克隆,否则执行普通复制。 +使用 [`.worktreeinclude`](/docs/worktrees/worktreeinclude),在 setup 运行前将 `.env` 等选定的 Git 忽略文件从源检出复制到新 worktree。 ## paseo.json {#paseojson} diff --git a/public-docs/worktrees/worktreeinclude.md b/public-docs/worktrees/worktreeinclude.md new file mode 100644 index 0000000000..50c5f0370a --- /dev/null +++ b/public-docs/worktrees/worktreeinclude.md @@ -0,0 +1,61 @@ +--- +title: .worktreeinclude +description: Copy gitignored local files into new Paseo worktrees. +nav: .worktreeinclude +order: 12 +category: Workspaces +--- + +# .worktreeinclude + +Add `.worktreeinclude` at the root of your source checkout to copy local configuration into new [Git worktrees](/docs/worktrees). Paseo copies matching gitignored files before setup, terminals, and the first Agent start. + +## Copy your local configuration + +For a project whose `.gitignore` includes `.env` and `config/*.local.json`, create this `.worktreeinclude`: + +```gitignore +.env +config/*.local.json +``` + +When you create a worktree through Paseo, an existing `.env` and matching files such as `config/dev.local.json` are copied to the same relative paths in the new worktree. Their contents stay out of Git. + +Commit `.worktreeinclude` to share the patterns with your team. Each teammate supplies their own ignored files in the checkout they create worktrees from. + +## Pattern syntax + +Use gitignore syntax, with one pattern per line. Comments start with `#`. Prefix a pattern with `!` to exclude a match: + +```gitignore +# Local environment and configuration +.env +config/*.local.json +!config/production.local.json +``` + +A pattern only selects files that Git already ignores. Adding a path here does not ignore it, so also configure `.gitignore` when needed. Tracked files and untracked files that Git does not ignore are excluded. + +## When files are copied + +The built-in `worktree-include` plugin reads the file from the checkout you create the worktree from. That source can itself be another worktree. Keep the plugin enabled for copying to run. + +- Without `.worktreeinclude`, the plugin copies nothing. +- Matching files must exist in the source checkout. +- Existing destination files are skipped, preserving their contents. +- Copies happen during creation, with no ongoing synchronization between checkouts. +- A copy error aborts creation and removes the new worktree. Avoid including symlinked directories or nested repositories, which cannot be copied as files. + +## Run commands during setup + +Use `.worktreeinclude` for files that already exist locally. Use a [setup hook](/docs/worktrees#setup-and-teardown) to install dependencies, generate files, or fetch secrets: + +```json +{ + "worktree": { + "setup": "npm ci" + } +} +``` + +Put this configuration in `paseo.json` on the base branch used for the new worktree. Copied files are available when setup runs. diff --git a/public-docs/worktrees/worktreeinclude.zh-CN.md b/public-docs/worktrees/worktreeinclude.zh-CN.md new file mode 100644 index 0000000000..e80e386a01 --- /dev/null +++ b/public-docs/worktrees/worktreeinclude.zh-CN.md @@ -0,0 +1,61 @@ +--- +title: .worktreeinclude +description: 将 Git 忽略的本地文件复制到新的 Paseo worktree。 +nav: .worktreeinclude +order: 12 +category: Workspaces +--- + +# .worktreeinclude + +在源检出的根目录添加 `.worktreeinclude`,将本地配置复制到新的 [Git worktree](/docs/worktrees)。Paseo 会在 setup、终端和首个 Agent 启动之前复制匹配的 Git 忽略文件。 + +## 复制本地配置 {#copy-your-local-configuration} + +如果项目的 `.gitignore` 包含 `.env` 和 `config/*.local.json`,创建以下 `.worktreeinclude`: + +```gitignore +.env +config/*.local.json +``` + +通过 Paseo 创建 worktree 时,已有的 `.env` 和 `config/dev.local.json` 等匹配文件会被复制到新 worktree 中的相同相对路径。文件内容仍不会纳入 Git。 + +提交 `.worktreeinclude`,与团队共享这些模式。每位团队成员在用于创建 worktree 的检出中提供自己的忽略文件。 + +## 模式语法 {#pattern-syntax} + +使用 gitignore 语法,每行一个模式。注释以 `#` 开头。模式前加 `!` 可排除匹配项: + +```gitignore +# Local environment and configuration +.env +config/*.local.json +!config/production.local.json +``` + +模式只会选择 Git 已忽略的文件。在此添加路径不会使 Git 忽略该文件,需要时还应配置 `.gitignore`。已跟踪的文件和未被 Git 忽略的未跟踪文件都不会被复制。 + +## 文件复制时机 {#when-files-are-copied} + +内置的 `worktree-include` 插件从用于创建 worktree 的检出读取该文件。源检出也可以是另一个 worktree。保持该插件启用才能执行复制。 + +- 没有 `.worktreeinclude` 时,插件不会复制任何文件。 +- 匹配的文件必须存在于源检出中。 +- 目标中已存在的文件会被跳过,保留原有内容。 +- 复制发生在创建期间,之后不会在检出之间持续同步。 +- 复制错误会中止创建并删除新 worktree。避免包含符号链接目录或嵌套仓库,因为它们无法作为文件复制。 + +## 在 setup 期间运行命令 {#run-commands-during-setup} + +使用 `.worktreeinclude` 复制本地已有文件。使用 [setup 钩子](/docs/worktrees#setup-and-teardown) 安装依赖、生成文件或获取密钥: + +```json +{ + "worktree": { + "setup": "npm ci" + } +} +``` + +将此配置放入新 worktree 所用基准分支的 `paseo.json` 中。setup 运行时,复制的文件已可使用。