In game development you collaborate on visual, structured content: levels filled with platforms and enemies, UI screens with arranged buttons, 3D environments with placed props.
Current version control tools like Git only see this content as plain text, stripping away spatial layout and hierarchical structure. When merge conflicts occur, developers have to resolve them line-by-line, looking just at the text representation.
In project Beckett, we are building a version control system for scene files in the Godot game engine that preserves these meaningful spatial and structural relationships.
Spatial scenes reduced to sequential text
Games built with Godot are made up of “scenes”, which are …
In game development you collaborate on visual, structured content: levels filled with platforms and enemies, UI screens with arranged buttons, 3D environments with placed props.
Current version control tools like Git only see this content as plain text, stripping away spatial layout and hierarchical structure. When merge conflicts occur, developers have to resolve them line-by-line, looking just at the text representation.
In project Beckett, we are building a version control system for scene files in the Godot game engine that preserves these meaningful spatial and structural relationships.
Spatial scenes reduced to sequential text
Games built with Godot are made up of “scenes”, which are stored as text files to allow version control with Git and other typical version control tools. A scene is composed of nodes—the engine’s smallest building blocks—arranged into trees. Each node has spatial properties (position, rotation, scale) and a place in the hierarchy. Here’s what a change to a scene file looks like in Git.

The visual properties are reduced to numeric parameters scattered across the file. The tree structure is flattened into a linear list. When reviewing changes, a developer must mentally reconstruct both the spatial layout and hierarchical relationships to understand what changed.
When two developers modify the same scene, Git may report a conflict even when changes are semantically independent. One developer adds platforms at the top of a level while another adds coins at the bottom, but if these touch adjacent lines in the text file, Git sees a conflict. The resulting file contains conflict markers and won’t load in Godot. Only once all conflicts are resolved can the scene be loaded again.
Version control inside the spatial editor
Our plugin for Godot shows changes in two distinct ways. In the viewport at the center, you see changed objects clearly highlighted. In the right sidebar, there is a complete list of all changes, which is important when changing non-visual properties like the player’s speed. These two views reinforce each other — when you hover over a changed property in the sidebar, the corresponding element lights up in the viewport.

When using Git with Godot, it’s unfortunately easy to corrupt scene files when merging, rendering them unable to load. We’ve heard that this leads game developers to isolate their work to avoid conflicts — one developer works on assets, another on layout — instead of collaborating freely. Since our plugin was designed to work with the proper structure of scene files, we can guarantee that you always get a loadable scene when merging. This doesn’t mean that all changes can be combined automatically — conflicts are still possible, as we’ll see in a moment. But we believe it’s better to have a project you can open and work on within the editor than a project that won’t open until you fix a bunch of conflicts in the abstract text representation.

Visualizing diffs within a visual editor, like a game editor viewport, is a tricky affair — you have no idea what the art style of the game will be, so simply highlighting diffs in green is unlikely to work in many cases. Our current approach is to grey-out all non-highlighted elements, giving the changed objects stark contrast with their surroundings.

Open questions and next steps
Our exploration of native version control within Godot is ongoing, and we have many ideas and open questions to drive further work. Here are three.
Handling conflicts. Automerge, which powers our version control plugin, will detect when multiple changes are made concurrently to the same property, resulting in a conflict. By default it selects one change as the “winner”, but the other changes are remembered and can be accessed. We’d like to explore various ways of surfacing these conflicting changes within Godot. [Ed: it’d be nice to add one example of how this might work.]
Conflicts for spatial data. Spatial and structural data, like the scene files used by Godot, are subject to a variety of conflicts that require resolution strategies specific to their domain. For instance, if two developers move the same node to different positions, that’s a conflict — but it’s the sort of conflict you’d experience in any domain where a property can only have a single value. But what if one developer moves a node while another deletes it? Or if both add a child to the same parent? While these conflicts can occur in other domains, it’s likely that the way to correctly resolve them in the context of a Godot scene requires special handling to preserve the invariants Godot and game developers expect to be upheld.
Other spatial / structural domains. The problems we’ve encountered when merging spatial data, displaying visual diffs, and preserving hierarchical structure apply beyond Godot and game development. 3D modeling tools, vector graphics editors, and visual programming environments face similar challenges. We’ve reviewed a variety of existing approaches to solving these problems in these other domains, and we’re keen to continue exploring the space.
This is the last entry for now, but you can go back to read the lab notebook from the beginning.
The Ink & Switch Dispatch
Keep up-to-date with the lab’s latest findings, appearances, and happenings by subscribing to our newsletter. For a sneak peek, browse the archive.