mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
* fix(mobile): compare branches and show diffs with desktop's base-ref and whitespace settings Base-ref resolution moves to src/shared and the host publishes the compare-against-upstream and show-whitespace settings, so the phone's branch compare and diffs match desktop. * fix(mobile): use the upstream compare policy on the review screen and rebuild diffs when whitespace changes * fix(mobile): reread Source Control settings on Refresh and restart diffs loading under the old whitespace setting * test(mobile): cover diffs dropped when desktop's whitespace setting arrives * refactor(mobile): share one Source Control settings read per host across screens
130 lines
4.1 KiB
TypeScript
130 lines
4.1 KiB
TypeScript
import type { GitUpstreamStatus } from './git-status-types'
|
|
|
|
export function resolveSourceControlBaseRef(input: {
|
|
worktreeBaseRef?: string | null
|
|
reviewBaseRefName?: string | null
|
|
repoBaseRef?: string | null
|
|
defaultBaseRef?: string | null
|
|
}): string | null {
|
|
const worktreeBaseRef = input.worktreeBaseRef?.trim() || null
|
|
const hasReviewBaseRefName = Boolean(input.reviewBaseRefName?.trim())
|
|
const reviewBaseRef = resolveHostedReviewCompareBaseRef(input.reviewBaseRefName, [
|
|
input.repoBaseRef,
|
|
input.defaultBaseRef
|
|
])
|
|
if (worktreeBaseRef && isFullGitCommitOid(worktreeBaseRef) && hasReviewBaseRefName) {
|
|
return reviewBaseRef
|
|
}
|
|
return worktreeBaseRef || input.repoBaseRef?.trim() || input.defaultBaseRef?.trim() || null
|
|
}
|
|
|
|
// Upstream comparison can differ from the PR/rebase target; fall back to effectiveBaseRef when unavailable.
|
|
export function resolveSourceControlCompareBaseRef(input: {
|
|
enabled: boolean
|
|
worktreeBaseRef?: string | null
|
|
repoBaseRef?: string | null
|
|
upstreamName?: string | null
|
|
fallbackBaseRef?: string | null
|
|
}): string | null {
|
|
if (!input.enabled) {
|
|
return input.fallbackBaseRef?.trim() || null
|
|
}
|
|
const pinned = input.worktreeBaseRef?.trim() || input.repoBaseRef?.trim()
|
|
if (pinned) {
|
|
return pinned
|
|
}
|
|
return input.upstreamName?.trim() || input.fallbackBaseRef?.trim() || null
|
|
}
|
|
|
|
// Wait for upstream resolution before clearing a missing base to avoid flicker.
|
|
export function shouldClearBranchCompareForMissingBase(input: {
|
|
isFolder: boolean
|
|
compareBaseRef: string | null
|
|
remoteStatus: GitUpstreamStatus | undefined
|
|
}): boolean {
|
|
if (input.isFolder || input.compareBaseRef) {
|
|
return false
|
|
}
|
|
return input.remoteStatus !== undefined
|
|
}
|
|
|
|
export function resolveSourceControlPickerBaseRef(input: {
|
|
pinnedBaseRef?: string | null
|
|
effectiveBaseRef?: string | null
|
|
}): string | undefined {
|
|
const pinnedBaseRef = input.pinnedBaseRef?.trim()
|
|
if (!pinnedBaseRef) {
|
|
return undefined
|
|
}
|
|
return input.effectiveBaseRef?.trim() || pinnedBaseRef
|
|
}
|
|
|
|
function isFullGitCommitOid(value: string): boolean {
|
|
return /^[0-9a-f]{40}$/i.test(value)
|
|
}
|
|
|
|
function resolveHostedReviewCompareBaseRef(
|
|
baseRefName: string | null | undefined,
|
|
candidates: (string | null | undefined)[]
|
|
): string | null {
|
|
const branch = baseRefName?.trim()
|
|
if (!branch) {
|
|
return null
|
|
}
|
|
for (const candidate of candidates) {
|
|
const trimmed = candidate?.trim()
|
|
if (!trimmed) {
|
|
continue
|
|
}
|
|
if (getCompareBaseCandidateBranchName(trimmed) === branch) {
|
|
return trimmed
|
|
}
|
|
}
|
|
for (const candidate of candidates) {
|
|
const rewritten = rewriteCompareBaseBranchFromCandidate(candidate, branch)
|
|
if (rewritten) {
|
|
return rewritten
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
function getCompareBaseCandidateBranchName(candidate: string): string {
|
|
const remoteRefPrefix = 'refs/remotes/'
|
|
if (candidate.startsWith(remoteRefPrefix)) {
|
|
const remoteAndBranch = candidate.slice(remoteRefPrefix.length)
|
|
const slashIndex = remoteAndBranch.indexOf('/')
|
|
return slashIndex > 0 ? remoteAndBranch.slice(slashIndex + 1) : remoteAndBranch
|
|
}
|
|
const headsRefPrefix = 'refs/heads/'
|
|
if (candidate.startsWith(headsRefPrefix)) {
|
|
return candidate.slice(headsRefPrefix.length)
|
|
}
|
|
const slashIndex = candidate.indexOf('/')
|
|
return slashIndex > 0 ? candidate.slice(slashIndex + 1) : candidate
|
|
}
|
|
|
|
function rewriteCompareBaseBranchFromCandidate(
|
|
candidate: string | null | undefined,
|
|
branch: string
|
|
): string | null {
|
|
const trimmed = candidate?.trim()
|
|
if (!trimmed) {
|
|
return null
|
|
}
|
|
const remoteRefPrefix = 'refs/remotes/'
|
|
if (trimmed.startsWith(remoteRefPrefix)) {
|
|
const remoteAndBranch = trimmed.slice(remoteRefPrefix.length)
|
|
const slashIndex = remoteAndBranch.indexOf('/')
|
|
return slashIndex > 0
|
|
? `${remoteRefPrefix}${remoteAndBranch.slice(0, slashIndex)}/${branch}`
|
|
: null
|
|
}
|
|
const headsRefPrefix = 'refs/heads/'
|
|
if (trimmed.startsWith(headsRefPrefix)) {
|
|
return `${headsRefPrefix}${branch}`
|
|
}
|
|
const slashIndex = trimmed.indexOf('/')
|
|
return slashIndex > 0 ? `${trimmed.slice(0, slashIndex)}/${branch}` : null
|
|
}
|