Skip to content

Structural diff

structural_diff

Structural branch diff — symbol-level comparison between git refs.

Uses git worktrees to checkout both refs and analyze_package() on each version, then compares symbol sets by name + signature.

Example

from axm_ast.core.structural_diff import structural_diff result = structural_diff(Path("src/mylib"), "main", "feature") result["added"][{"name": "new_func", "kind": "function", "file": "core.py", ...}]

structural_diff(pkg_path, base, head)

Compare two git refs at symbol level.

Uses git worktrees to checkout the base ref, runs analyze_package() on both versions, and diffs the symbol sets.

Parameters:

Name Type Description Default
pkg_path Path

Path to the package directory.

required
base str

Base git ref (branch, tag, or commit).

required
head str

Head git ref (branch, tag, or commit).

required

Returns:

Type Description
dict[str, Any]

Dict with added, removed, modified, and

dict[str, Any]

summary keys. On error, returns a dict with an

dict[str, Any]

error key.

Example

result = structural_diff(Path("src/mylib"), "main", "feature") len(result["added"]) 3

Source code in packages/axm-ast/src/axm_ast/core/structural_diff.py
def structural_diff(
    pkg_path: Path,
    base: str,
    head: str,
) -> dict[str, Any]:
    """Compare two git refs at symbol level.

    Uses git worktrees to checkout the *base* ref, runs
    ``analyze_package()`` on both versions, and diffs the
    symbol sets.

    Args:
        pkg_path: Path to the package directory.
        base: Base git ref (branch, tag, or commit).
        head: Head git ref (branch, tag, or commit).

    Returns:
        Dict with ``added``, ``removed``, ``modified``, and
        ``summary`` keys.  On error, returns a dict with an
        ``error`` key.

    Example:
        >>> result = structural_diff(Path("src/mylib"), "main", "feature")
        >>> len(result["added"])
        3
    """
    pkg_path = pkg_path.resolve()

    validated = _validate_diff_inputs(pkg_path, base, head)
    if isinstance(validated, dict):
        return validated
    project_root, pkg_rel = validated

    head_symbols = _extract_symbols_at_ref(project_root, pkg_rel, head)
    if isinstance(head_symbols, str):
        return {"error": head_symbols}

    base_symbols = _extract_symbols_at_ref(project_root, pkg_rel, base)
    if isinstance(base_symbols, str):
        return {"error": base_symbols}

    return _compute_diff(base_symbols, head_symbols)