Skip to content

graflo.architecture.evolution.lift_commit

Recording a lift as a commit.

Kept apart from aspects/, which plans a lift and knows nothing of history, for the reason merge_commit is kept apart from merge: recording is what a commit point does to a result afterwards.

A lift commit is an ordinary single-parent commit whose ops are the planned lift, not a diff of its result, so each op keeps its inverse and a revert retracts the lift like any other change. What makes it a lift is the :class:~graflo.architecture.evolution.aspects.recipe.LiftRecipe riding alongside, which a re-lift reads.

Attributes

__all__ = ['build_lift_commit', 'lift_recipe_of'] module-attribute

Classes

Functions:

build_lift_commit(base, ops, *, parent, recipe, label=None, created_at=None, notes=None)

Record the lift ops of base as a lift commit carrying recipe.

Parameters:

Name Type Description Default
base GraphManifest

The manifest the lift was planned on, the state of parent.

required
ops list[ManifestOp]

The planned lift, as plan_lift returned it.

required
parent str

The commit whose tree is base.

required
recipe LiftRecipe

What the lift was asked to do, planned on base.

required
label str | None

Short human-readable name; defaults to the aspects lifted.

None
created_at str | None

ISO-8601 timestamp.

None
notes str | None

Free-form annotation.

None

Raises:

Type Description
CommitError

recipe was planned on another manifest, or ops is empty or changes nothing -- a manifest that already carries the aspects has no lift to record.

Source code in graflo/architecture/evolution/lift_commit.py
def build_lift_commit(
    base: GraphManifest,
    ops: list[ManifestOp],
    *,
    parent: str,
    recipe: LiftRecipe,
    label: str | None = None,
    created_at: str | None = None,
    notes: str | None = None,
) -> Commit:
    """Record the lift *ops* of *base* as a ``lift`` commit carrying *recipe*.

    Args:
        base: The manifest the lift was planned on, the state of *parent*.
        ops: The planned lift, as ``plan_lift`` returned it.
        parent: The commit whose tree is *base*.
        recipe: What the lift was asked to do, planned on *base*.
        label: Short human-readable name; defaults to the aspects lifted.
        created_at: ISO-8601 timestamp.
        notes: Free-form annotation.

    Raises:
        CommitError: *recipe* was planned on another manifest, or *ops* is
            empty or changes nothing -- a manifest that already carries the
            aspects has no lift to record.
    """
    if recipe.base != manifest_hash(base):
        raise CommitError(
            f"the lift recipe was planned on tree {recipe.base[:12]}, not on the "
            f"manifest given as its base ({manifest_hash(base)[:12]})"
        )
    return build_commit(
        base,
        ops,
        parents=[parent],
        kind="lift",
        label=label or f"lift {', '.join(recipe.aspects)}",
        created_at=created_at,
        notes=notes,
        recipe=RecipeRef(
            hash=recipe.content_hash(), kind=recipe.kind, payload=recipe.to_dict()
        ),
    )

lift_recipe_of(commit)

The recipe a lift commit recorded.

Raises:

Type Description
CommitError

commit is not a lift commit, or records no recipe.

Source code in graflo/architecture/evolution/lift_commit.py
def lift_recipe_of(commit: Commit) -> LiftRecipe:
    """The recipe a ``lift`` commit recorded.

    Raises:
        CommitError: *commit* is not a lift commit, or records no recipe.
    """
    if commit.kind != "lift" or commit.recipe is None:
        raise CommitError(
            f"commit '{commit.short()}' is a {commit.kind} commit and records no "
            "lift recipe"
        )
    return LiftRecipe.model_validate(commit.recipe.payload)