Skip to content

git pull/clone always fails: --no-recurse-submodules placed before subcommand in GIT_SSRF_FLAGS #813

@seaneoliver

Description

@seaneoliver

Summary

pullRepo and cloneRepo in src/core/git-remote.ts always fail with error: unknown option: --no-recurse-submodules (exit 129) because --no-recurse-submodules is included in GIT_SSRF_FLAGS (line 29-34) and spliced before the subcommand. That flag is only valid as a clone/pull/fetch subcommand option, not a top-level git flag.

The error is masked at runtime because:

  1. The error message is sliced to 100 chars in src/commands/sync.ts:405 (msg.slice(0, 100)), which truncates before reaching the actual unknown option error.
  2. performSync catches the failure and prints a Warning: then proceeds with local state, so gbrain sync reports Already up to date. regardless. The pull never actually runs.

Net effect: every gbrain sync against a remote-backed brain silently no-ops on the pull phase. Anyone pushing to the same brain from a second machine never has their changes pulled down.

Reproduction

# Set up any brain repo with a remote
gbrain sync
# Output:
#   [gbrain phase] sync.git_pull error 11ms (git pull failed in <path>: Command failed: git -C <path> -c http.f
#   Warning: git pull failed: git pull failed in <path>: Command failed: git -C <path> -c http.followRedirects=
#   Already up to date.

Run gbrain's exact invocation manually to see the underlying error:

GIT_TERMINAL_PROMPT=0 GCM_INTERACTIVE=never GIT_ASKPASS=/bin/false SSH_ASKPASS=/bin/false \
  git -C <repo> \
    -c http.followRedirects=false -c protocol.file.allow=never -c protocol.ext.allow=never \
    --no-recurse-submodules pull --ff-only

# unknown option: --no-recurse-submodules
# (exit 129)

Move --no-recurse-submodules after pull (i.e. as a subcommand option) and it works:

git -C <repo> -c http.followRedirects=false ... pull --ff-only --no-recurse-submodules
# Already up to date.

Suggested fix

Split GIT_SSRF_FLAGS into top-level (-c flags) and subcommand-level (--no-recurse-submodules) sets:

export const GIT_SSRF_FLAGS = [
  '-c', 'http.followRedirects=false',
  '-c', 'protocol.file.allow=never',
  '-c', 'protocol.ext.allow=never',
] as const;

export const GIT_SSRF_SUBCMD_FLAGS = [
  '--no-recurse-submodules',
] as const;

Then update both call sites to append the subcommand flags after the subcommand:

// pullRepo (line 182):
const args = ['-C', repoPath, ...GIT_SSRF_FLAGS, 'pull', '--ff-only', ...GIT_SSRF_SUBCMD_FLAGS];

// cloneRepo (line 156):
const args = [...GIT_SSRF_FLAGS, 'clone', ...GIT_SSRF_SUBCMD_FLAGS];

Also worth fixing: the 100-char truncation in src/commands/sync.ts:405 hid the actual error message during debugging. Consider raising the limit or printing the underlying Error.message separately when verbose logging is on.

Environment

  • gbrain v0.31.3
  • macOS 25.3.0 (arm64)
  • git --version 2.x (Homebrew / Apple)
  • bun runtime

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions