First run
Turn exported LCOV files into one.
An LCOV .info file is a text file written by a coverage exporter. lcovmerge combines those existing files; it does not collect coverage.
Not sure you need a local merge? Check the one-minute workflow guide first. Service-side shard merging or a native profile merge may already fit your pipeline.
Choose the merge point for your ecosystem
Use lcovmerge only after your coverage tool has exported separate LCOV files and a local consumer needs one file. Keep your current path when raw profiles or your reporting service already provide the merge you need.
Scroll horizontally to view all columns.
| Pipeline | Check first | When lcovmerge may fit |
|---|---|---|
| gcov coverage | Try lcov -a for small shard sets. | Downloaded LCOV shards make the final local merge costly. |
| LLVM coverage | Merge raw profiles with llvm-profdata before llvm-cov. | Independent LCOV exports are already the handoff. |
| Rust | Use cargo-llvm-cov or grcov to handle native profiles. | Separate CI jobs leave LCOV files for a local consumer. |
| Python | Use Coverage.py combine while raw data is available. | Only exported LCOV shards remain and one file is required. |
| JavaScript | Use nyc merge while raw coverage JSON is available. | The local report step needs one LCOV file after export. |
| Go and other producers | Export LCOV with a producer or converter. | Confirm the converter's records work with your report consumer. |
| Bazel | Use the combined report from one bazel coverage run. | Separate CI jobs provide LCOV artifacts that need a final merge. |
| Codecov or Coveralls | Check service-side parallel shard aggregation. | A local consumer still needs a single LCOV artifact. |
These are workflow pointers, not compatibility claims for every tool version. Check the exporter and report consumer versions used by your pipeline before switching.
Your first merge in three steps
The current source is a native Rust CLI and library. Version 1.2.0 is the first Rust release.
Build the current Rust CLI
Install stable Rust, Python 3.11+, and Make, then build the portable optimized CLI:
git clone https://github.com/megasoft1978/lcovmerge.git
cd lcovmerge
make
./bin/lcovmerge coverage/shard-*.info -o coverage/merged.info
Published release downloads
-
Download and extract
Get lcovmerge v1.2.0 for your operating system and verify the archive checksum before extraction. The release includes Linux, macOS, and Windows binaries; see the platform verification scope.
Choose an operating system and verify the archive ↓ -
Merge the exported shards
Run this from the directory that contains
coverage/. The shell expands the wildcard.lcovmerge coverage/shard-*.info -o coverage/merged.info -
Keep your report or upload step
The output is still an LCOV
.infofile.genhtml coverage/merged.info --output-directory coverage/html
Download and verify a binary
Choose the archive that matches your system and verify it before extraction. Platform verification scope and limits are summarized in the release notes.
Linux · x86-64
For Linux ARM64, choose its matching archive from the release page.
set -eu
asset=lcovmerge-1.2.0-linux-x86_64.tar.gz
base=https://github.com/megasoft1978/lcovmerge/releases/download/v1.2.0
curl -fL "$base/$asset" -o "$asset"
curl -fL "$base/SHA256SUMS" -o SHA256SUMS
entry=$(awk -v file="$asset" '$2 == file || $2 == "*" file { if (found++) exit 1; line=$0 } END { if (found != 1) exit 1; print line }' SHA256SUMS)
printf '%s\n' "$entry" | sha256sum -c -
tar -xzf "$asset"
./lcovmerge --version
macOS · Apple silicon or Intel
set -eu
base=https://github.com/megasoft1978/lcovmerge/releases/download/v1.2.0
case "$(uname -m)" in
arm64|aarch64) asset=lcovmerge-1.2.0-macos-arm64.tar.gz ;;
x86_64|amd64) asset=lcovmerge-1.2.0-macos-x86_64.tar.gz ;;
*) echo "Unsupported Mac architecture: $(uname -m)" >&2; exit 1 ;;
esac
curl -fL "$base/$asset" -o "$asset"
curl -fL "$base/SHA256SUMS" -o SHA256SUMS
entry=$(awk -v file="$asset" '$2 == file || $2 == "*" file { if (found++) exit 1; line=$0 } END { if (found != 1) exit 1; print line }' SHA256SUMS)
printf '%s\n' "$entry" | shasum -a 256 -c -
tar -xzf "$asset"
./lcovmerge --version
Windows · verified in CI
The Windows x86-64 release archive is available; see platform limits for current coverage.
$asset = "lcovmerge-1.2.0-windows-x86_64.zip"
$base = "https://github.com/megasoft1978/lcovmerge/releases/download/v1.2.0"
Invoke-WebRequest "$base/$asset" -OutFile $asset
Invoke-WebRequest "$base/SHA256SUMS" -OutFile SHA256SUMS
$entry = Get-Content SHA256SUMS | Where-Object { ($_ -split '\s+', 2)[1].TrimStart('*') -eq $asset }
if (@($entry).Count -ne 1) { throw "Expected one checksum entry for $asset" }
$expected = ($entry -split '\s+', 2)[0].ToLowerInvariant()
$actual = (Get-FileHash -Algorithm SHA256 $asset).Hash.ToLowerInvariant()
if ($actual -ne $expected) { throw "SHA256 checksum mismatch" }
Expand-Archive $asset -DestinationPath .\lcovmerge
.\lcovmerge\lcovmerge.exe --version
Other install paths
Try a no-install sample
Save the downloaded binary as lcovmerge in your current directory, then paste this in a POSIX shell. It creates two synthetic files and runs the binary from the parent directory; it does not install anything on your PATH.
mkdir -p .lcovmerge-demo
cd .lcovmerge-demo
printf '%s\n' 'SF:src/example.rs' 'DA:1,2' 'end_of_record' > shard-a.info
printf '%s\n' 'SF:src/example.rs' 'DA:1,3' 'end_of_record' > shard-b.info
../lcovmerge shard-a.info shard-b.info -o merged.info
cat merged.info
Expected output:
SF:src/example.rs
DA:1,5
FNF:0
FNH:0
BRF:0
BRH:0
MCF:0
MCH:0
LF:1
LH:1
end_of_record
If the first run fails
| Error | Try this |
|---|---|
| Exit 1 · option or command error | Run lcovmerge --help; check the option spelling and that -o FILE is present. |
| Exit 2 · parse error with a line number | Open the named input at that line and confirm it is an exported LCOV .info file, not a raw profile. |
| No input matched | Check that the files exist under the current directory and match the wildcard. Use find coverage -name '*.info' or list paths in @coverage-files.txt. |
Exit 3 · --tmpdir is not writable | Choose an existing directory you can write to, and check that it has free disk space. |
| Exit 3 · output or temporary-file error | Check write access and free space for both the output directory and the temporary directory. |
See the full exit-status reference for all return codes.
Constrained CI example
This example budgets record memory and puts sorted temporary files in a directory created for this run:
set -eu
sort_dir=$(mktemp -d)
trap 'rm -rf "$sort_dir"' EXIT
lcovmerge --mem-limit 256M --tmpdir "$sort_dir" coverage/shard-*.info -o coverage/merged.info
genhtml coverage/merged.info --output-directory coverage/html
--mem-limit 256M is a coverage-record storage budget, not a total process RSS cap. Sorting needs free disk space in the selected temporary directory.
Check compatibility before switching
Native mode has its own merge policies. The experimental, opt-in LCOV 2.6 profile covers a version-pinned merge workflow; neither mode replaces LCOV collection or reporting. Compare normalized records and your report before switching. See the migration checklist, profile scope and status, documented limits, and private-input measurement guide.
For the same two inputs, create separate outputs with both tools:
lcov --branch-coverage --function-coverage -a coverage/shard-a.info -a coverage/shard-b.info -o coverage/lcov.info
lcovmerge coverage/shard-a.info coverage/shard-b.info -o coverage/lcovmerge.info --strict-checksum
Compare normalized records and the reports produced from both files; a plain text diff is not a semantic compatibility check.
Paths and input lists
Rebase paths, filter inputs, or use a list file
Rebase source paths
Use --rebase when SF: paths point to a build directory but reports use another root.
lcovmerge --rebase /build/worker/project=/workspace/project shard-a.info shard-b.info -o merged.info
A path below /build/worker/project is rewritten below /workspace/project. Check emitted paths against the source tree used by your report generator.
Use a list file
lcovmerge @coverage-files.txt -o merged.info
A list file has one input path per line. Blank lines and lines beginning with # are ignored. Paths are relative to the current working directory; nested list files are limited to eight levels. The shell expands globs; lcovmerge does not.
Advanced options
CI and reporting recipes
Use these recipes after test jobs have exported LCOV files:
- GitHub Actions · upload shard files, then merge them in a final job.
- GitLab CI · collect artifacts and merge after test jobs finish.
- Jenkins · merge files produced by parallel stages.
- CMake with gcov · capture LCOV, then merge independent outputs.
- Bazel · use only when a separate LCOV merge is still needed.
- Report and upload consumers · pass the merged file to an LCOV-aware tool.
Help validate where LCOV merging is useful
Current measurements show specific workloads, not typical production savings. If your pipeline already produces LCOV shards, the project guide explains how to compare both mergers safely and share only reviewed aggregate results.