Files
markitect-main/markitect/spaces/history/interfaces.py
tegwick 4588cbeee8 feat(spaces): implement Phase 8 Git History Tracking
Implements optional git-based version control for information spaces:
- HistoryConfig model for configuring history tracking
- Commit, Branch, HistoryEntry, DiffResult models
- IHistoryBackend and IHistoryQuery interfaces
- GitHistoryBackend using git CLI for version control
- GitHistoryEventHandler for event-driven auto-commits
- HistoryEventCoordinator for managing space history
- HistoryQueryService for high-level history queries
- Automatic commits on DOCUMENT_ADDED/REMOVED/CONTENT_CHANGED events
- Support for:
  * Commit log with pagination and filtering
  * Diff between versions
  * File content at specific versions
  * Branch creation and switching
  * Version restoration
  * Uncommitted changes detection
- 43 comprehensive unit tests with git availability checks

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 18:03:35 +01:00

351 lines
7.7 KiB
Python

"""
Abstract interfaces for history tracking.
Defines the contract for history backends and query services.
"""
from abc import ABC, abstractmethod
from pathlib import Path
from typing import List, Optional, Dict, Any
from .models import Commit, Branch, DiffResult, HistoryEntry, HistoryConfig
class IHistoryBackend(ABC):
"""
Abstract interface for history storage backends.
Implementations can use git, SQLite, or other storage mechanisms.
"""
@abstractmethod
def initialize(self, directory: Path, config: HistoryConfig) -> None:
"""
Initialize the history backend.
Args:
directory: Root directory for the space
config: History configuration
"""
pass
@abstractmethod
def is_initialized(self, directory: Path) -> bool:
"""
Check if history is initialized for a directory.
Args:
directory: Directory to check
Returns:
True if history tracking is initialized
"""
pass
@abstractmethod
def commit(
self,
directory: Path,
message: str,
files: Optional[List[str]] = None,
author: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
) -> Commit:
"""
Create a new commit.
Args:
directory: Root directory
message: Commit message
files: Specific files to commit (None for all changes)
author: Author string (name <email>)
metadata: Additional metadata to store
Returns:
The created commit
"""
pass
@abstractmethod
def get_commit(self, directory: Path, commit_id: str) -> Optional[Commit]:
"""
Get a commit by ID.
Args:
directory: Root directory
commit_id: Commit identifier
Returns:
The commit if found, None otherwise
"""
pass
@abstractmethod
def get_log(
self,
directory: Path,
limit: int = 50,
offset: int = 0,
path: Optional[str] = None,
) -> List[Commit]:
"""
Get commit history.
Args:
directory: Root directory
limit: Maximum number of commits
offset: Number of commits to skip
path: Filter by file path
Returns:
List of commits, newest first
"""
pass
@abstractmethod
def get_diff(
self,
directory: Path,
old_version: Optional[str] = None,
new_version: Optional[str] = None,
path: Optional[str] = None,
) -> List[DiffResult]:
"""
Get diff between versions.
Args:
directory: Root directory
old_version: Old commit ID (None for working directory)
new_version: New commit ID (None for working directory)
path: Filter by file path
Returns:
List of diff results
"""
pass
@abstractmethod
def checkout(
self,
directory: Path,
version: str,
path: Optional[str] = None,
) -> bool:
"""
Checkout a specific version.
Args:
directory: Root directory
version: Commit ID or ref to checkout
path: Specific file to checkout (None for all)
Returns:
True if successful
"""
pass
@abstractmethod
def get_file_at_version(
self,
directory: Path,
path: str,
version: str,
) -> Optional[str]:
"""
Get file content at a specific version.
Args:
directory: Root directory
path: File path relative to directory
version: Commit ID or ref
Returns:
File content as string, or None if not found
"""
pass
@abstractmethod
def list_branches(self, directory: Path) -> List[Branch]:
"""
List all branches.
Args:
directory: Root directory
Returns:
List of branches
"""
pass
@abstractmethod
def create_branch(
self,
directory: Path,
name: str,
start_point: Optional[str] = None,
) -> Branch:
"""
Create a new branch.
Args:
directory: Root directory
name: Branch name
start_point: Starting commit/ref (None for HEAD)
Returns:
The created branch
"""
pass
@abstractmethod
def switch_branch(self, directory: Path, name: str) -> bool:
"""
Switch to a branch.
Args:
directory: Root directory
name: Branch name
Returns:
True if successful
"""
pass
@abstractmethod
def get_current_branch(self, directory: Path) -> Optional[str]:
"""
Get the current branch name.
Args:
directory: Root directory
Returns:
Branch name or None if detached
"""
pass
@abstractmethod
def has_changes(self, directory: Path) -> bool:
"""
Check if there are uncommitted changes.
Args:
directory: Root directory
Returns:
True if there are uncommitted changes
"""
pass
class IHistoryQuery(ABC):
"""
Abstract interface for history queries.
Provides higher-level query operations on top of the backend.
"""
@abstractmethod
def get_history(
self,
space_id: str,
limit: int = 50,
offset: int = 0,
path: Optional[str] = None,
) -> List[HistoryEntry]:
"""
Get history entries for a space.
Args:
space_id: Space identifier
limit: Maximum entries
offset: Skip count
path: Filter by document path
Returns:
List of history entries
"""
pass
@abstractmethod
def get_version_content(
self,
space_id: str,
document_path: str,
version: str,
) -> Optional[str]:
"""
Get document content at a specific version.
Args:
space_id: Space identifier
document_path: Path to document
version: Version identifier
Returns:
Document content or None
"""
pass
@abstractmethod
def compare_versions(
self,
space_id: str,
document_path: str,
old_version: str,
new_version: Optional[str] = None,
) -> Optional[DiffResult]:
"""
Compare two versions of a document.
Args:
space_id: Space identifier
document_path: Path to document
old_version: Old version ID
new_version: New version ID (None for current)
Returns:
Diff result or None
"""
pass
@abstractmethod
def search_history(
self,
space_id: str,
query: str,
limit: int = 20,
) -> List[HistoryEntry]:
"""
Search commit messages.
Args:
space_id: Space identifier
query: Search query
limit: Maximum results
Returns:
Matching history entries
"""
pass
@abstractmethod
def restore_version(
self,
space_id: str,
document_path: str,
version: str,
) -> bool:
"""
Restore a document to a specific version.
Args:
space_id: Space identifier
document_path: Path to document
version: Version to restore
Returns:
True if successful
"""
pass