Skip to content
pb-diff Help

How merging works

Merging means deciding, change by change, which version of a piece of code you want to keep, and then saving the result as a valid PowerBuilder export file. pb-diff merges in two places: inside a File Compare, between an old version and a new one, and in a three-way merge, File1 and File2 against the Base they both came from. Nothing is written until you save, and none of the input files is modified.

A saved merge copies what you did not decide about. Only the parts you settled are written afresh. Everything else comes straight from the input, including trailing spaces, wrapped continuation lines, section order and anything between sections. A merge in which you decided nothing therefore gives back the version it started from, character for character. The one thing that is converted is the file's encoding: a saved .sr* is UTF-16LE with a byte-order mark and CRLF line endings, which is what the Library painter writes. The exception is a PowerBuilder 2025 Solution source, which is UTF-8: it is saved in UTF-8 and nothing about it is converted, so that the Solution still builds. A file that is not an object export - a build file, an INI, XML or text file - is not converted either: it is saved in the encoding it was read in.

Where What it starts from What it decides
File Compare, two-way New: before you decide anything, Save writes the new version's content back out unchanged. Which of the two versions each item or change block comes from.
Three-way merge The Base the two sides share. Which side's change is carried into the Base, and what to do where both sides changed the same lines.

Use the three-way merge whenever you have the version both sides started from, which is what a source-control client hands its merge tool: it can tell your addition from a deletion the other person made, and a two-way compare cannot. Use the two-way merge when there is no common ancestor to hand, or when the question is not "what did we each change" but "which of these two revisions do I want, piece by piece".

The two-way merge

Old is the reference and the result starts as New. Every operation moves one element of it back towards Old, or replaces it with something you typed:

Operation Effect Marker afterwards
Take Old Version, on something the new version deleted It comes back. This is the undelete. Deleted Restored
Take Old Version, on something the new version added It goes away; the old version does not have it. New Removed
Take Old Version, on something the new version changed It reverts to the old version. Modified Restored
Take New Version Abandons the decision: the element is as the new version has it again. Back to the plain compare marker
Edit the merged text by hand For the cases none of the above expresses. Edited

One verb covers the first three because they all make the same decision: this comes from Old. The effect depends on what the comparison found, and the marker says which, so the tree also tells you what you have settled so far.

Each operation acts on one of two levels. Select a hierarchy node to act on a whole item, such as a function, event, variable block, control, group or the whole object; put the caret on a changed script line to act only on that change block. You can work from coarse to fine: take the whole function from Old, then restore the one line New got right.

The three-way merge

The merged file starts as the Base. Every operation moves one piece of it towards one side or the other, or replaces it with something you typed:

Operation Effect
Take from Left File1's version of the item or the change block becomes the merged version.
Take from Right File2's version does.
Keep Base Neither side's change is taken; the region stays as the Base has it.
Edit the merged text by hand For the cases none of the above expresses: usually a real conflict, where the answer is a combination of both sides.

The same two levels apply: a whole item from the tree, or one change block from a script. Auto Merge applies in one step every action with only one possible outcome, and leaves the conflicts to you. See Three-way merge.

Watching the result

Every merged element's marker changes to the corresponding merge state, so the tree becomes a record of what you have decided: Deleted Restored, New Removed, Modified Restored, and Edited where you typed. All four are described in the Status Icon Legend. Items you have not touched keep their plain compare markers, which makes "what have I not decided yet" a visual question rather than a memory one. A script in which you took some change blocks and not others matches neither version, and is marked Edited for that reason.

Undo and redo

Edit ▸ Undo and Edit ▸ Redo act on the script that has focus. Undoing a change in one function never rolls back work in another. Ctrl+Z inside the merged text box also undoes the current script's last change.

Undo treats each decision as one step. Restoring a control moves its property block and scripts, and undoing the restore takes them all back together. A run of typing is also one step, so one undo removes the edit rather than only its last character. Where a decision replaces text you typed, the status bar says so, and undo brings the text back.

The four places a control lives

A control in a PowerBuilder export is not one block of text. It is described in four places, and an export file in which those four disagree will not import or compile:

  1. Its property block. The type cb_clear from commandbutton within w_customer_lookup declaration and everything inside it: position, text, font, tag. This is the control's definition. Without it the control does not exist.
  2. Its scripts. The events and functions attached to the control: cb_clear::clicked and the rest. Each is a separate item in the hierarchy tree, and each is separately yours to bring or leave.
  3. The .create and .destroy scripts. The object's generated constructor and destructor, which instantiate every control and clean it up. A control that is declared but never created is a null reference at run time.
  4. The control list. The Control[] array in the object's own property block, which enumerates the controls and fixes their z-order. A control missing from this list is not part of the object as far as the IDE is concerned.

