feat(cli): add --branch option to sync pull/push commands
Add a --branch argument that allows overriding the current git branch for sync operations. This enables: - Using branch-specific settings even when not in a git repository - Overriding the detected git branch when needed The branch override is applied to: - getEffectiveSettings() for branch-specific config overrides - getSpecificItemsForCurrentBranch() for branch-specific items Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -109,8 +109,9 @@ async function resolveEffectiveSyncOptions(
|
||||
workspace: Workspace,
|
||||
localConfig: SyncOptions,
|
||||
promotion?: string,
|
||||
branchOverride?: string,
|
||||
): Promise<SyncOptions> {
|
||||
return await getEffectiveSettings(localConfig, promotion);
|
||||
return await getEffectiveSettings(localConfig, promotion, false, false, branchOverride);
|
||||
}
|
||||
|
||||
type DynFSElement = {
|
||||
@@ -1858,7 +1859,7 @@ async function buildTracker(changes: Change[]) {
|
||||
|
||||
export async function pull(
|
||||
opts: GlobalOptions &
|
||||
SyncOptions & { repository?: string; promotion?: string },
|
||||
SyncOptions & { repository?: string; promotion?: string; branch?: string },
|
||||
) {
|
||||
const originalCliOpts = { ...opts };
|
||||
opts = await mergeConfigWithConfigFile(opts);
|
||||
@@ -1886,10 +1887,11 @@ export async function pull(
|
||||
workspace,
|
||||
opts,
|
||||
opts.promotion,
|
||||
opts.branch,
|
||||
);
|
||||
|
||||
// Extract specific items configuration before merging overwrites gitBranches
|
||||
const specificItems = getSpecificItemsForCurrentBranch(opts);
|
||||
const specificItems = getSpecificItemsForCurrentBranch(opts, opts.branch);
|
||||
|
||||
// Merge CLI flags with resolved settings (CLI flags take precedence only for explicit overrides)
|
||||
opts = mergeCliWithEffectiveOptions(originalCliOpts, effectiveOpts);
|
||||
@@ -2334,7 +2336,7 @@ function removeSuffix(str: string, suffix: string) {
|
||||
}
|
||||
|
||||
export async function push(
|
||||
opts: GlobalOptions & SyncOptions & { repository?: string },
|
||||
opts: GlobalOptions & SyncOptions & { repository?: string; branch?: string },
|
||||
) {
|
||||
// Save original CLI options before merging with config file
|
||||
const originalCliOpts = { ...opts };
|
||||
@@ -2361,10 +2363,11 @@ export async function push(
|
||||
workspace,
|
||||
opts,
|
||||
opts.promotion,
|
||||
opts.branch,
|
||||
);
|
||||
|
||||
// Extract specific items configuration BEFORE merging overwrites gitBranches
|
||||
const specificItems = getSpecificItemsForCurrentBranch(opts);
|
||||
const specificItems = getSpecificItemsForCurrentBranch(opts, opts.branch);
|
||||
|
||||
// Merge CLI flags with resolved settings (CLI flags take precedence only for explicit overrides)
|
||||
opts = mergeCliWithEffectiveOptions(originalCliOpts, effectiveOpts);
|
||||
@@ -3118,6 +3121,10 @@ const command = new Command()
|
||||
"--promotion <branch:string>",
|
||||
"Use promotionOverrides from the specified branch instead of regular overrides",
|
||||
)
|
||||
.option(
|
||||
"--branch <branch:string>",
|
||||
"Override the current git branch (works even outside a git repository)",
|
||||
)
|
||||
// deno-lint-ignore no-explicit-any
|
||||
.action(pull as any)
|
||||
.command("push")
|
||||
@@ -3171,6 +3178,10 @@ const command = new Command()
|
||||
"--repository <repo:string>",
|
||||
"Specify repository path (e.g., u/user/repo) when multiple repositories exist",
|
||||
)
|
||||
.option(
|
||||
"--branch <branch:string>",
|
||||
"Override the current git branch (works even outside a git repository)",
|
||||
)
|
||||
// deno-lint-ignore no-explicit-any
|
||||
.action(push as any);
|
||||
|
||||
|
||||
@@ -487,18 +487,26 @@ export async function getEffectiveSettings(
|
||||
config: SyncOptions,
|
||||
promotion?: string,
|
||||
skipBranchValidation?: boolean,
|
||||
suppressLogs?: boolean
|
||||
suppressLogs?: boolean,
|
||||
branchOverride?: string
|
||||
): Promise<SyncOptions> {
|
||||
// Start with top-level settings from config
|
||||
const { gitBranches, ...topLevelSettings } = config;
|
||||
const effective = { ...topLevelSettings };
|
||||
|
||||
if (isGitRepository()) {
|
||||
// Determine the branch to use: branchOverride takes precedence, then git detection
|
||||
let currentBranch: string | null = null;
|
||||
let originalBranchIfForked: string | null = null;
|
||||
|
||||
if (branchOverride) {
|
||||
currentBranch = branchOverride;
|
||||
if (!suppressLogs) {
|
||||
log.info(`Using branch override: ${branchOverride}`);
|
||||
}
|
||||
} else if (isGitRepository()) {
|
||||
const branch = getCurrentGitBranch();
|
||||
originalBranchIfForked = getOriginalBranchForWorkspaceForks(branch);
|
||||
|
||||
const originalBranchIfForked = getOriginalBranchForWorkspaceForks(branch);
|
||||
|
||||
let currentBranch: string | null;
|
||||
if (originalBranchIfForked) {
|
||||
log.info(
|
||||
`Using overrides from original branch \`${originalBranchIfForked}\``
|
||||
@@ -507,53 +515,53 @@ export async function getEffectiveSettings(
|
||||
} else {
|
||||
currentBranch = branch;
|
||||
}
|
||||
} else {
|
||||
log.debug("Not in a Git repository and no branch override provided, using top-level settings");
|
||||
}
|
||||
|
||||
// If promotion is specified, use that branch's promotionOverrides or overrides
|
||||
if (promotion && gitBranches && gitBranches[promotion]) {
|
||||
const targetBranch = gitBranches[promotion];
|
||||
// If promotion is specified, use that branch's promotionOverrides or overrides
|
||||
if (promotion && gitBranches && gitBranches[promotion]) {
|
||||
const targetBranch = gitBranches[promotion];
|
||||
|
||||
// First try promotionOverrides, then fall back to overrides
|
||||
if (targetBranch.promotionOverrides) {
|
||||
Object.assign(effective, targetBranch.promotionOverrides);
|
||||
if (!suppressLogs) {
|
||||
log.info(`Applied promotion settings from branch: ${promotion}`);
|
||||
}
|
||||
} else if (targetBranch.overrides) {
|
||||
Object.assign(effective, targetBranch.overrides);
|
||||
if (!suppressLogs) {
|
||||
log.info(
|
||||
`Applied settings from branch: ${promotion} (no promotionOverrides found)`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
log.debug(
|
||||
`No promotion or regular overrides found for branch '${promotion}', using top-level settings`
|
||||
);
|
||||
}
|
||||
}
|
||||
// Otherwise use current branch overrides (existing behavior)
|
||||
else if (
|
||||
currentBranch &&
|
||||
gitBranches &&
|
||||
gitBranches[currentBranch] &&
|
||||
gitBranches[currentBranch].overrides
|
||||
) {
|
||||
Object.assign(effective, gitBranches[currentBranch].overrides);
|
||||
// First try promotionOverrides, then fall back to overrides
|
||||
if (targetBranch.promotionOverrides) {
|
||||
Object.assign(effective, targetBranch.promotionOverrides);
|
||||
if (!suppressLogs) {
|
||||
log.info(`Applied promotion settings from branch: ${promotion}`);
|
||||
}
|
||||
} else if (targetBranch.overrides) {
|
||||
Object.assign(effective, targetBranch.overrides);
|
||||
if (!suppressLogs) {
|
||||
const extraLog = originalBranchIfForked
|
||||
? ` (because it is the origin of the workspace fork branch \`${branch}\`)`
|
||||
: "";
|
||||
log.info(
|
||||
`Applied settings for Git branch: ${currentBranch}${extraLog}`
|
||||
`Applied settings from branch: ${promotion} (no promotionOverrides found)`
|
||||
);
|
||||
}
|
||||
} else if (currentBranch) {
|
||||
} else {
|
||||
log.debug(
|
||||
`No branch-specific overrides found for '${currentBranch}', using top-level settings`
|
||||
`No promotion or regular overrides found for branch '${promotion}', using top-level settings`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
log.debug("Not in a Git repository, using top-level settings");
|
||||
}
|
||||
// Otherwise use current branch overrides (existing behavior)
|
||||
else if (
|
||||
currentBranch &&
|
||||
gitBranches &&
|
||||
gitBranches[currentBranch] &&
|
||||
gitBranches[currentBranch].overrides
|
||||
) {
|
||||
Object.assign(effective, gitBranches[currentBranch].overrides);
|
||||
if (!suppressLogs) {
|
||||
const extraLog = originalBranchIfForked
|
||||
? ` (because it is the origin of the workspace fork branch \`${currentBranch}\`)`
|
||||
: "";
|
||||
log.info(
|
||||
`Applied settings for Git branch: ${currentBranch}${extraLog}`
|
||||
);
|
||||
}
|
||||
} else if (currentBranch) {
|
||||
log.debug(
|
||||
`No branch-specific overrides found for '${currentBranch}', using top-level settings`
|
||||
);
|
||||
}
|
||||
|
||||
return effective;
|
||||
|
||||
@@ -60,12 +60,19 @@ function buildYamlTypePattern(): string {
|
||||
* Get the specific items configuration for the current git branch
|
||||
* Merges commonSpecificItems with branch-specific specificItems
|
||||
*/
|
||||
export function getSpecificItemsForCurrentBranch(config: SyncOptions): SpecificItemsConfig | undefined {
|
||||
if (!isGitRepository() || !config.gitBranches) {
|
||||
export function getSpecificItemsForCurrentBranch(config: SyncOptions, branchOverride?: string): SpecificItemsConfig | undefined {
|
||||
if (!config.gitBranches) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const currentBranch = getCurrentGitBranch();
|
||||
// Use branch override if provided, otherwise detect from git
|
||||
let currentBranch: string | null = null;
|
||||
if (branchOverride) {
|
||||
currentBranch = branchOverride;
|
||||
} else if (isGitRepository()) {
|
||||
currentBranch = getCurrentGitBranch();
|
||||
}
|
||||
|
||||
if (!currentBranch) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user