Resolve Source Control merge conflicts
Learn how to resolve merge conflicts when using Source Control.
You can resolve merge conflicts using either:
- Your source control provider's UI. GitLab and GitHub support merge conflict resolution using the web UI. This is often the simplest method for resolving conflicts.
- Your local Git CLI. You must have local access to your Git repository (e.g., SSH key) to use this option. See GitLab's documentation to learn how to resolve conflicted files and update your remote branch from your CLI.
Toolscript-specific resolution strategies
Toolscript is available on Retool Cloud and self-hosted Retool versions 3.6.0 and later. See the migration guide to learn how to migrate your apps from YAML to Toolscript.
It is important to understand how Toolscript files are structured to resolve merge conflicts correctly. Malformed Toolscript files can result in failed Source Control deployments and prevent builders from pushing new commits.
- Metadata files
- Positions files
- App code
- Library code
Metadata files are located in app/.metadata.json
. They hold metadata about the application, such as the Retool version the app was edited on and app-level settings.
If the conflict is in the version field, you should keep the later version number.
Most other fields in this file reference app-level settings. If two builders modify the same setting, keep only one valid change in the final commit. If two builders toggle different settings, it is safe to keep both settings.
Positions files are located in app/.positions.json
. They encode information about the visual properties for each plugin (e.g., positioning on the canvas).
In this file, each JSON key corresponds to a component's name. As such, all JSON keys must be unique.
Common causes
There are generally three causes for conflicts in this file.
Two builders repositioned the same components
<<<<<<< HEAD
"button1": { "row": 0, "col": 0, "height": 1 },
=======
"button1": { "row": 99, "col": 99, "height": 1 },
>>>>>>> main
In this case, each block in the conflict has the same JSON key. To maintain the widget’s position in your branch, keep your block. Otherwise, keep the block from the main branch.
Two builders positioned components at the same location
Both changes modified the same location in .positions.json
.
<<<<<<< HEAD
"button1": { "row": 0, "col": 0, "height": 1 },
=======
"table1": { "row": 99, "col": 99, "height": 1 },
>>>>>>> main
In this case, it is safe to keep both blocks.
If both of these causes happen (e.g., two builders repositioned the same components and added different components at the same location), review each conflict area and decide which strategy to apply. The .positions.json
file is sorted alphabetically by key to help you identify duplicate entries.
App code is located in app/main.rsx
and app/src/*.rsx
. These files contain the Retool widgets in the app and the app’s overall structure.
Common causes
There are generally three causes of conflicts in app source code.
Two builders edited the same widget
In this case, you must only keep the “correct” changes.
Two builders added new widgets at the same location
In this case, it is safe to keep changes from both blocks, but you should pay attention to ordering. See order of widgets in app conflicts.
Order of widgets in app conflicts
When resolving app conflicts, you must carefully consider where your changes should end up in the Toolscript file. The order of widgets is important, as it helps determine visual placement of the widget on the Retool canvas.
In general, your changes can go before, after, or within the other builder’s changes. In the last case, if the other builder introduced an import statement, you might have to place your changes in a different file entirely.
In this example, your changes should go after the existing changes.
// conflict
<<<<<<< HEAD
<Text id="message" value="hello world" />
=======
<Text id="title" value="My App" />
>>>>>>> main
// after resolution
<Text id="title" value="My App" />
<Text id="message" value="hello world" />
In this example, your changes should go within the existing changes.
// conflict
<<<<<<< HEAD
<Text id="message" value="Oh say can you see, by the dawn's early light" />
=======
<Container
id="container1"
showBody={true}
showHeader={true}
>
<Header/>
<View id="90751" >
<Text id="content" value="You can find the lyrics below:" />
</View>
</Container>
>>>>>>> main
// after resolution
<Container
id="container1"
showBody={true}
showHeader={true}
>
<Header/>
<View id="90751" >
<Text id="content" value="You can find the lyrics below:" />
<Text id="message" value="Oh say can you see, by the dawn's early light" />
</View>
</Container>
In this example, your changes should go within a different file because another builder added an import statement.
// conflict - main.rsx
<<<<<<< HEAD
<Text id="message" value="Oh say can you see, by the dawn's early light" />
=======
<Import src="./src/container.rsx" />
>>>>>>> main
// conflict - container.rsx
<Container
id="container1"
showBody={true}
showHeader={true}
>
<Header/>
<View id="90751" >
<Text id="content" value="You can find the lyrics below:" />
</View>
</Container>
// after resolution - main.rsx
<Import src="./src/container.rsx" />
// after resolution - container.rsx
<Container
id="container1"
showBody={true}
showHeader={true}
>
<Header/>
<View id="90751" >
<Text id="content" value="You can find the lyrics below:" />
</View>
</Container>
Library code is found in the lib
directory. It contains user-written executable code, such as JS and SQL files. These are most often used in queries, transformers, and certain code-first widgets.
Since this code is entirely user-written, there is no Toolscript-specific reasoning or advice for merge conflicts in these files. You may find the section on merge conflict prevention strategies useful.
Merge conflict prevention strategies
The development processes you use can help prevent merge conflicts from arising in the first place.
Keep branches and pull-requests short-lived
Trunk-based development best practices recommend branches are used for short-term feature work and kept open for a week or less. The faster a pull request is reviewed and merged into the main branch, the less likely it is for conflicting changes to be merged in during the same period of time.
Use modules to divide work
Retool modules are an excellent way to encapsulate a body of work, allowing one individual to develop on an isolated part of an app without affecting others. Before working on a complex app, it can be useful to plan what pieces of the app can be designed as reusable modules.
Assign different responsibilities to builders
Dividing responsibilities among builders helps ensure that builders make changes in different parts of the codebase. For example, you can assign one builder to work on the executable pieces of the app (e.g., queries and transformers), while another builder works on the visual pieces of the app (e.g., headers, titles, and images). Since the former exclusively affects files in the lib/
directory while the latter primarily affects files in the src/
directory, the chances of a conflict are minimal.
Preview changes in Retool after resolving conflicts
After the remote branch has been updated, you may want to preview your changes again in Retool before merging to ensure your conflict resolution was completed properly.
To do so, open your branch in Retool and use the "Reset branch” option to then select the branch and pull the updated branch into Retool to preview.
After completing your visual and interactive review, you can proceed with merging the updated branch and pull request.