Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package mergiraf for openSUSE:Factory 
checked in at 2026-09-28 10:45:52
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/mergiraf (Old)
 and      /work/SRC/openSUSE:Factory/.mergiraf.new.383539 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "mergiraf"

Mon Sep 28 10:45:52 2026 rev:14 rq:1380799 version:0.20.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/mergiraf/mergiraf.changes        2026-09-01 
15:52:36.757665291 +0200
+++ /work/SRC/openSUSE:Factory/.mergiraf.new.383539/mergiraf.changes    
2026-09-28 10:46:28.972028556 +0200
@@ -1,0 +2,25 @@
+Sat Sep 26 13:41:57 UTC 2026 - Andrei Dziahel <[email protected]>
+
+- Update to version 0.20.0:
+  * Features
+    + QML Language Support (#810) by @mward25
+  * Bug Fixes
+    + (mgf_dev) Restore proper logging, add verbosity control
+      (#821) by @wetneb
+    + Name the file in the structured merge timeout warning
+      (#828) by @robvenn
+  * Performance
+    + Compile tree-sitter queries once per parse (#839) by @HALtheWise
+  * Testing
+    + Utilities to benchmark mergiraf on large corpora (#537)
+      by @wetneb
+  * Miscellaneous Tasks
+    + Restore build.rs to refresh test binary if test data changed
+      (#822) by @wetneb
+    + Update NodeJS in CI (#834) by @wetneb
+  * New Contributors
+    + @HALtheWise made their first contribution in #839
+    + @mward25 made their first contribution in #810
+    + @robvenn made their first contribution in #828
+
+-------------------------------------------------------------------

Old:
----
  mergiraf-0.19.1.obscpio

New:
----
  mergiraf-0.20.0.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ mergiraf.spec ++++++
--- /var/tmp/diff_new_pack.qp6Fkw/_old  2026-09-28 10:46:30.433089769 +0200
+++ /var/tmp/diff_new_pack.qp6Fkw/_new  2026-09-28 10:46:30.435089853 +0200
@@ -18,7 +18,7 @@
 
 
 Name:           mergiraf
-Version:        0.19.1
+Version:        0.20.0
 Release:        0
 Summary:        A syntax-aware git merge driver
 License:        GPL-3.0-or-later

++++++ _service ++++++
--- /var/tmp/diff_new_pack.qp6Fkw/_old  2026-09-28 10:46:30.501092618 +0200
+++ /var/tmp/diff_new_pack.qp6Fkw/_new  2026-09-28 10:46:30.508092912 +0200
@@ -3,7 +3,7 @@
     <param name="url">https://codeberg.org/mergiraf/mergiraf.git</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="scm">git</param>
-    <param name="revision">v0.19.1</param>
+    <param name="revision">v0.20.0</param>
     <param name="versionrewrite-pattern">v(\d+\.\d+\.\d+)</param>
     <param name="changesgenerate">enable</param>
   </service>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.qp6Fkw/_old  2026-09-28 10:46:30.538094169 +0200
+++ /var/tmp/diff_new_pack.qp6Fkw/_new  2026-09-28 10:46:30.542094336 +0200
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://codeberg.org/mergiraf/mergiraf.git</param>
-              <param 
name="changesrevision">e1c6514fb364faca51132be2b336aa739cdfadb9</param></service></servicedata>
+              <param 
name="changesrevision">182d26ea63aa62f7f5dc213cf8ceb70d83e7750e</param></service></servicedata>
 (No newline at EOF)
 

++++++ mergiraf-0.19.1.obscpio -> mergiraf-0.20.0.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mergiraf-0.19.1/.forgejo/workflows/Dockerfile 
new/mergiraf-0.20.0/.forgejo/workflows/Dockerfile
--- old/mergiraf-0.19.1/.forgejo/workflows/Dockerfile   2026-09-01 
10:50:21.000000000 +0200
+++ new/mergiraf-0.20.0/.forgejo/workflows/Dockerfile   2026-09-26 
11:02:58.000000000 +0200
@@ -18,3 +18,7 @@
 
 # Install 'shellcheck'
 RUN apt-get install shellcheck
+
+# Install a newer nodejs
+# TODO: this should be dropped once we migrate to act-26 (it's not out yet as 
of 2026-09-24)
+RUN curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash - && sudo 
apt-get install -y nodejs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mergiraf-0.19.1/Cargo.lock 
new/mergiraf-0.20.0/Cargo.lock
--- old/mergiraf-0.19.1/Cargo.lock      2026-09-01 10:50:21.000000000 +0200
+++ new/mergiraf-0.20.0/Cargo.lock      2026-09-26 11:02:58.000000000 +0200
@@ -146,9 +146,9 @@
 
 [[package]]
 name = "cc"
-version = "1.4.3"
+version = "1.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d"
+checksum = "54413ede23c2daf518f35156dfde027feb2374004d63bd497f983c8db9c0e313"
 dependencies = [
  "find-msvc-tools",
  "shlex",
@@ -175,9 +175,9 @@
 
 [[package]]
 name = "clap"
-version = "4.6.6"
+version = "4.6.7"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca"
+checksum = "aa8876b300ab35ba921adea3dfd70157a46249b33f95c9084ae5709785478946"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -185,9 +185,9 @@
 
 [[package]]
 name = "clap_builder"
-version = "4.6.6"
+version = "4.6.7"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889"
+checksum = "ec0797fb7aeb1406c84efac526901f7ec3ead2124f946b494e72879d4b54704d"
 dependencies = [
  "anstream",
  "anstyle",
@@ -197,9 +197,9 @@
 
 [[package]]
 name = "clap_derive"
-version = "4.6.4"
+version = "4.6.7"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061"
+checksum = "f9c751b79415d4e559e3d1fcf128e09e720eb673a06d26cf6f392d37d75b66e0"
 dependencies = [
  "heck",
  "proc-macro2",
@@ -301,9 +301,9 @@
 
 [[package]]
 name = "either"
-version = "1.17.0"
+version = "1.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d"
+checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34"
 
 [[package]]
 name = "encode_unicode"
@@ -345,9 +345,9 @@
 
 [[package]]
 name = "find-msvc-tools"
-version = "0.1.11"
+version = "0.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890"
+checksum = "ef25905e51abafe4dcea6c15fec58c57b601cdbd0ee53d22ea1d3016c587d39b"
 
 [[package]]
 name = "fixedbitset"
@@ -612,9 +612,9 @@
 
 [[package]]
 name = "log"
-version = "0.4.33"
+version = "0.4.34"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
+checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
 
 [[package]]
 name = "memchr"
@@ -624,7 +624,7 @@
 
 [[package]]
 name = "mergiraf"
-version = "0.19.1"
+version = "0.20.0"
 dependencies = [
  "assert_cmd",
  "caplog",
@@ -682,6 +682,7 @@
  "tree-sitter-php",
  "tree-sitter-properties",
  "tree-sitter-python-orchard",
+ "tree-sitter-qmljs",
  "tree-sitter-r",
  "tree-sitter-requirements",
  "tree-sitter-ruby",
@@ -936,18 +937,18 @@
 
 [[package]]
 name = "rstest"
-version = "0.26.1"
+version = "0.27.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49"
+checksum = "948203e6d13b83e51a90d7cf236dd58a0065f23f4b902f00f306e7eb2bd09b9c"
 dependencies = [
  "rstest_macros",
 ]
 
 [[package]]
 name = "rstest_macros"
-version = "0.26.1"
+version = "0.27.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0"
+checksum = "a8d92eaf7b6e51e12471d71ddc72608e7151573de44099aacfbb1128177c0da4"
 dependencies = [
  "cfg-if",
  "glob",
@@ -1211,9 +1212,9 @@
 
 [[package]]
 name = "tree-sitter"
-version = "0.26.12"
+version = "0.26.13"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "83c567a8e18ae93f20982c90370b16fd24023aeaf52f6052b96957ab253a0fec"
+checksum = "17ebdd3a5a7e28a1890b876fdbd0c3c0fe0a6336cffaa104f11b9f720c9daa29"
 dependencies = [
  "cc",
  "regex",
@@ -1255,9 +1256,9 @@
 
 [[package]]
 name = "tree-sitter-cmake"
-version = "0.7.4"
+version = "0.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "164e0c4f4236ec5ceff14824a5528615cf462e100467e49826442ff57d327061"
+checksum = "d22893fac54133fdb93497efc45713d53a96d93b035288c7a8ab0c2af77c0ddd"
 dependencies = [
  "cc",
  "tree-sitter-language",
@@ -1275,9 +1276,9 @@
 
 [[package]]
 name = "tree-sitter-dart-orchard"
-version = "0.6.0"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "5e9e47f28354ae26b1ecfe2b2baa157e5145665d78838fe189008e921c0971eb"
+checksum = "a1a1fa2972c59e6f9c9529220baef8ec7c08246df12226d6a2562708a302cd59"
 dependencies = [
  "cc",
  "tree-sitter-language",
@@ -1405,9 +1406,9 @@
 
 [[package]]
 name = "tree-sitter-java-orchard"
-version = "0.5.15"
+version = "0.5.18"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "d653f4d9f16496ec19de6bcb7b7688d3f6b203c9c0af979bd5294fc058a3e16f"
+checksum = "7ab98a9ff17dc2fe04524bdf11abb8eb4da7252de249e65deb5dede10a953a5f"
 dependencies = [
  "cc",
  "tree-sitter-language",
@@ -1471,9 +1472,9 @@
 
 [[package]]
 name = "tree-sitter-matlab"
-version = "1.3.0"
+version = "1.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "8e8d8831a78547f54860dd8467166b1ab0c466d03d43c4bb447af55c024281f7"
+checksum = "97962a0daf32492453fbea1984d34c4d8460b66b95c6c04feecc4cbf1b00a984"
 dependencies = [
  "cc",
  "tree-sitter-language",
@@ -1501,9 +1502,9 @@
 
 [[package]]
 name = "tree-sitter-ocaml"
-version = "0.25.0"
+version = "0.26.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "0b943275476bac8f73e08bc4050e21d89d61ff68e1751b789ba74917b9147a85"
+checksum = "43adb10fcccbcbbce0f4adc9dab7cd55f0a5eafc2d71bd4c03fcc5d6767066e0"
 dependencies = [
  "cc",
  "tree-sitter-language",
@@ -1540,6 +1541,16 @@
 ]
 
 [[package]]
+name = "tree-sitter-qmljs"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "67445ea937cd7eadaf2f628e2e7dd234374586cc31b4d1d63dbb5f5e7f9d9b62"
+dependencies = [
+ "cc",
+ "tree-sitter-language",
+]
+
+[[package]]
 name = "tree-sitter-r"
 version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
@@ -1571,9 +1582,9 @@
 
 [[package]]
 name = "tree-sitter-rust-orchard"
-version = "0.16.8"
+version = "0.16.10"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1ef7e314bea5898ba33a1018251f0c5330e812fe71f5950f874ffcd85ab1ff10"
+checksum = "ae59062ce889b2581ac33361c6a3799673c7496aafa64dd958ba0268c00e014f"
 dependencies = [
  "cc",
  "tree-sitter-language",
@@ -1621,9 +1632,9 @@
 
 [[package]]
 name = "tree-sitter-systemverilog"
-version = "0.4.0"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "cc40d99fa6fb0a24b9eb62082b5667d40359b5da175d17d44052b3b1bf9dcca9"
+checksum = "7d9f02d7fd20691acb5edf44a782f0b9fa278d9987befb1ae597c768707b95e6"
 dependencies = [
  "cc",
  "tree-sitter-language",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mergiraf-0.19.1/Cargo.toml 
new/mergiraf-0.20.0/Cargo.toml
--- old/mergiraf-0.19.1/Cargo.toml      2026-09-01 10:50:21.000000000 +0200
+++ new/mergiraf-0.20.0/Cargo.toml      2026-09-26 11:02:58.000000000 +0200
@@ -11,7 +11,7 @@
 
 [package]
 name = "mergiraf"
-version = "0.19.1"
+version = "0.20.0"
 edition = "2024"
 license = "GPL-3.0-only"
 repository = "https://codeberg.org/mergiraf/mergiraf";
@@ -28,7 +28,7 @@
 
 [dependencies]
 tree-sitter = "0.26"
-tree-sitter-ocaml = "0.25"
+tree-sitter-ocaml = "0.26"
 tree-sitter-rust-orchard = "0.16"
 tree-sitter-json = "0.24"
 tree-sitter-yaml = "0.7"
@@ -44,7 +44,7 @@
 tree-sitter-go = "0.25"
 tree-sitter-gomod-orchard = "0.5"
 tree-sitter-gosum-orchard = "0.3"
-tree-sitter-dart-orchard = "0.6"
+tree-sitter-dart-orchard = "0.7"
 tree-sitter-typescript = "0.23"
 tree-sitter-python-orchard = "0.4"
 tree-sitter-php = "0.24"
@@ -62,6 +62,7 @@
 tree-sitter-gleam = "1.0.0"
 tree-sitter-erlang = "0.20.0"
 tree-sitter-matlab = "1.3.0"
+tree-sitter-qmljs = "0.3.0"
 regex = "1.12"
 itertools = "0.15"
 tree-sitter-nix = "0.3"
@@ -98,7 +99,7 @@
 
 [dev-dependencies]
 caplog = "0.3"
-rstest = { version = "0.26", default-features = false }
+rstest = { version = "0.27", default-features = false }
 tempfile = "3"
 insta = { version = "1.43", default-features = false, features = ["colors"] }
 pretty_assertions = "1.4.1"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mergiraf-0.19.1/build.rs new/mergiraf-0.20.0/build.rs
--- old/mergiraf-0.19.1/build.rs        1970-01-01 01:00:00.000000000 +0100
+++ new/mergiraf-0.20.0/build.rs        2026-09-26 11:02:58.000000000 +0200
@@ -0,0 +1,3 @@
+fn main() {
+    println!("cargo:rerun-if-changed=tests/data");
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mergiraf-0.19.1/doc/src/languages.md 
new/mergiraf-0.20.0/doc/src/languages.md
--- old/mergiraf-0.19.1/doc/src/languages.md    2026-09-01 10:50:21.000000000 
+0200
+++ new/mergiraf-0.20.0/doc/src/languages.md    2026-09-26 11:02:58.000000000 
+0200
@@ -22,6 +22,7 @@
 * OCaml (*.ml, *.mli)
 * PHP (*.php, *.phmtl)
 * Python (*.py)
+* QML (*.qml)
 * R (*.R, *.r, .Rprofile)
 * Ruby (*.rb)
 * Rust (*.rs)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mergiraf-0.19.1/examples/qml/working/add_enum_values/Base.qml 
new/mergiraf-0.20.0/examples/qml/working/add_enum_values/Base.qml
--- old/mergiraf-0.19.1/examples/qml/working/add_enum_values/Base.qml   
1970-01-01 01:00:00.000000000 +0100
+++ new/mergiraf-0.20.0/examples/qml/working/add_enum_values/Base.qml   
2026-09-26 11:02:58.000000000 +0200
@@ -0,0 +1,5 @@
+import QtQuick 2.15
+
+Item {
+    enum Color { Red }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mergiraf-0.19.1/examples/qml/working/add_enum_values/Expected.qml 
new/mergiraf-0.20.0/examples/qml/working/add_enum_values/Expected.qml
--- old/mergiraf-0.19.1/examples/qml/working/add_enum_values/Expected.qml       
1970-01-01 01:00:00.000000000 +0100
+++ new/mergiraf-0.20.0/examples/qml/working/add_enum_values/Expected.qml       
2026-09-26 11:02:58.000000000 +0200
@@ -0,0 +1,11 @@
+import QtQuick 2.15
+
+Item {
+<<<<<<< LEFT
+    enum Color { Red, Green }
+||||||| BASE
+    enum Color { Red }
+=======
+    enum Color { Red, Blue }
+>>>>>>> RIGHT
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mergiraf-0.19.1/examples/qml/working/add_enum_values/Left.qml 
new/mergiraf-0.20.0/examples/qml/working/add_enum_values/Left.qml
--- old/mergiraf-0.19.1/examples/qml/working/add_enum_values/Left.qml   
1970-01-01 01:00:00.000000000 +0100
+++ new/mergiraf-0.20.0/examples/qml/working/add_enum_values/Left.qml   
2026-09-26 11:02:58.000000000 +0200
@@ -0,0 +1,5 @@
+import QtQuick 2.15
+
+Item {
+    enum Color { Red, Green }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mergiraf-0.19.1/examples/qml/working/add_enum_values/Right.qml 
new/mergiraf-0.20.0/examples/qml/working/add_enum_values/Right.qml
--- old/mergiraf-0.19.1/examples/qml/working/add_enum_values/Right.qml  
1970-01-01 01:00:00.000000000 +0100
+++ new/mergiraf-0.20.0/examples/qml/working/add_enum_values/Right.qml  
2026-09-26 11:02:58.000000000 +0200
@@ -0,0 +1,5 @@
+import QtQuick 2.15
+
+Item {
+    enum Color { Red, Blue }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mergiraf-0.19.1/examples/qml/working/add_functions/Base.qml 
new/mergiraf-0.20.0/examples/qml/working/add_functions/Base.qml
--- old/mergiraf-0.19.1/examples/qml/working/add_functions/Base.qml     
1970-01-01 01:00:00.000000000 +0100
+++ new/mergiraf-0.20.0/examples/qml/working/add_functions/Base.qml     
2026-09-26 11:02:58.000000000 +0200
@@ -0,0 +1,5 @@
+import QtQuick 2.15
+
+Rectangle {
+    function f() { return 1; }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mergiraf-0.19.1/examples/qml/working/add_functions/Expected.qml 
new/mergiraf-0.20.0/examples/qml/working/add_functions/Expected.qml
--- old/mergiraf-0.19.1/examples/qml/working/add_functions/Expected.qml 
1970-01-01 01:00:00.000000000 +0100
+++ new/mergiraf-0.20.0/examples/qml/working/add_functions/Expected.qml 
2026-09-26 11:02:58.000000000 +0200
@@ -0,0 +1,8 @@
+import QtQuick 2.15
+
+Rectangle {
+    function f() { return 1; }
+    function g() { return 2; }
+
+    function h() { return 3; }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mergiraf-0.19.1/examples/qml/working/add_functions/Left.qml 
new/mergiraf-0.20.0/examples/qml/working/add_functions/Left.qml
--- old/mergiraf-0.19.1/examples/qml/working/add_functions/Left.qml     
1970-01-01 01:00:00.000000000 +0100
+++ new/mergiraf-0.20.0/examples/qml/working/add_functions/Left.qml     
2026-09-26 11:02:58.000000000 +0200
@@ -0,0 +1,6 @@
+import QtQuick 2.15
+
+Rectangle {
+    function f() { return 1; }
+    function g() { return 2; }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mergiraf-0.19.1/examples/qml/working/add_functions/Right.qml 
new/mergiraf-0.20.0/examples/qml/working/add_functions/Right.qml
--- old/mergiraf-0.19.1/examples/qml/working/add_functions/Right.qml    
1970-01-01 01:00:00.000000000 +0100
+++ new/mergiraf-0.20.0/examples/qml/working/add_functions/Right.qml    
2026-09-26 11:02:58.000000000 +0200
@@ -0,0 +1,6 @@
+import QtQuick 2.15
+
+Rectangle {
+    function f() { return 1; }
+    function h() { return 3; }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mergiraf-0.19.1/examples/qml/working/add_imports/Base.qml 
new/mergiraf-0.20.0/examples/qml/working/add_imports/Base.qml
--- old/mergiraf-0.19.1/examples/qml/working/add_imports/Base.qml       
1970-01-01 01:00:00.000000000 +0100
+++ new/mergiraf-0.20.0/examples/qml/working/add_imports/Base.qml       
2026-09-26 11:02:58.000000000 +0200
@@ -0,0 +1,5 @@
+import QtQuick 2.15
+
+Rectangle {
+    width: 640
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mergiraf-0.19.1/examples/qml/working/add_imports/Expected.qml 
new/mergiraf-0.20.0/examples/qml/working/add_imports/Expected.qml
--- old/mergiraf-0.19.1/examples/qml/working/add_imports/Expected.qml   
1970-01-01 01:00:00.000000000 +0100
+++ new/mergiraf-0.20.0/examples/qml/working/add_imports/Expected.qml   
2026-09-26 11:02:58.000000000 +0200
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import QtQuick.Layouts 1.15
+
+Rectangle {
+    width: 640
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mergiraf-0.19.1/examples/qml/working/add_imports/Left.qml 
new/mergiraf-0.20.0/examples/qml/working/add_imports/Left.qml
--- old/mergiraf-0.19.1/examples/qml/working/add_imports/Left.qml       
1970-01-01 01:00:00.000000000 +0100
+++ new/mergiraf-0.20.0/examples/qml/working/add_imports/Left.qml       
2026-09-26 11:02:58.000000000 +0200
@@ -0,0 +1,6 @@
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+
+Rectangle {
+    width: 640
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mergiraf-0.19.1/examples/qml/working/add_imports/Right.qml 
new/mergiraf-0.20.0/examples/qml/working/add_imports/Right.qml
--- old/mergiraf-0.19.1/examples/qml/working/add_imports/Right.qml      
1970-01-01 01:00:00.000000000 +0100
+++ new/mergiraf-0.20.0/examples/qml/working/add_imports/Right.qml      
2026-09-26 11:02:58.000000000 +0200
@@ -0,0 +1,6 @@
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+
+Rectangle {
+    width: 640
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mergiraf-0.19.1/examples/qml/working/add_properties/Base.qml 
new/mergiraf-0.20.0/examples/qml/working/add_properties/Base.qml
--- old/mergiraf-0.19.1/examples/qml/working/add_properties/Base.qml    
1970-01-01 01:00:00.000000000 +0100
+++ new/mergiraf-0.20.0/examples/qml/working/add_properties/Base.qml    
2026-09-26 11:02:58.000000000 +0200
@@ -0,0 +1,6 @@
+import QtQuick 2.15
+
+Rectangle {
+    width: 640
+    height: 480
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mergiraf-0.19.1/examples/qml/working/add_properties/Expected.qml 
new/mergiraf-0.20.0/examples/qml/working/add_properties/Expected.qml
--- old/mergiraf-0.19.1/examples/qml/working/add_properties/Expected.qml        
1970-01-01 01:00:00.000000000 +0100
+++ new/mergiraf-0.20.0/examples/qml/working/add_properties/Expected.qml        
2026-09-26 11:02:58.000000000 +0200
@@ -0,0 +1,9 @@
+import QtQuick 2.15
+
+Rectangle {
+    width: 640
+    height: 480
+    property int counter: 0
+
+    property string title: "Hello"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mergiraf-0.19.1/examples/qml/working/add_properties/Left.qml 
new/mergiraf-0.20.0/examples/qml/working/add_properties/Left.qml
--- old/mergiraf-0.19.1/examples/qml/working/add_properties/Left.qml    
1970-01-01 01:00:00.000000000 +0100
+++ new/mergiraf-0.20.0/examples/qml/working/add_properties/Left.qml    
2026-09-26 11:02:58.000000000 +0200
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Rectangle {
+    width: 640
+    height: 480
+    property int counter: 0
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mergiraf-0.19.1/examples/qml/working/add_properties/Right.qml 
new/mergiraf-0.20.0/examples/qml/working/add_properties/Right.qml
--- old/mergiraf-0.19.1/examples/qml/working/add_properties/Right.qml   
1970-01-01 01:00:00.000000000 +0100
+++ new/mergiraf-0.20.0/examples/qml/working/add_properties/Right.qml   
2026-09-26 11:02:58.000000000 +0200
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Rectangle {
+    width: 640
+    height: 480
+    property string title: "Hello"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mergiraf-0.19.1/helpers/benchmark.sh 
new/mergiraf-0.20.0/helpers/benchmark.sh
--- old/mergiraf-0.19.1/helpers/benchmark.sh    1970-01-01 01:00:00.000000000 
+0100
+++ new/mergiraf-0.20.0/helpers/benchmark.sh    2026-09-26 11:02:58.000000000 
+0200
@@ -0,0 +1,73 @@
+#!/usr/bin/env bash
+
+if [ "$#" -ne 2 ]; then
+    echo "usage: $0 <path> <executable>"
+    echo "where <path> is a path to a directory containing test cases, such as 
examples/java/working,"
+    echo "and <executable> is a path to a mergiraf binary to benchmark."
+    echo "This prints a TSV file summarizing benchmarking results on the 
standard output."
+    echo "It can then be used with helpers/summarize_benchmark.py to get 
statistics in Markdown."
+    exit 1
+fi
+
+suite=$1
+executable=$2
+
+script_path="$(realpath "${BASH_SOURCE[0]}")"
+script_dir="$(dirname "${script_path}")"
+
+tmp_dir=/tmp/tmp$$
+mkdir -p $tmp_dir
+
+# Print TSV headers
+echo -e "status\ttiming\tlanguage\tcase"
+
+# For each test case…
+
+find -L "$suite" -type d | while read -r testid ; do
+    # Detect its language
+    ext=$(ls "$testid" | grep Base | sed -e 's/Base//')
+    if [ ! -e "$testid/Expected$ext" ]; then
+        continue
+    fi
+
+    language="*$ext"
+    extra_args=""
+    if [ -e "$testid/language" ]; then
+        language=$(cat "$testid/language")
+        extra_args="$extra_args --language $language"
+    fi
+
+    # Run the executable to benchmark
+    /usr/bin/env time -o $tmp_dir/timings -f "%e" "$executable" merge -v 
"$testid/Base$ext" "$testid/Left$ext" "$testid/Right$ext" -s BASE -x LEFT -y 
RIGHT $extra_args > "$tmp_dir/our_merge_raw$ext" 2> $tmp_dir/mergiraf_log
+    retcode=$?
+
+    # Normalize line endings both for the expected value and our output
+    sed -e '$a\' "$tmp_dir/our_merge_raw$ext" > "$tmp_dir/our_merge$ext"
+    sed -e '$a\' "$testid/Expected$ext" > "$tmp_dir/expected_merge$ext"
+
+    timing=$(tail -1 $tmp_dir/timings)
+
+    # Categorize the test outcome
+    if [ $retcode -ge 128 ]; then
+        outcome="Panic"
+    elif diff -B "$tmp_dir/our_merge$ext" "$tmp_dir/expected_merge$ext" > 
/dev/null 2>&1; then
+        outcome="Exact"
+    else
+        conflict_count=$(grep -c "<<<<<<<" "$tmp_dir/our_merge_raw$ext")
+        if [ "$conflict_count" -ge 1 ]; then
+            # Check that we were able to parse the files correctly
+            if grep "encountered an error: parse error at " 
$tmp_dir/mergiraf_log > /dev/null; then
+                outcome="Parse"
+            else
+                outcome="Conflict"
+            fi
+        elif cargo compare --commutative "$tmp_dir/our_merge$ext" 
"$tmp_dir/expected_merge$ext" > /dev/null 2>&1; then
+            outcome="Format"
+        else
+            outcome="Differ"
+        fi;
+    fi;
+    echo -e "${outcome}\t${timing}\t${language}\t${testid}"
+done;
+
+rm -rf ${tmp_dir}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mergiraf-0.19.1/helpers/summarize_benchmark.py 
new/mergiraf-0.20.0/helpers/summarize_benchmark.py
--- old/mergiraf-0.19.1/helpers/summarize_benchmark.py  1970-01-01 
01:00:00.000000000 +0100
+++ new/mergiraf-0.20.0/helpers/summarize_benchmark.py  2026-09-26 
11:02:58.000000000 +0200
@@ -0,0 +1,219 @@
+#!/usr/bin/env python3
+import sys
+import csv
+import math
+from collections import defaultdict
+from typing import Any, Optional, Self
+
+"""
+Utility that summarizes the outcomes of a benchmark run with the 
`helpers/benchmark.sh` script.
+When supplied with the path to a single benchmark TSV log, it gives statistics 
about how many
+test cases ended up in different categories, as a Markdown table.
+When supplied with two such paths, it generates
+- a comparison between the logs, as a similar table
+- a confusion matrix indicating from which status to which status test cases 
changed
+"""
+
+# The order in which test case categories should be presented
+# Roughly from "best to worst", so that the confusion matrix
+# can be intuitively interpreted as satisfying if it is lower-triangular
+STATUS_ORDER = ["Exact", "Format", "Conflict", "Differ", "Parse", "Panic"]
+
+STATUS = "status"  # aka `outcome`
+TIMING = "timing"
+LANGUAGE = "language"
+CASE = "case"  # aka `test_id`
+
+
+class TimingStats:
+    """
+    Utility to compute an average out of many data points.
+    We could also compute the standard deviation by computing the sum of 
squares.
+    """
+
+    def __init__(self):
+        self.count = 0
+        self.sum = 0
+
+    def add(self, timing: int):
+        self.count += 1
+        self.sum += timing
+
+    def average(self):
+        return float(self.sum) / self.count
+
+
+class StatsLine:
+    """
+    Statistics about how many test cases land in each status category,
+    and how long they took.
+    """
+
+    def __init__(self):
+        self.timing = TimingStats()
+        self.states = defaultdict(int)
+
+    def add(self, case: dict[str, Any]):
+        timing = float(case[TIMING])
+        self.timing.add(timing)
+        status = case[STATUS]
+        self.states[status] += 1
+
+    def to_markdown(self):
+        cases = self.timing.count
+        parts = [f"{cases:,}"]
+        for status in STATUS_ORDER:
+            count = self.states[status]
+            if count:
+                parts.append(f"{count:,} ({100 * count / cases:.0f}%)")
+            else:
+                parts.append("0")
+
+        timing = self.timing.average()
+        parts.append(f"{timing:.3f}")
+        return "| " + (" | ".join(parts)) + " |"
+
+
+class StatsDiff:
+    """
+    Difference between two `StatsLine` from two different benchmark runs
+    """
+
+    def __init__(self, first: StatsLine, second: StatsLine):
+        self.first = first
+        self.second = second
+
+    def to_markdown(self):
+        timing = self.second.timing.average()
+        timing_diff = timing - self.first.timing.average()
+        parts = [f"{self.second.timing.count:,}"]
+        for status in STATUS_ORDER:
+            first = self.first.states[status]
+            second = self.second.states[status]
+            if second - first and self.first.timing.count:
+                percent_change = 100 * (second - first) / 
self.first.timing.count
+                parts.append(f"{second - first:+,} 
**({percent_change:+.1f}%)**")
+            else:
+                parts.append(f"{second - first:+,}")
+
+        if math.fabs(timing_diff) > 0.001:
+            if timing > 0:
+                percent_change = 100 * timing_diff / 
self.first.timing.average()
+                parts.append(f"{timing_diff:+.3f} ({percent_change:+.1f}%)")
+            else:
+                parts.append(f"{timing_diff:+.3f})")
+        else:
+            parts.append(f"{timing:.3f} (+0)")
+        return "| " + (" | ".join(parts)) + " |"
+
+
+class BenchmarkLog:
+    def __init__(self, path: str, restrict_to: Optional[Self] = None):
+        """
+        Parses a benchmark log as a TSV file,
+        returning global and per-language statistics.
+        If another BenchmarkLog is passed as `restrict_to`,
+        then only cases which are also covered by the other
+        log are taken into account.
+        """
+        self.global_stats = StatsLine()
+        self.per_language: defaultdict[str, StatsLine] = defaultdict(StatsLine)
+        self.case_to_status: dict[str, str] = {}
+        with open(path, "r") as f:
+            csv_reader = csv.DictReader(f, delimiter="\t")
+            for case in csv_reader:
+                if restrict_to is None or case[CASE] in 
restrict_to.case_to_status:
+                    self.global_stats.add(case)
+                    self.per_language[case[LANGUAGE]].add(case)
+                    self.case_to_status[case[CASE]] = case[STATUS]
+
+
+def print_header():
+    """
+    Prints the header of a Markdown table representing test case categories
+    """
+    # fmt: off
+    print("| Language | Cases | " + " | ".join(STATUS_ORDER) + " | Time (s) |")
+    print("| -------- | ----- | " + " | ".join(("-" * len(status) for status 
in STATUS_ORDER)) + " | -------- |")
+    # fmt: on
+
+
+def summarize_benchmark_log(path: str):
+    """
+    Prints a summary of a single benchmark
+    """
+    print_header()
+    log = BenchmarkLog(path)
+    lang_stats = list(log.per_language.items())
+    lang_stats.sort(key=lambda pair: -pair[1].timing.count)
+    for lang, stats in lang_stats:
+        print(f"| `{lang}` {stats.to_markdown()}")
+
+    if len(log.per_language) > 1:
+        print(f"| **Total** {log.global_stats.to_markdown()}")
+
+
+def compare_benchmark_logs(path_before: str, path_after: str):
+    """
+    Prints a summary of the differences between two benchmark runs
+    """
+    print_header()
+    after_log = BenchmarkLog(path_after)
+    before_log = BenchmarkLog(path_before, restrict_to=after_log)
+    after_lang_stats = list(after_log.per_language.items())
+    after_lang_stats.sort(key=lambda pair: -pair[1].timing.count)
+    for lang, stats in after_lang_stats:
+        diff = StatsDiff(before_log.per_language[lang], stats)
+        print(f"| `{lang}` {diff.to_markdown()}")
+
+    if len(after_log.per_language) > 1:
+        global_diff = StatsDiff(before_log.global_stats, 
after_log.global_stats)
+        print(f"| **Total** {global_diff.to_markdown()}")
+
+    print("")
+
+    confusion_matrix = defaultdict(set)
+    for case, status in after_log.case_to_status.items():
+        previous_status = before_log.case_to_status.get(case)
+        if not previous_status:
+            print(
+                f"warning: case not found in previous benchmark: {case}",
+                file=sys.stderr,
+            )
+        else:
+            confusion_matrix[(previous_status, status)].add(case)
+
+    # fmt: off
+    print("| ↓ Before \\ After → | " + " | ".join(STATUS_ORDER) + " |")
+    print("| ------------------ | "  + " | ".join(["-" * len(status) for 
status in STATUS_ORDER]) + " |")
+    # fmt: on
+
+    for status in STATUS_ORDER:
+        row = []
+        for new_status in STATUS_ORDER:
+            cell = ""
+            if confusion_matrix[(status, new_status)]:
+                cell = f"{len(confusion_matrix[(status, new_status)]):,}"
+            if cell and status != new_status:
+                cell = f"**{cell}**"
+            row.append(cell)
+        print("| " + status + " | " + " | ".join(row) + " |")
+
+    print("")
+    print("## Suspicious status changes")
+    print("")
+    for index, status in enumerate(STATUS_ORDER):
+        for new_status in STATUS_ORDER[(index + 1) :]:
+            cases = confusion_matrix[(status, new_status)]
+            if cases:
+                print(f"### {status} → {new_status}")
+                for case in cases:
+                    print(case)
+                print("")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) == 3:
+        compare_benchmark_logs(sys.argv[1], sys.argv[2])
+    else:
+        summarize_benchmark_log(sys.argv[1])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mergiraf-0.19.1/mgf_dev/src/main.rs 
new/mergiraf-0.20.0/mgf_dev/src/main.rs
--- old/mergiraf-0.19.1/mgf_dev/src/main.rs     2026-09-01 10:50:21.000000000 
+0200
+++ new/mergiraf-0.20.0/mgf_dev/src/main.rs     2026-09-26 11:02:58.000000000 
+0200
@@ -25,6 +25,9 @@
     /// Override automatic language detection.
     #[arg(short = 'L', long, global = true)]
     language: Option<String>,
+    /// Be verbose
+    #[arg(short = 'v', long, global = true)]
+    verbose: bool,
 }
 
 #[derive(Subcommand, Debug)]
@@ -76,9 +79,16 @@
 }
 
 fn main() {
-    stderrlog::new().module(module_path!()).init().unwrap();
+    let args = CliArgs::parse();
+    stderrlog::new()
+        // most (currently, all) logging statements are made in the `mergiraf` 
crate,
+        // so initialize the logger there as well
+        .modules([module_path!(), "mergiraf"])
+        .verbosity(if args.verbose { 3 } else { 2 })
+        .init()
+        .unwrap();
 
-    match real_main(&CliArgs::parse()) {
+    match real_main(&args) {
         Ok(exit_code) => exit(exit_code),
         Err(error) => {
             eprintln!("mgf_dev: {error}");
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mergiraf-0.19.1/src/ast.rs 
new/mergiraf-0.20.0/src/ast.rs
--- old/mergiraf-0.19.1/src/ast.rs      2026-09-01 10:50:21.000000000 +0200
+++ new/mergiraf-0.20.0/src/ast.rs      2026-09-26 11:02:58.000000000 +0200
@@ -102,6 +102,22 @@
     pub lang_profile: &'a LangProfile,
 }
 
+/// A cache of compiled Tree-sitter queries. This avoids needing to re-compile 
the same query for
+/// every recursive injection (e.g. every Rust macro invocation).
+#[derive(Default)]
+struct QueryCache(FxHashMap<(&'static str, &'static str), Query>);
+
+impl QueryCache {
+    fn get(&mut self, lang_profile: &LangProfile, query_str: &'static str, 
kind: &str) -> &Query {
+        self.0
+            .entry((lang_profile.name, query_str))
+            .or_insert_with(|| {
+                Query::new(&lang_profile.language, query_str)
+                    .unwrap_or_else(|err| panic!("Invalid {kind} query: 
{err}"))
+            })
+    }
+}
+
 impl<'a> AstNode<'a> {
     /// Parse a string to a tree using the language supplied
     pub fn parse(
@@ -111,7 +127,15 @@
         ref_arena: &'a Arena<&'a Self>,
     ) -> Result<&'a Self, ParsingError> {
         let mut next_node_id = 1;
-        let root = Self::parse_root(source, None, lang_profile, arena, &mut 
next_node_id)?;
+        let mut query_cache = QueryCache::default();
+        let root = Self::parse_root(
+            source,
+            None,
+            lang_profile,
+            arena,
+            &mut next_node_id,
+            &mut query_cache,
+        )?;
         root.internal_precompute_root_dfs(ref_arena);
         Ok(root)
     }
@@ -125,6 +149,7 @@
         lang_profile: &'a LangProfile,
         arena: &'a Arena<Self>,
         next_node_id: &mut usize,
+        query_cache: &mut QueryCache,
     ) -> Result<&'a Self, ParsingError> {
         let mut parser = Parser::new();
         parser
@@ -139,9 +164,10 @@
         let tree = parser
             .parse(source, None)
             .expect("Parsing source code failed");
-        let node_id_to_injection_lang = Self::locate_injections(&tree, source, 
lang_profile);
+        let node_id_to_injection_lang =
+            Self::locate_injections(&tree, source, lang_profile, query_cache);
         let node_id_to_commutative_parent =
-            Self::locate_commutative_parents_by_query(&tree, source, 
lang_profile);
+            Self::locate_commutative_parents_by_query(&tree, source, 
lang_profile, query_cache);
         let range_for_root = if let Some(range) = range {
             range.start_byte..range.end_byte
         } else {
@@ -153,6 +179,7 @@
             lang_profile,
             arena,
             next_node_id,
+            query_cache,
             &node_id_to_injection_lang,
             &node_id_to_commutative_parent,
             Some(range_for_root),
@@ -165,19 +192,19 @@
         tree: &Tree,
         source: &'a str,
         lang_profile: &'b LangProfile,
+        query_cache: &mut QueryCache,
     ) -> FxHashMap<usize, &'b CommutativeParent> {
         let mut node_id_to_commutative_parent = FxHashMap::default();
         // For each commutative parent that is defined by a tree-sitter query
         for commutative_parent in &lang_profile.commutative_parents {
             if let ParentType::ByQuery(query_str) = 
commutative_parent.parent_type() {
                 // Execute this query over the tree
-                let query = Query::new(&lang_profile.language, query_str)
-                    .expect("Invalid commutative parent query");
+                let query = query_cache.get(lang_profile, query_str, 
"commutative parent");
                 let commutative_capture_index = query
                     .capture_index_for_name("commutative")
                     .expect("Commutative parent query without a '@commutative' 
capture");
                 let mut cursor = QueryCursor::new();
-                let matches = cursor.matches(&query, tree.root_node(), 
source.as_bytes());
+                let matches = cursor.matches(query, tree.root_node(), 
source.as_bytes());
                 // For each match, mark the captured node(s) as commutative
                 matches.for_each(|m| {
                     node_id_to_commutative_parent.extend(
@@ -195,12 +222,13 @@
         tree: &Tree,
         source: &'a str,
         lang_profile: &'a LangProfile,
+        query_cache: &mut QueryCache,
     ) -> FxHashMap<usize, &'static LangProfile> {
         let Some(query_str) = lang_profile.injections else {
             return FxHashMap::default();
         };
         let mut node_id_to_injection_lang = FxHashMap::default();
-        let query = Query::new(&lang_profile.language, 
query_str).expect("Invalid injection query");
+        let query = query_cache.get(lang_profile, query_str, "injection");
         let content_capture_index = query
             .capture_index_for_name("injection.content")
             .expect("Injection query without an injection.content capture");
@@ -208,7 +236,7 @@
         // or statically defined as a property (fixed by the injection query), 
in which case the capture below won't be defined.
         let language_capture_index = 
query.capture_index_for_name("injection.language");
         let mut cursor = QueryCursor::new();
-        let matches = cursor.matches(&query, tree.root_node(), 
source.as_bytes());
+        let matches = cursor.matches(query, tree.root_node(), 
source.as_bytes());
         matches.for_each(|m| {
             let pattern_properties = query.property_settings(m.pattern_index);
             // first, check if the language is statically defined in this 
clause of the query as a property
@@ -247,6 +275,7 @@
         lang_profile: &'a LangProfile,
         arena: &'a Arena<Self>,
         next_node_id: &mut usize,
+        query_cache: &mut QueryCache,
         node_id_to_injection_lang: &FxHashMap<usize, &'static LangProfile>,
         node_id_to_commutative_parent: &FxHashMap<usize, &'a 
CommutativeParent>,
         range_for_root: Option<Range<usize>>,
@@ -271,6 +300,7 @@
                 injection_lang,
                 arena,
                 next_node_id,
+                query_cache,
             ) {
                 children.push(injected_root);
                 last_child_end = injected_root.byte_range.end;
@@ -284,6 +314,7 @@
                     lang_profile,
                     arena,
                     next_node_id,
+                    query_cache,
                     node_id_to_injection_lang,
                     node_id_to_commutative_parent,
                     None,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mergiraf-0.19.1/src/lang_profile.rs 
new/mergiraf-0.20.0/src/lang_profile.rs
--- old/mergiraf-0.19.1/src/lang_profile.rs     2026-09-01 10:50:21.000000000 
+0200
+++ new/mergiraf-0.20.0/src/lang_profile.rs     2026-09-26 11:02:58.000000000 
+0200
@@ -353,7 +353,7 @@
     }
 
     /// the type of the root node
-    pub(crate) fn parent_type(&self) -> &ParentType<'_> {
+    pub(crate) fn parent_type(&self) -> &ParentType<'static> {
         &self.parent_type
     }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mergiraf-0.19.1/src/merge.rs 
new/mergiraf-0.20.0/src/merge.rs
--- old/mergiraf-0.19.1/src/merge.rs    2026-09-01 10:50:21.000000000 +0200
+++ new/mergiraf-0.20.0/src/merge.rs    2026-09-26 11:02:58.000000000 +0200
@@ -80,6 +80,7 @@
         Arc::clone(&contents_base),
         Arc::clone(&contents_left),
         Arc::clone(&contents_right),
+        fname_base,
         lang_profile,
         settings,
         full_merge,
@@ -125,6 +126,7 @@
     contents_base: Arc<Cow<'static, str>>,
     contents_left: Arc<Cow<'static, str>>,
     contents_right: Arc<Cow<'static, str>>,
+    fname_base: &Path,
     lang_profile: Arc<Cow<'static, LangProfile>>,
     settings: DisplaySettings<'static>,
     full_merge: bool,
@@ -197,7 +199,10 @@
         match rx.recv_timeout(timeout) {
             Ok(merges) => merges,
             Err(oneshot::RecvTimeoutError::Timeout) => {
-                warn!("structured merge took too long, falling back to Git");
+                warn!(
+                    "structured merge of '{}' took too long, falling back to 
Git",
+                    fname_base.display()
+                );
                 vec![]
             }
             Err(oneshot::RecvTimeoutError::Disconnected) => unreachable!(),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mergiraf-0.19.1/src/supported_langs.rs 
new/mergiraf-0.20.0/src/supported_langs.rs
--- old/mergiraf-0.19.1/src/supported_langs.rs  2026-09-01 10:50:21.000000000 
+0200
+++ new/mergiraf-0.20.0/src/supported_langs.rs  2026-09-26 11:02:58.000000000 
+0200
@@ -1577,6 +1577,50 @@
             extra_comment_nodes: &[],
             allow_parse_errors: false,
         },
+        LangProfile {
+            name: "qml",
+            alternate_names: &[],
+            extensions: &["qml"],
+            file_names: &[],
+            language: tree_sitter_qmljs::LANGUAGE.into(),
+            commutative_parents: vec![
+                // QML import statements are unordered
+                CommutativeParent::without_delimiters("program", "\n")
+                    .restricted_to_groups(&[&["ui_import"]]),
+                CommutativeParent::new("ui_object_initializer", " {\n", 
"\n\n", "\n}\n")
+                    .restricted_to(vec![
+                        // Most things in qml bodies are globally accessible 
within the module.
+                        ChildrenGroup::with_separator(&["ui_binding"], "\n"),
+                        ChildrenGroup::with_separator(&["ui_property"], 
"\n\n"),
+                        ChildrenGroup::with_separator(
+                            &["function_declaration", 
"generator_function_declaration"],
+                            "\n\n",
+                        ),
+                        ChildrenGroup::with_separator(&["ui_signal"], "\n\n"),
+                        ChildrenGroup::with_separator(&["enum_declaration"], 
"\n\n"),
+                    ]),
+            ],
+            signatures: vec![
+                // UI Imports should be unique (don't import the same thing 
twice)
+                signature("ui_import", vec![vec![]]),
+                // Bindings, properties, and function names, signals, and 
enums are also unique within they're scope
+                signature("ui_binding", vec![vec![Field("name")]]),
+                signature("ui_property", vec![vec![Field("name")]]),
+                signature("function_declaration", vec![vec![Field("name")]]),
+                signature("generator_function_declaration", 
vec![vec![Field("name")]]),
+                signature("ui_signal", vec![vec![Field("name")]]),
+                signature(
+                    "variable_declaration",
+                    vec![vec![ChildKind("variable_declarator"), 
Field("name")]],
+                ),
+                signature("enum_declaration", vec![vec![Field("name")]]),
+            ],
+            atomic_nodes: &[],
+            injections: None,
+            flattened_nodes: &[],
+            extra_comment_nodes: &[],
+            allow_parse_errors: false,
+        },
     ]
 });
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mergiraf-0.19.1/tests/timeout_support.rs 
new/mergiraf-0.20.0/tests/timeout_support.rs
--- old/mergiraf-0.19.1/tests/timeout_support.rs        2026-09-01 
10:50:21.000000000 +0200
+++ new/mergiraf-0.20.0/tests/timeout_support.rs        2026-09-26 
11:02:58.000000000 +0200
@@ -1,3 +1,4 @@
+use assert_cmd::prelude::*;
 use diffy_imara::{PatchFormatter, create_patch};
 
 mod common;
@@ -28,3 +29,18 @@
         panic!();
     }
 }
+
+/// The timeout warning should name the file which fell back to Git
+#[test]
+fn timeout_warning_names_the_file() {
+    merge()
+        .arg("tests/data/java/working/move_and_modify_conflict/Base.java")
+        .arg("tests/data/java/working/move_and_modify_conflict/Left.java")
+        .arg("tests/data/java/working/move_and_modify_conflict/Right.java")
+        .arg("--path-name=src/main/java/Example.java")
+        .arg("--timeout=1")
+        .assert()
+        .stderr(
+            "WARN structured merge of 'src/main/java/Example.java' took too 
long, falling back to Git\n",
+        );
+}

++++++ mergiraf.obsinfo ++++++
--- /var/tmp/diff_new_pack.qp6Fkw/_old  2026-09-28 10:46:31.223122869 +0200
+++ /var/tmp/diff_new_pack.qp6Fkw/_new  2026-09-28 10:46:31.232123246 +0200
@@ -1,5 +1,5 @@
 name: mergiraf
-version: 0.19.1
-mtime: 1788252621
-commit: e1c6514fb364faca51132be2b336aa739cdfadb9
+version: 0.20.0
+mtime: 1790413378
+commit: 182d26ea63aa62f7f5dc213cf8ceb70d83e7750e
 

++++++ vendor.tar.zst ++++++
/work/SRC/openSUSE:Factory/mergiraf/vendor.tar.zst 
/work/SRC/openSUSE:Factory/.mergiraf.new.383539/vendor.tar.zst differ: char 7, 
line 1

Reply via email to