(
projectPath: string,
layout: RemoteProjectLayout,
currentSnapshotPath: string,
initLogger: InitLogger,
abortSignal?: AbortSignal,
options: { forceNoThin?: boolean } = {}
)
| 2061 | } |
| 2062 | |
| 2063 | private async syncProjectSnapshotViaGitPush( |
| 2064 | projectPath: string, |
| 2065 | layout: RemoteProjectLayout, |
| 2066 | currentSnapshotPath: string, |
| 2067 | initLogger: InitLogger, |
| 2068 | abortSignal?: AbortSignal, |
| 2069 | options: { forceNoThin?: boolean } = {} |
| 2070 | ): Promise<void> { |
| 2071 | // `forceNoThin` opts the push out of thin-pack delta-base optimization. |
| 2072 | // This is set by the retry path after an "unresolved deltas" / "unpacker |
| 2073 | // error" failure: the remote couldn't resolve the thin pack's delta |
| 2074 | // bases, so we resend a self-contained pack on the next attempt instead |
| 2075 | // of rerunning the same thin push and failing the same way. |
| 2076 | const forceNoThin = options.forceNoThin === true; |
| 2077 | const prepareSnapshotDir = await execBuffered( |
| 2078 | this, |
| 2079 | `mkdir -p ${this.quoteForRemote(path.posix.dirname(currentSnapshotPath))}`, |
| 2080 | { cwd: "/tmp", timeout: 10, abortSignal } |
| 2081 | ); |
| 2082 | if (prepareSnapshotDir.exitCode !== 0) { |
| 2083 | throw new Error( |
| 2084 | `Failed to prepare remote snapshot directory: ${prepareSnapshotDir.stderr || prepareSnapshotDir.stdout}` |
| 2085 | ); |
| 2086 | } |
| 2087 | |
| 2088 | await this.transport.acquireConnection({ |
| 2089 | abortSignal, |
| 2090 | onWait: (waitMs) => logSSHBackoffWait(initLogger, waitMs), |
| 2091 | }); |
| 2092 | |
| 2093 | if (abortSignal?.aborted) { |
| 2094 | throw new Error("Sync aborted"); |
| 2095 | } |
| 2096 | |
| 2097 | initLogger.logStep("Pushing to remote..."); |
| 2098 | |
| 2099 | // Build the SSH remote URL pointing to the shared bare base repo. |
| 2100 | // Use ssh:// URL format (not SCP-style host:path) because: |
| 2101 | // - SCP-style breaks on IPv6 literals (first : is ambiguous) |
| 2102 | // - ssh:// handles ~/ paths natively via /~/ syntax |
| 2103 | // - ssh:// respects the port from GIT_SSH_COMMAND without -p duplication |
| 2104 | const baseRepoPath = layout.baseRepoPath; |
| 2105 | // ssh:// URLs: /~/ means home-relative, / means absolute, and relative |
| 2106 | // paths need /~/ prefix (resolved relative to home on the remote). |
| 2107 | let urlPath: string; |
| 2108 | if (baseRepoPath.startsWith("~/")) { |
| 2109 | urlPath = `/~/${baseRepoPath.slice(2)}`; |
| 2110 | } else if (baseRepoPath.startsWith("/")) { |
| 2111 | urlPath = baseRepoPath; |
| 2112 | } else { |
| 2113 | // Relative path (e.g., "src/project/.mux-base.git") — treat as |
| 2114 | // home-relative to match the old bundle flow's shell resolution. |
| 2115 | urlPath = `/~/${baseRepoPath}`; |
| 2116 | } |
| 2117 | // Bracket bare IPv6 addresses for URL syntax. The host field can be: |
| 2118 | // hostname → no change |
| 2119 | // user@hostname → no change |
| 2120 | // 2001:db8::1 → [2001:db8::1] (bare IPv6) |
no test coverage detected