Skip to content

storage: filesystem, Add support for worktreeConfig extension#1877

Merged
pjbgf merged 7 commits intomainfrom
worktree-config
Mar 3, 2026
Merged

storage: filesystem, Add support for worktreeConfig extension#1877
pjbgf merged 7 commits intomainfrom
worktree-config

Conversation

@pjbgf
Copy link
Copy Markdown
Member

@pjbgf pjbgf commented Mar 2, 2026

Previous to #1850, go-git was oblivious to Git extensions, which meant that at times it would try to handle repositories where the underlying feature was not fully supported.

An example of that being worktreeConfig, meaning that any worktree-specific config were being ignored.

This PR adds that support to storage/filesystem, resulting into worktree configs being overlayed over the local config, providing parity with upstream Git.

Other changes were required in order to support this feature properly:

  • Fix of config.Merge bug whereby a nil or empty map resulted in incorrect merge. For example, if local config had remotes and worktree didn't, the resulting merge would not contain any remotes.
  • Removal of PlainOpenOptions.EnableDotGitCommonDir, making commondir detection unconditional, abstracting this complexity away from callers.
  • Update SetConfig so that it can distinguish delta changes and save them to the worktree-specific config only.

Follow-up from #1850 and #1808.
Relates to #1812.

Copilot AI review requested due to automatic review settings March 2, 2026 15:14
Comment thread storage/filesystem/config.go Fixed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds extensions.worktreeConfig support to the filesystem-backed storage so linked worktrees can read and overlay per-worktree config.worktree onto the common .git/config, aligning behavior with upstream Git worktree configuration.

Changes:

  • Overlay .git/worktrees/<name>/config.worktree onto the base repo config when extensions.worktreeConfig=true (filesystem storage only).
  • Fix config.Merge behavior for map fields so nil/empty maps don’t wipe previously-merged entries.
  • Remove PlainOpenOptions.EnableDotGitCommonDir and make commondir detection unconditional.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
storage/filesystem/config.go Reads config.worktree (when enabled) and merges it into the returned config.
config/config.go Adds Extensions.WorktreeConfig, improves map merge semantics, and marshals/unmarshals the new extension key.
storage/filesystem/storage.go Declares filesystem storage support for worktreeconfig extension.
storage/filesystem/dotgit/dotgit.go Adds helpers to open/create config.worktree.
repository.go Makes commondir detection unconditional; improves nil options handling and not-exist checks.
options.go Removes EnableDotGitCommonDir from PlainOpenOptions.
repository_extensions.go Normalizes v0-valid extension keys to lowercase and documents the requirement.
config/config_test.go Expands merge tests to cover map merge behavior and new extension field.
x/plumbing/worktree/worktree_test.go Adds coverage for reading overlaid config.worktree in linked worktrees.
worktree_test.go Updates linked-worktree open tests to reflect new PlainOpen behavior.
COMPATIBILITY.md Documents worktreeConfig and config --worktree behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread config/config.go
Comment thread config/config.go
Comment thread storage/filesystem/config.go
Comment thread COMPATIBILITY.md
pjbgf added 4 commits March 3, 2026 13:40
The extension matching is case insensitive, so keep references to them
in lower case format, as that is what is used during the matching
process.

Signed-off-by: Paulo Gomes <pjbgf@linux.com>
EnableDotGitCommonDir was an opt-in flag to detect and honor the
commondir mechanism used by Git linked worktrees.

Requiring callers to explicitly enable this behavior is both surprising
and error-prone. Consumers of PlainOpen should not need to know whether
a repository is a primary working tree or a linked worktree, nor whether
it relies on a shared common directory.

This distinction directly impacts core repository semantics, including
object resolution and configuration lookup. Leaving it opt-in can lead
to subtle and inconsistent behavior.

Making commondir detection unconditional, abstracting this complexity
away from callers, ensures a consistent and predictable repository
handling which aligns with upstream Git.

Signed-off-by: Paulo Gomes <pjbgf@linux.com>
Signed-off-by: Paulo Gomes <pjbgf@linux.com>
Previous to #1850, go-git was oblivious to Git extensions, which
meant that at times it would try to handle repositories where the
underlying feature was not fully supported.

An example of that being worktreeConfig, meaning that any worktree
specific config were being ignored.

This commit adds that support to storage/filesystem, resulting into
worktree configs being overlayed the local config, providing upstream
parity with upstream Git.

References:
- https://git-scm.com/docs/git-worktree#_configuration_file

Signed-off-by: Paulo Gomes <pjbgf@linux.com>
@pjbgf pjbgf force-pushed the worktree-config branch from 4170f46 to 524f02e Compare March 3, 2026 14:37
pjbgf added 3 commits March 3, 2026 15:44
The internal merge helper had no case for reflect.Map, so map-typed
fields (Remotes, Branches, Submodules, URLs) fell through to the default
branch which called df.Set(sf), replacing the destination map wholesale.

This caused two bugs:

* An empty but non-nil source map (as produced by NewConfig) would
  overwrite a populated destination map, silently discarding all
  accumulated entries.

* A non-empty source map would replace the destination map entirely,
  dropping any destination keys absent from the source instead of
  merging them together.

Add a reflect.Map case that skips empty source maps unchanged and
otherwise copies entries one-by-one, so destination keys not present
in the source are preserved and source entries override same-key
destination entries.

Add table-driven tests covering nil source map, empty source map,
disjoint key sets, and same-key override.

