Files
headquarter/apps/api/src/services/git/control.py
T
Developer cccf4379d8 refactor: extract CSS modules for session/settings and delete styles.css (Task 2.3)
- Create InstanceList.module.css, AppShell.module.css, SettingsTabLayout.module.css
- Create CommitPanel.module.css, FileViewer.module.css
- Create page CSS files: sessions, repo-workspace, dashboard, projects,
  git-history, ssh-keys, settings
- Update components to import and use CSS modules
- Delete monolithic styles.css (2,255 lines)
- Update main.tsx to import page CSS and new modules

Quality gates: tsc (pass), eslint (pass), build (pass)
Refs: repo-restructure Task 2.3
2026-06-02 21:09:57 +00:00

197 lines
5.8 KiB
Python

"""Git control operations with repo validation."""
import logging
import os
import uuid
from fastapi import HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from src.models.git_repository import GitRepository
from src.models.user import User
from src.schemas.git_repository import (
BranchCreateRequest,
CheckoutRequest,
CommitRequest,
FetchResponse,
MergeRequest,
MergeResponse,
PullResponse,
PushResponse,
StatusResponse,
)
from src.services.git.repository import ensure_repo_on_disk, get_repo_and_validate
from src.utils.git_control import (
checkout_branch,
commit_changes,
create_branch,
delete_branch,
fetch,
get_status,
merge,
pull,
push,
)
logger = logging.getLogger(__name__)
async def get_status_with_validation(
session: AsyncSession,
project_id: uuid.UUID,
repo_id: uuid.UUID,
) -> StatusResponse:
repo = await get_repo_and_validate(session, repo_id, project_id)
ensure_repo_on_disk(repo)
try:
result = get_status(repo.path)
return StatusResponse(
branch=result.branch,
modified=result.modified,
added=result.added,
deleted=result.deleted,
untracked=result.untracked,
renamed=result.renamed,
ahead=result.ahead,
behind=result.behind,
)
except RuntimeError as e:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
async def create_branch_with_validation(
session: AsyncSession,
project_id: uuid.UUID,
repo_id: uuid.UUID,
data: BranchCreateRequest,
) -> dict:
repo = await get_repo_and_validate(session, repo_id, project_id)
ensure_repo_on_disk(repo)
try:
create_branch(repo.path, data.name, data.base_branch)
return {"message": f"Branch '{data.name}' created", "branch": data.name}
except RuntimeError as e:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
async def delete_branch_with_validation(
session: AsyncSession,
project_id: uuid.UUID,
repo_id: uuid.UUID,
branch_name: str,
force: bool = False,
) -> dict:
repo = await get_repo_and_validate(session, repo_id, project_id)
ensure_repo_on_disk(repo)
try:
delete_branch(repo.path, branch_name, force)
return {"message": f"Branch '{branch_name}' deleted"}
except RuntimeError as e:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
async def checkout_branch_with_validation(
session: AsyncSession,
project_id: uuid.UUID,
repo_id: uuid.UUID,
data: CheckoutRequest,
) -> dict:
repo = await get_repo_and_validate(session, repo_id, project_id)
ensure_repo_on_disk(repo)
try:
checkout_branch(repo.path, data.branch)
return {"message": f"Checked out branch '{data.branch}'", "branch": data.branch}
except RuntimeError as e:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
async def commit_changes_with_validation(
session: AsyncSession,
project_id: uuid.UUID,
repo_id: uuid.UUID,
data: CommitRequest,
user: User,
) -> dict:
repo = await get_repo_and_validate(session, repo_id, project_id)
ensure_repo_on_disk(repo)
author_name = user.name or "Unknown"
author_email = user.email or "unknown@example.com"
try:
commit_hash = commit_changes(
repo_path=repo.path,
message=data.message,
author_name=author_name,
author_email=author_email,
files=data.files,
)
return {"commit_hash": commit_hash, "message": data.message}
except RuntimeError as e:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
async def fetch_with_validation(
session: AsyncSession,
project_id: uuid.UUID,
repo_id: uuid.UUID,
) -> FetchResponse:
repo = await get_repo_and_validate(session, repo_id, project_id)
ensure_repo_on_disk(repo)
try:
fetch(repo.path)
return FetchResponse(message="Fetched from remote")
except RuntimeError as e:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
async def pull_with_validation(
session: AsyncSession,
project_id: uuid.UUID,
repo_id: uuid.UUID,
branch: str | None = None,
) -> PullResponse:
repo = await get_repo_and_validate(session, repo_id, project_id)
ensure_repo_on_disk(repo)
try:
pull(repo.path, branch)
return PullResponse(message="Pulled from remote")
except RuntimeError as e:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
async def push_with_validation(
session: AsyncSession,
project_id: uuid.UUID,
repo_id: uuid.UUID,
branch: str | None = None,
) -> PushResponse:
repo = await get_repo_and_validate(session, repo_id, project_id)
ensure_repo_on_disk(repo)
try:
push(repo.path, branch)
return PushResponse(message="Pushed to remote")
except RuntimeError as e:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
async def merge_with_validation(
session: AsyncSession,
project_id: uuid.UUID,
repo_id: uuid.UUID,
data: MergeRequest,
) -> MergeResponse:
repo = await get_repo_and_validate(session, repo_id, project_id)
ensure_repo_on_disk(repo)
try:
commit_hash = merge(
repo_path=repo.path,
source_branch=data.source_branch,
target_branch=data.target_branch,
message=data.message,
)
return MergeResponse(
commit_hash=commit_hash,
message=data.message or f"Merge {data.source_branch}",
)
except RuntimeError as e:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))