mirror of
https://github.com/glittercowboy/get-shit-done
synced 2026-04-25 17:25:23 +02:00
Merge pull request #1279 from gsd-build/fix/early-branch-creation-1278
fix: create strategy branch before first commit, not at execute-phase
This commit is contained in:
@@ -247,6 +247,43 @@ function cmdCommit(cwd, message, files, raw, amend, noVerify) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure branching strategy branch exists before first commit (#1278).
|
||||
// Pre-execution workflows (discuss, plan, research) commit artifacts but the branch
|
||||
// was previously only created during execute-phase — too late.
|
||||
if (config.branching_strategy && config.branching_strategy !== 'none') {
|
||||
let branchName = null;
|
||||
if (config.branching_strategy === 'phase') {
|
||||
// Determine which phase we're committing for from the file paths
|
||||
const phaseMatch = (files || []).join(' ').match(/(\d+)-/);
|
||||
if (phaseMatch) {
|
||||
const phaseNum = phaseMatch[1];
|
||||
const phaseInfo = findPhaseInternal(cwd, phaseNum);
|
||||
if (phaseInfo) {
|
||||
branchName = config.phase_branch_template
|
||||
.replace('{phase}', phaseInfo.phase_number)
|
||||
.replace('{slug}', phaseInfo.phase_slug || 'phase');
|
||||
}
|
||||
}
|
||||
} else if (config.branching_strategy === 'milestone') {
|
||||
const milestone = getMilestoneInfo(cwd);
|
||||
if (milestone && milestone.version) {
|
||||
branchName = config.milestone_branch_template
|
||||
.replace('{milestone}', milestone.version)
|
||||
.replace('{slug}', generateSlugInternal(milestone.name) || 'milestone');
|
||||
}
|
||||
}
|
||||
if (branchName) {
|
||||
const currentBranch = execGit(cwd, ['rev-parse', '--abbrev-ref', 'HEAD']);
|
||||
if (currentBranch.exitCode === 0 && currentBranch.stdout.trim() !== branchName) {
|
||||
// Create branch if it doesn't exist, or switch to it if it does
|
||||
const create = execGit(cwd, ['checkout', '-b', branchName]);
|
||||
if (create.exitCode !== 0) {
|
||||
execGit(cwd, ['checkout', branchName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stage files
|
||||
const filesToStage = files && files.length > 0 ? files : ['.planning/'];
|
||||
for (const file of filesToStage) {
|
||||
|
||||
@@ -1185,6 +1185,71 @@ describe('commit command', () => {
|
||||
const logCount = execSync('git log --oneline', { cwd: tmpDir, encoding: 'utf-8' }).trim().split('\n').length;
|
||||
assert.strictEqual(logCount, 2, 'should have 2 commits (initial + amended)');
|
||||
});
|
||||
test('creates strategy branch before first commit when branching_strategy is milestone', () => {
|
||||
// Configure milestone branching strategy
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'config.json'),
|
||||
JSON.stringify({
|
||||
commit_docs: true,
|
||||
branching_strategy: 'milestone',
|
||||
milestone_branch_template: 'gsd/{milestone}-{slug}',
|
||||
})
|
||||
);
|
||||
// getMilestoneInfo reads ROADMAP.md for milestone version/name
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||
'## v1.0: Initial Release\n\n### Phase 1: Setup\n'
|
||||
);
|
||||
|
||||
// Create a file to commit
|
||||
fs.writeFileSync(path.join(tmpDir, '.planning', 'test-context.md'), '# Context\n');
|
||||
|
||||
const result = runGsdTools('commit "docs: add context" --files .planning/test-context.md', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.committed, true, 'should have committed');
|
||||
|
||||
// Verify we're on the strategy branch
|
||||
const { execFileSync } = require('child_process');
|
||||
const branch = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: tmpDir, encoding: 'utf-8' }).trim();
|
||||
assert.strictEqual(branch, 'gsd/v1.0-initial-release', 'should be on milestone branch');
|
||||
});
|
||||
|
||||
test('creates strategy branch before first commit when branching_strategy is phase', () => {
|
||||
// Configure phase branching strategy
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'config.json'),
|
||||
JSON.stringify({
|
||||
commit_docs: true,
|
||||
branching_strategy: 'phase',
|
||||
phase_branch_template: 'gsd/phase-{phase}-{slug}',
|
||||
})
|
||||
);
|
||||
// Create ROADMAP.md with a phase
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '01-setup'), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||
'# Roadmap\n\n## Phase 1: Setup\nGoal: Initial setup\n'
|
||||
);
|
||||
|
||||
// Create a context file for phase 1
|
||||
fs.writeFileSync(path.join(tmpDir, '.planning', 'phases', '01-setup', '01-CONTEXT.md'), '# Context\n');
|
||||
|
||||
const result = runGsdTools(
|
||||
'commit "docs(01): add context" --files .planning/phases/01-setup/01-CONTEXT.md',
|
||||
tmpDir
|
||||
);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.committed, true, 'should have committed');
|
||||
|
||||
// Verify we're on the strategy branch
|
||||
const { execFileSync } = require('child_process');
|
||||
const branch = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: tmpDir, encoding: 'utf-8' }).trim();
|
||||
assert.strictEqual(branch, 'gsd/phase-01-setup', 'should be on phase branch');
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user