mirror of
https://github.com/stablyai/orca.git
synced 2026-10-22 16:03:47 +00:00
* Normalize branch prefixes and flag invalid ones in settings A custom branch prefix ending in a slash (e.g. "team/") produced a double-slashed branch name like "team//feature" that git rejects, and the raw check-ref-format error gave no hint that the prefix caused it. - Normalize the configured prefix (trim whitespace, strip leading/ trailing and duplicate slashes) in the shared branch-name builder so the common trailing-slash case just works, for local and SSH worktrees. - Validate the prefix on the worktree-create path (computeValidatedBranchName) so a genuinely invalid prefix fails fast with a clear "update it in Settings -> Git" message instead of an opaque git error. - Add a live BranchPrefixFeedback under the Branch Prefix setting: previews the resulting branch name, warns on invalid characters, and notes when a prefix collapses to none. - Keep the background first-work rename on the non-throwing builder since the prefix is already validated at create time. * Keep caret in place when editing the branch prefix The custom branch prefix input was directly controlled by settings, but updateSettings persists through an async IPC round-trip, so the value updated a tick late and React re-assigned it, snapping the caret to the end on mid-string edits. Drive the input from a local draft and only adopt genuine external settings changes so the caret stays put (and fast typing survives slow SSH round-trips). Co-authored-by: Cursor <cursoragent@cursor.com> * Return ReactNode from BranchPrefixFeedback JSX.Element needlessly excludes null/string/number returns; ReactNode keeps the component's return type from over-constraining future changes. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
105 lines
3.8 KiB
TypeScript
105 lines
3.8 KiB
TypeScript
import type { BranchPrefixStrategy } from './types'
|
|
|
|
/** The branch-prefix settings slice the prefix helpers read. */
|
|
export type BranchPrefixSettings = {
|
|
branchPrefix: BranchPrefixStrategy
|
|
branchPrefixCustom?: string
|
|
}
|
|
|
|
/**
|
|
* Pick the raw, un-normalized value the configured strategy contributes, or
|
|
* null when no prefix applies. Shared so the main-process branch builder and
|
|
* the renderer's live settings feedback agree on which field each strategy uses.
|
|
*/
|
|
export function selectBranchPrefixInput(
|
|
settings: BranchPrefixSettings,
|
|
gitUsername: string | null
|
|
): string | null {
|
|
switch (settings.branchPrefix) {
|
|
case 'git-username':
|
|
return gitUsername
|
|
case 'custom':
|
|
return settings.branchPrefixCustom ?? null
|
|
case 'none':
|
|
return null
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Normalize a configured branch prefix into the segment that gets prepended
|
|
* before the `/` separator when building a branch name.
|
|
*
|
|
* Why: the branch-name join (`${prefix}/${leaf}`) already inserts a single `/`,
|
|
* so a user-typed prefix like `team/` would otherwise yield `team//name`, which
|
|
* git check-ref-format rejects. Strip surrounding whitespace and slashes and
|
|
* collapse internal runs so the join always produces exactly one separator.
|
|
* Legitimate multi-segment prefixes (e.g. `team/frontend`) are preserved.
|
|
*/
|
|
export function normalizeBranchPrefix(rawPrefix: string): string {
|
|
return rawPrefix
|
|
.trim()
|
|
.replace(/^\/+|\/+$/g, '')
|
|
.replace(/\/{2,}/g, '/')
|
|
}
|
|
|
|
// Ref-reserved characters git check-ref-format rejects inside a branch name.
|
|
const INVALID_BRANCH_PREFIX_CHARS = /[~^:?*[\\]/
|
|
|
|
/**
|
|
* Whether the value contains an ASCII control character or space, both of which
|
|
* git rejects. Checked by code point (like `sanitizeWorktreeDisplayName`) to
|
|
* avoid a control-character regex that the linter forbids.
|
|
*/
|
|
function hasControlOrSpace(value: string): boolean {
|
|
return [...value].some((char) => {
|
|
const code = char.charCodeAt(0)
|
|
return code <= 0x20 || code === 0x7f
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Detect whether a branch prefix, after normalization, still contains characters
|
|
* git check-ref-format rejects. Returns a reason code (not a UI string, so the
|
|
* renderer owns translation) or null when the prefix is usable.
|
|
*
|
|
* This is a lightweight mirror of the relevant check-ref-format rules for live
|
|
* settings feedback; git remains the source of truth at worktree-create time.
|
|
*/
|
|
export function getBranchPrefixIssue(rawPrefix: string): 'invalid-characters' | null {
|
|
const normalized = normalizeBranchPrefix(rawPrefix)
|
|
if (!normalized) {
|
|
// Empty after normalization means "no prefix" — valid.
|
|
return null
|
|
}
|
|
// Mirror git check-ref-format exactly so we don't reject prefixes git accepts:
|
|
// control chars/space, the ref-reserved set, `..`, and `@{` are forbidden
|
|
// anywhere; the whole ref may not start with `-` (arg-injection / leading-dash)
|
|
// nor end with `.`; and each `/`-segment may not start with `.` nor end with
|
|
// `.lock`. Hyphens and mid-segment dots elsewhere are fine (e.g. `team./x`).
|
|
if (
|
|
hasControlOrSpace(normalized) ||
|
|
INVALID_BRANCH_PREFIX_CHARS.test(normalized) ||
|
|
normalized.includes('..') ||
|
|
normalized.includes('@{') ||
|
|
normalized.startsWith('-') ||
|
|
normalized.endsWith('.') ||
|
|
normalized.split('/').some((seg) => seg.startsWith('.') || seg.endsWith('.lock'))
|
|
) {
|
|
return 'invalid-characters'
|
|
}
|
|
return null
|
|
}
|
|
|
|
/**
|
|
* Fail fast when a configured prefix would produce a branch name git rejects.
|
|
* Used on the main-process worktree-create path so users get a clear error
|
|
* instead of an opaque check-ref-format failure later.
|
|
*/
|
|
export function assertBranchPrefixValid(prefix: string): void {
|
|
if (getBranchPrefixIssue(prefix) !== null) {
|
|
throw new Error(
|
|
`Branch prefix "${prefix}" contains characters git rejects — update it in Settings → Git`
|
|
)
|
|
}
|
|
}
|