← Blog

Version control for 3D model files: the limits

Sliced output gets labels, outcomes, and a recommended marker. Source meshes get an ordered history and nothing else. Where the line falls, what to do past it.

versioningworkfloworganization

Version control for 3D printing files splits unevenly, and the split is worth knowing before you design a workflow around it. Sliced G-code has real tooling: a label, an outcome, notes, and exactly one revision marked as the one to reprint. Source meshes have far less. In PrintStash they get an ordered history and a version number, and none of the bookkeeping, because the revision endpoints reject anything that is not G-code.

That is a limitation rather than a design flaw waiting to be argued away, and knowing which side of the line you are on saves you from expecting a feature that is not there.

What a model actually holds

A model is a logical asset rather than a file: the bracket, not bracket.stl. It owns every file underneath it, which includes the source mesh, each sliced export, the thumbnail, and the parsed metadata. Models are deduplicated by the SHA-256 of their source mesh, so re-uploading the same STL resolves to the model that already holds it instead of starting a second one.

Each file underneath carries a version number within that model, so the history of a model is the ordered list of its files. That much applies to every format.

What only G-code gets

The bookkeeping. A G-code file can carry a label, free-text notes, and an outcome of needs_test, known_good, failed, or archived. One of them holds the recommended marker, and the rule is enforced rather than encouraged: a model with G-code always has exactly one recommended revision, never zero and never two. The first G-code uploaded to a model claims it automatically, and marking another clears it everywhere else.

Try to set any of that on an STL, an OBJ, a STEP file, or a 3MF and the API answers revision_not_supported. There is no hidden path through the interface either, because the interface calls the same endpoints.

So the practical shape is: you can keep five versions of a mesh under one model and see them in order, and you cannot record which of the five is the good one. For the sliced output, you can.

What to do on the mesh side instead

Keep the editable source, not only the export. A STEP file or a CAD project is the thing a future edit starts from, and an STL is a lossy end product that nobody enjoys modifying. PrintStash stores STEP and STP normally and previews them in the browser on both amd64 and ARM since v0.12.0, which keeping STEP files beside the printable mesh covers.

Put the reasoning somewhere durable. Collections carry documents and a README, which is the right home for build notes covering a project’s models, since individual meshes have nowhere to hold a paragraph.

And accept the identity limit rather than working around it. Content hashing tells you two files are byte-identical; it cannot tell you that a repaired, rescaled, or re-exported mesh is a later version of an earlier one. Those are different bytes and therefore a different file, and no tool decides they are the same model without guessing. Finding duplicate STL files goes into where that boundary sits.

Where Git genuinely fits

If your source is text, use Git, because the objection to Git for 3D printing files is entirely about binary blobs. OpenSCAD, CadQuery, and build123d produce models from code, and a repository of those gives you real diffs, branches, and blame, none of which a mesh can support. The exported STL then becomes a build output rather than something to preserve.

That framing extends to the sliced side too. Pinning a slicer so a commit regenerates the same G-code turns the export into a build artifact you can discard, which versioning G-code as a reproducible build works through in full, including where the record of what actually printed still has to live.

For a mesh modelled in a GUI, Git buys you very little. Each save is an opaque blob, the diff is meaningless, and the repository grows by the full file size every time. A version number under a model record gives you the ordering without the overhead.

Questions that come up

Can I keep multiple versions of an STL in PrintStash?

Yes, and they stay grouped and ordered. Each file under a model has a version number, so the model’s history is the ordered list of its files, and uploading a revised mesh adds to that list rather than replacing what was there. What you cannot do is annotate them. Labels, notes, outcome statuses, and the recommended marker are G-code only, and the API returns revision_not_supported for any other file type. In practice that means you can see that five versions exist and in what order, and recording which one is current is on you.

Does OpenSCAD or code-CAD change the answer?

Completely, because the versioning problem moves to a file type Git was built for. A .scad file is text, so commits produce readable diffs, branches work normally, and the repository stays small no matter how many revisions it holds. The mesh becomes a build output you regenerate rather than an asset you archive, which sidesteps the whole question of how to version a binary. The library then holds what the repository cannot: the sliced output, its parsed settings, and the record of which slice printed well.

What should I keep when I remix someone else’s model?

The original download, your edited source, and the link to where it came from with its license. People routinely keep only the edited STL, which works until you want to change something and discover the edit was destructive, or until someone asks what licence the derivative inherits. The licence in particular cannot be reconstructed once the source page changes or disappears, and it is the piece that determines whether you can share or sell the result at all.

How do I know if an upstream model has been updated?

Nothing in the library will tell you, and it is worth being clear about that rather than hoping. A library holds your copy of the file, deduplicated by content hash, so it has no relationship with the page you downloaded from and no way to notice that the designer posted a fix. Checking is manual and belongs on the model site, where following the designer or the model gives you the notification. If you keep the source URL on import, that check is a click, which is most of the argument for keeping it.

Sources

  • PrintStash core concepts for the model, artifact, and revision vocabulary, the four outcome statuses, and the recommended-revision invariant.
  • The G-code restriction was read from the revision endpoints in backend/app/api/v1/models.py in the product repository, which reject a non-G-code file type with revision_not_supported.
  • Known limitations for the STEP tessellation budget and the image variants.

For the outcome rules on their own, one model, many G-codes. For the four ways people track sliced iterations, including naming conventions and controller statistics, tracking G-code revisions.