A Git Worktree Is Not a Separate Configuration Scope
A release engineer opens a linked worktree, checks its branch, and changes a setting with git config --local. Another checkout of the same repository immediately reports the new value. The tempting explanation is that Git confused the branches. The more useful question is which configuration file the command actually changed.
The Git worktree configuration manual describes repository configuration as shared by default. A separate working directory is therefore not sufficient evidence of separate configuration ownership. A linked worktree has private administrative resources, but the repository also has a common administrative directory. The diagnostic target is the relationship between those resources, not a comparison of branch names.
This playbook follows one synthetic, disposable experiment using the harmless custom key lab.profile. The reproduced run used Git 2.47.3 and Python 3.13.5; all eight diagnostic cases passed. The preparation record reported the same results. These observations concern the stated client and fixture, not every historical Git version, and they do not establish that this patch is the newest release.
The required evidence is an effective value together with its scope, origin, and expected fallback in every checkout. The experiment deliberately includes a refused command, a temporary command override, a changed shared default, a newly added worktree, and removal of a private override. Real-repository migration remains a separate decision requiring configuration backups, compatibility review, and explicit ownership.
Diagnose a setting that crossed checkout boundaries
Start with three checkouts called main, alpha, and beta. Each has a different checkout directory, and each is on its corresponding branch. The claim under test is that writing a local setting while operating in alpha changes only alpha. Do not accept that claim merely because the shell prompt displays the expected branch.
Three identities matter. The checkout directory identifies the working files. The branch identifies the development line currently checked out. The administrative paths identify where Git stores the repository resources that a command reads or writes. In this experiment, the first two identities differ across checkouts while the common configuration resource remains the same.
Define the desired policy before experimenting. Initially, all three checkouts should use a shared default. Later, alpha and beta should have explicit private overrides while main continues using that default. A new checkout should start with the common value, and removing alpha's override should reveal the common value again. Those transitions make the intended configuration ownership observable.
A successful setter is not an acceptance result. It proves only that the requested operation completed under that invocation's conditions. The useful follow-up is a read from every affected checkout, with enough provenance to determine whether the operation changed a common file or a private one. Checking only the directory where the setter ran leaves the original isolation assumption untested.
Treat this as a configuration experiment, not a general repair recipe. No production repository, credential setting, hook configuration, or remote endpoint is needed. The seed commit exists only to support the worktree fixture. The acceptance question remains narrow: did the intended value come from the intended configuration scope, and does the next lifecycle transition preserve the declared policy?
Create a disposable multi-worktree environment
Use an owned working directory for the script and evidence output. The driver creates a fresh temporary root containing the repository, linked checkouts, and isolated user configuration. Its TemporaryDirectory context removes that temporary root on normal completion and when an exception unwinds the context. It does not retain the repository for later interactive investigation.
Save the complete source below as worktree_lab.py. Select Git 2.47.3 and Python 3.13.5 for a reproduction of this result, and record what actually executes rather than relying on an installation label. The driver locates Git through PATH and records its version in the final JSON. It records the running Python version separately.
git --version
python --version
python worktree_lab.py > worktree-config-evidence.json
Run Python without optimization: the fixture uses assert for its checks, as described in the Python assertion statement documentation. A process launched with assertions disabled cannot supply the same acceptance evidence. Retain the process exit status and any stderr alongside the JSON. If an assertion or Git command fails, the script raises an exception before printing its final report; an empty output file is not a passed experiment.
The observed environment reported Git 2.47.3 and Python 3.13.5. This driver was executed successfully, and its recorded origins were checked separately against the intended common and private paths. This distinguishes value assertions embedded in the driver from the additional provenance review required by the acceptance policy.
Isolate global configuration without falsifying the test
The driver supplies a temporary HOME, a temporary XDG_CONFIG_HOME, GIT_CONFIG_NOSYSTEM=1, and an explicit GIT_CONFIG_GLOBAL path. It then deliberately writes global-default into that controlled global file. The Git configuration environment documentation explains the configuration-path overrides and system-configuration exclusion used here.
This arrangement does not edit the operator's normal global configuration. It also preserves a meaningful precedence test: the controlled global value exists, but the repository value should win when present. Removing every lower layer would make the experiment less informative about fallback.
The subprocess environment is an explicit allowlist, not a copy of the full parent environment. Nevertheless, this is a controlled fixture, not an operating-system sandbox. Use trusted executables and an owned directory. The script also writes synthetic commit identity settings and enables the extension; lab.profile is the only application-style setting whose changing values are under test.
Readers needing prerequisite orientation can use Git setup and version-control foundations. The procedure here starts after that foundation and does not depend on adoption statistics or a broader beginner tutorial.
Capture the real administrative paths
Resolve the paths through Git rather than constructing them from the checkout name. The repository layout reference distinguishes common repository configuration from the private worktrees/<id>/config.worktree resource. A linked checkout's .git entry points to its administrative directory; treating it as an ordinary directory containing that checkout's independent config starts from the wrong model.
The rev-parse path-resolution options provide the necessary observations: --absolute-git-dir identifies the private administrative directory, --git-common-dir identifies the common directory, and --git-path config.worktree resolves the worktree configuration location. The driver records all three alongside values and origins.
A resolved config.worktree path is a location, not proof that the file exists, contains the key, or participates in the effective lookup. Keep path discovery separate from configuration discovery. The origin record establishes which sources actually supplied the sentinel candidates for the invocation. This avoids another misleading shortcut: finding an apparently private pathname and assuming that a prior setter must have used it. The observed path and the observed source must agree with the operation being qualified.
The complete executable driver follows. It is reproduced once, including its refusal check, exception path, assertions, and temporary-directory cleanup.
import json
import os
import platform
import shutil
import subprocess
import tempfile
from pathlib import Path
GIT = shutil.which('git')
if not GIT:
raise SystemExit('Git executable required')
def main():
with tempfile.TemporaryDirectory(prefix='worktree-config-') as tmp:
root = Path(tmp)
home = root / 'home'
home.mkdir()
(root / 'xdg').mkdir()
env = {'PATH': os.environ.get('PATH', '/usr/bin:/bin'),
'HOME': str(home), 'XDG_CONFIG_HOME': str(root / 'xdg'),
'GIT_CONFIG_NOSYSTEM': '1',
'GIT_CONFIG_GLOBAL': str(home / '.gitconfig'),
'LC_ALL': 'C', 'GIT_TERMINAL_PROMPT': '0'}
def git(where, args):
result = subprocess.run([GIT, '-C', str(where), args],
env=env, text=True, capture_output=True)
if result.returncode:
raise RuntimeError(f'{args}: {result.returncode}: {result.stderr}')
return result.stdout.strip()
main_tree, alpha, beta = (root / x for x in ('main', 'alpha', 'beta'))
main_tree.mkdir()
git(main_tree, 'init', '-q', '-b', 'main')
git(main_tree, 'config', 'user.name', 'Disposable Lab')
git(main_tree, 'config', 'user.email', '[email protected]')
(main_tree / 'README.txt').write_text('fixture\n')
git(main_tree, 'add', 'README.txt')
git(main_tree, 'commit', '-q', '-m', 'fixture')
git(main_tree, 'worktree', 'add', '-q', '-b', 'alpha', str(alpha))
git(main_tree, 'worktree', 'add', '-q', '-b', 'beta', str(beta))
trees = [main_tree, alpha, beta]
ledger = []
def capture(case, expected):
values = [git(p, 'config', '--get', 'lab.profile') for p in trees]
assert values == expected, (case, values)
details = []
for p in trees:
assert git(p, 'status', '--porcelain=v1') == ''
details.append({
'tree': p.name,
'effective': git(p, 'config', '--get', 'lab.profile'),
'origin_scope': git(
p, 'config', '--show-origin', '--show-scope',
'--get-all', 'lab.profile'
).replace(tmp, '<LAB>'),
'git_dir': git(
p, 'rev-parse', '--absolute-git-dir'
).replace(tmp, '<LAB>'),
'common_dir': git(
p, 'rev-parse', '--path-format=absolute', '--git-common-dir'
).replace(tmp, '<LAB>'),
'worktree_config_path': git(
p, 'rev-parse', '--git-path', 'config.worktree'
).replace(tmp, '<LAB>')
})
ledger.append({'case': case, 'details': details})
git(main_tree, 'config', '--global', 'lab.profile', 'global-default')
git(main_tree, 'config', '--local', 'lab.profile', 'shared-default')
capture('G1 shared baseline', ['shared-default'] 3)
git(alpha, 'config', '--local', 'lab.profile', 'from-alpha-local')
capture('G2 local means repository', ['from-alpha-local'] 3)
refused = subprocess.run([GIT, '-C', str(beta), 'config', '--worktree',
'lab.profile', 'from-beta-no-extension'],
env=env, text=True, capture_output=True)
assert refused.returncode == 128 and 'worktreeConfig' in refused.stderr
capture('G3 worktree flag refused before extension', ['from-alpha-local'] * 3)
ledger[-1]['refusal'] = {
'status': refused.returncode,
'stderr': refused.stderr.strip()
}
git(main_tree, 'config', '--local', 'lab.profile', 'shared-default')
git(main_tree, 'config', '--local', 'extensions.worktreeConfig', 'true')
git(alpha, 'config', '--worktree', 'lab.profile', 'alpha-only')
git(beta, 'config', '--worktree', 'lab.profile', 'beta-only')
capture(
'G4 explicit per-worktree overrides',
['shared-default', 'alpha-only', 'beta-only']
)
override = git(
alpha, '-c', 'lab.profile=one-command', 'config',
'--show-origin', '--show-scope', '--get', 'lab.profile'
)
assert override.endswith('one-command') and 'command' in override
capture(
'G5 command override does not persist',
['shared-default', 'alpha-only', 'beta-only']
)
ledger[-1]['command_override'] = override
git(main_tree, 'config', '--local', 'lab.profile', 'shared-v2')
capture('G6 shared default changed', ['shared-v2', 'alpha-only', 'beta-only'])
gamma = root / 'gamma'
git(main_tree, 'worktree', 'add', '-q', '-b', 'gamma', str(gamma))
trees.append(gamma)
capture(
'G7 new checkout has no inherited per-tree override',
['shared-v2', 'alpha-only', 'beta-only', 'shared-v2']
)
git(alpha, 'config', '--worktree', '--unset', 'lab.profile')
capture(
'G8 unset exposes shared fallback',
['shared-v2', 'shared-v2', 'beta-only', 'shared-v2']
)
print(json.dumps({'git': git(main_tree, '--version'),
'python': platform.python_version(), 'controlled_environment': True,
'cases': ledger}, indent=2))
if name == '__main__':
main()In the recorded output, <LAB> replaces the temporary root for readability. main has both its private and common Git directory at <LAB>/main/.git. alpha has its private directory at <LAB>/main/.git/worktrees/alpha, while its common directory remains <LAB>/main/.git. beta follows the same pattern with its own private directory.
The main checkout reports .git/config.worktree as a relative path; the linked checkouts report absolute paths in this run. Preserve that distinction in the raw evidence, then resolve relative paths against the command's checkout when comparing identities. Differently spelled paths can identify the same file, and an administrative directory name should not be guessed from a branch name.
This boundary also explains why linked worktrees used for stash recovery address a different operational problem. Separate working files and index state are not a promise that every administrative resource is private. This article examines configuration provenance rather than importing a stash-recovery procedure.
Show what --local changes from alpha
G1 establishes a controlled global value and a common repository default. The global file contains global-default; the repository configuration contains shared-default. An unqualified lookup from each checkout returns shared-default. The complete provenance output also retains the lower global candidate, making the precedence relationship visible rather than merely implied.
G2 then makes one change from alpha:
git -C alpha config --local lab.profile from-alpha-local
This command is an explanatory excerpt, to be issued from the common parent of the named checkouts in an equivalent owned setup. The full driver instead supplies absolute checkout paths. Its temporary directories disappear when it exits, so these excerpts are not commands to run against leftover directories from the completed script.
The observed comparison is below. Here and in later tables, C means the canonical common Git directory, <LAB>/main/.git. Thus C/config identifies the same repository configuration file in every row.
Case | Checkout | Effective value | Winning scope | Resolved origin |
G1 | main | shared-default | local | C/config |
G1 | alpha | shared-default | local | C/config |
G1 | beta | shared-default | local | C/config |
G2 | main | from-alpha-local | local | C/config |
G2 | alpha | from-alpha-local | local | C/config |
G2 | beta | from-alpha-local | local | C/config |
The change did not travel between three independent configuration files. The three reads selected the same changed resource. That is the practical distinction between a shared setting and an unexpectedly synchronized set of private settings. The command's checkout selected the repository context; it did not redefine the ownership of the common file.
Read the common file rather than the checkout name
Use both a value lookup and a provenance lookup:
git config --get lab.profile
git config --show-origin --show-scope --get-all lab.profile
The configuration scope and provenance options distinguish a scope-limited operation from a lookup across available configuration. A --local write selects repository configuration. A later unqualified read can select a different, higher-precedence source once such a source exists.
For main, the raw origin in G2 is file:.git/config; for alpha and beta, it is the absolute common-config path with the temporary prefix normalized. Do not compare those strings as though relative and absolute spelling proved different files. Normalize them using the recorded command context, while retaining the original output for review.
Do not discard the controlled global row merely because it is not currently winning. Its continued presence explains the lower layer available beneath the common setting. At the same time, do not label it a tested effective fallback: G1 and G2 leave the common entry present. The record proves that the global candidate was read, whereas an actual transition selecting that candidate would require another operation. Separating candidate visibility from effective selection prevents the provenance report from claiming more than the experiment executed.
This is why the evidence belongs to every checkout. A correct value in alpha alone is consistent with both the intended private-change hypothesis and the actual shared-change result. The other two rows discriminate between those explanations.
Record the pre-extension refusal honestly
G3 tests the initially tempting repair: use --worktree without first enabling worktree-specific configuration. At this point the repository contains the main checkout plus two linked worktrees, and the extension is not enabled. The command attempted from beta is:
git -C beta config --worktree lab.profile from-beta-no-extension
In the reproduced Git 2.47.3 run, the process returned status 128. Its stderr was:
fatal: --worktree cannot be used with multiple working trees unless the config
extension worktreeConfig is enabled. Please read "CONFIGURATION FILE"
section in "git help worktree" for details
All three effective values remained from-alpha-local, with the same common configuration provenance. The preparation record reported this refusal too. There is therefore no basis for describing the tested attempt as a silent write to the shared file.
The current git-config option description gives a generic equivalence between --worktree and --local when the extension is not enabled. That wording must not erase the observed refusal in this particular multi-worktree setup. Conversely, this experiment does not establish the behavior of a single-worktree repository or every older client. Preserve the tested condition instead of inventing a release date for a supposed behavior change.
Operationally, the failed hypothesis is useful evidence. A test suite that silently changed the expected status to zero would lose the negative control. A report that retained only the final successful overrides would hide the reason the initial repair attempt was unsuitable. Record the client version, extension state, participating worktrees, return status, stderr, and subsequent value/origin observations together.
The scope of the unchanged-state claim also matters. G3 verifies the sentinel values and their origins after the refusal. It is not a byte-for-byte audit of every administrative resource. Similarly, all eight cases assert clean git status --porcelain=v1 output even while configuration values change. That status observation cannot stand in for a configuration comparison.
A tracked application configuration file raises another distinct question. The discussion of tracked files versus ignore rules concerns index ownership, not the destination of Git's administrative setters. Do not apply index cleanup to this problem simply because both situations involve something called configuration. The corrective action must match the resource whose ownership was misunderstood.
Audit extension compatibility before changing a real repository
The disposable fixture owns its repository and knows its initial configuration. A real repository may already contain settings with consequences far beyond an inert label. Before enabling extensions.worktreeConfig, identify the client population, configuration owners, current extension state, common configuration file, and every existing private configuration file.
The worktree migration guidance warns about older Git versions that do not understand the extension. Treat that warning as a compatibility requirement, not as a qualification result for an untested client. Inventory the actual Git binaries used by developer tools, CI agents, maintenance jobs, and other processes opening the same repository on disk. A successful test with one executable does not approve the others.
Back up the common configuration and each existing config.worktree before changing their contents. Record original paths and whether each file was present or absent. Identify who may write to those files during the change window. An unexplained file, an unowned setting, an inaccessible checkout, or an unknown client moves the decision to HOLD.
The backup inventory should follow resources, not repeat checkout labels. All linked checkouts can point to one common file, so identify that shared file once while recording every checkout that depends on it. Private files need their own entries. An existing private file may contain settings even when the current extension state keeps them from participating in the tested lookup. Preserve that file instead of treating an inactive layer as disposable. If a path cannot be resolved or inspected, record the gap before approving a migration.
Treat core.worktree and core.bare as migration-sensitive
The specific worktreeConfig migration requirements call for moving a present core.worktree value from common configuration to the main worktree's configuration file. They also call for moving core.bare when its value is true. The extension removes the former main-worktree-only treatment of these shared settings, so their location must be reviewed before enabling it.
Do not turn this into an unconditional instruction to relocate every core.bare value. In particular, the true-value caveat matters. The fixture's ordinary non-bare initialization is not a tested migration of a bare repository. The appropriate treatment of an unfamiliar repository layout must be decided by its administrator, not inferred from the harmless sentinel demonstration.
The following are read-only inventory examples for the repository being assessed. Capture each exit status immediately:
git --version
git worktree list --porcelain
git config --local --show-origin --show-scope --get-all core.worktree
printf 'core.worktree status=%s\n' "$?"
git config --local --show-origin --show-scope --get-all core.bare
printf 'core.bare status=%s\n' "$?"
git config --local --show-origin --show-scope --get-all extensions.worktreeConfig
printf 'worktreeConfig status=%s\n' "$?"For these lookups, an absent key and an operational failure require different interpretations. Do not discard all nonzero statuses with a blanket success fallback. These commands inspect selected repository-scope keys; they are not a complete include-chain, environment, or client-compatibility audit. The migration decision remains HOLD until those ownership and compatibility questions are resolved and a backed-up change plan exists.
Enable explicit per-worktree overrides
G4 begins only after the failed pre-extension attempt has been recorded. In the disposable repository, the driver restores the shared sentinel to shared-default and enables extensions.worktreeConfig in common configuration. It then writes alpha-only and beta-only using --worktree from the corresponding checkouts.
git -C main config --local lab.profile shared-default
git -C main config --local extensions.worktreeConfig true
git -C alpha config --worktree lab.profile alpha-only
git -C beta config --worktree lab.profile beta-only
The worktree-specific configuration mechanism is explicit: with the extension enabled, --worktree addresses the configuration path resolved for that checkout. This is not a change in the meaning of --local, and it does not make the common configuration disappear.
The reproduced result was:
Checkout | Effective value | Winning scope | Resolved origin |
main | shared-default | local | C/config |
alpha | alpha-only | worktree | C/worktrees/alpha/config.worktree |
beta | beta-only | worktree | C/worktrees/beta/config.worktree |
The complete alpha provenance output still lists the global default and the common default before its private override. The private value wins for that scalar lookup; it does not delete the lower layers. That distinction becomes important when the common value changes or when the private value is removed.
Inspect all three rows rather than concentrating on the two successful setters. main is the control proving that the per-worktree writes did not replace the shared sentinel. alpha and beta prove that separate private files can supply distinct effective values. The recorded private paths supply the ownership evidence missing from the original assumption about directory boundaries.
A correct value with a local/common origin would fail this policy even if it happened to equal alpha-only. The declared target is a private override for alpha, not merely a particular string. Likewise, placing both private-looking values in a single common file would not implement the desired relationship between checkouts.
The main checkout also has a resolved config.worktree location. Its lack of a private sentinel in G4 is a deliberate control, not evidence that the main checkout is forbidden from having worktree-specific configuration. No main-worktree override is written in this experiment. This distinction helps reviewers read the matrix correctly: the main row demonstrates the selected fallback policy for this setup, while the private paths show the administrative locations available to Git. Neither a path's existence nor a branch label establishes the intended policy by itself.
In a real migration, this sequence is appropriate only after compatibility, backups, and migration-sensitive settings have been qualified. G4 shows the behavior of the controlled repository after deliberate enablement. It does not authorize enabling the extension in an uninspected repository or copying a private configuration file between unrelated worktrees.
Test precedence beyond the first successful override
G5 asks whether a value observed during one invocation is persistent configuration. From alpha, the driver supplies -c lab.profile=one-command and requests the effective value with scope and origin:
git -C alpha -c lab.profile=one-command \
config --show-origin --show-scope --get lab.profile
The recorded result identifies command scope and the value one-command. The subsequent ordinary capture returns alpha-only again. No new persistent sentinel was established by that command override. The configuration scope rules classify -c under command scope, separately from configuration-file scopes.
Keep those two observations distinct in the evidence matrix. During the explicitly overridden invocation, alpha resolves one-command. Afterward, its persistent effective value remains alpha-only. A table that labels the entire G5 state as permanently changed would contradict the driver's actual capture order.
G6 tests the opposite direction: change a lower, shared layer while private overrides remain present. The common value becomes shared-v2. The reproduced effective values are shared-v2 in main, alpha-only in alpha, and beta-only in beta. Both private checkouts still list the new common value among their candidates.
Notice that G6 still uses --local after the extension has been enabled. The extension did not make that setter private to main; its changed candidate appears in the provenance collected from both linked checkouts as well. Their effective values remain private because of their higher-precedence entries, not because the common write stopped reaching their configuration context. An audit that prints only the winning values in alpha and beta would miss that the shared layer beneath them changed.
This paired test answers two different questions. G5 shows a higher-precedence, nonpersistent override. G6 shows that a changed common default does not displace the existing private sentinels. Neither result means that config.worktree defeats every possible higher-precedence source, nor that a wrapper's process configuration is equivalent to the repository's stored configuration.
Distinguish effective reads from scope-limited reads
An unqualified git config --get lab.profile reports the selected value for this scalar lookup. By contrast, git config --local --get-all lab.profile inspects the repository scope, and git config --worktree --get-all lab.profile inspects the worktree-specific file when that scope is valid. A local read can therefore report shared-v2 while the effective read reports alpha-only.
Use the scope-limited readings to inspect ownership and the unqualified reading to inspect selection. A missing private key is not necessarily a missing effective key. An unexpected read failure is not an invitation to replace the result with an empty string and continue.
The fixture intentionally avoids multivalued settings, conditional includes, and environment-supplied configuration pairs. Those features require their own resolution and acceptance policy. Do not promote a successful scalar lookup into a complete audit of configuration merging, include selection, or every process context used by a release system.
Add a new worktree and remove an old override
A policy that works only for the current checkout set is incomplete. G7 creates gamma after the extension is enabled and after alpha and beta have private overrides. The question is not whether creation succeeds, but which source supplies the new checkout's sentinel.
The reproduced run returns shared-v2 in gamma, with local scope and the common configuration as its origin. gamma does not receive alpha-only. Its private administrative directory exists, but there is no private lab.profile entry supplying the result in this fixture.
This is fallback through an existing layer, not evidence that Git copied the common setting into a private file. The distinction matters for the next shared change. A new checkout that reads common configuration follows that shared policy until a higher-precedence value intervenes. A private copy with the same current string would represent a different ownership decision.
G8 then removes only alpha's private sentinel:
git -C alpha config --worktree --unset lab.profile
After the unset, alpha returns shared-v2 from common configuration. It does not return an absent value. The result across the lifecycle transition is:
Checkout | G7 effective value | G8 effective value | G8 winning source |
main | shared-v2 | shared-v2 | local, common config |
alpha | alpha-only | shared-v2 | local, common config |
beta | beta-only | beta-only | worktree, beta private file |
gamma | shared-v2 | shared-v2 | local, common config |
The unchanged rows are part of the evidence. Removing alpha's override should not remove beta's override, change the common default, or provision a private value in gamma. The four-checkout capture checks the effective values and records the origins needed to examine those distinctions.
For the unset control, preserve the before and after candidate lists, not just the changed effective string. In G7, alpha has global, local, and worktree candidates. In G8, the private sentinel candidate is no longer in its record and the common candidate supplies the result. That is stronger evidence of the intended transition than merely seeing shared-v2 afterward. It also distinguishes the operation from a command-scoped override that could temporarily produce the same visible string without removing the private entry.
The next operational decision depends on policy. An organization may require all new worktrees to begin with shared defaults, matching G7. Alternatively, it may require explicit per-worktree provisioning. In that second policy, successful creation with a common default is only an intermediate state; acceptance must wait for the private configuration and its provenance check.
Similarly, an unset is not automatically a repair. A syntactically valid fallback can still violate the intended checkout policy. G8 qualifies the particular fallback from alpha-only to shared-v2; it does not test removing the common key or every other private override. Record those remaining transitions as expectations unless they are separately exercised. Lifecycle acceptance should state both what changed and which expected relationships remained intact.
Build the scope acceptance matrix
Make configuration ownership a reviewable record rather than a label attached to a successful command. Each row should identify the checkout, effective value, winning scope, resolved source, common directory, private Git directory, and expected fallback. The policy and the observed record must be distinguishable so that a reviewer can detect an accidental match.
For compactness, retain C for <LAB>/main/.git. Let A, B, and G represent C/worktrees/alpha, C/worktrees/beta, and C/worktrees/gamma. These are aliases for the observed fixture paths, not rules for constructing administrative names in another repository.
The following matrix describes G7, before alpha's override is removed. Fallback means the value expected after removing that row's winning sentinel entry while leaving lower layers unchanged.
Checkout | Effective value | Scope | Origin | Common dir | Private Git dir | Expected fallback |
main | shared-v2 | local | C/config | C | C | global-default |
alpha | alpha-only | worktree | A/config.worktree | C | A | shared-v2 |
beta | beta-only | worktree | B/config.worktree | C | B | shared-v2 |
gamma | shared-v2 | local | C/config | C | G | global-default |
G8 actually exercises the alpha fallback. Removing beta's private entry and removing the common entry are not executed by this driver. Their table entries are expectations from the controlled layer arrangement, not additional passed cases. Removing a common entry would also affect other checkouts that select it; the row does not imply that such a removal is private.
Reject apparently correct values with the wrong provenance
Consider an illustrative policy error: common configuration and alpha's private file both contain shared-v2. The current string alone cannot distinguish a private override from common fallback. A later change to shared-v3 would discriminate between the two arrangements. That later change is a reasoning example here, not a ninth executed case.
Require the expected source even when the visible value looks right. Preserve raw origins and normalize relative paths only with recorded command context. A missing origin, unexpected source, or unqualified fallback cannot be repaired by declaring the value close enough.
Use four explicit decisions:
Decision | Evidence and owner | Authorized next step |
ACCEPT | Repository owner confirms value, scope, provenance, and required fallback evidence for every affected checkout. | Proceed only within the qualified policy and client set. |
REPAIR | Intended ownership is known, but a value, source, or tested transition is wrong. | Back up, migrate deliberately, and rerun the affected matrix. |
HOLD | Client support, scope ownership, required evidence, or an audit result is unresolved. | Resolve the uncertainty before enabling or migrating configuration. |
RESTORE | Repository owner approves a qualified rollback using identified configuration backups. | Revert only authorized, backed-up changes and revalidate the result. |
For an automated gate, explicitly compare the intended origin and scope alongside the value; the supplied driver records those fields but does not assert every origin string internally. Path comparison must account for the main checkout's relative origin. The provenance review for the reproduced run checked those recorded relationships separately. A later implementation should retain the underlying records when a comparison fails, so an operator can distinguish a wrong source from a path-normalization mistake rather than treating both as an unexplained value mismatch.
A fixture pass and a production ACCEPT are different claims. The eight cases supply evidence about the stated transitions; they do not approve untested fallbacks or an unknown client fleet. The automation owner should make missing evidence fail the gate, while the repository owner decides whether the resulting configuration matches the intended policy.
Keep config qualification separate from release content
A release pipeline should capture the configuration context before the command whose behavior it intends to qualify. At minimum, record the relevant effective value, all candidate origins for the audited key, the private Git directory, and the common directory. Keep that record associated with the job invocation rather than copying a value from an interactive shell on the same machine.
For this inert fixture, the diagnostic ingredients are:
git config --get lab.profile
git config --show-origin --show-scope --get-all lab.profile
git rev-parse --absolute-git-dir
git rev-parse --path-format=absolute --git-common-dir
git rev-parse --git-path config.worktree
These readings describe a configuration context. They do not demonstrate that an application or build system actually consumes lab.profile; the key is deliberately inert. In a real adaptation, identify the documented setting that the relevant command consumes and the scope in which that setting is honored. A generic configuration reader is not proof of every consumer's behavior.
Configuration qualification also does not establish release-payload completeness. The separate release archive file-selection checks address which tracked files enter an archive. Keep that artifact-selection question separate from the source of an administrative setting. The present fixture neither creates a release archive nor verifies a package manifest.
The same restriction applies to repository status. A clean working tree does not establish unchanged administrative configuration, as the observed sequence demonstrates. Conversely, an accepted configuration matrix does not recover pending work, repair an index, resolve a stash conflict, or authorize a branch reset. Those operations are outside this procedure.
There is also a timing boundary. A recorded value describes the observation point. Without controlling later writers and preserving the command environment, it does not prove that a subsequent process sees the identical configuration. This fixture does not test concurrent mutation. Treat a changed wrapper, unexpected override, or unexplained intervening write as a reason to requalify context rather than reuse an earlier green result.
The release owner should therefore receive two separate kinds of evidence when both are relevant: configuration provenance for the command context, and the appropriate content evidence for the produced artifact. Neither verdict substitutes for the other.
Plan restoration without losing per-worktree settings
Restoration begins before migration, with backups and a stated target. Preserve the common configuration and every existing config.worktree, including the distinction between an absent file and a present file without the audited key. Record which values were deliberately common, which were private, and which checkouts depended on fallback.
Stop concurrent configuration writers during the approved change and restoration window. Identify changes made after the backup before replacing any whole file. A backup taken earlier is not permission to erase unrelated, subsequently authorized settings. Where file replacement would overwrite legitimate changes, the owner must qualify a narrower restoration plan or keep the repository on HOLD.
Disabling the extension is not a universal, lossless rollback. The documented worktree configuration loading rule makes private-file participation conditional on the extension. Making those private values ineffective does not automatically restore the intended common values, relocate migration-sensitive settings correctly, or reconcile changes made since the backup.
A qualified RESTORE decision therefore identifies the authoritative backup, the exact configuration changes covered, the client performing the restoration, and the observations required afterward. It must address any core.worktree or true core.bare value moved during the approved migration. Do not infer the inverse procedure from the sentinel experiment alone.
After restoration, repeat effective and scope-limited reads in every affected checkout. A worktree-scope read must be used only where valid, with missing-key and command-refusal statuses handled explicitly. Recheck the intended fallback rather than treating a familiar string as proof that the previous ownership model returned.
The restoration target must also state what happens to private values intentionally introduced after the original backup. Keeping them active, preserving them only as inactive files, and removing only the approved additions are different outcomes. None follows automatically from changing one extension flag. The administrator should resolve that choice before executing restoration, then assess the resulting value and origin in every dependent checkout. An unresolved ownership decision remains HOLD even when all relevant files are available in the backup.
The fixture's automatic cleanup is not this production rollback. It deletes an entirely owned temporary experiment at the end of its lifetime. RESTORE, by contrast, concerns carefully bounded configuration changes in a repository that must retain unrelated state. Confusing those two meanings of cleanup would remove the very ownership discipline the playbook is intended to establish.
Assign ownership and rerun after toolchain changes
Assign three responsibilities even when one person fills more than one role. The repository administrator decides which values should be shared or private and approves the migration-sensitive settings and backups. The automation owner implements the evidence capture and acceptance gate. The client-maintenance owner identifies and qualifies the Git executables that open the repository.
Keep the complete eight-case ledger rather than only a passed label. Retain the driver version or digest, actual Git and Python versions, ordered case names, normalized and raw path context where appropriate, values, origins, refusal evidence, and temporary command-override result. This allows a later reviewer to compare behavior without relying on a narrative reconstructed after the environment has changed.
The supplied driver automatically records the versions and diagnostic fields shown in its source. It does not automatically collect every item a production change record might require, such as an operator identity, client inventory, script digest, or every subprocess's successful stderr. Add those records through an explicit evidence procedure rather than claiming they already exist in its JSON.
Rerun after a Git upgrade, wrapper change, or configuration-policy change. Compare the negative control as carefully as the successful cases. An unexpected success at G3, a different origin spelling, or a missing fallback requires investigation; do not weaken the expected result merely to restore a green dashboard. First determine whether the setup, client contract, or policy has changed.
The organizational context of Git ownership in data-engineering workflows is relevant to this division of responsibility. Versioned project content and accountable collaboration are useful foundations, but they do not replace direct evidence about Git's own administrative files.
Keep the compatibility boundary visible
No bare-repository migration, unsupported-client execution, conditional-include matrix, multivalued-key merge policy, environment-supplied configuration-pair matrix, concurrent-writer race, or worktree removal was exercised. The fixture does not change shared credentials or production remotes, and it is not a hook-validation procedure. These exclusions belong to the technical interpretation of the results, not merely to an internal disclaimer.
The successful run establishes the eight named transitions on Git 2.47.3 with Python 3.13.5 in the controlled environment. The same sequence is a useful regression candidate elsewhere, but a new environment earns its own observed result only when it is actually run and its provenance inspected. An unchanged branch name or a familiar directory layout cannot substitute for that rerun.
Before sharing evidence from a real adaptation, review it for user paths, private configuration values, and other sensitive details. The public fixture uses inert values; that does not make every later configuration dump suitable for publication. Retention and disclosure should preserve reviewability without exposing data unrelated to the acceptance question.
Strengthen practical DevOps configuration judgement
The operational lesson is to separate location, ownership, selection, and fallback. A checkout directory tells you where you are working. Provenance tells you which configuration resource supplied a value. A controlled transition tells you whether the intended relationship between those resources survives change.
For broader foundations, review Refonte Learning's DevOps Engineering Program curriculum. Its published page describes three months at 12–14 hours per week, covering Linux and scripting, Git and GitHub, CI/CD, Docker and Kubernetes, Terraform, cloud platforms, monitoring and logging, and a capstone. It describes mentoring and virtual internship opportunities while also using potential-internship language. Admission refers to learners pursuing a bachelor's or postgraduate route; successful completion is associated with a Training Certificate and a Certificate of Internship.
These advertised features do not establish that this specific worktree experiment is taught or guarantee placement or employment. The practical standard remains evidence-based: identify the write scope, inspect every affected checkout, prove provenance, and test the fallback required by the policy.
