Written by Claude (Claude Code) on behalf of Matt Edmondson. The analysis and proposed shape below are Claude's, filed at Matt's request.
Five verbs in this library wrap git commands that accept a submodule-recursion flag, and none of them expose it. grep -rn 'recurse\|Recurse' GitIntegration --include='*.cs' (excluding obj/) returns nothing.
Assumes submodules are now in scope for the local layer — see the submodule listing issue for the scope note.
Current option surfaces, for reference
IGitCloneBuilder WithBranch, WithDepth, Bare, ReportingProgress
IGitFetchBuilder FromRemote, AllRemotes, Prune, WithTags, WithDepth, ReportingProgress
IGitPullBuilder FromRemote, WithBranch, FastForwardOnly, Rebase, Merge, ...
IGitCheckoutBuilder CreatingBranch, Force, Detach
IGitPushBuilder ToRemote, WithBranch, SettingUpstream, Force, ForceWithLease, DeletingRemoteBranch, DryRun, ReportingProgress
Push's flag is a different thing and must not share a name
This is the part worth getting right. clone, checkout, fetch, and pull all use --recurse-submodules to mean "also operate on the submodules' working trees or refs". push --recurse-submodules means something else entirely: it takes check, on-demand, only, or no, and it governs whether git verifies or pushes the submodules' own commits to their own remotes before pushing the superproject. Giving both the same method name would put one word on two unrelated behaviours.
Suggested shape
Two enums, because the accepted values differ:
GitSubmoduleRecursion { No, Yes, OnDemand } for fetch and pull, which accept --recurse-submodules=<value>.
GitSubmodulePushCheck { No, Check, OnDemand, Only } for push.
Then:
IGitCloneBuilder.RecursingSubmodules() — a plain flag; git also accepts an optional pathspec, which can be added later if wanted.
IGitCheckoutBuilder.RecursingSubmodules() — a plain flag.
IGitFetchBuilder.RecursingSubmodules(GitSubmoduleRecursion) and IGitPullBuilder.RecursingSubmodules(GitSubmoduleRecursion).
IGitPushBuilder.CheckingSubmodules(GitSubmodulePushCheck) — deliberately a different verb in the name.
Each should throw InvalidEnumArgumentException for an unrecognised enum value, matching IGitStatusBuilder.WithUntrackedFiles(GitUntrackedFilesMode).
Two things to verify during implementation
Does fetch --porcelain still behave with --recurse-submodules? GitFetchBuilder probes the git version and degrades below 2.41 because fetch --porcelain only exists from then (CLAUDE.md architecture point 7). Whether the porcelain stream still covers only the superproject's refs, or interleaves submodule output that GitFetchParser would then misread, needs checking against a real repository rather than assuming. If it interleaves, GitFetchResult.DetailAvailable is already the honest way to report that detail is unavailable for this combination.
checkout --recurse-submodules can discard work. Unlike the other four, it will overwrite a submodule's checked-out state. IGitCheckoutBuilder already has Force(), and its remarks explain the checkout-versus-switch choice; the new flag's remarks should say plainly what it can destroy.
Motivating use case
A consumer implementing a guarded pull workflow wants pull --recurse-submodules so that a single invocation moves the superproject and its submodules together, rather than pulling and then discovering the submodules are stale.
Written by Claude (Claude Code) on behalf of Matt Edmondson. The analysis and proposed shape below are Claude's, filed at Matt's request.
Five verbs in this library wrap git commands that accept a submodule-recursion flag, and none of them expose it.
grep -rn 'recurse\|Recurse' GitIntegration --include='*.cs'(excludingobj/) returns nothing.Assumes submodules are now in scope for the local layer — see the submodule listing issue for the scope note.
Current option surfaces, for reference
Push's flag is a different thing and must not share a name
This is the part worth getting right.
clone,checkout,fetch, andpullall use--recurse-submodulesto mean "also operate on the submodules' working trees or refs".push --recurse-submodulesmeans something else entirely: it takescheck,on-demand,only, orno, and it governs whether git verifies or pushes the submodules' own commits to their own remotes before pushing the superproject. Giving both the same method name would put one word on two unrelated behaviours.Suggested shape
Two enums, because the accepted values differ:
GitSubmoduleRecursion { No, Yes, OnDemand }forfetchandpull, which accept--recurse-submodules=<value>.GitSubmodulePushCheck { No, Check, OnDemand, Only }forpush.Then:
IGitCloneBuilder.RecursingSubmodules()— a plain flag; git also accepts an optional pathspec, which can be added later if wanted.IGitCheckoutBuilder.RecursingSubmodules()— a plain flag.IGitFetchBuilder.RecursingSubmodules(GitSubmoduleRecursion)andIGitPullBuilder.RecursingSubmodules(GitSubmoduleRecursion).IGitPushBuilder.CheckingSubmodules(GitSubmodulePushCheck)— deliberately a different verb in the name.Each should throw
InvalidEnumArgumentExceptionfor an unrecognised enum value, matchingIGitStatusBuilder.WithUntrackedFiles(GitUntrackedFilesMode).Two things to verify during implementation
Does
fetch --porcelainstill behave with--recurse-submodules?GitFetchBuilderprobes the git version and degrades below 2.41 becausefetch --porcelainonly exists from then (CLAUDE.mdarchitecture point 7). Whether the porcelain stream still covers only the superproject's refs, or interleaves submodule output thatGitFetchParserwould then misread, needs checking against a real repository rather than assuming. If it interleaves,GitFetchResult.DetailAvailableis already the honest way to report that detail is unavailable for this combination.checkout --recurse-submodulescan discard work. Unlike the other four, it will overwrite a submodule's checked-out state.IGitCheckoutBuilderalready hasForce(), and its remarks explain thecheckout-versus-switchchoice; the new flag's remarks should say plainly what it can destroy.Motivating use case
A consumer implementing a guarded pull workflow wants
pull --recurse-submodulesso that a single invocation moves the superproject and its submodules together, rather than pulling and then discovering the submodules are stale.