Signed-off-by: Paulo Gomes <paulo@entire.io>
Signed-off-by: Paulo Gomes <pjbgf@linux.com>
When the worktreeConfig extension is active and a config.worktree file
already exists, only the delta — options whose values are absent from or
differ from the base config — is written to config.worktree. The base
config is left untouched in that case.

This mirrors the behaviour of 'git config --worktree' to some extend, and
enable the use case whereby worktree-specific overrides live in config.worktree
while shared settings remain in the common config.

Signed-off-by: Paulo Gomes <paulo@entire.io>
@pjbgf pjbgf force-pushed the worktree-config branch from 524f02e to ca02134 Compare March 3, 2026 16:06
@pjbgf pjbgf merged commit ee63665 into main Mar 3, 2026
16 checks passed
@pjbgf pjbgf deleted the worktree-config branch March 3, 2026 22:11
charithe added a commit to charithe/cerbos that referenced this pull request Mar 9, 2026
v5.17.0 includes strict extension checks (go-git/go-git#1861) but it
causes problems for `worktreeconfig` because it wasn't added to the
supported extension list until go-git/go-git#1877. Until it's released,
we need to keep the version back.

Signed-off-by: Charith Ellawala <charith@cerbos.dev>
charithe added a commit to cerbos/cerbos that referenced this pull request Mar 9, 2026
v5.17.0 includes strict extension checks (go-git/go-git#1861) but it
causes problems for `worktreeconfig` because it wasn't added to the
supported extension list until go-git/go-git#1877. Until it's released,
we need to keep the version back.

Signed-off-by: Charith Ellawala <charith@cerbos.dev>

Signed-off-by: Charith Ellawala <charith@cerbos.dev>
haines pushed a commit to haines/cerbos that referenced this pull request Mar 16, 2026
v5.17.0 includes strict extension checks (go-git/go-git#1861) but it
causes problems for `worktreeconfig` because it wasn't added to the
supported extension list until go-git/go-git#1877. Until it's released,
we need to keep the version back.

Signed-off-by: Charith Ellawala <charith@cerbos.dev>
cgrdavies added a commit to cgrdavies/go-git that referenced this pull request Apr 12, 2026
extensions() normalises all extension names with strings.ToLower, but
the extensionsValidForV0 map keys were stored in camelCase. Because
Go map lookups are case-sensitive, this caused every repository with
extensions.worktreeConfig = true (set automatically by modern git when
running `git sparse-checkout set` or `disable`) to fail to open with:

  core.repositoryformatversion does not support extension: worktreeconfig

This regression was introduced in v5.17.0 by go-git#1861 and reported in
go-git#1939 and go-git#1943. The fix has landed in main (v6) via go-git#1877 but was
not backported to v5.

This patch lowercases the three affected map keys to match the
normalised input, resolving the regression without changing any
other behaviour.
cgrdavies added a commit to cgrdavies/go-git that referenced this pull request Apr 12, 2026
This patch makes go-git open repositories that have
`extensions.worktreeConfig = true` set in their config — a common
situation for modern git clients, since `git sparse-checkout set` and
`git sparse-checkout disable` enable the extension automatically.

There are two separate bugs in v5.17.x that must be fixed together:

1. extensions() normalises extension names with strings.ToLower, but
   the extensionsValidForV0 map keys were stored in camelCase. Go map
   lookups are case-sensitive, so the lookup for the normalised key
   "worktreeconfig" never matched the map key "worktreeConfig", and
   the V0 validation unconditionally rejected the extension.

2. After passing the V0 gate, verifyExtensions then checks every
   extension against builtinExtensions. worktreeConfig was missing
   from that map, so v5 still rejected it with
   "unknown extension: worktreeconfig".

The first bug was introduced in v5.17.0 (go-git#1861). Both are fixed in
main (go-git#1877) which is only available in v6-alpha. This patch applies
the minimum viable backport for v5: lowercase the V0 map keys and add
worktreeconfig to builtinExtensions. Per-worktree config files
(.git/config.worktree) are not read, which matches behaviour prior
to v5.17.0 — the shared repository config continues to work
unchanged.

Refs: go-git#1939, go-git#1943, go-git#1877
cgrdavies added a commit to cgrdavies/go-git that referenced this pull request Apr 12, 2026
This patch makes go-git open repositories that have
`extensions.worktreeConfig = true` set in their config — a common
situation for modern git clients, since `git sparse-checkout set` and
`git sparse-checkout disable` enable the extension automatically.

There are two separate bugs in v5.17.x that must be fixed together:

1. extensions() normalises extension names with strings.ToLower, but
   the extensionsValidForV0 map keys were stored in camelCase. Go map
   lookups are case-sensitive, so the lookup for the normalised key
   "worktreeconfig" never matched the map key "worktreeConfig", and
   the V0 validation unconditionally rejected the extension.

2. After passing the V0 gate, verifyExtensions then checks every
   extension against builtinExtensions. worktreeConfig was missing
   from that map, so v5 still rejected it with
   "unknown extension: worktreeconfig".

The first bug was introduced in v5.17.0 (go-git#1861). Both are fixed in
main (go-git#1877) which is only available in v6-alpha. This patch applies
the minimum viable backport for v5: lowercase the V0 map keys and add
worktreeconfig to builtinExtensions. Per-worktree config files
(.git/config.worktree) are not read, which matches behaviour prior
to v5.17.0 — the shared repository config continues to work
unchanged.

Refs: go-git#1939, go-git#1943, go-git#1877
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants