CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-reuse

A tool for compliance with the REUSE recommendations for software licensing and copyright management.

Pending
Overview
Eval results
Files

vcs-integration.mddocs/

VCS Integration

REUSE provides pluggable version control system support through a strategy pattern. The system supports Git, Mercurial, Jujutsu, and Pijul, with automatic VCS detection and root directory discovery.

Capabilities

Abstract VCS Strategy

Base class for all version control system implementations.

class VCSStrategy(ABC):
    """
    Abstract base class for VCS strategies using the strategy pattern.
    
    Provides the interface for integrating with different version
    control systems, handling ignore patterns, and discovering
    project boundaries.
    """
    
    EXE: Optional[str] = None  # Path to VCS executable
    
    def __init__(self, root: StrPath):
        """
        Initialize VCS strategy.
        
        Args:
            root: Root directory of the project
        """
        self.root = Path(root)
    
    @abstractmethod
    def is_ignored(self, path: StrPath) -> bool:
        """
        Check if path is ignored by VCS.
        
        Args:
            path: File or directory path to check (accepts str or Path-like)
            
        Returns:
            True if path should be ignored by REUSE processing
        """
    
    @abstractmethod
    def is_submodule(self, path: StrPath) -> bool:
        """
        Check if path is a VCS submodule.
        
        Args:
            path: Directory path to check (accepts str or Path-like)
            
        Returns:
            True if path is a submodule/subrepo
        """
    
    @classmethod
    @abstractmethod
    def in_repo(cls, directory: StrPath) -> bool:
        """
        Check if directory is inside of the VCS repository.
        
        Args:
            directory: Directory path to check
            
        Returns:
            True if directory is within a VCS repository
            
        Raises:
            NotADirectoryError: if directory is not a directory.
        """
    
    @classmethod
    @abstractmethod
    def find_root(cls, cwd: Optional[StrPath] = None) -> Optional[Path]:
        """
        Try to find the root of the project from cwd.
        
        Args:
            cwd: Directory to start search from (defaults to current directory)
            
        Returns:
            Root directory path if found, None otherwise
            
        Raises:
            NotADirectoryError: if directory is not a directory.
        """

Git VCS Strategy

Implementation for Git version control system.

class VCSStrategyGit(VCSStrategy):
    """
    Git VCS support.
    
    Provides Git-specific functionality including .gitignore processing,
    submodule detection, and Git repository root discovery.
    """
    
    def is_ignored(self, path: Path) -> bool:
        """
        Check if path is ignored by Git (.gitignore).
        
        Args:
            path: Path to check against Git ignore patterns
            
        Returns:
            True if Git would ignore this path
        """
    
    def is_submodule(self, path: Path) -> bool:
        """
        Check if path is a Git submodule.
        
        Args:
            path: Directory path to check
            
        Returns:
            True if path is a Git submodule
        """
    
    @classmethod
    def find_root(cls, path: Path) -> Optional[Path]:
        """
        Find Git repository root by looking for .git directory.
        
        Args:
            path: Starting path for search
            
        Returns:
            Git repository root, or None if not in Git repo
        """

Usage Examples:

from reuse.vcs import VCSStrategyGit
from pathlib import Path

# Create Git strategy for project
git_strategy = VCSStrategyGit(Path("/path/to/git/project"))

# Check if files are ignored
test_files = [
    Path("src/main.py"),
    Path("build/output.o"),
    Path(".env"),
    Path("node_modules/package/index.js")
]

for file_path in test_files:
    if git_strategy.is_ignored(file_path):
        print(f"Ignored: {file_path}")
    else:
        print(f"Tracked: {file_path}")

# Check for submodules
subdirs = [Path("lib/external"), Path("src/core")]
for subdir in subdirs:
    if git_strategy.is_submodule(subdir):
        print(f"Submodule: {subdir}")

# Find Git root
git_root = VCSStrategyGit.find_root(Path("/path/to/git/project/src"))
print(f"Git root: {git_root}")

Mercurial VCS Strategy

Implementation for Mercurial version control system.

class VCSStrategyHg(VCSStrategy):
    """
    Mercurial VCS support.
    
    Provides Mercurial-specific functionality including .hgignore processing,
    subrepo detection, and Mercurial repository root discovery.
    """
    
    def is_ignored(self, path: Path) -> bool:
        """
        Check if path is ignored by Mercurial (.hgignore).
        
        Args:
            path: Path to check against Mercurial ignore patterns
            
        Returns:
            True if Mercurial would ignore this path
        """
    
    def is_submodule(self, path: Path) -> bool:
        """
        Check if path is a Mercurial subrepo.
        
        Args:
            path: Directory path to check
            
        Returns:
            True if path is a Mercurial subrepo
        """
    
    @classmethod
    def find_root(cls, path: Path) -> Optional[Path]:
        """
        Find Mercurial repository root by looking for .hg directory.
        
        Args:
            path: Starting path for search
            
        Returns:
            Mercurial repository root, or None if not in Hg repo
        """

