Changes from fix/list-branch-issue-on-fresh-repo

This commit is contained in:
Kacper
2025-12-23 20:46:10 +01:00
parent 629b7e7433
commit 4958ee1dda
15 changed files with 386 additions and 47 deletions

View File

@@ -111,6 +111,19 @@ export async function isGitRepo(repoPath: string): Promise<boolean> {
}
}
/**
* Check if a git repository has at least one commit (i.e., HEAD exists)
* Returns false for freshly initialized repos with no commits
*/
export async function hasCommits(repoPath: string): Promise<boolean> {
try {
await execAsync('git rev-parse --verify HEAD', { cwd: repoPath });
return true;
} catch {
return false;
}
}
/**
* Check if an error is ENOENT (file/path not found or spawn failed)
* These are expected in test environments with mock paths

View File

@@ -5,7 +5,7 @@
import type { Request, Response } from 'express';
import { exec } from 'child_process';
import { promisify } from 'util';
import { getErrorMessage, logError } from '../common.js';
import { getErrorMessage, logError, isGitRepo, hasCommits } from '../common.js';
const execAsync = promisify(exec);
@@ -43,6 +43,26 @@ export function createCheckoutBranchHandler() {
return;
}
// Check if path is a git repository
if (!(await isGitRepo(worktreePath))) {
res.status(400).json({
success: false,
error: 'Not a git repository',
code: 'NOT_GIT_REPO',
});
return;
}
// Check if repository has at least one commit
if (!(await hasCommits(worktreePath))) {
res.status(400).json({
success: false,
error: 'Repository has no commits yet',
code: 'NO_COMMITS',
});
return;
}
// Get current branch for reference
const { stdout: currentBranchOutput } = await execAsync('git rev-parse --abbrev-ref HEAD', {
cwd: worktreePath,

View File

@@ -5,7 +5,7 @@
import type { Request, Response } from 'express';
import { exec } from 'child_process';
import { promisify } from 'util';
import { getErrorMessage, logError } from '../common.js';
import { getErrorMessage, logError, isGitRepo } from '../common.js';
const execAsync = promisify(exec);
@@ -25,6 +25,16 @@ export function createCommitHandler() {
return;
}
// Check if path is a git repository
if (!(await isGitRepo(worktreePath))) {
res.status(400).json({
success: false,
error: 'Not a git repository',
code: 'NOT_GIT_REPO',
});
return;
}
// Check for uncommitted changes
const { stdout: status } = await execAsync('git status --porcelain', {
cwd: worktreePath,

View File

@@ -5,7 +5,7 @@
import type { Request, Response } from 'express';
import { exec } from 'child_process';
import { promisify } from 'util';
import { getErrorMessage, logWorktreeError } from '../common.js';
import { getErrorMessage, logWorktreeError, isGitRepo, hasCommits } from '../common.js';
const execAsync = promisify(exec);
@@ -30,6 +30,26 @@ export function createListBranchesHandler() {
return;
}
// Check if the path is a git repository before running git commands
if (!(await isGitRepo(worktreePath))) {
res.status(400).json({
success: false,
error: 'Not a git repository',
code: 'NOT_GIT_REPO',
});
return;
}
// Check if the repository has any commits (freshly init'd repos have no HEAD)
if (!(await hasCommits(worktreePath))) {
res.status(400).json({
success: false,
error: 'Repository has no commits yet',
code: 'NO_COMMITS',
});
return;
}
// Get current branch
const { stdout: currentBranchOutput } = await execAsync('git rev-parse --abbrev-ref HEAD', {
cwd: worktreePath,

View File

@@ -6,7 +6,7 @@ import type { Request, Response } from 'express';
import { exec } from 'child_process';
import { promisify } from 'util';
import path from 'path';
import { getErrorMessage, logError } from '../common.js';
import { getErrorMessage, logError, isGitRepo, hasCommits } from '../common.js';
const execAsync = promisify(exec);
@@ -27,6 +27,26 @@ export function createMergeHandler() {
return;
}
// Check if path is a git repository
if (!(await isGitRepo(projectPath))) {
res.status(400).json({
success: false,
error: 'Not a git repository',
code: 'NOT_GIT_REPO',
});
return;
}
// Check if repository has at least one commit
if (!(await hasCommits(projectPath))) {
res.status(400).json({
success: false,
error: 'Repository has no commits yet',
code: 'NO_COMMITS',
});
return;
}
const branchName = `feature/${featureId}`;
// Git worktrees are stored in project directory
const worktreePath = path.join(projectPath, '.worktrees', featureId);

View File

@@ -5,7 +5,7 @@
import type { Request, Response } from 'express';
import { exec } from 'child_process';
import { promisify } from 'util';
import { getErrorMessage, logError } from '../common.js';
import { getErrorMessage, logError, isGitRepo, hasCommits } from '../common.js';
const execAsync = promisify(exec);
@@ -24,6 +24,26 @@ export function createPullHandler() {
return;
}
// Check if path is a git repository
if (!(await isGitRepo(worktreePath))) {
res.status(400).json({
success: false,
error: 'Not a git repository',
code: 'NOT_GIT_REPO',
});
return;
}
// Check if repository has at least one commit
if (!(await hasCommits(worktreePath))) {
res.status(400).json({
success: false,
error: 'Repository has no commits yet',
code: 'NO_COMMITS',
});
return;
}
// Get current branch name
const { stdout: branchOutput } = await execAsync('git rev-parse --abbrev-ref HEAD', {
cwd: worktreePath,

View File

@@ -5,7 +5,7 @@
import type { Request, Response } from 'express';
import { exec } from 'child_process';
import { promisify } from 'util';
import { getErrorMessage, logError } from '../common.js';
import { getErrorMessage, logError, isGitRepo, hasCommits } from '../common.js';
const execAsync = promisify(exec);
@@ -25,6 +25,26 @@ export function createPushHandler() {
return;
}
// Check if path is a git repository
if (!(await isGitRepo(worktreePath))) {
res.status(400).json({
success: false,
error: 'Not a git repository',
code: 'NOT_GIT_REPO',
});
return;
}
// Check if repository has at least one commit
if (!(await hasCommits(worktreePath))) {
res.status(400).json({
success: false,
error: 'Repository has no commits yet',
code: 'NO_COMMITS',
});
return;
}
// Get branch name
const { stdout: branchOutput } = await execAsync('git rev-parse --abbrev-ref HEAD', {
cwd: worktreePath,

View File

@@ -9,7 +9,7 @@
import type { Request, Response } from 'express';
import { exec } from 'child_process';
import { promisify } from 'util';
import { getErrorMessage, logError } from '../common.js';
import { getErrorMessage, logError, isGitRepo, hasCommits } from '../common.js';
const execAsync = promisify(exec);
@@ -83,6 +83,26 @@ export function createSwitchBranchHandler() {
return;
}
// Check if path is a git repository
if (!(await isGitRepo(worktreePath))) {
res.status(400).json({
success: false,
error: 'Not a git repository',
code: 'NOT_GIT_REPO',
});
return;
}
// Check if repository has at least one commit
if (!(await hasCommits(worktreePath))) {
res.status(400).json({
success: false,
error: 'Repository has no commits yet',
code: 'NO_COMMITS',
});
return;
}
// Get current branch
const { stdout: currentBranchOutput } = await execAsync('git rev-parse --abbrev-ref HEAD', {
cwd: worktreePath,