You must bring the property block. Bringing a deleted button back means bringing its property block; there is no control without it, and pb-diff will not infer one.

The scripts are optional and individually selectable. You may take the control and only one of its four events, if that is the merge you want.

pb-diff rewrites the .create and .destroy scripts automatically when a merge adds a control or takes one out, so the object continues to instantiate and destroy exactly the controls it declares. The same holds when the scripts come from a side that removed or added a control you decided to keep or leave out: they are brought back into line with the controls the merged object holds. It maintains the control list automatically too: a control the merge brings in is appended at the top-most position, and a control it takes out is removed. If the original z-order mattered to your layout, check it after the merge and reorder in the IDE. You can edit the two scripts like any others, but if an edit conflicts with the controls the object holds, the rule wins and the merge panel says why; undo restores the text you typed.

Both rules apply to either merge, because the object you check in has to compile whichever tab produced it.

The declarations are still yours. Neither merge invents the control's property block, its entry in the object's own type block or its line in the forward block: those are items you bring across like any other. A control that arrives without them is reported as a warning, not repaired.

The same reasoning applies wherever generated structure and hand-written code sit in the same file. User objects carry controls and a .create/.destroy pair exactly as windows do. Menus carry a hierarchy of menu items with their own generated blocks. DataWindows have no scripts, but their bands, columns and presentation objects reference each other, so restoring a column without the presentation object that displays it produces a DataWindow that imports and shows nothing.

Warnings

A two-way merge lists, in the merge panel of the File Compare, any script left referring to something that is no longer in the file: a function or event you chose not to restore, or a control you removed. They are recalculated after every operation, so restoring what a warning names makes it go away as you work, and saving reports how many the file was written with. They are not errors: the file is still written, and a warning may be about a reference that is resolved elsewhere. Read them before you import, because they are the cheapest description you will get of what the merged object is missing.

Only functions, events and controls are checked. A variable is not: a script that reads an instance variable the merge left behind produces no warning at all, and neither does one that refers to a variable only the other side declared.

A three-way merge reports its unresolved conflicts and what the consistency rules could not put right by themselves: a control it merged in that nothing declares, an owner with no .create script to rewrite, and a script you typed into that had to be brought back into line. It does not check a merged script for calls to functions or controls the merged object no longer holds. Compile the result to find those problems.

Saving

Save, in the tool bar or as File ▸ Save, writes the merged result as a complete PowerBuilder export file: the $PBExportHeader$ line naming the object, the $PBExportComments$ line where the object has comments, and the encoding the Library painter itself writes, UTF-16LE with a byte-order mark and CRLF line endings. That is the form regardless of how the compared files were encoded, and it is the same file pb-diff-cli merge3 produces. It is a file, not a change set: importing it replaces the object in the target library. Every object type is written the same way; a file that is not an object export keeps its own encoding.

A PowerBuilder 2025 Solution keeps each object as a UTF-8 source file in a PBL folder, with no $PBExportHeader$ line and the comment as a first line //objectcomments. When the file a merge writes back is one of those, the New side of a two-way merge or the Base of a three-way merge, the saved file stays in that form: UTF-8, with or without a byte-order mark exactly as it was, no export header added, and every line you did not decide copied byte for byte. The Solution compiler reads UTF-8 only; see PowerBuilder 2025 Solutions.

One input encoding leaves a trace. HEXASCII exports, the ASCII-safe format marked by the letters HA before the export header, write their Unicode string literals as hex escapes. The saved file is a proper Unicode export and no longer claims to be HEXASCII, but the escapes themselves are part of the merged text and are carried through as they stand. If you merge HEXASCII files, check the string literals in the result before you import it.

If pb-diff starts with a -Save <path> switch, that path is used without asking, for either merge. The switch presets a destination and does nothing else: it writes no file at start-up and applies no merge operation. pb-diff asks before discarding an unsaved merge, whether you close the tab, refresh the comparison with F5, or close the application. That question is not the Prompt for confirmation on application exit preference in Options, which is about leaving pb-diff rather than about losing work. Undoing every decision leaves nothing to ask about.

pb-diff saves the result as an export file and never writes into a .pbl. Use PowerBuilder's own Import to put the merged object into a library.

pb-diff does not compile, regenerate or execute PowerBuilder code, and it cannot tell you whether the merged object works. It produces a file that is structurally valid and consistent with the merge you performed; whether the code in it is correct is still your judgement. Import it into a scratch target and build it before it goes anywhere near a release.

Importing merged output is supported for PowerBuilder 19 and later; it is unverified for versions 10 to 18. Reading, comparing and exporting libraries is certified from PowerBuilder 10 onwards. Merged sources of PowerBuilder 2025 Solutions build in the Solution with PowerBuilder 2025 and later.