ifc-commit/docs/history.md
2026-03-25 10:36:30 +01:00

5.2 KiB

Embedding Commit History in IFC Files

A survey of IFC mechanisms for storing git commit metadata.


1. IfcOwnerHistory — The Native Mechanism

Every IfcRoot-derived entity (walls, spaces, products, etc.) carries an optional IfcOwnerHistory attribute. It is the closest thing IFC has to built-in change tracking.

Fields:

Field Type Notes
OwningUser IfcPersonAndOrganization Who created the element
OwningApplication IfcApplication Software used
State IfcStateEnum READWRITE, READONLY, LOCKED
ChangeAction IfcChangeActionEnum ADDED, MODIFIED, DELETED, NOCHANGE
LastModifiedDate IfcTimeStamp Unix timestamp
LastModifyingUser IfcPersonAndOrganization
LastModifyingApplication IfcApplication
CreationDate IfcTimeStamp Unix timestamp

Raw IFC line from samples/duplex.ifc:

#33=IFCOWNERHISTORY(#32,#2,$,.NOCHANGE.,$,$,$,0);

Limitations:

  • Only the current state — no history chain
  • ChangeAction is a coarse enum; no room for a commit hash, message, or branch
  • IfcApplication.Version is a short string, not designed for structured data
  • One record per element; previous owners are lost on update

Verdict: Good for standard compliance and timestamping. Not sufficient alone for git metadata.


Custom property sets (Pset_*) are the standard IFC way to attach arbitrary key-value metadata to any IfcObject. They survive round-trips through most IFC-aware tools (unknown Psets are ignored, not discarded).

Proposed schema — Pset_GitCommit:

Property Type Example
CommitHash IfcLabel a1b2c3d4f5e6c7b8
CommitMessage IfcText Fix wall thickness
CommitAuthor IfcLabel alice <alice@example.com>
CommitDate IfcLabel 2026-03-24T14:30:00Z
CommitBranch IfcLabel main
OperationName IfcLabel Modify

ifcopenshell snippet:

pset = ifcopenshell.api.pset.add_pset(model, product=element, name="Pset_GitCommit")
ifcopenshell.api.pset.edit_pset(model, pset=pset, properties={
    "CommitHash":    commit_hash,
    "CommitMessage": commit_message,
    "CommitAuthor":  commit_author,
    "CommitDate":    commit_date,
    "CommitBranch":  branch,
})

Reading back:

for rel in element.IsDefinedBy or []:
    if rel.is_a("IfcRelDefinesByProperties"):
        pset = rel.RelatingPropertyDefinition
        if pset.Name == "Pset_GitCommit":
            props = {p.Name: p.NominalValue.wrappedValue for p in pset.HasProperties}

Verdict: Best fit for per-element traceability. Flexible, queryable, spec-compliant.


3. IfcDocumentInformation — For Linking to External Commits

IfcDocumentInformation + IfcRelAssociatesDocument lets you attach a document reference (URL, identifier, description) to any IfcRoot entity. It is designed for spec sheets and drawings but can carry a git commit URL.

doc = ifcopenshell.api.document.add_information(model)
ifcopenshell.api.document.edit_information(model, information=doc, attributes={
    "Identification": commit_hash[:8],
    "Name":           commit_message,
    "Location":       f"https://gitaec.org/rvba/ifc-commit/commit/{commit_hash}",
})
ref = ifcopenshell.api.document.add_reference(model, information=doc)
ifcopenshell.api.document.assign_document(model, products=[element], document=ref)

Verdict: Useful if you want to link elements back to a hosted commit URL. More verbose than a Pset. Better for file-level "source revision" than per-element tracking.


4. IfcApplication — File-Level Commit Stamp

IfcApplication is referenced by every IfcOwnerHistory. Its Version field can carry the current commit hash as a lightweight file-level stamp.

app = ifcopenshell.api.owner.add_application(model, ...)
ifcopenshell.api.owner.edit_application(model, application=app, attributes={
    "ApplicationIdentifier": "ifc-commit",
    "Version":               commit_hash,   # e.g. "a1b2c3d4"
    "Name":                  "ifc-commit",
})

Verdict: Zero overhead, but limited to one hash per file. Good as a quick "what commit produced this file" marker.


5. Comparison

Mechanism Granularity Stores hash/message IFC compliance Overhead
IfcOwnerHistory per-element No Native Minimal
Pset_GitCommit per-element Yes (all fields) Standard extension Medium
IfcDocumentInformation per-element Yes (via Location) Standard High
IfcApplication.Version per-file Hash only Native Minimal

6. Recommendation

A two-layer approach:

  1. File level — Set IfcApplication.Version to the commit hash. Every tool that reads IfcOwnerHistory will expose this.

  2. Element level — On elements touched by an operation (extract, merge, replace), write a Pset_GitCommit property set with the full commit metadata. Update IfcOwnerHistory.ChangeAction to ADDED or MODIFIED accordingly.

This keeps standard IFC compliance intact while making full git provenance queryable directly from the model.