Just like Git, Wrgl can merge two commits automatically using three-way merge. When Wrgl cannot decide how to merge certain rows, they are presented as conflicts which can be resolved via the built-in mergetool or with external tools. There are three phases in the merging process:
- Automatic resolution
- Handle conflicts with either the built-in mergetool or external tools
- Create a merge commit. The merge commit will be created under the same branch as the first merge head (the first argument to wrgl merge), and will have both merge heads as its parents.
Merge two commits together.
wrgl merge BRANCH COMMIT [flags]
If merge is successful then create a merge commit under BRANCH.
don't perform merge, just create a merge commit with the specified CSV file
CSV delimiter during commit with --commit-csv, defaults to comma
when merging a descendant commit into a branch, don't create a merge commit but simply fast-forward branch to the descendant commit. Create an extra merge commit otherwise. This is the default behavior unless merge.fastForward is configured.
only allow fast-forward merges. This is the default when merge.fastForward is set to "only".
help for merge
merge commit message
perform the merge but don't create a merge commit, instead output merge result to file MERGE_SUM1_SUM2_..._SUMn.csv
always create a merge commit, even when a simple fast-forward is possible. This is the default when merge.fastFoward is set to "never".
don't show mergetool, instead output conflicts (and resolved rows) to file CONFLICTS_SUM1_SUM2_..._SUMn.csv
number of CPU threads to utilize (default to GOMAXPROCS)
merge commit primary key. This is only used when --commit-csv is in use. If this isn't specified then primary key is the same as BRANCH HEAD's
set Badger log level, valid options are "error", "warning", "debug", and "info" (defaults to "error")
write cpu profile to file
write heap profile to file
output logs to specified file
log verbosity. Higher value means more log
don't display progress bar
parent directory of repo, default to current working directory.
# merge two branches wrgl merge branch-1 branch-2 # merge a commit into a branch wrgl merge my-branch 43a5f3447e82b53a2574ef5af470df96 # perform merge but don't create a merge commit, output result to file MERGE_SUM1_SUM2.csv instead wrgl merge branch-1 branch-2 --no-commit # don't show merge UI, output conflicts and resolved rows to CONFLICTS_SUM1_SUM2.csv instead wrgl merge branch-1 branch-2 --no-gui # create a merge commit from an already resolved CSV file wrgl merge branch-1 branch-2 --commit-csv resolved.csv
The merging process always start by trying to automatically merge rows without user input. At the end of this phase, rows that Wrgl doesn't know how to merge will be presented to you as conflicts. In the best case scenario, all rows are merged automatically and no human input is needed.
The commits that need to be merged (specified as arguments to wrgl merge) are called merge heads. Wrgl perform three-way merge which follow three steps:
- Identify the closest common ancestor for both heads (henceforth called the base commit).
- For each merge head, identify changes compared to the base commit. Similar to wrgl diff, changes are identified by matching rows using the primary key and do pairwise comparison.
- Group changes using the primary key value. For each group, try to compare and reconcile changes.
For this process to work, both heads as well as the base commit must share the same primary key, which mean the columns that make up the primary key and their order must remain the same.
For each primary key value, the following cases are handled:
- Row remain unchanged across all merge heads: Add row verbatim to the end result.
- Row was removed from all merge heads: Remove row from the end result.
- Row was removed in one merge head but unchanged in the other: Remove row from the end result.
- Row removed in one merge head, modified in the other: Mark as conflict.
- Row was added in one merge head, or added in both merge heads and are identical: Add row to the end result.
- Row was added in both merge heads but are not identical: Mark as conflict.
- Row was modified in one merge head, or modified in both merge heads and are identical: Add modified row verbatim to the end result.
- Row was modified in both merge heads but are not identical: Mark as conflict.
Within each conflicting row, each cell is further marked as unresolved if one of the following conditions is true:
- The cell was modified in one head and removed in the other which is also true if the entire row was removed.
- The cell was modified in both heads but with different values.
- The cell was added in both heads (new column) but with different values.
If there are conflicts of any kind, the next step is to handle conflicts using the built-in mergetool or external tools.
Using the built-in mergetool
Running wrgl merge without
--no-gui flag displays the built-in mergetool.
From top to bottom:
- Names of merge heads and the base commit in the format
Merging <merge head 1>, <merge head 2> (base <base commit>)
- Resolution progress and column/cell status in the format
<resolution progress> - <column/cell status>. Resolution progress display number and percentage of rows that still need to be manually resolved. Column/cell status displays additional information for any column/cell that the cursor is on top of.
- Resolution table which is described in the next section.
- Keyboard shortcuts.
The purpose of this table is to manually resolve conflicts. Each conflicting row is presented as a set of three rows in this order:
- Row from the first merge head with row label
<name of first merge head> (<commit sum>).
- Row from the second merge head with row label
<name of second merge head> (<commit sum>).
- Row resolution (final value) with row label
unresolvedif there are still unresolved cells, or
resolvedif all cells are resolved. Conflicting cells have red background and must be edit to be considered resolved.
Row label of each mergehead are color-coded as follow:
- Red: row was removed from this head, in which case the entire row is empty.
- Yellow: row was modified in this head.
- Green: row was added in this head (was not present in the base commit).
Each column label is color-coded as follow:
- White: column was preserved across all merge heads.
- Green: column was added in one or both heads.
- Red: column was removed in one or both heads.
Each cell is color-coded as follow:
- Teal: cell belongs to one of the primary key columns. Primary key columns are always freezed to the left, right after the row label.
- White: cell value is unchanged compared to the base commit.
- Green: cell was added.
- Empty cell (red background when hovered): cell was removed.
- Yellow: cell was modified.
Interacting with resolution table
You can of course hit enter on any cell in the resolution row to edit their final value. There are however many shortcuts that make manual resolution as easy as possible:
- Hit enter on any cell in one of the merge head rows to copy the value in that cell to the corresponding cell in the resolution row.
dto delete row from the end result.
Shift+dto delete column from the end result.
Shift+uto undo, redo.
nto go to the next conflicting cell.
rto mark the current row as resolved (output the row as is).
Shift+rto mark the current row as unresolved.
When you are satisfied with the result, hit
Shift+x to commit the result.
Resolving conflicts with external tools
Resolving conflicts with external tools follows these three steps:
- Output conflicts to a CSV file by running wrgl merge with
- Resolve conflicts with any tool of your choosing e.g. Excel or Python script.
- Commit the resolved file as the merge commit by running wrgl merge with
The conflicts file will be named
CONFLICTS_<SUM1>_<SUM2>.csv. The very first row in this file list the column names. The next two rows list column statuses (
NEW, or empty) for two merge heads.
The next group of rows are conflicts, each conflict are presented as four rows:
- The row from the base commit.
- The row from the first merge head.
- The row from the second merge head.
- The suggested resolution (or lack thereof for unresolved cells).
The final group of rows are resolved rows with zero conflicts. They are presented as is. Non-conflicting removals are not presented in this group of course.
Example conflicts file
Given these merge heads:
a,b,d 1,g,e 2,h,d
a,c,e 1,q,w 3,z,x
Which share this commit as the closest common ancestor:
a,b,c 1,q,w 2,a,s 4,v,b
Running this command:
wrgl merge branch-1 branch-2 --no-gui
Will produce this file:
,a,c,e,b,d COLUMNS IN branch-1 (0402137),,REMOVED,,,NEW COLUMNS IN branch-2 (ac8cf44),,,NEW,REMOVED, BASE 2ca57c7,1,w,,q, branch-1 (0402137),1,,,g,e branch-2 (ac8cf44),1,q,w,, RESOLUTION,1,w,w,q,e BASE 2ca57c7,2,s,,a, branch-1 (0402137),2,,,h,d branch-2 (ac8cf44),REMOVED IN ac8cf44,REMOVED IN ac8cf44,REMOVED IN ac8cf44,REMOVED IN ac8cf44,REMOVED IN ac8cf44 RESOLUTION,2,,,a,d ,3,z,x,,