git: a8fe902da5a1 - main - textproc/patdiff: NEW PORT - diff for the discerning developer

From: Dave Cottlehuber <dch_at_FreeBSD.org>
Date: Fri, 09 Oct 2026 12:44:07 UTC
The branch main has been updated by dch:

URL: https://cgit.FreeBSD.org/ports/commit/?id=a8fe902da5a10bd5b7f6172713154568ea937f39

commit a8fe902da5a10bd5b7f6172713154568ea937f39
Author:     Dave Cottlehuber <dch@FreeBSD.org>
AuthorDate: 2026-10-09 09:59:59 +0000
Commit:     Dave Cottlehuber <dch@FreeBSD.org>
CommitDate: 2026-10-09 12:43:25 +0000

    textproc/patdiff:  NEW PORT - diff for the discerning developer
    
    Patdiff is an OCaml implementation of Bram Cohen's patience difference
    algorithm, with a few extra conveniences for comparing code and config
    files:
    
    - gorgeous coloured patience diffs with word-level refinements
    - outputs plain ASCII, ANSI color codes, or HTML
    - optional semantic diffing of numbers
    - good word-level diffing out of the box
    - recursive diffing of directories
    - extensively configurable output (markers, colors, location format, context)
    - whitespace-aware diffing
    
    Sponsored by:   SkunkWerks, GmbH
---
 textproc/Makefile                                  |   1 +
 textproc/patdiff/Makefile                          | 102 +++++++++++++++++++++
 textproc/patdiff/distinfo                          | 101 ++++++++++++++++++++
 ...r_core__unix_linux__ext_src_linux__ext__stubs.c |  34 +++++++
 textproc/patdiff/pkg-descr                         |  14 +++
 5 files changed, 252 insertions(+)

diff --git a/textproc/Makefile b/textproc/Makefile
index 510130688e5d..cdd6870d9746 100644
--- a/textproc/Makefile
+++ b/textproc/Makefile
@@ -1244,6 +1244,7 @@
     SUBDIR += pa-aspell
     SUBDIR += pageedit
     SUBDIR += par
+    SUBDIR += patdiff
     SUBDIR += pdf-form-filler-mcp
     SUBDIR += pdf-reader-mcp
     SUBDIR += pdfgrep
