diff --git a/CHANGELOG.md b/CHANGELOG.md index 1484df0..79682c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ BRIEF: Release changelog # Changelog ## [Unreleased] +### Fixed +- branch_rename.php (promote-rc): force-update the target branch ref in place instead of delete+recreate, so promotion works when the target (e.g. `rc`) is a protected branch that cannot be deleted — previously the delete silently failed and the recreate returned HTTP 409 + ## [09.41.00] --- 2026-07-04 ## [09.41.00] --- 2026-07-04 diff --git a/cli/branch_rename.php b/cli/branch_rename.php index 80fc1dd..eb7c8bf 100644 --- a/cli/branch_rename.php +++ b/cli/branch_rename.php @@ -64,26 +64,46 @@ class BranchRenameCli extends CliFramework return 1; } - // Step 2: Delete target branch if it already exists + // Step 2: Point the target branch at the source commit. + // If the target already exists (e.g. a protected `rc` branch that cannot be + // deleted), force-update its ref in place instead of delete+recreate: deleting a + // protected branch fails, which then makes the recreate return HTTP 409. + $sourceSha = ''; + if (isset($check['body']['commit']['id']) && is_string($check['body']['commit']['id'])) { + $sourceSha = $check['body']['commit']['id']; + } $targetCheck = $this->apiRequest('GET', "{$apiBase}/branches/{$to}", $headers); if ($targetCheck['code'] === 200) { - echo "Target branch '{$to}' already exists — deleting\n"; + echo "Target branch '{$to}' already exists - force-updating to {$from} +"; if (!$this->dryRun) { - $this->apiRequest('DELETE', "{$apiBase}/branches/{$to}", $headers); + if ($sourceSha === '') { + $this->log('ERROR', "Cannot resolve HEAD commit of source '{$from}'"); + return 1; + } + $ref = $this->apiRequest('PATCH', "{$apiBase}/git/refs/heads/{$to}", $headers, [ + 'sha' => $sourceSha, + 'force' => true, + ]); + if ($ref['code'] < 200 || $ref['code'] >= 300) { + $this->log('ERROR', "Failed to force-update '{$to}': HTTP {$ref['code']} (needs force-push perm)"); + $this->log('ERROR', json_encode($ref['body'])); + return 1; + } } - } - - // Step 3: Create new branch from source - echo "Creating branch: {$to} (from {$from})\n"; - if (!$this->dryRun) { - $create = $this->apiRequest('POST', "{$apiBase}/branches", $headers, [ - 'new_branch_name' => $to, - 'old_branch_name' => $from, - ]); - if ($create['code'] < 200 || $create['code'] >= 300) { - $this->log('ERROR', "Failed to create branch '{$to}': HTTP {$create['code']}"); - $this->log('ERROR', json_encode($create['body'])); - return 1; + } else { + echo "Creating branch: {$to} (from {$from}) +"; + if (!$this->dryRun) { + $create = $this->apiRequest('POST', "{$apiBase}/branches", $headers, [ + 'new_branch_name' => $to, + 'old_branch_name' => $from, + ]); + if ($create['code'] < 200 || $create['code'] >= 300) { + $this->log('ERROR', "Failed to create branch '{$to}': HTTP {$create['code']}"); + $this->log('ERROR', json_encode($create['body'])); + return 1; + } } }