Merging Metadata
BimlFlex provides stored procedures for merging project metadata from one version (or customer) into another. This is useful when consolidating work from parallel development branches, promoting metadata between environments, or combining metadata from multiple teams into a single version.
The merge workflow is a two-step process:
- Preview — inspect what will change, identify conflicts
- Execute — apply the merge, providing conflict resolutions where needed
Prerequisites
- Direct access to the BimlFlex metadata database (SQL Server Management Studio, Azure Data Studio, or similar)
- Knowledge of the source and target Customer UID and Version Name values
- The source version must already exist; the target version will be created automatically if it does not exist
You can find Customer UIDs and Version Names by querying the [app].[Versions] table:
SELECT [CustomerUID], [Name] AS VersionName
FROM [app].[Versions]
ORDER BY [CustomerUID], [Name]
Entity Scope
A project merge covers five entity types, processed in dependency order:
| Order | Entity Type | Scoped Name Format | Example |
|---|---|---|---|
| 1 | Batch | BatchName | LOAD_BDV |
| 2 | Connection | ConnectionName | AWLT_SRC |
| 3 | Project | ProjectName | EXT_AWLT_SRC |
| 4 | Object | Connection.Schema.ObjectName | AWLT_SRC.SalesLT.Customer |
| 5 | Column | Connection.Schema.Object.ColumnName | AWLT_SRC.SalesLT.Customer.CustomerID |
Only entities that belong to the specified project (and their dependencies) are included in the merge.
Step 1 — Preview the Merge
Use merge.PreviewProject to generate a read-only conflict report without making any changes to the target version.
Syntax
EXEC [merge].[PreviewProject]
@SourceCustomerUID = '<source-customer-uid>'
,@SourceVersionName = '<source-version-name>'
,@TargetCustomerUID = '<target-customer-uid>'
,@TargetVersionName = '<target-version-name>'
,@ProjectName = '<project-name>'
Parameters
| Parameter | Type | Description |
|---|---|---|
@SourceCustomerUID | NVARCHAR(40) | The Customer UID of the source version |
@SourceVersionName | NVARCHAR(256) | The name of the source version to merge from |
@TargetCustomerUID | NVARCHAR(40) | The Customer UID of the target version |
@TargetVersionName | NVARCHAR(256) | The name of the target version to merge into |
@ProjectName | NVARCHAR(256) | The project whose metadata will be merged |
Example
EXEC [merge].[PreviewProject]
@SourceCustomerUID = 'A0E1B2C3-D4F5-6789-ABCD-EF0123456789'
,@SourceVersionName = 'Sprint 24'
,@TargetCustomerUID = 'A0E1B2C3-D4F5-6789-ABCD-EF0123456789'
,@TargetVersionName = 'Sprint 25'
,@ProjectName = 'EXT_AWLT_SRC'
Understanding the Results
The preview returns one row per entity with the following columns:
| Column | Description |
|---|---|
| ConflictId | Sequential number identifying each row. Use this value to reference specific conflicts when providing overrides. |
| EntityType | The type of entity: Batch, Connection, Project, Object, or Column. |
| ScopedName | The fully-qualified name of the entity (format varies by entity type — see table above). |
| SourceUID | The unique identifier of the entity in the source version. |
| TargetUID | The unique identifier of the matching entity in the target version, or NULL if no match was found. |
| MatchType | How the entity was matched: ScopedName (matched by name), ClonedFromUID (matched via clone lineage), or NULL (no match — new entity). |
| HasConflict | 1 if the source and target entities differ in one or more business columns; 0 otherwise. |
| DiffColumns | A comma-separated list of column names that differ between source and target. NULL if there is no conflict. |
| MergeAction | The action that will be taken: Add (new entity), Conflict (requires resolution), or Unchanged (identical in both versions). |
Interpreting Merge Actions
- Add — The entity exists in the source but has no match in the target. It will be added during execution.
- Unchanged — The entity exists in both versions and all business columns are identical. No action needed.
- Conflict — The entity exists in both versions but one or more business columns differ. You must provide a conflict resolution before executing the merge.
Example Output
| ConflictId | EntityType | ScopedName | MatchType | HasConflict | MergeAction |
|---|---|---|---|---|---|
| 1 | Batch | LOAD_BDV | ScopedName | 0 | Unchanged |
| 2 | Connection | AWLT_SRC | ScopedName | 1 | Conflict |
| 3 | Connection | BDV_STG | NULL | 0 | Add |
| 4 | Project | EXT_AWLT_SRC | ScopedName | 0 | Unchanged |
| 5 | Object | AWLT_SRC.SalesLT.Customer | ScopedName | 1 | Conflict |
In this example:
- ConflictId 2 (
AWLT_SRCconnection) has a conflict — check theDiffColumnsvalue to see which properties differ - ConflictId 3 (
BDV_STGconnection) is new and will be added automatically - ConflictId 5 (
Customerobject) has a conflict that needs to be resolved
Step 2 — Execute the Merge
After reviewing the preview, use merge.ExecuteProject to apply the merge. If the preview identified any conflicts, you must provide resolutions via the @ConflictOverrides parameter.
Syntax
EXEC [merge].[ExecuteProject]
@SourceCustomerUID = '<source-customer-uid>'
,@SourceVersionName = '<source-version-name>'
,@TargetCustomerUID = '<target-customer-uid>'
,@TargetVersionName = '<target-version-name>'
,@ProjectName = '<project-name>'
,@ConflictOverrides = '<json-overrides>'
,@UserName = '<user-name>'
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
@SourceCustomerUID | NVARCHAR(40) | Yes | The Customer UID of the source version |
@SourceVersionName | NVARCHAR(256) | Yes | The name of the source version to merge from |
@TargetCustomerUID | NVARCHAR(40) | No | The Customer UID of the target version. Defaults to the source customer if omitted. |
@TargetVersionName | NVARCHAR(256) | Yes | The name of the target version to merge into. Created automatically if it does not exist. |
@ProjectName | NVARCHAR(256) | Yes | The project whose metadata will be merged |
@ConflictOverrides | NVARCHAR(MAX) | No | A JSON document specifying how to resolve conflicts (see below). Required if the preview shows any Conflict rows. |
@UserName | NVARCHAR(256) | No | The user name recorded as the author of the merge. Defaults to the current SQL login. |
Conflict Resolution
Each conflict must be resolved as either:
take-incoming— replace the target entity with the source version (accept the incoming change)take-target— keep the target entity as-is (reject the incoming change)
The @ConflictOverrides parameter accepts two JSON formats. You can use whichever is more convenient.
Format 1: By Entity Type and Scoped Name
Reference conflicts using the entity type and scoped name from the preview results:
{
"Connection": {
"AWLT_SRC": "take-incoming"
},
"Object": {
"AWLT_SRC.SalesLT.Customer": "take-target"
}
}
Format 2: By Conflict ID
Reference conflicts using the ConflictId number from the preview results:
{
"2": "take-incoming",
"5": "take-target"
}
Conflict IDs are assigned dynamically and may change if metadata is modified between preview and execution. If you are working in an environment where other users may be making concurrent changes, prefer Format 1 (entity type + scoped name) for more stable references.
Example — No Conflicts
When the preview shows only Add and Unchanged actions, no overrides are needed:
EXEC [merge].[ExecuteProject]
@SourceCustomerUID = 'A0E1B2C3-D4F5-6789-ABCD-EF0123456789'
,@SourceVersionName = 'Sprint 24'
,@TargetCustomerUID = 'A0E1B2C3-D4F5-6789-ABCD-EF0123456789'
,@TargetVersionName = 'Sprint 25'
,@ProjectName = 'EXT_AWLT_SRC'
Example — With Conflict Resolutions
Using the preview output from Step 1, resolve both conflicts:
EXEC [merge].[ExecuteProject]
@SourceCustomerUID = 'A0E1B2C3-D4F5-6789-ABCD-EF0123456789'
,@SourceVersionName = 'Sprint 24'
,@TargetCustomerUID = 'A0E1B2C3-D4F5-6789-ABCD-EF0123456789'
,@TargetVersionName = 'Sprint 25'
,@ProjectName = 'EXT_AWLT_SRC'
,@ConflictOverrides = '{
"Connection": { "AWLT_SRC": "take-incoming" },
"Object": { "AWLT_SRC.SalesLT.Customer": "take-target" }
}'
,@UserName = 'kim@contoso.com'
Understanding the Results
The execution returns a summary with one row per entity:
| Column | Description |
|---|---|
| ConflictId | Sequential row identifier |
| EntityType | The entity type |
| ScopedName | The fully-qualified entity name |
| MergeResult | The outcome: Added, Updated (take-incoming), Kept (take-target), or Unchanged |
Unresolved Conflicts
If any conflicts remain unresolved, the merge is blocked — no changes are made. The procedure returns the full conflict report with unresolved rows marked as Conflict (UNRESOLVED) and raises an error:
Merge blocked: unresolved conflicts exist. Provide @ConflictOverrides for all conflicts.
Review the unresolved conflicts and re-execute with the appropriate overrides.
Full Workflow Example
The following example walks through a complete merge from Sprint 24 to Sprint 25 within the same customer.
1. Preview
-- Step 1: See what will happen
EXEC [merge].[PreviewProject]
@SourceCustomerUID = 'A0E1B2C3-D4F5-6789-ABCD-EF0123456789'
,@SourceVersionName = 'Sprint 24'
,@TargetCustomerUID = 'A0E1B2C3-D4F5-6789-ABCD-EF0123456789'
,@TargetVersionName = 'Sprint 25'
,@ProjectName = 'EXT_AWLT_SRC'
Review the results. Note any rows where MergeAction = 'Conflict' and check DiffColumns to understand what differs.
2. Decide Resolutions
For each conflict, decide whether to accept the incoming (source) value or keep the current target value. Use the DiffColumns output to guide your decision.
3. Execute
-- Step 2: Apply the merge with resolutions
EXEC [merge].[ExecuteProject]
@SourceCustomerUID = 'A0E1B2C3-D4F5-6789-ABCD-EF0123456789'
,@SourceVersionName = 'Sprint 24'
,@TargetCustomerUID = 'A0E1B2C3-D4F5-6789-ABCD-EF0123456789'
,@TargetVersionName = 'Sprint 25'
,@ProjectName = 'EXT_AWLT_SRC'
,@ConflictOverrides = '{"2": "take-incoming", "5": "take-target"}'
,@UserName = 'kim@contoso.com'
4. Verify
Review the MergeResult column in the output to confirm all entities were handled as expected.
Cross-Customer Merges
The merge procedures support merging metadata between different customers by specifying different values for @SourceCustomerUID and @TargetCustomerUID. This is useful when:
- Consolidating metadata from a development customer into a production customer
- Combining work from isolated teams that used separate customers
- Migrating a project from one BimlFlex deployment to another (same database)
When merging across customers, entity matching relies on scoped names and clone lineage (ClonedFromUID). Entities that were independently created in both customers with the same name will be matched by name and may show as conflicts if their properties differ.
Tips and Best Practices
- Always preview before executing. The preview is read-only and shows you exactly what will happen.
- Snapshot your target version before executing a merge, so you can roll back if needed.
- Use scoped-name overrides (Format 1) when working in environments with concurrent metadata changes, as ConflictIds can shift.
- Review
DiffColumnscarefully before choosing a resolution. The column-level detail helps you understand the impact of each conflict. - Coordinate with your team. If multiple developers are working on the same version, ensure no one is actively editing the target version during a merge.
- The merge operates within a single transaction. If any step fails, all changes are rolled back automatically.