diff --git a/textproc/patdiff/Makefile b/textproc/patdiff/Makefile
new file mode 100644
index 000000000000..7c309d0c57be
--- /dev/null
+++ b/textproc/patdiff/Makefile
@@ -0,0 +1,102 @@
+PORTNAME=	patdiff
+DISTVERSIONPREFIX=	v
+DISTVERSION=	0.16.1
+CATEGORIES=	textproc
+
+MAINTAINER=	dch@FreeBSD.org
+COMMENT=	Colored patience diffs for the discerning developer
+WWW=		https://github.com/janestreet/patdiff
+
+LICENSE=	MIT
+LICENSE_FILE=	${WRKSRC}/LICENSE.md
+
+BUILD_DEPENDS=	bash:shells/bash \
+		${SA_DIR}/base/META:devel/ocaml-base \
+		${SA_DIR}/ppx_compare/META:devel/ocaml-ppx_compare \
+		${SA_DIR}/ppx_hash/META:devel/ocaml-ppx_hash \
+		${SA_DIR}/ppx_optcomp/META:devel/ocaml-ppx_optcomp \
+		${SA_DIR}/ppx_sexp_conv/META:devel/ocaml-ppx_sexp_conv \
+		${SA_DIR}/ppxlib/META:devel/ocaml-ppxlib \
+		${SA_DIR}/re/META:devel/ocaml-re \
+		${SA_DIR}/sexplib/META:devel/ocaml-sexplib \
+		${SA_DIR}/sexplib0/META:devel/ocaml-sexplib0 \
+		${SA_DIR}/stdio/META:devel/ocaml-stdio
+
+USES=		ocaml:build,dune
+USE_GITHUB=	yes
+GH_ACCOUNT=	janestreet
+# Jane Street libraries not (yet) in the ports tree are vendored
+GH_TUPLE=	\
+		janestreet:base_bigstring:v0.16.0:base_bigstring/vendor/base_bigstring \
+		janestreet:base_quickcheck:v0.16.0:base_quickcheck/vendor/base_quickcheck \
+		janestreet:bin_prot:v0.16.0:bin_prot/vendor/bin_prot \
+		janestreet:core:v0.16.2:core/vendor/core \
+		janestreet:core_kernel:v0.16.0:core_kernel/vendor/core_kernel \
+		janestreet:core_unix:v0.16.0:core_unix/vendor/core_unix \
+		janestreet:expect_test_helpers_core:v0.16.0:expect_test_helpers_core/vendor/expect_test_helpers_core \
+		janestreet:fieldslib:v0.16.0:fieldslib/vendor/fieldslib \
+		janestreet:int_repr:v0.16.0:int_repr/vendor/int_repr \
+		janestreet:jane-street-headers:v0.16.0:jane_street_headers/vendor/jane-street-headers \
+		janestreet:jst-config:v0.16.0:jst_config/vendor/jst-config \
+		janestreet:ocaml_intrinsics:v0.16.2:ocaml_intrinsics/vendor/ocaml_intrinsics \
+		janestreet:patience_diff:v0.16.0:patience_diff/vendor/patience_diff \
+		janestreet:ppx_assert:v0.16.0:ppx_assert/vendor/ppx_assert \
+		janestreet:ppx_base:v0.16.0:ppx_base/vendor/ppx_base \
+		janestreet:ppx_bench:v0.16.0:ppx_bench/vendor/ppx_bench \
+		janestreet:ppx_bin_prot:v0.16.0:ppx_bin_prot/vendor/ppx_bin_prot \
+		janestreet:ppx_cold:v0.16.0:ppx_cold/vendor/ppx_cold \
+		janestreet:ppx_custom_printf:v0.16.0:ppx_custom_printf/vendor/ppx_custom_printf \
+		janestreet:ppx_disable_unused_warnings:v0.16.0:ppx_disable_unused_warnings/vendor/ppx_disable_unused_warnings \
+		janestreet:ppx_enumerate:v0.16.0:ppx_enumerate/vendor/ppx_enumerate \
+		janestreet:ppx_expect:v0.16.2:ppx_expect/vendor/ppx_expect \
+		janestreet:ppx_fields_conv:v0.16.0:ppx_fields_conv/vendor/ppx_fields_conv \
+		janestreet:ppx_fixed_literal:v0.16.0:ppx_fixed_literal/vendor/ppx_fixed_literal \
+		janestreet:ppx_globalize:v0.16.0:ppx_globalize/vendor/ppx_globalize \
+		janestreet:ppx_here:v0.16.0:ppx_here/vendor/ppx_here \
+		janestreet:ppx_ignore_instrumentation:v0.16.0:ppx_ignore_instrumentation/vendor/ppx_ignore_instrumentation \
+		janestreet:ppx_inline_test:v0.16.0:ppx_inline_test/vendor/ppx_inline_test \
+		janestreet:ppx_jane:v0.16.0:ppx_jane/vendor/ppx_jane \
+		janestreet:ppx_let:v0.16.0:ppx_let/vendor/ppx_let \
+		janestreet:ppx_log:v0.16.0:ppx_log/vendor/ppx_log \
+		janestreet:ppx_module_timer:v0.16.0:ppx_module_timer/vendor/ppx_module_timer \
+		janestreet:ppx_optional:v0.16.0:ppx_optional/vendor/ppx_optional \
+		janestreet:ppx_pipebang:v0.16.0:ppx_pipebang/vendor/ppx_pipebang \
+		janestreet:ppx_sexp_message:v0.16.0:ppx_sexp_message/vendor/ppx_sexp_message \
+		janestreet:ppx_sexp_value:v0.16.0:ppx_sexp_value/vendor/ppx_sexp_value \
+		janestreet:ppx_stable:v0.16.0:ppx_stable/vendor/ppx_stable \
+		janestreet:ppx_stable_witness:v0.16.0:ppx_stable_witness/vendor/ppx_stable_witness \
+		janestreet:ppx_string:v0.16.0:ppx_string/vendor/ppx_string \
+		janestreet:ppx_tydi:v0.16.0:ppx_tydi/vendor/ppx_tydi \
+		janestreet:ppx_typerep_conv:v0.16.0:ppx_typerep_conv/vendor/ppx_typerep_conv \
+		janestreet:ppx_variants_conv:v0.16.0:ppx_variants_conv/vendor/ppx_variants_conv \
+		janestreet:sexp_pretty:v0.16.0:sexp_pretty/vendor/sexp_pretty \
+		janestreet:spawn:v0.15.1:spawn/vendor/spawn \
+		janestreet:splittable_random:v0.16.0:splittable_random/vendor/splittable_random \
+		janestreet:time_now:v0.16.0:time_now/vendor/time_now \
+		janestreet:timezone:v0.16.0:timezone/vendor/timezone \
+		janestreet:typerep:v0.16.0:typerep/vendor/typerep \
+		janestreet:variantslib:v0.16.0:variantslib/vendor/variantslib
+
+DUNE_INSTALL_ARGS=	--prefix=${PREFIX} --mandir=${PREFIX}/share/man \
+			--destdir=${STAGEDIR} --sections=bin,man
+DUNE_INSTALL_TARGETS=	${OCAML_PACKAGES}
+
+PLIST_FILES=	bin/patdiff \
+		bin/patdiff-git-wrapper \
+		share/man/man1/patdiff.1.gz
+
+SA_DIR=		${LOCALBASE}/${OCAML_SITELIBDIR}
+
+post-patch:
+# vendored_dirs needs dune lang >= 1.11
+	@${REINPLACE_CMD} -e 's/(lang dune 1.10)/(lang dune 1.11)/' \
+		${WRKSRC}/dune-project
+	@${ECHO_CMD} '(vendored_dirs *)' > ${WRKSRC}/vendor/dune
+# fix the missing version string
+	@${REINPLACE_CMD} -e 's|Command_unix.run Compare.command|Command_unix.run \
+		~version:"${DISTVERSION}" Compare.command|' ${WRKSRC}/bin/main.ml
+
+post-install:
+	@${STRIP_CMD} ${STAGEDIR}${PREFIX}/bin/patdiff
+
+.include <bsd.port.mk>
diff --git a/textproc/patdiff/distinfo b/textproc/patdiff/distinfo
new file mode 100644
index 000000000000..7b093956417b
--- /dev/null
+++ b/textproc/patdiff/distinfo
@@ -0,0 +1,101 @@
+TIMESTAMP = 1791531594
+SHA256 (janestreet-patdiff-v0.16.1_GH0.tar.gz) = 24046c6558d346725c2f14d3a9f4b71d981e98a18e93a2fdfa2849e0eec2756b
+SIZE (janestreet-patdiff-v0.16.1_GH0.tar.gz) = 225543
+SHA256 (janestreet-base_bigstring-v0.16.0_GH0.tar.gz) = 8a2d60fe0a544e2d69ed0a9eac8c2fbb9726f46578ff3c41ff34028ac812cb66
+SIZE (janestreet-base_bigstring-v0.16.0_GH0.tar.gz) = 19275
+SHA256 (janestreet-base_quickcheck-v0.16.0_GH0.tar.gz) = c67c04a4cfff2472be777500fafb519f071d63b3d5ef3c7366ff0ee3856b67c8
+SIZE (janestreet-base_quickcheck-v0.16.0_GH0.tar.gz) = 76738
+SHA256 (janestreet-bin_prot-v0.16.0_GH0.tar.gz) = d0b0db60aa4b61ff0a44ec4a2d571313f30d8fd80d0c3a0b701abb0e70df4a98
+SIZE (janestreet-bin_prot-v0.16.0_GH0.tar.gz) = 343886
+SHA256 (janestreet-core-v0.16.2_GH0.tar.gz) = 9ba3390bd8ea5a018a5d84dbfd6b5c652989bf541159173e76180eb3de5b99fd
+SIZE (janestreet-core-v0.16.2_GH0.tar.gz) = 542390
+SHA256 (janestreet-core_kernel-v0.16.0_GH0.tar.gz) = b3b1ce50cb3a1607f5701a90f5d5b37d841325591b49ba11bfbee285f510d2d1
+SIZE (janestreet-core_kernel-v0.16.0_GH0.tar.gz) = 276775
+SHA256 (janestreet-core_unix-v0.16.0_GH0.tar.gz) = 9978ae7f56c18cc37b5e3a1adf1c70a7da542a57e17ff73f4e00811eb817d7bd
+SIZE (janestreet-core_unix-v0.16.0_GH0.tar.gz) = 391719
+SHA256 (janestreet-expect_test_helpers_core-v0.16.0_GH0.tar.gz) = 82adfb4510ec2cdc4f59c62eeb4e90b51d9e5daa1628bd2c1fda06ba524b67dc
+SIZE (janestreet-expect_test_helpers_core-v0.16.0_GH0.tar.gz) = 22549
+SHA256 (janestreet-fieldslib-v0.16.0_GH0.tar.gz) = 7bf728eb9db49f639ceda39d408d619271648e8840c5822724c1f64292c47146
+SIZE (janestreet-fieldslib-v0.16.0_GH0.tar.gz) = 3711
+SHA256 (janestreet-int_repr-v0.16.0_GH0.tar.gz) = f0efa8b2cf81039f9b73b92287906211f97d56f3988dda891898f27d79293d3a
+SIZE (janestreet-int_repr-v0.16.0_GH0.tar.gz) = 9227
+SHA256 (janestreet-jane-street-headers-v0.16.0_GH0.tar.gz) = bfb57ae96d4925f50f443480caa709749ca3012118f5b7580b2d8c84c93ee9d4
+SIZE (janestreet-jane-street-headers-v0.16.0_GH0.tar.gz) = 4905
+SHA256 (janestreet-jst-config-v0.16.0_GH0.tar.gz) = 94d60f5d4b4bae0bfdfeab247293155f812ae8f2f660cd1812f4696b1c56d8c9
+SIZE (janestreet-jst-config-v0.16.0_GH0.tar.gz) = 4952
+SHA256 (janestreet-ocaml_intrinsics-v0.16.2_GH0.tar.gz) = b16385ed02f591ca37e55640a8de22ff53e5c76c593d09101d53c41b24de2c3e
+SIZE (janestreet-ocaml_intrinsics-v0.16.2_GH0.tar.gz) = 39096
+SHA256 (janestreet-patience_diff-v0.16.0_GH0.tar.gz) = 4af5d390fb7bffe1bc0f874ee6f71ce549273afc6c6ee3b27688ff0ef8456f5a
+SIZE (janestreet-patience_diff-v0.16.0_GH0.tar.gz) = 22756
+SHA256 (janestreet-ppx_assert-v0.16.0_GH0.tar.gz) = 265ab1ea89526044728db6b035db76897749a1f3b136353acbb601559fafb065
+SIZE (janestreet-ppx_assert-v0.16.0_GH0.tar.gz) = 7419
+SHA256 (janestreet-ppx_base-v0.16.0_GH0.tar.gz) = cea0e5a30a99a8b182fcd0f67d676bd554759b34f46b78e349530eea3f49d02e
+SIZE (janestreet-ppx_base-v0.16.0_GH0.tar.gz) = 2996
+SHA256 (janestreet-ppx_bench-v0.16.0_GH0.tar.gz) = 0e6cb46edad0a5e405081ddb187e9e254bec01364f7e2923e55bea0e0c3e1dbd
+SIZE (janestreet-ppx_bench-v0.16.0_GH0.tar.gz) = 9555
+SHA256 (janestreet-ppx_bin_prot-v0.16.0_GH0.tar.gz) = 4a6463fb398d5f6476563e10a8ebbe22f0bbb1496022145d59e593a4ae2cbbed
+SIZE (janestreet-ppx_bin_prot-v0.16.0_GH0.tar.gz) = 37632
+SHA256 (janestreet-ppx_cold-v0.16.0_GH0.tar.gz) = 6ce67b0160db2d81bb1394d29453d15b9348a748233ca6cf6b053dea31601e68
+SIZE (janestreet-ppx_cold-v0.16.0_GH0.tar.gz) = 3814
+SHA256 (janestreet-ppx_custom_printf-v0.16.0_GH0.tar.gz) = 6f1d36859077c061b92934bd9127b2cae008365db922385ecf4ce9780e7ef627
+SIZE (janestreet-ppx_custom_printf-v0.16.0_GH0.tar.gz) = 9729
+SHA256 (janestreet-ppx_disable_unused_warnings-v0.16.0_GH0.tar.gz) = 08373b67743db84274b76e7fdcd3a71e35adffa677d91a99668aa00fb399f308
+SIZE (janestreet-ppx_disable_unused_warnings-v0.16.0_GH0.tar.gz) = 4004
+SHA256 (janestreet-ppx_enumerate-v0.16.0_GH0.tar.gz) = 17f75b7741d49f06e3db3ef2e2aee4934c1682224b80d272f05a42b6ba506f7f
+SIZE (janestreet-ppx_enumerate-v0.16.0_GH0.tar.gz) = 9414
+SHA256 (janestreet-ppx_expect-v0.16.2_GH0.tar.gz) = 1fc15e3842d7544e35a54a902520b146ef299cfee8956db4c300dadbfa71c706
+SIZE (janestreet-ppx_expect-v0.16.2_GH0.tar.gz) = 48495
+SHA256 (janestreet-ppx_fields_conv-v0.16.0_GH0.tar.gz) = fb7328ee2a6a17456cbe6e7083f07f5ccb4e43786c83f7e1213119919e10972c
+SIZE (janestreet-ppx_fields_conv-v0.16.0_GH0.tar.gz) = 21481
+SHA256 (janestreet-ppx_fixed_literal-v0.16.0_GH0.tar.gz) = 7a2d595a362d3190023e251bee75588051cfb468e76d375aa71f22cb1b2a5416
+SIZE (janestreet-ppx_fixed_literal-v0.16.0_GH0.tar.gz) = 3679
+SHA256 (janestreet-ppx_globalize-v0.16.0_GH0.tar.gz) = 426855d04be2c15beb70884fd2e6d662000f63bcca0166f5629e369e645fec97
+SIZE (janestreet-ppx_globalize-v0.16.0_GH0.tar.gz) = 8328
+SHA256 (janestreet-ppx_here-v0.16.0_GH0.tar.gz) = 072fd8ce903ce95ff75ddcca4898d250fe7a5d276b3011b67df1473d9ef95669
+SIZE (janestreet-ppx_here-v0.16.0_GH0.tar.gz) = 5034
+SHA256 (janestreet-ppx_ignore_instrumentation-v0.16.0_GH0.tar.gz) = 4e8613957fdeed68d35bda53d657cffe3bc9804e25777283e859799dbb12da7b
+SIZE (janestreet-ppx_ignore_instrumentation-v0.16.0_GH0.tar.gz) = 3712
+SHA256 (janestreet-ppx_inline_test-v0.16.0_GH0.tar.gz) = 9e08c01add3bfb8c10b1d4af576a6b4922c62f67251eb231ff19ea40cda73cd7
+SIZE (janestreet-ppx_inline_test-v0.16.0_GH0.tar.gz) = 21926
+SHA256 (janestreet-ppx_jane-v0.16.0_GH0.tar.gz) = 4c5ee082ae1d3d1372355ca85deb9f667324c8d6a50c13cc2124e880281c6306
+SIZE (janestreet-ppx_jane-v0.16.0_GH0.tar.gz) = 4688
+SHA256 (janestreet-ppx_let-v0.16.0_GH0.tar.gz) = 8ccc4f27668e9daf613f6dc779bcd9bdc6cd678826cd35c401be65566c436043
+SIZE (janestreet-ppx_let-v0.16.0_GH0.tar.gz) = 15460
+SHA256 (janestreet-ppx_log-v0.16.0_GH0.tar.gz) = e0105f1a22199231be9258ced23300c4c4404d1b65e367a332848c4356cc7032
+SIZE (janestreet-ppx_log-v0.16.0_GH0.tar.gz) = 6706
+SHA256 (janestreet-ppx_module_timer-v0.16.0_GH0.tar.gz) = 54b49c7fe3b2d7d72874e21286eb8424903db60ebd730441eb310620df597478
+SIZE (janestreet-ppx_module_timer-v0.16.0_GH0.tar.gz) = 7281
+SHA256 (janestreet-ppx_optional-v0.16.0_GH0.tar.gz) = e6654625008e75ec3e55163d6dd8b360e8724ea108583ecb8b65ba37ac853a5f
+SIZE (janestreet-ppx_optional-v0.16.0_GH0.tar.gz) = 8333
+SHA256 (janestreet-ppx_pipebang-v0.16.0_GH0.tar.gz) = 9435f425b6ee9d736cee5ed8579402be085656c4d31dd2bd4c256363ed7bdc8c
+SIZE (janestreet-ppx_pipebang-v0.16.0_GH0.tar.gz) = 3755
+SHA256 (janestreet-ppx_sexp_message-v0.16.0_GH0.tar.gz) = 4f2e0c32d371895e2e5ad3266942d6a20bd66f8ab824653209bf41a40fea028e
+SIZE (janestreet-ppx_sexp_message-v0.16.0_GH0.tar.gz) = 8435
+SHA256 (janestreet-ppx_sexp_value-v0.16.0_GH0.tar.gz) = 06345d475a918ca9160ce58b323f6ca4388288015efaaaf82915be25afb20947
+SIZE (janestreet-ppx_sexp_value-v0.16.0_GH0.tar.gz) = 8460
+SHA256 (janestreet-ppx_stable-v0.16.0_GH0.tar.gz) = 55cda9b7c2a8f3e86a499f205b8fd863cc600e7feafa41049b3c9b27a9235a62
+SIZE (janestreet-ppx_stable-v0.16.0_GH0.tar.gz) = 18298
+SHA256 (janestreet-ppx_stable_witness-v0.16.0_GH0.tar.gz) = f397aa471a92da2272e96787dc64e9ba10e1464a6ef821c1edf68a7601ae2736
+SIZE (janestreet-ppx_stable_witness-v0.16.0_GH0.tar.gz) = 7641
+SHA256 (janestreet-ppx_string-v0.16.0_GH0.tar.gz) = c29664a9a34a26540e00ab2b9b08d2181e970c54f30e0e3dcb5fb28764217dd8
+SIZE (janestreet-ppx_string-v0.16.0_GH0.tar.gz) = 6105
+SHA256 (janestreet-ppx_tydi-v0.16.0_GH0.tar.gz) = 3dc321a790e7228de9487fa93fc2976be5bc41e733486c64c03eced01ad0e3ff
+SIZE (janestreet-ppx_tydi-v0.16.0_GH0.tar.gz) = 4583
+SHA256 (janestreet-ppx_typerep_conv-v0.16.0_GH0.tar.gz) = e35cf53870f7c9aa3bf12072cbb23f0c4ed4dc6f0ee8a62b13bdfd9b77bb52e9
+SIZE (janestreet-ppx_typerep_conv-v0.16.0_GH0.tar.gz) = 9421
+SHA256 (janestreet-ppx_variants_conv-v0.16.0_GH0.tar.gz) = a4ef1446de2ab91492ca56c46cdae8491f2bb398797e69bf7ae794ddce4adad1
+SIZE (janestreet-ppx_variants_conv-v0.16.0_GH0.tar.gz) = 12331
+SHA256 (janestreet-sexp_pretty-v0.16.0_GH0.tar.gz) = 9d15e7be22a7d27d1e037571491c58ffd0165f01082110e748e073a99cd7864b
+SIZE (janestreet-sexp_pretty-v0.16.0_GH0.tar.gz) = 22557
+SHA256 (janestreet-spawn-v0.15.1_GH0.tar.gz) = 9afdee314fab6c3fcd689ab6eb5608d6b78078e6dede3953a47debde06c19d50
+SIZE (janestreet-spawn-v0.15.1_GH0.tar.gz) = 15861
+SHA256 (janestreet-splittable_random-v0.16.0_GH0.tar.gz) = 4d08f7e4d56f2b432ba3ee5f66d807983038b834ddac1d23d0ab3a528244be15
+SIZE (janestreet-splittable_random-v0.16.0_GH0.tar.gz) = 9978
+SHA256 (janestreet-time_now-v0.16.0_GH0.tar.gz) = 1796c6ac4c06fb85eb068bd223244c8d25abd4b95cb567c18a9223168107999d
+SIZE (janestreet-time_now-v0.16.0_GH0.tar.gz) = 3729
+SHA256 (janestreet-timezone-v0.16.0_GH0.tar.gz) = 0acd70fb2a889f58caeba276433e6208f8346e25f0feb0f7721b5916dfc04937
+SIZE (janestreet-timezone-v0.16.0_GH0.tar.gz) = 9881
+SHA256 (janestreet-typerep-v0.16.0_GH0.tar.gz) = 943ef91a18d26d530873630a76fc12bc0f0ce5ee8133be69f1e3026525357b11
+SIZE (janestreet-typerep-v0.16.0_GH0.tar.gz) = 23002
+SHA256 (janestreet-variantslib-v0.16.0_GH0.tar.gz) = 4a4f74d1ba234a1ff0f751ea2d86f94fdd1f0809e0fe6c20aab9eae8dec4d2a1
+SIZE (janestreet-variantslib-v0.16.0_GH0.tar.gz) = 2982
diff --git a/textproc/patdiff/files/patch-vendor_core__unix_linux__ext_src_linux__ext__stubs.c b/textproc/patdiff/files/patch-vendor_core__unix_linux__ext_src_linux__ext__stubs.c
new file mode 100644
index 000000000000..cca723a83ba9
--- /dev/null
+++ b/textproc/patdiff/files/patch-vendor_core__unix_linux__ext_src_linux__ext__stubs.c
@@ -0,0 +1,34 @@
+--- vendor/core_unix/linux_ext/src/linux_ext_stubs.c.orig	2026-10-09 07:50:35 UTC
++++ vendor/core_unix/linux_ext/src/linux_ext_stubs.c
+@@ -635,10 +635,22 @@ CAMLprim value core_linux_epoll_offsetof_readyflags(va
+   return Val_int( offsetof(struct epoll_event, events));
+ }
+ 
++#endif /* JSC_LINUX_EXT */
++
++/* timerfd(2) also exists on FreeBSD >= 14, independently of the other
++   Linux-only extensions, so these bindings live outside JSC_LINUX_EXT. */
+ #ifdef JSC_TIMERFD
+ 
+ /** timerfd bindings **/
+ 
++#include <errno.h>
++#include <stdint.h>
++#include <unistd.h>
++#include <time.h>
++
++#include "ocaml_utils.h"
++#include "unix_utils.h"
++
+ #include <sys/timerfd.h>
+ 
+ /* These values are from timerfd.h. They are not defined in Linux
+@@ -721,6 +733,8 @@ CAMLprim value core_linux_timerfd_gettime(value v_fd)
+ }
+ 
+ #endif /* JSC_TIMERFD */
++
++#ifdef JSC_LINUX_EXT
+ 
+ #ifdef JSC_EVENTFD
+ 
diff --git a/textproc/patdiff/pkg-descr b/textproc/patdiff/pkg-descr
new file mode 100644
index 000000000000..1503a8dfbb24
--- /dev/null
+++ b/textproc/patdiff/pkg-descr
@@ -0,0 +1,14 @@
+Patdiff is an OCaml implementation of Bram Cohen's patience diff
+algorithm, with a few extra conveniences for comparing code and
+configuration files:
+
+- outputs plain ASCII, ANSI color codes, or HTML
+- optional semantic diffing of numbers (float tolerance)
+- good word-level diffing out of the box
+- recursive diffing of directories
+- extensively configurable output (markers, colors, location format,
+  context)
+- whitespace-aware diffing
+
+A wrapper script, patdiff-git-wrapper, lets git use patdiff as its
+external diff tool.