{"owner":"microsoft","repo":"VFSForGit","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md"],"skills":{"AGENTS.md":"# Agent instructions for VFS for Git\r\n\r\nThis file is for AI coding assistants (GitHub Copilot, Claude Code, Cursor, OpenAI\r\nCodex, Gemini CLI, Aider, etc.). It captures project-specific knowledge that isn't\r\nobvious from a fresh `git clone` and which routinely trips up agents. See\r\n[CONTRIBUTING.md](CONTRIBUTING.md) for coding standards (StyleCop rules, error\r\nhandling, exception logging) — do not duplicate those here.\r\n\r\n## Repository layout\r\n\r\nThe build scripts (`scripts\\Build.bat` and friends) put build outputs **one\r\nlevel up** from the git working tree. The Readme documents the recommended\r\nclone-into-`src\\` convention, which matches the layout that `gvfs clone`\r\ncreates for end users:\r\n\r\n```powershell\r\n# Recommended (matches Readme.md and CI):\r\ngit clone https://github.com/microsoft/VFSForGit C:\\Repos\\VFSForGit\\src\r\n```\r\n\r\n```\r\nC:\\Repos\\VFSForGit\\\r\n├── src\\                    ← git working tree (.git, GVFS.sln, all source)\r\n├── out\\                    ← build output (gitignored — it's outside the working tree)\r\n└── packages\\               ← NuGet cache\r\n```\r\n\r\nCloning without the `src\\` suffix also works — outputs just land one level\r\nabove wherever you cloned. The rest of this document and the project's own\r\nscripts (CI, Readme) use the `src\\` form, so commands assume it. **The\r\nparent directory must be writable** (cloning to `C:\\` itself won't work\r\nbecause the build can't create `C:\\out`).\r\n\r\nAll commands below run from the **enlistment root** (the parent of `src\\`):\r\n\r\n```powershell\r\ncd C:\\Repos\\VFSForGit         # NOT C:\\Repos\\VFSForGit\\src\r\n```\r\n\r\nThis keeps `src\\...` and `out\\...` paths symmetric and matches what\r\n`Build.bat` does internally.\r\n\r\n## Build paths\r\n\r\n`scripts\\Build.bat` does a full installer build with NativeAOT publish plus\r\nInno Setup. That's ~5 minutes minimum, and AOT has no incremental support\r\n— ilc relinks every executable on every `dotnet publish`. **Do not use\r\n`Build.bat` for dev-loop iteration.** Pick the right path for what you're\r\ndoing.\r\n\r\n### Path A — Unit-test inner loop (~10–15 s incremental)\r\n\r\nFor C# changes verified by unit tests only.\r\n\r\n```powershell\r\ndotnet build src\\GVFS\\GVFS.UnitTests\\GVFS.UnitTests.csproj -c Debug\r\n& \"out\\GVFS.UnitTests\\bin\\Debug\\net10.0-windows10.0.17763.0\\win-x64\\GVFS.UnitTests.exe\" --test Fully.Qualified.Class.Or.Method\r\n```\r\n\r\nSkips `dotnet publish`, AOT, native C++ projects, payload assembly, installer.\r\n\r\n### Path B — Functional-test inner loop (~30–60 s incremental)\r\n\r\nFor changes that need the GVFS payload (`gvfs.exe`, hooks, service) but not\r\nan installer. `PublishAot=false` skips ilc (~3–4 min saved);\r\n`SkipCreateInstaller=true` skips Inno Setup (~95 s saved).\r\n`GVFS.Payload` cascades to its dependencies (GVFS, GVFS.Mount, GVFS.Hooks,\r\nGVFS.Service) via `ProjectReference`.\r\n\r\n> **Prerequisite: the native C++ projects must already be built.** They are\r\n> `.vcxproj` (see [Native C++ projects](#native-c-projects-need-msbuild-not-dotnet-build)\r\n> below) and `dotnet publish` will not build them for you. If you have not\r\n> already done a `Build.bat` once in this enlistment, build the native\r\n> projects via VS MSBuild first (or run `Build.bat` once to populate `out\\`,\r\n> then iterate with the commands below). After that they are incremental and\r\n> only rebuild when their own sources change.\r\n\r\n```powershell\r\ndotnet publish src\\GVFS\\GVFS.FunctionalTests\\GVFS.FunctionalTests.csproj `\r\n    -c Debug /p:PublishAot=false\r\ndotnet publish src\\GVFS\\GVFS.Payload\\GVFS.Payload.csproj `\r\n    -c Debug /p:PublishAot=false /p:SkipCreateInstaller=true\r\n\r\nsrc\\scripts\\RunFunctionalTests-Dev.ps1 Debug --test=GVFS.FunctionalTests.Tests.<Namespace>.<Class>.<Method>\r\n```\r\n\r\n`layout.bat` (invoked by GVFS.Payload) `xcopy`s from each project's `publish\\`\r\nor native-output directory — the C# projects do not require AOT, so\r\n`PublishAot=false` produces a fully functional test payload. The native\r\nhook binaries are copied straight from the vcxproj output.\r\n\r\n`RunFunctionalTests-Dev.ps1` runs functional tests against the build output\r\nwithout requiring admin or a system-wide install. It launches the test\r\nservice as a console process. Each invocation gets a unique service name\r\nand data dir, so concurrent runs from different worktrees don't collide.\r\n\r\n### Path C — Installer build (~5 min — only when you need an installer)\r\n\r\nFor producing an installable package (testing install/upgrade flows, or\r\nshipping a build). This is the only correct use of `Build.bat`.\r\n\r\n```powershell\r\n# Build.bat takes (configuration, version, verbosity). The 0. prefix on the\r\n# version tells GVFS to treat this as a development build and skip the\r\n# server-side version check.\r\n$v = & { $n = [DateTime]::Now; \"0.2.$($n.ToString('yy'))$($n.DayOfYear.ToString('D3')).$([int]($n.TimeOfDay.TotalSeconds / 2))\" }\r\nsrc\\scripts\\Build.bat Debug $v minimal\r\n```\r\n\r\nInstaller output: `out\\GVFS.Installers\\bin\\Debug\\win-x64\\SetupGVFS.<version>.exe`.\r\n\r\n## Native C++ projects (need MSBuild, not `dotnet build`)\r\n\r\nThese five projects are `.vcxproj` and require Visual Studio MSBuild with\r\nthe C++ workload. `Build.bat` invokes MSBuild for them automatically; if\r\nyou need to rebuild them outside `Build.bat`, use `msbuild`, not\r\n`dotnet build`.\r\n\r\n- `GVFS\\GitHooksLoader\\GitHooksLoader.vcxproj`\r\n- `GVFS\\GVFS.NativeTests\\GVFS.NativeTests.vcxproj`\r\n- `GVFS\\GVFS.PostIndexChangedHook\\GVFS.PostIndexChangedHook.vcxproj`\r\n- `GVFS\\GVFS.ReadObjectHook\\GVFS.ReadObjectHook.vcxproj`\r\n- `GVFS\\GVFS.VirtualFileSystemHook\\GVFS.VirtualFileSystemHook.vcxproj`\r\n\r\nThese only need to be (re)built when their own sources change; they don't\r\nparticipate in the C# inner-loop paths above.\r\n\r\n## Running tests\r\n\r\n### NUnit filter syntax — `--test`, NEVER `--where`\r\n\r\n> **⚠️ This codebase uses NUnitLite, which supports ONLY `--test` for name\r\n> filtering. The `--where` filter (from NUnit Console) is silently ignored\r\n> and runs every test.**\r\n\r\n```powershell\r\n# ✅ Correct\r\n& \"out\\GVFS.UnitTests\\bin\\...\\GVFS.UnitTests.exe\"  --test \"GVFS.UnitTests.Common.WorktreeInfoTests\"\r\nsrc\\scripts\\RunFunctionalTests-Dev.ps1 Debug       --test=GVFS.FunctionalTests.Tests.GVFSVerbTests.UnknownVerb\r\n\r\n# ❌ Wrong — silently runs the entire suite\r\n& \"out\\GVFS.UnitTests\\bin\\...\\GVFS.UnitTests.exe\"  --where \"class =~ Worktree\"\r\nsrc\\scripts\\RunFunctionalTests-Dev.ps1 Debug       --where \"cat == Smoke\"\r\n```\r\n\r\nFor unit tests, `--where` is merely annoying (the whole suite runs in\r\n~10 seconds). For functional tests, it's a disaster: each test provisions\r\nits own fresh enlistment, so accidentally running the full suite eats\r\nhours and masks whichever failure you were actually investigating.\r\n\r\n### Fully qualified names required\r\n\r\n`--test` matches against the fully qualified name (`Namespace.Class.Method`\r\nor `Namespace.Class`). Short names like `--test=ReproCherryPickRestoreCorruption`\r\nsilently match nothing and the runner reports \"0 tests selected\" without\r\nmaking the typo obvious.\r\n\r\n## vcpkg caching\r\n\r\n`Build.bat` checks for `out\\vcpkg_installed\\dynamic\\x64-windows-dynamic\\bin\\\r\ngit2.dll` as a \"vcpkg already installed\" marker and skips the vcpkg step\r\nif present. The vcpkg step is the slowest part of a from-scratch build\r\n(several minutes of native compilation), so this caching matters.\r\n\r\nDo not manually delete or re-run vcpkg unless you've changed an overlay\r\nport. If you've copied `out\\` from another enlistment as a build-time\r\nshortcut, vcpkg results come along with it.\r\n\r\n## What ships in a public release\r\n\r\nThe signed release pipeline builds several artifacts, but the **public GitHub\r\nrelease only carries the installer and symbols**: `SetupGVFS.<version>.exe`\r\n(per arch) and `Symbols.zip`.\r\n\r\n- **FastFetch is NOT shipped publicly.** `FastFetch.exe` is built and\r\n  ESRP-signed as a pipeline artifact, but it is not attached to the GitHub\r\n  release. Treat FastFetch-only changes as **non-shipping** when scoping a\r\n  release or writing changelog notes — they don't reach the installer end\r\n  users get.\r\n- **Git is NOT bundled.** VFS for Git does not ship Microsoft Git. PRs titled\r\n  \"update default Microsoft Git version\" change only the CI `GIT_VERSION` in\r\n  `.github/workflows/build.yaml` (used to install Git for build + functional\r\n  test runs) — they are **non-shipping**. The only shipped Git constraint is\r\n  the compiled `MinimumGitVersion` constant in `Version.props`, which is\r\n  changed separately and rarely.\r\n\r\n## Feature flags\r\n\r\nProduct feature flags are **git config** keys under the `gvfs.` prefix (not a\r\nseparate config system). To add one, mirror `gvfs.show-hydration-status`:\r\n\r\n- Declare the key name and default in `GVFS.Common/GVFSConstants.cs` under\r\n  `GitConfig` (e.g. `ShowHydrationStatus = GVFSPrefix + \"show-hydration-status\"`\r\n  and `ShowHydrationStatusDefault = false`).\r\n- Read it via `repo.GetConfigBoolOrDefault(name, default)` (or\r\n  `LibGit2RepoInvoker.GetConfigBoolOrDefault`) at the point of use.\r\n\r\nDefault new gates to `false` and gate the **runtime entry point** into a\r\nfeature, not its build, so the code still compiles and ships (and keeps\r\ngetting exercised) while its behavior stays off by default.\r\n\r\n## Coding standards\r\n\r\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for StyleCop rules, error-handling\r\npatterns (`TryXxx` over exceptions, \"fail fast\" on data-loss risks),\r\ntracing/logging conventions (Error level reserved for unrecoverable\r\nfailures), and the `mock:\\\\` / `mock://` URL convention for unit tests.\r\n"},"files":{"AGENTS.md":"# Agent instructions for VFS for Git\r\n\r\nThis file is for AI coding assistants (GitHub Copilot, Claude Code, Cursor, OpenAI\r\nCodex, Gemini CLI, Aider, etc.). It captures project-specific knowledge that isn't\r\nobvious from a fresh `git clone` and which routinely trips up agents. See\r\n[CONTRIBUTING.md](CONTRIBUTING.md) for coding standards (StyleCop rules, error\r\nhandling, exception logging) — do not duplicate those here.\r\n\r\n## Repository layout\r\n\r\nThe build scripts (`scripts\\Build.bat` and friends) put build outputs **one\r\nlevel up** from the git working tree. The Readme documents the recommended\r\nclone-into-`src\\` convention, which matches the layout that `gvfs clone`\r\ncreates for end users:\r\n\r\n```powershell\r\n# Recommended (matches Readme.md and CI):\r\ngit clone https://github.com/microsoft/VFSForGit C:\\Repos\\VFSForGit\\src\r\n```\r\n\r\n```\r\nC:\\Repos\\VFSForGit\\\r\n├── src\\                    ← git working tree (.git, GVFS.sln, all source)\r\n├── out\\                    ← build output (gitignored — it's outside the working tree)\r\n└── packages\\               ← NuGet cache\r\n```\r\n\r\nCloning without the `src\\` suffix also works — outputs just land one level\r\nabove wherever you cloned. The rest of this document and the project's own\r\nscripts (CI, Readme) use the `src\\` form, so commands assume it. **The\r\nparent directory must be writable** (cloning to `C:\\` itself won't work\r\nbecause the build can't create `C:\\out`).\r\n\r\nAll commands below run from the **enlistment root** (the parent of `src\\`):\r\n\r\n```powershell\r\ncd C:\\Repos\\VFSForGit         # NOT C:\\Repos\\VFSForGit\\src\r\n```\r\n\r\nThis keeps `src\\...` and `out\\...` paths symmetric and matches what\r\n`Build.bat` does internally.\r\n\r\n## Build paths\r\n\r\n`scripts\\Build.bat` does a full installer build with NativeAOT publish plus\r\nInno Setup. That's ~5 minutes minimum, and AOT has no incremental support\r\n— ilc relinks every executable on every `dotnet publish`. **Do not use\r\n`Build.bat` for dev-loop iteration.** Pick the right path for what you're\r\ndoing.\r\n\r\n### Path A — Unit-test inner loop (~10–15 s incremental)\r\n\r\nFor C# changes verified by unit tests only.\r\n\r\n```powershell\r\ndotnet build src\\GVFS\\GVFS.UnitTests\\GVFS.UnitTests.csproj -c Debug\r\n& \"out\\GVFS.UnitTests\\bin\\Debug\\net10.0-windows10.0.17763.0\\win-x64\\GVFS.UnitTests.exe\" --test Fully.Qualified.Class.Or.Method\r\n```\r\n\r\nSkips `dotnet publish`, AOT, native C++ projects, payload assembly, installer.\r\n\r\n### Path B — Functional-test inner loop (~30–60 s incremental)\r\n\r\nFor changes that need the GVFS payload (`gvfs.exe`, hooks, service) but not\r\nan installer. `PublishAot=false` skips ilc (~3–4 min saved);\r\n`SkipCreateInstaller=true` skips Inno Setup (~95 s saved).\r\n`GVFS.Payload` cascades to its dependencies (GVFS, GVFS.Mount, GVFS.Hooks,\r\nGVFS.Service) via `ProjectReference`.\r\n\r\n> **Prerequisite: the native C++ projects must already be built.** They are\r\n> `.vcxproj` (see [Native C++ projects](#native-c-projects-need-msbuild-not-dotnet-build)\r\n> below) and `dotnet publish` will not build them for you. If you have not\r\n> already done a `Build.bat` once in this enlistment, build the native\r\n> projects via VS MSBuild first (or run `Build.bat` once to populate `out\\`,\r\n> then iterate with the commands below). After that they are incremental and\r\n> only rebuild when their own sources change.\r\n\r\n```powershell\r\ndotnet publish src\\GVFS\\GVFS.FunctionalTests\\GVFS.FunctionalTests.csproj `\r\n    -c Debug /p:PublishAot=false\r\ndotnet publish src\\GVFS\\GVFS.Payload\\GVFS.Payload.csproj `\r\n    -c Debug /p:PublishAot=false /p:SkipCreateInstaller=true\r\n\r\nsrc\\scripts\\RunFunctionalTests-Dev.ps1 Debug --test=GVFS.FunctionalTests.Tests.<Namespace>.<Class>.<Method>\r\n```\r\n\r\n`layout.bat` (invoked by GVFS.Payload) `xcopy`s from each project's `publish\\`\r\nor native-output directory — the C# projects do not require AOT, so\r\n`PublishAot=false` produces a fully functional test payload. The native\r\nhook binaries are copied straight from the vcxproj output.\r\n\r\n`RunFunctionalTests-Dev.ps1` runs functional tests against the build output\r\nwithout requiring admin or a system-wide install. It launches the test\r\nservice as a console process. Each invocation gets a unique service name\r\nand data dir, so concurrent runs from different worktrees don't collide.\r\n\r\n### Path C — Installer build (~5 min — only when you need an installer)\r\n\r\nFor producing an installable package (testing install/upgrade flows, or\r\nshipping a build). This is the only correct use of `Build.bat`.\r\n\r\n```powershell\r\n# Build.bat takes (configuration, version, verbosity). The 0. prefix on the\r\n# version tells GVFS to treat this as a development build and skip the\r\n# server-side version check.\r\n$v = & { $n = [DateTime]::Now; \"0.2.$($n.ToString('yy'))$($n.DayOfYear.ToString('D3')).$([int]($n.TimeOfDay.TotalSeconds / 2))\" }\r\nsrc\\scripts\\Build.bat Debug $v minimal\r\n```\r\n\r\nInstaller output: `out\\GVFS.Installers\\bin\\Debug\\win-x64\\SetupGVFS.<version>.exe`.\r\n\r\n## Native C++ projects (need MSBuild, not `dotnet build`)\r\n\r\nThese five projects are `.vcxproj` and require Visual Studio MSBuild with\r\nthe C++ workload. `Build.bat` invokes MSBuild for them automatically; if\r\nyou need to rebuild them outside `Build.bat`, use `msbuild`, not\r\n`dotnet build`.\r\n\r\n- `GVFS\\GitHooksLoader\\GitHooksLoader.vcxproj`\r\n- `GVFS\\GVFS.NativeTests\\GVFS.NativeTests.vcxproj`\r\n- `GVFS\\GVFS.PostIndexChangedHook\\GVFS.PostIndexChangedHook.vcxproj`\r\n- `GVFS\\GVFS.ReadObjectHook\\GVFS.ReadObjectHook.vcxproj`\r\n- `GVFS\\GVFS.VirtualFileSystemHook\\GVFS.VirtualFileSystemHook.vcxproj`\r\n\r\nThese only need to be (re)built when their own sources change; they don't\r\nparticipate in the C# inner-loop paths above.\r\n\r\n## Running tests\r\n\r\n### NUnit filter syntax — `--test`, NEVER `--where`\r\n\r\n> **⚠️ This codebase uses NUnitLite, which supports ONLY `--test` for name\r\n> filtering. The `--where` filter (from NUnit Console) is silently ignored\r\n> and runs every test.**\r\n\r\n```powershell\r\n# ✅ Correct\r\n& \"out\\GVFS.UnitTests\\bin\\...\\GVFS.UnitTests.exe\"  --test \"GVFS.UnitTests.Common.WorktreeInfoTests\"\r\nsrc\\scripts\\RunFunctionalTests-Dev.ps1 Debug       --test=GVFS.FunctionalTests.Tests.GVFSVerbTests.UnknownVerb\r\n\r\n# ❌ Wrong — silently runs the entire suite\r\n& \"out\\GVFS.UnitTests\\bin\\...\\GVFS.UnitTests.exe\"  --where \"class =~ Worktree\"\r\nsrc\\scripts\\RunFunctionalTests-Dev.ps1 Debug       --where \"cat == Smoke\"\r\n```\r\n\r\nFor unit tests, `--where` is merely annoying (the whole suite runs in\r\n~10 seconds). For functional tests, it's a disaster: each test provisions\r\nits own fresh enlistment, so accidentally running the full suite eats\r\nhours and masks whichever failure you were actually investigating.\r\n\r\n### Fully qualified names required\r\n\r\n`--test` matches against the fully qualified name (`Namespace.Class.Method`\r\nor `Namespace.Class`). Short names like `--test=ReproCherryPickRestoreCorruption`\r\nsilently match nothing and the runner reports \"0 tests selected\" without\r\nmaking the typo obvious.\r\n\r\n## vcpkg caching\r\n\r\n`Build.bat` checks for `out\\vcpkg_installed\\dynamic\\x64-windows-dynamic\\bin\\\r\ngit2.dll` as a \"vcpkg already installed\" marker and skips the vcpkg step\r\nif present. The vcpkg step is the slowest part of a from-scratch build\r\n(several minutes of native compilation), so this caching matters.\r\n\r\nDo not manually delete or re-run vcpkg unless you've changed an overlay\r\nport. If you've copied `out\\` from another enlistment as a build-time\r\nshortcut, vcpkg results come along with it.\r\n\r\n## What ships in a public release\r\n\r\nThe signed release pipeline builds several artifacts, but the **public GitHub\r\nrelease only carries the installer and symbols**: `SetupGVFS.<version>.exe`\r\n(per arch) and `Symbols.zip`.\r\n\r\n- **FastFetch is NOT shipped publicly.** `FastFetch.exe` is built and\r\n  ESRP-signed as a pipeline artifact, but it is not attached to the GitHub\r\n  release. Treat FastFetch-only changes as **non-shipping** when scoping a\r\n  release or writing changelog notes — they don't reach the installer end\r\n  users get.\r\n- **Git is NOT bundled.** VFS for Git does not ship Microsoft Git. PRs titled\r\n  \"update default Microsoft Git version\" change only the CI `GIT_VERSION` in\r\n  `.github/workflows/build.yaml` (used to install Git for build + functional\r\n  test runs) — they are **non-shipping**. The only shipped Git constraint is\r\n  the compiled `MinimumGitVersion` constant in `Version.props`, which is\r\n  changed separately and rarely.\r\n\r\n## Feature flags\r\n\r\nProduct feature flags are **git config** keys under the `gvfs.` prefix (not a\r\nseparate config system). To add one, mirror `gvfs.show-hydration-status`:\r\n\r\n- Declare the key name and default in `GVFS.Common/GVFSConstants.cs` under\r\n  `GitConfig` (e.g. `ShowHydrationStatus = GVFSPrefix + \"show-hydration-status\"`\r\n  and `ShowHydrationStatusDefault = false`).\r\n- Read it via `repo.GetConfigBoolOrDefault(name, default)` (or\r\n  `LibGit2RepoInvoker.GetConfigBoolOrDefault`) at the point of use.\r\n\r\nDefault new gates to `false` and gate the **runtime entry point** into a\r\nfeature, not its build, so the code still compiles and ships (and keeps\r\ngetting exercised) while its behavior stays off by default.\r\n\r\n## Coding standards\r\n\r\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for StyleCop rules, error-handling\r\npatterns (`TryXxx` over exceptions, \"fail fast\" on data-loss risks),\r\ntracing/logging conventions (Error level reserved for unrecoverable\r\nfailures), and the `mock:\\\\` / `mock://` URL convention for unit tests.\r\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","title":"AGENTS.md","content":"# Agent instructions for VFS for Git\r\n\r\nThis file is for AI coding assistants (GitHub Copilot, Claude Code, Cursor, OpenAI\r\nCodex, Gemini CLI, Aider, etc.). It captures project-specific knowledge that isn't\r\nobvious from a fresh `git clone` and which routinely trips up agents. See\r\n[CONTRIBUTING.md](CONTRIBUTING.md) for coding standards (StyleCop rules, error\r\nhandling, exception logging) — do not duplicate those here.\r\n\r\n## Repository layout\r\n\r\nThe build scripts (`scripts\\Build.bat` and friends) put build outputs **one\r\nlevel up** from the git working tree. The Readme documents the recommended\r\nclone-into-`src\\` convention, which matches the layout that `gvfs clone`\r\ncreates for end users:\r\n\r\n```powershell\r\n# Recommended (matches Readme.md and CI):\r\ngit clone https://github.com/microsoft/VFSForGit C:\\Repos\\VFSForGit\\src\r\n```\r\n\r\n```\r\nC:\\Repos\\VFSForGit\\\r\n├── src\\                    ← git working tree (.git, GVFS.sln, all source)\r\n├── out\\                    ← build output (gitignored — it's outside the working tree)\r\n└── packages\\               ← NuGet cache\r\n```\r\n\r\nCloning without the `src\\` suffix also works — outputs just land one level\r\nabove wherever you cloned. The rest of this document and the project's own\r\nscripts (CI, Readme) use the `src\\` form, so commands assume it. **The\r\nparent directory must be writable** (cloning to `C:\\` itself won't work\r\nbecause the build can't create `C:\\out`).\r\n\r\nAll commands below run from the **enlistment root** (the parent of `src\\`):\r\n\r\n```powershell\r\ncd C:\\Repos\\VFSForGit         # NOT C:\\Repos\\VFSForGit\\src\r\n```\r\n\r\nThis keeps `src\\...` and `out\\...` paths symmetric and matches what\r\n`Build.bat` does internally.\r\n\r\n## Build paths\r\n\r\n`scripts\\Build.bat` does a full installer build with NativeAOT publish plus\r\nInno Setup. That's ~5 minutes minimum, and AOT has no incremental support\r\n— ilc relinks every executable on every `dotnet publish`. **Do not use\r\n`Build.bat` for dev-loop iteration.** Pick the right path for what you're\r\ndoing.\r\n\r\n### Path A — Unit-test inner loop (~10–15 s incremental)\r\n\r\nFor C# changes verified by unit tests only.\r\n\r\n```powershell\r\ndotnet build src\\GVFS\\GVFS.UnitTests\\GVFS.UnitTests.csproj -c Debug\r\n& \"out\\GVFS.UnitTests\\bin\\Debug\\net10.0-windows10.0.17763.0\\win-x64\\GVFS.UnitTests.exe\" --test Fully.Qualified.Class.Or.Method\r\n```\r\n\r\nSkips `dotnet publish`, AOT, native C++ projects, payload assembly, installer.\r\n\r\n### Path B — Functional-test inner loop (~30–60 s incremental)\r\n\r\nFor changes that need the GVFS payload (`gvfs.exe`, hooks, service) but not\r\nan installer. `PublishAot=false` skips ilc (~3–4 min saved);\r\n`SkipCreateInstaller=true` skips Inno Setup (~95 s saved).\r\n`GVFS.Payload` cascades to its dependencies (GVFS, GVFS.Mount, GVFS.Hooks,\r\nGVFS.Service) via `ProjectReference`.\r\n\r\n> **Prerequisite: the native C++ projects must already be built.** They are\r\n> `.vcxproj` (see [Native C++ projects](#native-c-projects-need-msbuild-not-dotnet-build)\r\n> below) and `dotnet publish` will not build them for you. If you have not\r\n> already done a `Build.bat` once in this enlistment, build the native\r\n> projects via VS MSBuild first (or run `Build.bat` once to populate `out\\`,\r\n> then iterate with the commands below). After that they are incremental and\r\n> only rebuild when their own sources change.\r\n\r\n```powershell\r\ndotnet publish src\\GVFS\\GVFS.FunctionalTests\\GVFS.FunctionalTests.csproj `\r\n    -c Debug /p:PublishAot=false\r\ndotnet publish src\\GVFS\\GVFS.Payload\\GVFS.Payload.csproj `\r\n    -c Debug /p:PublishAot=false /p:SkipCreateInstaller=true\r\n\r\nsrc\\scripts\\RunFunctionalTests-Dev.ps1 Debug --test=GVFS.FunctionalTests.Tests.<Namespace>.<Class>.<Method>\r\n```\r\n\r\n`layout.bat` (invoked by GVFS.Payload) `xcopy`s from each project's `publish\\`\r\nor native-output directory — the C# projects do not require AOT, so\r\n`PublishAot=false` produces a fully functional test payload. The native\r\nhook binaries are copied straight from the vcxproj output.\r\n\r\n`RunFunctionalTests-Dev.ps1` runs functional tests against the build output\r\nwithout requiring admin or a system-wide install. It launches the test\r\nservice as a console process. Each invocation gets a unique service name\r\nand data dir, so concurrent runs from different worktrees don't collide.\r\n\r\n### Path C — Installer build (~5 min — only when you need an installer)\r\n\r\nFor producing an installable package (testing install/upgrade flows, or\r\nshipping a build). This is the only correct use of `Build.bat`.\r\n\r\n```powershell\r\n# Build.bat takes (configuration, version, verbosity). The 0. prefix on the\r\n# version tells GVFS to treat this as a development build and skip the\r\n# server-side version check.\r\n$v = & { $n = [DateTime]::Now; \"0.2.$($n.ToString('yy'))$($n.DayOfYear.ToString('D3')).$([int]($n.TimeOfDay.TotalSeconds / 2))\" }\r\nsrc\\scripts\\Build.bat Debug $v minimal\r\n```\r\n\r\nInstaller output: `out\\GVFS.Installers\\bin\\Debug\\win-x64\\SetupGVFS.<version>.exe`.\r\n\r\n## Native C++ projects (need MSBuild, not `dotnet build`)\r\n\r\nThese five projects are `.vcxproj` and require Visual Studio MSBuild with\r\nthe C++ workload. `Build.bat` invokes MSBuild for them automatically; if\r\nyou need to rebuild them outside `Build.bat`, use `msbuild`, not\r\n`dotnet build`.\r\n\r\n- `GVFS\\GitHooksLoader\\GitHooksLoader.vcxproj`\r\n- `GVFS\\GVFS.NativeTests\\GVFS.NativeTests.vcxproj`\r\n- `GVFS\\GVFS.PostIndexChangedHook\\GVFS.PostIndexChangedHook.vcxproj`\r\n- `GVFS\\GVFS.ReadObjectHook\\GVFS.ReadObjectHook.vcxproj`\r\n- `GVFS\\GVFS.VirtualFileSystemHook\\GVFS.VirtualFileSystemHook.vcxproj`\r\n\r\nThese only need to be (re)built when their own sources change; they don't\r\nparticipate in the C# inner-loop paths above.\r\n\r\n## Running tests\r\n\r\n### NUnit filter syntax — `--test`, NEVER `--where`\r\n\r\n> **⚠️ This codebase uses NUnitLite, which supports ONLY `--test` for name\r\n> filtering. The `--where` filter (from NUnit Console) is silently ignored\r\n> and runs every test.**\r\n\r\n```powershell\r\n# ✅ Correct\r\n& \"out\\GVFS.UnitTests\\bin\\...\\GVFS.UnitTests.exe\"  --test \"GVFS.UnitTests.Common.WorktreeInfoTests\"\r\nsrc\\scripts\\RunFunctionalTests-Dev.ps1 Debug       --test=GVFS.FunctionalTests.Tests.GVFSVerbTests.UnknownVerb\r\n\r\n# ❌ Wrong — silently runs the entire suite\r\n& \"out\\GVFS.UnitTests\\bin\\...\\GVFS.UnitTests.exe\"  --where \"class =~ Worktree\"\r\nsrc\\scripts\\RunFunctionalTests-Dev.ps1 Debug       --where \"cat == Smoke\"\r\n```\r\n\r\nFor unit tests, `--where` is merely annoying (the whole suite runs in\r\n~10 seconds). For functional tests, it's a disaster: each test provisions\r\nits own fresh enlistment, so accidentally running the full suite eats\r\nhours and masks whichever failure you were actually investigating.\r\n\r\n### Fully qualified names required\r\n\r\n`--test` matches against the fully qualified name (`Namespace.Class.Method`\r\nor `Namespace.Class`). Short names like `--test=ReproCherryPickRestoreCorruption`\r\nsilently match nothing and the runner reports \"0 tests selected\" without\r\nmaking the typo obvious.\r\n\r\n## vcpkg caching\r\n\r\n`Build.bat` checks for `out\\vcpkg_installed\\dynamic\\x64-windows-dynamic\\bin\\\r\ngit2.dll` as a \"vcpkg already installed\" marker and skips the vcpkg step\r\nif present. The vcpkg step is the slowest part of a from-scratch build\r\n(several minutes of native compilation), so this caching matters.\r\n\r\nDo not manually delete or re-run vcpkg unless you've changed an overlay\r\nport. If you've copied `out\\` from another enlistment as a build-time\r\nshortcut, vcpkg results come along with it.\r\n\r\n## What ships in a public release\r\n\r\nThe signed release pipeline builds several artifacts, but the **public GitHub\r\nrelease only carries the installer and symbols**: `SetupGVFS.<version>.exe`\r\n(per arch) and `Symbols.zip`.\r\n\r\n- **FastFetch is NOT shipped publicly.** `FastFetch.exe` is built and\r\n  ESRP-signed as a pipeline artifact, but it is not attached to the GitHub\r\n  release. Treat FastFetch-only changes as **non-shipping** when scoping a\r\n  release or writing changelog notes — they don't reach the installer end\r\n  users get.\r\n- **Git is NOT bundled.** VFS for Git does not ship Microsoft Git. PRs titled\r\n  \"update default Microsoft Git version\" change only the CI `GIT_VERSION` in\r\n  `.github/workflows/build.yaml` (used to install Git for build + functional\r\n  test runs) — they are **non-shipping**. The only shipped Git constraint is\r\n  the compiled `MinimumGitVersion` constant in `Version.props`, which is\r\n  changed separately and rarely.\r\n\r\n## Feature flags\r\n\r\nProduct feature flags are **git config** keys under the `gvfs.` prefix (not a\r\nseparate config system). To add one, mirror `gvfs.show-hydration-status`:\r\n\r\n- Declare the key name and default in `GVFS.Common/GVFSConstants.cs` under\r\n  `GitConfig` (e.g. `ShowHydrationStatus = GVFSPrefix + \"show-hydration-status\"`\r\n  and `ShowHydrationStatusDefault = false`).\r\n- Read it via `repo.GetConfigBoolOrDefault(name, default)` (or\r\n  `LibGit2RepoInvoker.GetConfigBoolOrDefault`) at the point of use.\r\n\r\nDefault new gates to `false` and gate the **runtime entry point** into a\r\nfeature, not its build, so the code still compiles and ships (and keeps\r\ngetting exercised) while its behavior stays off by default.\r\n\r\n## Coding standards\r\n\r\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for StyleCop rules, error-handling\r\npatterns (`TryXxx` over exceptions, \"fail fast\" on data-loss risks),\r\ntracing/logging conventions (Error level reserved for unrecoverable\r\nfailures), and the `mock:\\\\` / `mock://` URL convention for unit tests.\r\n","category":"root","tokens":2365}]}