Jujutsu VCS Strategy

Implementation for Jujutsu version control system.

class VCSStrategyJujutsu(VCSStrategy):
    """
    Jujutsu VCS support.
    
    Provides Jujutsu-specific functionality including ignore pattern processing
    and repository root discovery for the modern Jujutsu VCS.
    """
    
    def is_ignored(self, path: Path) -> bool:
        """
        Check if path is ignored by Jujutsu.
        
        Args:
            path: Path to check against Jujutsu ignore patterns
            
        Returns:
            True if Jujutsu would ignore this path
        """
    
    def is_submodule(self, path: Path) -> bool:
        """
        Check if path is a Jujutsu workspace/submodule.
        
        Args:
            path: Directory path to check
            
        Returns:
            True if path is a Jujutsu submodule
        """
    
    @classmethod
    def find_root(cls, path: Path) -> Optional[Path]:
        """
        Find Jujutsu repository root.
        
        Args:
            path: Starting path for search
            
        Returns:
            Jujutsu repository root, or None if not in Jujutsu repo
        """

Pijul VCS Strategy

Implementation for Pijul version control system.

class VCSStrategyPijul(VCSStrategy):
    """
    Pijul VCS support.
    
    Provides Pijul-specific functionality including ignore pattern processing
    and repository root discovery for the Pijul patch-based VCS.
    """
    
    def is_ignored(self, path: Path) -> bool:
        """
        Check if path is ignored by Pijul.
        
        Args:
            path: Path to check against Pijul ignore patterns
            
        Returns:
            True if Pijul would ignore this path
        """
    
    def is_submodule(self, path: Path) -> bool:
        """
        Check if path is a Pijul subrepository.
        
        Args:
            path: Directory path to check
            
        Returns:
            True if path is a Pijul subrepository
        """
    
    @classmethod
    def find_root(cls, path: Path) -> Optional[Path]:
        """
        Find Pijul repository root.
        
        Args:
            path: Starting path for search
            
        Returns:
            Pijul repository root, or None if not in Pijul repo
        """

No VCS Strategy

Implementation for projects without version control.

class VCSStrategyNone(VCSStrategy):
    """
    No VCS support.
    
    Used for projects that don't use version control or when
    VCS detection fails. Provides minimal functionality with
    no ignore pattern processing.
    """
    
    def is_ignored(self, path: Path) -> bool:
        """
        Always returns False (no ignore patterns).
        
        Args:
            path: Path to check (ignored)
            
        Returns:
            False (nothing is ignored without VCS)
        """
        return False
    
    def is_submodule(self, path: Path) -> bool:
        """
        Always returns False (no submodules without VCS).
        
        Args:
            path: Directory path to check (ignored)
            
        Returns:
            False (no submodules without VCS)
        """
        return False
    
    @classmethod
    def find_root(cls, path: Path) -> Optional[Path]:
        """
        Returns None (no VCS root to find).
        
        Args:
            path: Starting path (ignored)
            
        Returns:
            None (no VCS root exists)
        """
        return None

VCS Discovery Functions

Utility functions for discovering and working with VCS systems.

def all_vcs_strategies() -> Generator[Type[VCSStrategy], None, None]:
    """
    Get all available VCS strategies.
    
    Yields:
        VCS strategy classes in order of preference
        
    Note:
        Yields concrete VCS strategy classes (Git, Mercurial, etc.)
        but not the abstract base class or VCSStrategyNone.
    """

def find_root(cwd: Optional[StrPath] = None) -> Optional[Path]:
    """
    Find VCS root directory starting from current working directory.
    
    Args:
        cwd: Starting directory (default: current working directory)
        
    Returns:
        VCS root directory, or None if no VCS found
        
    Note:
        Tries all available VCS strategies in order until one
        successfully finds a repository root.
    """

Usage Examples:

from reuse.vcs import all_vcs_strategies, find_root
from pathlib import Path

# List all available VCS strategies
print("Available VCS strategies:")
for strategy_class in all_vcs_strategies():
    print(f"  - {strategy_class.__name__}")

# Find VCS root automatically
project_root = find_root()
if project_root:
    print(f"Found VCS root: {project_root}")
else:
    print("No VCS repository found")

# Find VCS root from specific directory
specific_root = find_root("/path/to/project/subdir")
if specific_root:
    print(f"VCS root: {specific_root}")

# Try each VCS strategy manually
test_path = Path("/path/to/project")
for strategy_class in all_vcs_strategies():
    root = strategy_class.find_root(test_path)
    if root:
        print(f"Found {strategy_class.__name__} repo at: {root}")
        break

VCS Integration Examples

Automatic VCS Strategy Selection

from reuse.vcs import all_vcs_strategies, VCSStrategyNone
from pathlib import Path

