Migrating from Legacy Systems

Fix LabVIEW Merge Conflicts on Binary VIs

Learn how to resolve a Git merge conflict on a binary VI by hand or with LVMerge, and the four habits that stop the same conflict coming back.

JJulien Buteau
intermediate8 min readSeptember 23, 2026

You can't merge the bytes of two edited VIs, so a LabVIEW merge conflict has two ways out. Pick one side with git checkout --ours or --theirs, then redo the other side's change by hand in LabVIEW. Or run LVMerge, if you have a Professional seat and both branches load on your machine. Either way you'll reopen the VI, because there's no text to resolve.

What the Conflict Looks Like

Git tells you up front that it won't try. The warning line is the one to read.

git-status.txt
$ git merge widen-3v3-limitwarning: Cannot merge binary files: FCT/Power Rails.vi (HEAD vs. widen-3v3-limit)Auto-merging FCT/Power Rails.viCONFLICT (content): Merge conflict in FCT/Power Rails.viAutomatic merge failed; fix conflicts and then commit the result.$ git statusOn branch mainYou have unmerged paths.  (fix conflicts and run "git commit")  (use "git merge --abort" to abort the merge)Unmerged paths:  (use "git add <file>..." to mark resolution)        both modified:   FCT/Power Rails.vi

The file on disk right now is one of the two versions, not a mix, and Git won't say which without asking. Don't open it in LabVIEW yet. Decide first.

The Decision

SituationDo this
One side's change is small (a constant, a limit, a wire)Keep the other side, redo the small change by hand
Both sides made real diagram changes, you have Professional, both branches loadLVMerge
The VI is a class member or a typedefManual, always. LVMerge and the rename it needs don't hold up on classes
No machine on the team has ProfessionalManual, always
You're in a rebase, not a mergeSame steps, but --ours and --theirs swap meaning (see below)
You don't know what the other side changedAsk, or run git log -1 --format=%B <branch> -- "FCT/Power Rails.vi" and read the commit message. That message is all the history you have

That last row is the real cost. On a text file you'd read the diff. On a VI the commit message is the diff, which is why "fixed limits" as a message costs you an afternoon later. Why LabVIEW version control is so painful has the full mechanism.

The Manual Path

This works on any seat and any machine that can open the VI.

resolve-manually.sh
# Keep one side of the VI, then redo the other side's change in LabVIEW by hand.git checkout --ours -- "FCT/Power Rails.vi"# or, to keep the incoming branch's version instead:# git checkout --theirs -- "FCT/Power Rails.vi"# Open the VI in LabVIEW, redo the other side's change, save. Then:git add "FCT/Power Rails.vi"git commit -m "Merge widen-3v3-limit: redo the 3.15 V floor in Power Rails.vi by hand"

Step by step:

  1. Pick the side with the larger change and check it out. In a merge, --ours is the branch you're on and --theirs is the branch you're merging in. In a rebase they swap: --ours is the upstream you're rebasing onto and --theirs is your own commit being replayed.
  2. Open the VI in LabVIEW and redo the smaller change. Use the other branch's commit message, or ask the author.
  3. Save. If the two branches were built on the same LabVIEW version and bitness, you're done with LabVIEW. If the merge crossed a version or bitness change, mass compile the project now, and only now. A mass compile rewrites every VI it touches, so doing it for a single-VI conflict adds noise to the commit for no reason.
  4. git add the VI and commit. Put what you redid in the message, with the value. That's the only place it will ever be written down.

Run the VI once before you push. There's no diff to review, so the test is the review.

The LVMerge Path

LVMerge does a real three-way merge: Base (the common ancestor), Theirs, Yours, into a merged VI you accept difference by difference. It needs the mergetool config from LVCompare and LVMerge: what they cannot do, a Professional seat, and every subVI and class in both branches resolvable on this machine.

