Three-way merge
A three-way merge answers the question a source-control merge asks: two developers started from the same object and each changed it. What does the combined version look like? You give it File1, File2 and the Base both came from, and it produces a merged Base. Your first three-way merge walks through one.
Choosing the three files
Choose Compare ▸ New Comparison, then select
Three-way merge. New
Comparison shows File1, Base and File2. Type the paths, browse for
them, or drop three files onto the dialog; the button with the two
arrows between two rows swaps them. Dropping three files onto the window
opens New Comparison in its three-way mode with the three filled in, so
that you can check which is the Base before the merge starts. The
command line does the same without the dialog:
-Mode ThreeWay -File1 … -Base … -File2 ….
An object that lives in a library is given as
container!entry (old.pbl!w_main.srw): Browse
to the library and choose it with the Object… button
that appears beside the path, as Select
PB Object describes, or type or paste the address. A merge therefore
does not have to start by exporting three objects. All three inputs must
be source files or all three must be objects selected from libraries,
and they must be of the same type: three windows, or three DataWindows,
not a window and a user object. The dialog says so before anything is
parsed.
The files are fixed when the merge opens, as they are in a two-way compare: the tab names them over their three columns, F5 compares the same three again, and other files are a new comparison. When you have decisions that are not saved yet, F5 asks before discarding them, and so does closing the tab.
When the Start-up sheet is set to a three-way merge, pb-diff starts on the three files of your last merge, as long as Automatically load last three-way compare is on in Options. If all three are still where they were, the compare runs straight away. Otherwise New Comparison opens, set to three-way and filled with what was remembered: a path that has since moved or gone is left exactly as it was remembered, the status bar names it, and nothing is compared until you correct it. pb-diff never substitutes another file for one that has gone. Paths you supply yourself, on the command line or in the dialog, always win.
Reading the tab
One hierarchy tree is shared by all three files, and it carries two icon rails: the left rail shows what File1 changed relative to the Base, the right rail what File2 changed. A node with a marker in both rails is one that both sides touched, which is where a conflict may be waiting. A node neither side changed has empty markers, so that names stay in line with their siblings, except the file's row and a group's, whose names start where the markers would.
Beside it, the three versions are shown side by side, each once: File1, the Base and File2, in three columns lined up on the Base. A line one side changed sits on the same row as the Base line it replaced, and lines only one side inserted leave an empty space in the other columns, so the three versions never drift apart. File1 and File2 are coloured by what they changed against the Base, and the Base by the change a side made to it. A row both sides changed differently, which is a conflict, carries a rule down the left edge of all three columns. Selecting a content node, a structural parent or the root follows the same exact-source rule as a File Compare, for both comparisons against the Base.
Underneath, across the full width, is the Merged element: the merged text of the action selected in the list; Save writes the whole object. Above it, a line names the merge target, and below it is the Merge Actions list. Every division here is a splitter you can drag: between File1 and the Base and between the Base and File2, between the three versions and the merged pane, between the merged pane and the actions list, and between the tree and all of them. Dragging the line between two versions moves width only between those two, and the three stay aligned row by row; double-click it, or choose View ▸ Equal Column Widths, to put them back to equal widths, and from the keyboard Ctrl+Shift+← and Ctrl+Shift+→ move the line beside the column the caret is in, as in a File Compare.
The Merge Actions list
The list tracks every tree-level change that must be carried into the Base. Its Kind column holds one of exactly three words:
| Kind | What it means |
|---|---|
| To apply | Only one side changed this item, or both changed it identically, or both changed it in places that do not overlap. There is no decision to make, so Auto Merge can apply it. |
| Conflict | File1 and File2 both changed the same line or lines, differently; or one side deleted the item, or the control or XML element it is in, and the other changed or added it. There is no correct answer that pb-diff can derive, so this one is yours. |
| Resolved | An action you have since settled. It started as one of the two above and is no longer waiting for you. |
Above the list, a line says what to do next and keeps count as you go: how many changes Auto Merge can still apply, how many conflicts wait on their rows, then Save. After every decision the status bar says how many actions are still to decide. When none is left, the line turns green and says All actions are resolved , and only saving remains.
The merge commands
All four live under Compare ▸ Merge, and all four are in the tool bar while a three-way merge is the tab in front, as Auto Merge, Take Left, Keep Base and Take Right. The three decisions are also on every row of the list, as the buttons under Left, Base and Right and on its right-click menu. A row's buttons always decide that row's whole item. Auto Merge is not on a row, because it acts on the whole merge:
| Command | What it does |
|---|---|
| Auto Merge | Applies every action whose Kind is To apply in one step and leaves the conflicts alone. |
| Take from Left | Brings File1's version of what is selected into the Base. |
| Take from Right | Brings File2's version into the Base. |
| Keep Base | Leaves the region as the Base has it, which resolves a conflict by declining both sides. |
On a typical merge, the actions Auto Merge can apply are most of the list, and running it first is the right way to start: run it, then deal with what is left. Everything it inserts is marked with the Edited marker and an edited background, so the merged file tells you which of its parts were produced by the merge rather than inherited from the Base.
A whole item or one block
With a node selected in the hierarchy tree, Take from Left and Take from Right bring that whole item into the Base, as it stands; line-level detail is not consulted. This is what you want for a function only one side touched, or one whose two versions are different enough that merging them line by line makes no sense.
Click a coloured row in the File1 or File2 column and the same two commands work at the level of one change block: take this block from that side into the Base. A click in the Base column points at the side that changed that line. The merge target line names the target, for example Merge target: wf_load — change block 2 of 5, from File1, so you can check what the next command will do before running it. Click a row both sides agree about and it goes back to naming the whole item.
A block belongs to the side it was clicked on, so Take from Left after clicking in the File2 column is a decision about the whole item; the block number means nothing on the other side. Blocks compose from the Base rather than on top of one another, so taking one block from each side is exactly that, and answering the same region twice replaces the first answer instead of inserting the region twice. Use them when both sides changed the same function in different places.
Editing the merged text
The merged pane shows the element selected in the list or the tree. Select the object or a group such as Functions and it shows the merged text of everything under it, read only, so the whole merge can be read before it is saved; select an item under it to edit.
The merged pane of an item is editable at all times. When neither side's version is what you want, which is the usual outcome of a genuine conflict, where the correct code is a combination of both, type it. Every line that differs from the Base uses the edited background and has a marker bar on its left, whether it was taken from a side, applied by Auto Merge or typed by hand, so you can identify the merge's work without another comparison. Edit ▸ Undo and Edit ▸ Redo act on the script that has focus, without changing your work in other scripts.
Conflicts
An unresolved conflict stays marked across the three versions and in the merged pane until you resolve it, so it cannot be lost by scrolling past it. Resolve it by taking one side's block, by taking an item wholesale, by keeping the Base, or by editing the merged text.
An event or a property block belongs to its control, and an XML element to the element it is in. If you keep one while the merge leaves out its control or element, the merged file has no place for it: it is left out, and the save says so among its warnings. Take the control or element as well to keep both.
pb-diff never writes conflict markers such as
<<<<<<< into the merged file. Those
markers are not valid PowerScript and would stop the export from
importing. If you save while conflicts remain, pb-diff asks whether to
save a partial result; choose No to return to the merge
without writing a file.
Filtering and navigation
Only Changed Items and Only Changed Lines work exactly as they do in a two-way File Compare: the first hides unchanged nodes, the second folds unchanged runs of text to a placeholder saying how many lines it hides. They are the same two commands on the same View menu and the same two tick boxes in the tool bar. A merge opens with Only Changed Items on, so the tree holds what a side changed and the nodes above it; three files with nothing changed keep their whole tree. A folded run opens on a click, or move onto the placeholder with ↑ and ↓ and press Enter or Space. Folding never changes what the merge writes.
The four navigation commands on the Navigate menu (Alt+←, Alt+↑, Alt+↓, Alt+→) walk the changes of both sides in order: every change inside the action on show, taken in Base order whichever file made it, and then on to the next changed action. They are also the last four buttons this tab puts in the tool bar, the same group in the same place as in a two-way compare. The status bar shows Changed item 4 of 17 and, beside it, Change 2 of 6, which shows where you are inside the current action.
Compare Current Line (Ctrl+L) sends the line the caret is on to a Line Compare tab, against the Base: File1's version in File1's column, File2's in File2's, and in the Base column the version of the side that changed that line. Double-clicking a line does the same thing.
Saving
Save, in the tool bar or as File ▸
Save (Ctrl+S), writes the merged Base as
a PowerBuilder export file, ready to be checked in. The dialog opens in
the Base's folder and proposes the Base's name with .merged
before the extension: n_pricing.sru becomes
n_pricing.merged.sru, so the Base stays as it is unless you
choose its name. For a Base inside a library the name is the entry's,
and the folder is the library's. When pb-diff was started with
-Save, that file is written without asking. Close the tab
when you are done.
The file is written the way every merge writes one: UTF-16LE with a
byte-order mark and CRLF, the form the Library painter writes and the
same one pb-diff-cli merge3 produces, or UTF-8 when the
Base is a PowerBuilder 2025 Solution source. A build file, an INI, XML
or text file keeps the encoding it was read in. A control merged in
brings its create call, its destroy call and
its place in the control list with it; its declarations are actions in
the list like any other, and yours to merge. See How merging works.
pb-diff does not compile the result. Import it into a scratch target and build it before you rely on it. Importing merged output is supported for PowerBuilder 19 and later; it is unverified for versions 10 to 18.
The headless equivalent is pb-diff-cli merge3; see Automate with
pb-diff-cli.