def detect_vcs_strategy(project_path: Path) -> VCSStrategy:
    """Automatically detect and create appropriate VCS strategy."""
    
    # Try each VCS strategy
    for strategy_class in all_vcs_strategies():
        root = strategy_class.find_root(project_path)
        if root:
            print(f"Detected {strategy_class.__name__} at {root}")
            return strategy_class(root)
    
    # Fallback to no VCS
    print("No VCS detected, using VCSStrategyNone")
    return VCSStrategyNone(project_path)

# Usage
project = Path("/path/to/project")
vcs_strategy = detect_vcs_strategy(project)
print(f"Using strategy: {type(vcs_strategy).__name__}")

VCS-Aware File Processing

from reuse.vcs import find_root, all_vcs_strategies
from pathlib import Path
from typing import Iterator

def get_vcs_tracked_files(project_path: Path) -> Iterator[Path]:
    """Get all files that should be processed by REUSE (not ignored by VCS)."""
    
    # Detect VCS strategy
    vcs_strategy = None
    for strategy_class in all_vcs_strategies():
        root = strategy_class.find_root(project_path)
        if root:
            vcs_strategy = strategy_class(root)
            break
    
    if not vcs_strategy:
        # No VCS, process all files
        for file_path in project_path.rglob("*"):
            if file_path.is_file():
                yield file_path
        return
    
    # Process files not ignored by VCS
    for file_path in project_path.rglob("*"):
        if file_path.is_file() and not vcs_strategy.is_ignored(file_path):
            yield file_path

# Usage
project_files = list(get_vcs_tracked_files(Path("/path/to/project")))
print(f"Found {len(project_files)} VCS-tracked files")

Submodule Handling

from reuse.vcs import VCSStrategyGit
from pathlib import Path

def analyze_submodules(project_path: Path) -> dict:
    """Analyze Git submodules in a project."""
    
    git_root = VCSStrategyGit.find_root(project_path)
    if not git_root:
        return {"error": "Not a Git repository"}
    
    git_strategy = VCSStrategyGit(git_root)
    
    analysis = {
        "root": str(git_root),
        "submodules": [],
        "submodule_files": 0,
        "main_repo_files": 0
    }
    
    # Check all directories
    for dir_path in git_root.rglob("*"):
        if dir_path.is_dir() and git_strategy.is_submodule(dir_path):
            submodule_info = {
                "path": str(dir_path.relative_to(git_root)),
                "absolute_path": str(dir_path),
                "file_count": sum(1 for f in dir_path.rglob("*") if f.is_file())
            }
            analysis["submodules"].append(submodule_info)
            analysis["submodule_files"] += submodule_info["file_count"]
    
    # Count main repository files
    for file_path in git_root.rglob("*"):
        if file_path.is_file():
            # Check if file is in a submodule
            in_submodule = any(
                git_strategy.is_submodule(parent) 
                for parent in file_path.parents
            )
            if not in_submodule:
                analysis["main_repo_files"] += 1
    
    return analysis

# Usage
submodule_analysis = analyze_submodules(Path("/path/to/git/project"))
print(f"Found {len(submodule_analysis.get('submodules', []))} submodules")
for submod in submodule_analysis.get("submodules", []):
    print(f"  {submod['path']}: {submod['file_count']} files")

Multi-VCS Project Detection

from reuse.vcs import all_vcs_strategies
from pathlib import Path
from typing import Dict, List

def detect_all_vcs_repos(search_path: Path) -> Dict[str, List[Path]]:
    """Detect all VCS repositories under a search path."""
    
    repos = {}
    
    # Initialize results for each VCS type
    for strategy_class in all_vcs_strategies():
        vcs_name = strategy_class.__name__.replace("VCSStrategy", "").lower()
        repos[vcs_name] = []
    
    # Search for repositories
    for dir_path in search_path.rglob("*"):
        if dir_path.is_dir():
            for strategy_class in all_vcs_strategies():
                root = strategy_class.find_root(dir_path)
                if root and root not in repos[strategy_class.__name__.replace("VCSStrategy", "").lower()]:
                    vcs_name = strategy_class.__name__.replace("VCSStrategy", "").lower()
                    repos[vcs_name].append(root)
    
    return repos

# Usage
all_repos = detect_all_vcs_repos(Path("/workspace"))
for vcs_type, repo_list in all_repos.items():
    if repo_list:
        print(f"{vcs_type.capitalize()} repositories:")
        for repo in repo_list:
            print(f"  - {repo}")

Type Definitions

# Type alias used in VCS functions
StrPath = Union[str, PathLike[str]]  # Something that looks like a path

This type alias is used throughout the VCS integration system to accept both string paths and Path objects.

Install with Tessl CLI

npx tessl i tessl/pypi-reuse

docs

cli.md

comment-handling.md

global-licensing.md

index.md

project-management.md

report-generation.md

reuse-info.md

vcs-integration.md

tile.json