resolve-with-lvmerge.sh
# Three-way merge with LVMerge. Needs Professional and the lvmerge mergetool config.git config mergetool.keepBackup falsegit mergetool --tool=lvmerge -- "FCT/Power Rails.vi"# LVMerge opens Base, Theirs and Yours. Accept or reject each difference, then save.git add "FCT/Power Rails.vi"git commit

Step by step:

  1. Run git mergetool on the one file. Git writes the three inputs to temporary absolute paths and calls LVMerge in Base, Theirs, Yours, Merged order.
  2. Work through the difference list. Each entry is a front panel, block diagram or attribute change from one side.
  3. Before saving, read the merged block diagram once. LVMerge can duplicate a node that one branch deleted and re-added. Remove the duplicate.
  4. Save, git add, commit. keepBackup false stops Git leaving a .orig copy of the VI next to the real one, which LabVIEW would otherwise find on the next load.

If LVMerge stalls on a dependency dialog, abort, and take the manual path on the development machine that has the full project.

How to Stop It Recurring

The conflict is a symptom of two people needing the same binary file at the same time. Four habits reduce how often that happens, in order of cost.

One person per VI at a time. Say which VIs you're in before you start, in the channel or in a LOCKS.md at the repository root, and nobody else opens them until you push. It's a convention, not a mechanism, and it holds on a small team.

Smaller VIs. A 40-node VI that does the whole test is one file two people will both need. Split by phase and the odds of two engineers in the same file drop.

Limits in a config file, not in the diagram. Most conflicts on test VIs are two people changing two different constants in one file. Move the constants out, and the VI stops changing when a limit does.

limits.ini
; Read by the test VI with the Config File VIs. This file diffs and merges in plain Git.[rail_3v3]min = 3.15max = 3.4[rail_5v]min = 4.85max = 5.15

Separate compiled code from the source file. It doesn't prevent a real two-edit conflict, but it removes the fake ones, where a VI shows as modified because a callee changed. Separate compiled code in LabVIEW, explained covers the setting. Together with the hand-over notes in document a test station for hand-over, it's what makes the manual path survivable when the author of the other branch has left.

The Contrast

A TofuPilot Framework station is text: procedure.yaml, phases/*.py, plugs/*.py. The same conflict, two branches both changing the 3.3 V floor, looks like this.

procedure.yaml
- name: rail_3v3         unit: V         validators:           - operator: ">="<<<<<<< HEAD             expected_value: 3.15=======             expected_value: 3.1>>>>>>> widen-3v3-limit           - operator: "<="             expected_value: 3.4

You read both values, keep one, delete the markers, git add, commit. Every other change in the two branches merged automatically. Afterwards, git blame answers the question the LabVIEW commit message couldn't.

git-blame.txt
$ git blame -L 25,26 procedure.yamla41f9c2e (Priya Raman  2026-09-14 10:22:41 +0200 25)           - operator: ">="7d03be51 (Marc Dubois  2026-09-18 16:05:09 +0200 26)             expected_value: 3.15

Who set the floor to 3.15, on what day, in which commit. And once that commit is pushed, the station pulls the deployment and every run it uploads to TofuPilot carries the deployment id, so the units tested under 3.15 are a filter on the run list.

Start With One Station

Pick the station whose VIs conflict most often, or the one only one person can open. Rebuild it as a procedure.yaml with phases and plugs in Python with the TofuPilot Framework, keep the same limits, and run it side by side with the LabVIEW version on the same units for two weeks.

install-and-run.sh
# Install the CLI, run the station locally (no account needed), or run with upload.curl -fsSL https://www.tofupilot.app/install | shtofupilot run ./procedure.yamltofupilot run ./procedure.yaml --upload

The rebuild is in how to migrate from LabVIEW to Python for manufacturing tests with TofuPilot, and the framework is at tofupilot.com/products/framework. The Lab tier is free, and tofupilot run works without an account.

More Guides

Put this guide into practice