markdown-merge 1.0.2 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/lib/markdown/merge/version.rb +3 -4
  4. data/lib/markdown/merge.rb +538 -137
  5. data/lib/markdown-merge.rb +3 -4
  6. data.tar.gz.sig +0 -0
  7. metadata +28 -283
  8. metadata.gz.sig +0 -0
  9. data/CHANGELOG.md +0 -283
  10. data/CITATION.cff +0 -20
  11. data/CODE_OF_CONDUCT.md +0 -134
  12. data/CONTRIBUTING.md +0 -227
  13. data/FUNDING.md +0 -74
  14. data/LICENSE.txt +0 -21
  15. data/README.md +0 -1090
  16. data/REEK +0 -0
  17. data/RUBOCOP.md +0 -71
  18. data/SECURITY.md +0 -21
  19. data/lib/markdown/merge/cleanse/block_spacing.rb +0 -253
  20. data/lib/markdown/merge/cleanse/code_fence_spacing.rb +0 -294
  21. data/lib/markdown/merge/cleanse/condensed_link_refs.rb +0 -405
  22. data/lib/markdown/merge/cleanse.rb +0 -42
  23. data/lib/markdown/merge/code_block_merger.rb +0 -300
  24. data/lib/markdown/merge/conflict_resolver.rb +0 -128
  25. data/lib/markdown/merge/debug_logger.rb +0 -26
  26. data/lib/markdown/merge/document_problems.rb +0 -190
  27. data/lib/markdown/merge/file_aligner.rb +0 -196
  28. data/lib/markdown/merge/file_analysis.rb +0 -353
  29. data/lib/markdown/merge/file_analysis_base.rb +0 -629
  30. data/lib/markdown/merge/freeze_node.rb +0 -93
  31. data/lib/markdown/merge/gap_line_node.rb +0 -136
  32. data/lib/markdown/merge/link_definition_formatter.rb +0 -49
  33. data/lib/markdown/merge/link_definition_node.rb +0 -157
  34. data/lib/markdown/merge/link_parser.rb +0 -421
  35. data/lib/markdown/merge/link_reference_rehydrator.rb +0 -320
  36. data/lib/markdown/merge/markdown_structure.rb +0 -123
  37. data/lib/markdown/merge/merge_result.rb +0 -166
  38. data/lib/markdown/merge/node_type_normalizer.rb +0 -126
  39. data/lib/markdown/merge/output_builder.rb +0 -166
  40. data/lib/markdown/merge/partial_template_merger.rb +0 -334
  41. data/lib/markdown/merge/smart_merger.rb +0 -221
  42. data/lib/markdown/merge/smart_merger_base.rb +0 -621
  43. data/lib/markdown/merge/table_match_algorithm.rb +0 -504
  44. data/lib/markdown/merge/table_match_refiner.rb +0 -136
  45. data/lib/markdown/merge/whitespace_normalizer.rb +0 -251
  46. data/sig/markdown/merge.rbs +0 -341
data/README.md DELETED
@@ -1,1090 +0,0 @@
1
- | 📍 NOTE |
2
- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
3
- | RubyGems (the [GitHub org][rubygems-org], not the website) [suffered][draper-security] a [hostile takeover][ellen-takeover] in September 2025. |
4
- | Ultimately [4 maintainers][simi-removed] were [hard removed][martin-removed] and a reason has been given for only 1 of those, while 2 others resigned in protest. |
5
- | It is a [complicated story][draper-takeover] which is difficult to [parse quickly][draper-lies]. |
6
- | Simply put - there was active policy for adding or removing maintainers/owners of [rubygems][rubygems-maint-policy] and [bundler][bundler-maint-policy], and those [policies were not followed][policy-fail]. |
7
- | I'm adding notes like this to gems because I [don't condone theft][draper-theft] of repositories or gems from their rightful owners. |
8
- | If a similar theft happened with my repos/gems, I'd hope some would stand up for me. |
9
- | Disenfranchised former-maintainers have started [gem.coop][gem-coop]. |
10
- | Once available I will publish there exclusively; unless RubyCentral makes amends with the community. |
11
- | The ["Technology for Humans: Joel Draper"][reinteractive-podcast] podcast episode by [reinteractive][reinteractive] is the most cogent summary I'm aware of. |
12
- | See [here][gem-naming], [here][gem-coop] and [here][martin-ann] for more info on what comes next. |
13
- | What I'm doing: A (WIP) proposal for [bundler/gem scopes][gem-scopes], and a (WIP) proposal for a federated [gem server][gem-server]. |
14
-
15
- [rubygems-org]: https://github.com/rubygems/
16
- [draper-security]: https://joel.drapper.me/p/ruby-central-security-measures/
17
- [draper-takeover]: https://joel.drapper.me/p/ruby-central-takeover/
18
- [ellen-takeover]: https://pup-e.com/blog/goodbye-rubygems/
19
- [simi-removed]: https://www.reddit.com/r/ruby/s/gOk42POCaV
20
- [martin-removed]: https://bsky.app/profile/martinemde.com/post/3m3occezxxs2q
21
- [draper-lies]: https://joel.drapper.me/p/ruby-central-fact-check/
22
- [draper-theft]: https://joel.drapper.me/p/ruby-central/
23
- [reinteractive]: https://reinteractive.com/ruby-on-rails
24
- [gem-coop]: https://gem.coop
25
- [gem-naming]: https://github.com/gem-coop/gem.coop/issues/12
26
- [martin-ann]: https://martinemde.com/2025/10/05/announcing-gem-coop.html
27
- [gem-scopes]: https://github.com/galtzo-floss/bundle-namespace
28
- [gem-server]: https://github.com/galtzo-floss/gem-server
29
- [reinteractive-podcast]: https://youtu.be/_H4qbtC5qzU?si=BvuBU90R2wAqD2E6
30
- [bundler-maint-policy]: https://github.com/ruby/rubygems/blob/b1ab33a3d52310a84d16b193991af07f5a6a07c0/doc/bundler/playbooks/TEAM_CHANGES.md
31
- [rubygems-maint-policy]: https://github.com/ruby/rubygems/blob/b1ab33a3d52310a84d16b193991af07f5a6a07c0/doc/rubygems/POLICIES.md?plain=1#L187-L196
32
- [policy-fail]: https://www.reddit.com/r/ruby/comments/1ove9vp/rubycentral_hates_this_one_fact/
33
-
34
- [![Galtzo FLOSS Logo by Aboling0, CC BY-SA 4.0][🖼️galtzo-i]][🖼️galtzo-discord] [![ruby-lang Logo, Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5][🖼️ruby-lang-i]][🖼️ruby-lang] [![kettle-rb Logo by Aboling0, CC BY-SA 4.0][🖼️kettle-rb-i]][🖼️kettle-rb]
35
-
36
- [🖼️galtzo-i]: https://logos.galtzo.com/assets/images/galtzo-floss/avatar-192px.svg
37
- [🖼️galtzo-discord]: https://discord.gg/3qme4XHNKN
38
- [🖼️ruby-lang-i]: https://logos.galtzo.com/assets/images/ruby-lang/avatar-192px.svg
39
- [🖼️ruby-lang]: https://www.ruby-lang.org/
40
- [🖼️kettle-rb-i]: https://logos.galtzo.com/assets/images/kettle-rb/avatar-192px.svg
41
- [🖼️kettle-rb]: https://github.com/kettle-rb
42
-
43
- # ☯️ Markdown::Merge
44
-
45
- [![Version][👽versioni]][👽dl-rank] [![GitHub tag (latest SemVer)][⛳️tag-img]][⛳️tag] [![License: MIT][📄license-img]][📄license-ref] [![Downloads Rank][👽dl-ranki]][👽dl-rank] [![Open Source Helpers][👽oss-helpi]][👽oss-help] [![CodeCov Test Coverage][🏀codecovi]][🏀codecov] [![Coveralls Test Coverage][🏀coveralls-img]][🏀coveralls] [![QLTY Test Coverage][🏀qlty-covi]][🏀qlty-cov] [![QLTY Maintainability][🏀qlty-mnti]][🏀qlty-mnt] [![CI Heads][🚎3-hd-wfi]][🚎3-hd-wf] [![CI Runtime Dependencies @ HEAD][🚎12-crh-wfi]][🚎12-crh-wf] [![CI Current][🚎11-c-wfi]][🚎11-c-wf] [![CI Truffle Ruby][🚎9-t-wfi]][🚎9-t-wf] [![Deps Locked][🚎13-🔒️-wfi]][🚎13-🔒️-wf] [![Deps Unlocked][🚎14-🔓️-wfi]][🚎14-🔓️-wf] [![CI Supported][🚎6-s-wfi]][🚎6-s-wf] [![CI Test Coverage][🚎2-cov-wfi]][🚎2-cov-wf] [![CI Style][🚎5-st-wfi]][🚎5-st-wf] [![CodeQL][🖐codeQL-img]][🖐codeQL] [![Apache SkyWalking Eyes License Compatibility Check][🚎15-🪪-wfi]][🚎15-🪪-wf]
46
-
47
- `if ci_badges.map(&:color).detect { it != "green"}` ☝️ [let me know][🖼️galtzo-discord], as I may have missed the [discord notification][🖼️galtzo-discord].
48
-
49
- -----
50
-
51
- `if ci_badges.map(&:color).all? { it == "green"}` 👇️ send money so I can do more of this. FLOSS maintenance is now my full-time job.
52
-
53
- [![OpenCollective Backers][🖇osc-backers-i]][🖇osc-backers] [![OpenCollective Sponsors][🖇osc-sponsors-i]][🖇osc-sponsors] [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Donate on PayPal][🖇paypal-img]][🖇paypal] [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate on Polar][🖇polar-img]][🖇polar] [![Donate at ko-fi.com][🖇kofi-img]][🖇kofi]
54
-
55
- ## 🌻 Synopsis
56
-
57
- Markdown::Merge provides **intelligent Markdown file merging** using tree\_haver backends. It can be used standalone or through parser-specific wrappers.
58
-
59
- **Direct usage** (with auto-detected or specified backend):
60
-
61
- ```ruby
62
- require "markdown/merge"
63
-
64
- # Auto-detect available backend (commonmarker or markly)
65
- merger = Markdown::Merge::SmartMerger.new(template_content, dest_content)
66
- result = merger.merge
67
-
68
- # Or specify a backend explicitly
69
- merger = Markdown::Merge::SmartMerger.new(template_content, dest_content, backend: :markly)
70
- ```
71
-
72
- **Via parser-specific wrappers** (for hard dependencies and backend-specific defaults):
73
-
74
- - [commonmarker-merge][commonmarker-merge] - Uses Comrak (Rust) via Commonmarker
75
- - [markly-merge][markly-merge] - Uses libcmark-gfm (C) via Markly
76
-
77
- ### Key Features
78
-
79
- - **Multiple Backends**: Supports Commonmarker and Markly through tree\_haver's unified API
80
- - **Type Normalization**: Canonical node types (`:heading`, `:paragraph`, etc.) work across all backends
81
- - **Extensible**: Register custom backends via `NodeTypeNormalizer.register_backend`
82
- - **Structure-Aware**: Understands headings, paragraphs, lists, code blocks, tables, and other block elements
83
- - **Freeze Block Support**: Respects freeze markers (default: `markdown-merge:freeze` / `markdown-merge:unfreeze`) for template merge control - customizable to match your project's conventions
84
- - **Inner-Merge Code Blocks**: Optionally merge fenced code blocks using language-specific mergers (Ruby → prism-merge, YAML → psych-merge, JSON → json-merge, TOML → toml-merge)
85
- - **Table Match Refiner**: Fuzzy matching algorithm for tables with similar but not identical headers
86
- - **Full Provenance**: Tracks origin of every node
87
- - **Customizable**:
88
- - `backend` - select `:commonmarker`, `:markly`, or `:auto`
89
- - `signature_generator` - callable custom signature generators
90
- - `preference` - setting of `:template`, `:destination`, or a Hash for per-node-type preferences
91
- - `add_template_only_nodes` - setting to retain sections that do not exist in destination
92
- - `freeze_token` - customize freeze block markers (default: `"markdown-merge"`)
93
- - `inner_merge_code_blocks` - enable language-aware code block merging
94
- - `match_refiner` - fuzzy matching for unmatched nodes (e.g., `TableMatchRefiner`)
95
-
96
- ### Supported Node Types
97
-
98
- Signatures computed by default for common Markdown block elements:
99
-
100
- | Node Type | Signature Format | Matching Behavior |
101
- |---------------------|-----------------------------------------|-----------------------------------------------------|
102
- | Heading | `[:heading, level, text]` | Headings match by level and text content |
103
- | Paragraph | `[:paragraph, content_hash]` | Paragraphs match by content hash |
104
- | List | `[:list, type, item_count]` | Lists match by type (bullet/ordered) and item count |
105
- | Code Block | `[:code_block, language, content_hash]` | Code blocks match by language and content |
106
- | Block Quote | `[:blockquote, content_hash]` | Block quotes match by content hash |
107
- | Table | `[:table, row_count, header_hash]` | Tables match by structure and header content |
108
- | HTML Block | `[:html, content_hash]` | HTML blocks match by content hash |
109
- | Thematic Break | `[:hrule]` | Horizontal rules always match |
110
- | Footnote Definition | `[:footnote_definition, label]` | Footnotes match by label/name |
111
-
112
- ### The `*-merge` Gem Family
113
-
114
- The `*-merge` gem family provides intelligent, AST-based merging for various file formats. At the foundation is [tree_haver][tree_haver], which provides a unified cross-Ruby parsing API that works seamlessly across MRI, JRuby, and TruffleRuby.
115
-
116
- | Gem | Version / CI | Language<br>/ Format | Parser Backend(s) | Description |
117
- |------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------:|----------------------|-------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
118
- | [tree_haver][tree_haver] | [![Version][tree_haver-gem-i]][tree_haver-gem] <br/> [![CI][tree_haver-ci-i]][tree_haver-ci] | Multi | Supported Backends: MRI C, Rust, FFI, Java, Prism, Psych, Commonmarker, Markly, Citrus, Parslet | **Foundation**: Cross-Ruby adapter for parsing libraries (like Faraday for HTTP) |
119
- | [ast-merge][ast-merge] | [![Version][ast-merge-gem-i]][ast-merge-gem] <br/> [![CI][ast-merge-ci-i]][ast-merge-ci] | Text | internal | **Infrastructure**: Shared base classes and merge logic for all `*-merge` gems |
120
- | [bash-merge][bash-merge] | [![Version][bash-merge-gem-i]][bash-merge-gem] <br/> [![CI][bash-merge-ci-i]][bash-merge-ci] | Bash | [tree-sitter-bash][ts-bash] (via tree_haver) | Smart merge for Bash scripts |
121
- | [commonmarker-merge][commonmarker-merge] | [![Version][commonmarker-merge-gem-i]][commonmarker-merge-gem] <br/> [![CI][commonmarker-merge-ci-i]][commonmarker-merge-ci] | Markdown | [Commonmarker][commonmarker] (via tree_haver) | Smart merge for Markdown (CommonMark via comrak Rust) |
122
- | [dotenv-merge][dotenv-merge] | [![Version][dotenv-merge-gem-i]][dotenv-merge-gem] <br/> [![CI][dotenv-merge-ci-i]][dotenv-merge-ci] | Dotenv | internal | Smart merge for `.env` files |
123
- | [json-merge][json-merge] | [![Version][json-merge-gem-i]][json-merge-gem] <br/> [![CI][json-merge-ci-i]][json-merge-ci] | JSON | [tree-sitter-json][ts-json] (via tree_haver) | Smart merge for JSON files |
124
- | [jsonc-merge][jsonc-merge] | [![Version][jsonc-merge-gem-i]][jsonc-merge-gem] <br/> [![CI][jsonc-merge-ci-i]][jsonc-merge-ci] | JSONC | [tree-sitter-jsonc][ts-jsonc] (via tree_haver) | ⚠️ Proof of concept; Smart merge for JSON with Comments |
125
- | [markdown-merge][markdown-merge] | [![Version][markdown-merge-gem-i]][markdown-merge-gem] <br/> [![CI][markdown-merge-ci-i]][markdown-merge-ci] | Markdown | [Commonmarker][commonmarker] / [Markly][markly] (via tree_haver), [Parslet][parslet] | **Foundation**: Shared base for Markdown mergers with inner code block merging |
126
- | [markly-merge][markly-merge] | [![Version][markly-merge-gem-i]][markly-merge-gem] <br/> [![CI][markly-merge-ci-i]][markly-merge-ci] | Markdown | [Markly][markly] (via tree_haver) | Smart merge for Markdown (CommonMark via cmark-gfm C) |
127
- | [prism-merge][prism-merge] | [![Version][prism-merge-gem-i]][prism-merge-gem] <br/> [![CI][prism-merge-ci-i]][prism-merge-ci] | Ruby | [Prism][prism] (`prism` std lib gem) | Smart merge for Ruby source files |
128
- | [psych-merge][psych-merge] | [![Version][psych-merge-gem-i]][psych-merge-gem] <br/> [![CI][psych-merge-ci-i]][psych-merge-ci] | YAML | [Psych][psych] (`psych` std lib gem) | Smart merge for YAML files |
129
- | [rbs-merge][rbs-merge] | [![Version][rbs-merge-gem-i]][rbs-merge-gem] <br/> [![CI][rbs-merge-ci-i]][rbs-merge-ci] | RBS | [tree-sitter-bash][ts-rbs] (via tree_haver), [RBS][rbs] (`rbs` std lib gem) | Smart merge for Ruby type signatures |
130
- | [toml-merge][toml-merge] | [![Version][toml-merge-gem-i]][toml-merge-gem] <br/> [![CI][toml-merge-ci-i]][toml-merge-ci] | TOML | [Parslet + toml][toml], [Citrus + toml-rb][toml-rb], [tree-sitter-toml][ts-toml] (all via tree_haver) | Smart merge for TOML files |
131
-
132
- #### Backend Platform Compatibility
133
-
134
- tree_haver supports multiple parsing backends, but not all backends work on all Ruby platforms:
135
-
136
- | Platform 👉️<br> TreeHaver Backend 👇️ | MRI | JRuby | TruffleRuby | Notes |
137
- |-------------------------------------------------|:---:|:-----:|:-----------:|----------------------------------------------------------------------------|
138
- | **MRI** ([ruby_tree_sitter][ruby_tree_sitter]) | ✅ | ❌ | ❌ | C extension, MRI only |
139
- | **Rust** ([tree_stump][tree_stump]) | ✅ | ❌ | ❌ | Rust extension via magnus/rb-sys, MRI only |
140
- | **FFI** ([ffi][ffi]) | ✅ | ✅ | ❌ | TruffleRuby's FFI doesn't support `STRUCT_BY_VALUE` |
141
- | **Java** ([jtreesitter][jtreesitter]) | ❌ | ✅ | ❌ | JRuby only, requires grammar JARs |
142
- | **Prism** ([prism][prism]) | ✅ | ✅ | ✅ | Ruby parsing, stdlib in Ruby 3.4+ |
143
- | **Psych** ([psych][psych]) | ✅ | ✅ | ✅ | YAML parsing, stdlib |
144
- | **Citrus** ([citrus][citrus]) | ✅ | ✅ | ✅ | Pure Ruby PEG parser, no native dependencies |
145
- | **Parslet** ([parslet][parslet]) | ✅ | ✅ | ✅ | Pure Ruby PEG parser, no native dependencies |
146
- | **Commonmarker** ([commonmarker][commonmarker]) | ✅ | ❌ | ❓ | Rust extension for Markdown (via [commonmarker-merge][commonmarker-merge]) |
147
- | **Markly** ([markly][markly]) | ✅ | ❌ | ❓ | C extension for Markdown (via [markly-merge][markly-merge]) |
148
-
149
- **Legend**: ✅ = Works, ❌ = Does not work, ❓ = Untested
150
-
151
- **Why some backends don't work on certain platforms**:
152
-
153
- - **JRuby**: Runs on the JVM; cannot load native C/Rust extensions (`.so` files)
154
- - **TruffleRuby**: Has C API emulation via Sulong/LLVM, but it doesn't expose all MRI internals that native extensions require (e.g., `RBasic.flags`, `rb_gc_writebarrier`)
155
- - **FFI on TruffleRuby**: TruffleRuby's FFI implementation doesn't support returning structs by value, which tree-sitter's C API requires
156
-
157
- **Example implementations** for the gem templating use case:
158
-
159
- | Gem | Purpose | Description |
160
- |--------------------------|-----------------|-----------------------------------------------|
161
- | [kettle-dev][kettle-dev] | Gem Development | Gem templating tool using `*-merge` gems |
162
- | [kettle-jem][kettle-jem] | Gem Templating | Gem template library with smart merge support |
163
-
164
- [tree_haver]: https://github.com/kettle-rb/tree_haver
165
- [ast-merge]: https://github.com/kettle-rb/ast-merge
166
- [prism-merge]: https://github.com/kettle-rb/prism-merge
167
- [psych-merge]: https://github.com/kettle-rb/psych-merge
168
- [json-merge]: https://github.com/kettle-rb/json-merge
169
- [jsonc-merge]: https://github.com/kettle-rb/jsonc-merge
170
- [bash-merge]: https://github.com/kettle-rb/bash-merge
171
- [rbs-merge]: https://github.com/kettle-rb/rbs-merge
172
- [dotenv-merge]: https://github.com/kettle-rb/dotenv-merge
173
- [toml-merge]: https://github.com/kettle-rb/toml-merge
174
- [markdown-merge]: https://github.com/kettle-rb/markdown-merge
175
- [markly-merge]: https://github.com/kettle-rb/markly-merge
176
- [commonmarker-merge]: https://github.com/kettle-rb/commonmarker-merge
177
- [kettle-dev]: https://github.com/kettle-rb/kettle-dev
178
- [kettle-jem]: https://github.com/kettle-rb/kettle-jem
179
- [tree_haver-gem]: https://bestgems.org/gems/tree_haver
180
- [ast-merge-gem]: https://bestgems.org/gems/ast-merge
181
- [prism-merge-gem]: https://bestgems.org/gems/prism-merge
182
- [psych-merge-gem]: https://bestgems.org/gems/psych-merge
183
- [json-merge-gem]: https://bestgems.org/gems/json-merge
184
- [jsonc-merge-gem]: https://bestgems.org/gems/jsonc-merge
185
- [bash-merge-gem]: https://bestgems.org/gems/bash-merge
186
- [rbs-merge-gem]: https://bestgems.org/gems/rbs-merge
187
- [dotenv-merge-gem]: https://bestgems.org/gems/dotenv-merge
188
- [toml-merge-gem]: https://bestgems.org/gems/toml-merge
189
- [markdown-merge-gem]: https://bestgems.org/gems/markdown-merge
190
- [markly-merge-gem]: https://bestgems.org/gems/markly-merge
191
- [commonmarker-merge-gem]: https://bestgems.org/gems/commonmarker-merge
192
- [kettle-dev-gem]: https://bestgems.org/gems/kettle-dev
193
- [kettle-jem-gem]: https://bestgems.org/gems/kettle-jem
194
- [tree_haver-gem-i]: https://img.shields.io/gem/v/tree_haver.svg
195
- [ast-merge-gem-i]: https://img.shields.io/gem/v/ast-merge.svg
196
- [prism-merge-gem-i]: https://img.shields.io/gem/v/prism-merge.svg
197
- [psych-merge-gem-i]: https://img.shields.io/gem/v/psych-merge.svg
198
- [json-merge-gem-i]: https://img.shields.io/gem/v/json-merge.svg
199
- [jsonc-merge-gem-i]: https://img.shields.io/gem/v/jsonc-merge.svg
200
- [bash-merge-gem-i]: https://img.shields.io/gem/v/bash-merge.svg
201
- [rbs-merge-gem-i]: https://img.shields.io/gem/v/rbs-merge.svg
202
- [dotenv-merge-gem-i]: https://img.shields.io/gem/v/dotenv-merge.svg
203
- [toml-merge-gem-i]: https://img.shields.io/gem/v/toml-merge.svg
204
- [markdown-merge-gem-i]: https://img.shields.io/gem/v/markdown-merge.svg
205
- [markly-merge-gem-i]: https://img.shields.io/gem/v/markly-merge.svg
206
- [commonmarker-merge-gem-i]: https://img.shields.io/gem/v/commonmarker-merge.svg
207
- [kettle-dev-gem-i]: https://img.shields.io/gem/v/kettle-dev.svg
208
- [kettle-jem-gem-i]: https://img.shields.io/gem/v/kettle-jem.svg
209
- [tree_haver-ci-i]: https://github.com/kettle-rb/tree_haver/actions/workflows/current.yml/badge.svg
210
- [ast-merge-ci-i]: https://github.com/kettle-rb/ast-merge/actions/workflows/current.yml/badge.svg
211
- [prism-merge-ci-i]: https://github.com/kettle-rb/prism-merge/actions/workflows/current.yml/badge.svg
212
- [psych-merge-ci-i]: https://github.com/kettle-rb/psych-merge/actions/workflows/current.yml/badge.svg
213
- [json-merge-ci-i]: https://github.com/kettle-rb/json-merge/actions/workflows/current.yml/badge.svg
214
- [jsonc-merge-ci-i]: https://github.com/kettle-rb/jsonc-merge/actions/workflows/current.yml/badge.svg
215
- [bash-merge-ci-i]: https://github.com/kettle-rb/bash-merge/actions/workflows/current.yml/badge.svg
216
- [rbs-merge-ci-i]: https://github.com/kettle-rb/rbs-merge/actions/workflows/current.yml/badge.svg
217
- [dotenv-merge-ci-i]: https://github.com/kettle-rb/dotenv-merge/actions/workflows/current.yml/badge.svg
218
- [toml-merge-ci-i]: https://github.com/kettle-rb/toml-merge/actions/workflows/current.yml/badge.svg
219
- [markdown-merge-ci-i]: https://github.com/kettle-rb/markdown-merge/actions/workflows/current.yml/badge.svg
220
- [markly-merge-ci-i]: https://github.com/kettle-rb/markly-merge/actions/workflows/current.yml/badge.svg
221
- [commonmarker-merge-ci-i]: https://github.com/kettle-rb/commonmarker-merge/actions/workflows/current.yml/badge.svg
222
- [kettle-dev-ci-i]: https://github.com/kettle-rb/kettle-dev/actions/workflows/current.yml/badge.svg
223
- [kettle-jem-ci-i]: https://github.com/kettle-rb/kettle-jem/actions/workflows/current.yml/badge.svg
224
- [tree_haver-ci]: https://github.com/kettle-rb/tree_haver/actions/workflows/current.yml
225
- [ast-merge-ci]: https://github.com/kettle-rb/ast-merge/actions/workflows/current.yml
226
- [prism-merge-ci]: https://github.com/kettle-rb/prism-merge/actions/workflows/current.yml
227
- [psych-merge-ci]: https://github.com/kettle-rb/psych-merge/actions/workflows/current.yml
228
- [json-merge-ci]: https://github.com/kettle-rb/json-merge/actions/workflows/current.yml
229
- [jsonc-merge-ci]: https://github.com/kettle-rb/jsonc-merge/actions/workflows/current.yml
230
- [bash-merge-ci]: https://github.com/kettle-rb/bash-merge/actions/workflows/current.yml
231
- [rbs-merge-ci]: https://github.com/kettle-rb/rbs-merge/actions/workflows/current.yml
232
- [dotenv-merge-ci]: https://github.com/kettle-rb/dotenv-merge/actions/workflows/current.yml
233
- [toml-merge-ci]: https://github.com/kettle-rb/toml-merge/actions/workflows/current.yml
234
- [markdown-merge-ci]: https://github.com/kettle-rb/markdown-merge/actions/workflows/current.yml
235
- [markly-merge-ci]: https://github.com/kettle-rb/markly-merge/actions/workflows/current.yml
236
- [commonmarker-merge-ci]: https://github.com/kettle-rb/commonmarker-merge/actions/workflows/current.yml
237
- [kettle-dev-ci]: https://github.com/kettle-rb/kettle-dev/actions/workflows/current.yml
238
- [kettle-jem-ci]: https://github.com/kettle-rb/kettle-jem/actions/workflows/current.yml
239
- [prism]: https://github.com/ruby/prism
240
- [psych]: https://github.com/ruby/psych
241
- [ffi]: https://github.com/ffi/ffi
242
- [ts-json]: https://github.com/tree-sitter/tree-sitter-json
243
- [ts-jsonc]: https://gitlab.com/WhyNotHugo/tree-sitter-jsonc
244
- [ts-bash]: https://github.com/tree-sitter/tree-sitter-bash
245
- [ts-rbs]: https://github.com/joker1007/tree-sitter-rbs
246
- [ts-toml]: https://github.com/tree-sitter-grammars/tree-sitter-toml
247
- [dotenv]: https://github.com/bkeepers/dotenv
248
- [rbs]: https://github.com/ruby/rbs
249
- [toml-rb]: https://github.com/emancu/toml-rb
250
- [toml]: https://github.com/jm/toml
251
- [markly]: https://github.com/ioquatix/markly
252
- [commonmarker]: https://github.com/gjtorikian/commonmarker
253
- [ruby_tree_sitter]: https://github.com/Faveod/ruby-tree-sitter
254
- [tree_stump]: https://github.com/joker1007/tree_stump
255
- [jtreesitter]: https://central.sonatype.com/artifact/io.github.tree-sitter/jtreesitter
256
- [citrus]: https://github.com/mjackson/citrus
257
- [parslet]: https://github.com/kschiess/parslet
258
-
259
- ## 💡 Info you can shake a stick at
260
-
261
- | Tokens to Remember | [![Gem name][⛳️name-img]][👽dl-rank] [![Gem namespace][⛳️namespace-img]][📜src-gh] |
262
- | --- | --- |
263
- | Works with JRuby | [![JRuby 10.0 Compat][💎jruby-c-i]][🚎11-c-wf] [![JRuby HEAD Compat][💎jruby-headi]][🚎3-hd-wf] |
264
- | Works with Truffle Ruby | [![Truffle Ruby 23.1 Compat][💎truby-23.1i]][🚎9-t-wf] [![Truffle Ruby 24.1 Compat][💎truby-c-i]][🚎11-c-wf] |
265
- | Works with MRI Ruby 3 | [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎6-s-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎6-s-wf] [![Ruby 3.4 Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf] |
266
- | Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]][✉️ruby-friends] [![Live Chat on Discord][✉️discord-invite-img-ftb]][🖼️galtzo-discord] [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork] [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor] |
267
- | Source | [![Source on GitLab.com][📜src-gl-img]][📜src-gl] [![Source on CodeBerg.org][📜src-cb-img]][📜src-cb] [![Source on Github.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ\!][🧮kloc-img]][🧮kloc] |
268
- | Documentation | [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![YARD on Galtzo.com][📜docs-head-rd-img]][🚎yard-head] [![Maintainer Blog][🚂maint-blog-img]][🚂maint-blog] [![GitLab Wiki][📜gl-wiki-img]][📜gl-wiki] [![GitHub Wiki][📜gh-wiki-img]][📜gh-wiki] |
269
- | Compliance | [![License: MIT][📄license-img]][📄license-ref] [![Compatible with Apache Software Projects: Verified by SkyWalking Eyes][📄license-compat-img]][📄license-compat] [![📄ilo-declaration-img][📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] |
270
- | Style | [![Enforced Code Style Linter][💎rlts-img]][💎rlts] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji] [![Compatibility appraised by: appraisal2][💎appraisal2-img]][💎appraisal2] |
271
- | Maintainer 🎖️ | [![Follow Me on LinkedIn][💖🖇linkedin-img]][💖🖇linkedin] [![Follow Me on Ruby.Social][💖🐘ruby-mast-img]][💖🐘ruby-mast] [![Follow Me on Bluesky][💖🦋bluesky-img]][💖🦋bluesky] [![Contact Maintainer][🚂maint-contact-img]][🚂maint-contact] [![My technical writing][💖💁🏼‍♂️devto-img]][💖💁🏼‍♂️devto] |
272
- | `...` 💖 | [![Find Me on WellFound:][💖✌️wellfound-img]][💖✌️wellfound] [![Find Me on CrunchBase][💖💲crunchbase-img]][💖💲crunchbase] [![My LinkTree][💖🌳linktree-img]][💖🌳linktree] [![More About Me][💖💁🏼‍♂️aboutme-img]][💖💁🏼‍♂️aboutme] [🧊][💖🧊berg] [🐙][💖🐙hub] [🛖][💖🛖hut] [🧪][💖🧪lab] |
273
-
274
- ### Compatibility
275
-
276
- Compatible with MRI Ruby 3.2.0+, and concordant releases of JRuby, and TruffleRuby.
277
-
278
- | 🚚 *Amazing* test matrix was brought to you by | 🔎 appraisal2 🔎 and the color 💚 green 💚 |
279
- | --- | --- |
280
- | 👟 Check it out\! | ✨ [github.com/appraisal-rb/appraisal2][💎appraisal2] ✨ |
281
-
282
- ### Federated DVCS
283
-
284
- <details markdown="1">
285
- <summary>Find this repo on federated forges (Coming soon!)</summary>
286
-
287
- | Federated [DVCS][💎d-in-dvcs] Repository | Status | Issues | PRs | Wiki | CI | Discussions |
288
- | --- | --- | --- | --- | --- | --- | --- |
289
- | 🧪 [kettle-rb/markdown-merge on GitLab][📜src-gl] | The Truth | [💚][🤝gl-issues] | [💚][🤝gl-pulls] | [💚][📜gl-wiki] | 🐭 Tiny Matrix | ➖ |
290
- | 🧊 [kettle-rb/markdown-merge on CodeBerg][📜src-cb] | An Ethical Mirror ([Donate][🤝cb-donate]) | [💚][🤝cb-issues] | [💚][🤝cb-pulls] | ➖ | ⭕️ No Matrix | ➖ |
291
- | 🐙 [kettle-rb/markdown-merge on GitHub][📜src-gh] | Another Mirror | [💚][🤝gh-issues] | [💚][🤝gh-pulls] | [💚][📜gh-wiki] | 💯 Full Matrix | [💚][gh-discussions] |
292
- | 🎮️ [Discord Server][🖼️galtzo-discord] | [![Live Chat on Discord][✉️discord-invite-img-ftb]][🖼️galtzo-discord] | [Let's][🖼️galtzo-discord] | [talk][🖼️galtzo-discord] | [about][🖼️galtzo-discord] | [this][🖼️galtzo-discord] | [library\!][🖼️galtzo-discord] |
293
-
294
- </details>
295
-
296
- [gh-discussions]: https://github.com/kettle-rb/markdown-merge/discussions
297
-
298
- ### Enterprise Support [![Tidelift](https://tidelift.com/badges/package/rubygems/markdown-merge)][🏙️entsup-tidelift]
299
-
300
- Available as part of the Tidelift Subscription.
301
-
302
- <details markdown="1">
303
- <summary>Need enterprise-level guarantees?</summary>
304
-
305
- The maintainers of this and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use.
306
-
307
- [![Get help from me on Tidelift][🏙️entsup-tidelift-img]][🏙️entsup-tidelift]
308
-
309
- - 💡Subscribe for support guarantees covering *all* your FLOSS dependencies
310
- - 💡Tidelift is part of [Sonar][🏙️entsup-tidelift-sonar]
311
- - 💡Tidelift pays maintainers to maintain the software you depend on\!<br/>📊`@`Pointy Haired Boss: An [enterprise support][🏙️entsup-tidelift] subscription is "[never gonna let you down][🧮kloc]", and *supports* open source maintainers
312
- Alternatively:
313
- - [![Live Chat on Discord][✉️discord-invite-img-ftb]][🖼️galtzo-discord]
314
- - [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork]
315
- - [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor]
316
-
317
- </details>
318
-
319
- ## ✨ Installation
320
-
321
- Install the gem and add to the application's Gemfile by executing:
322
-
323
- ```console
324
- bundle add markdown-merge
325
- ```
326
-
327
- If bundler is not being used to manage dependencies, install the gem by executing:
328
-
329
- ```console
330
- gem install markdown-merge
331
- ```
332
-
333
- ### 🔒 Secure Installation
334
-
335
- <details markdown="1">
336
- <summary>For Medium or High Security Installations</summary>
337
-
338
- This gem is cryptographically signed, and has verifiable [SHA-256 and SHA-512][💎SHA_checksums] checksums by
339
- [stone\_checksums][💎stone_checksums]. Be sure the gem you install hasn’t been tampered with
340
- by following the instructions below.
341
-
342
- Add my public key (if you haven’t already, expires 2045-04-29) as a trusted certificate:
343
-
344
- ```console
345
- gem cert --add <(curl -Ls https://raw.github.com/galtzo-floss/certs/main/pboling.pem)
346
- ```
347
-
348
- You only need to do that once. Then proceed to install with:
349
-
350
- ```console
351
- gem install markdown-merge -P HighSecurity
352
- ```
353
-
354
- The `HighSecurity` trust profile will verify signed gems, and not allow the installation of unsigned dependencies.
355
-
356
- If you want to up your security game full-time:
357
-
358
- ```console
359
- bundle config set --global trust-policy MediumSecurity
360
- ```
361
-
362
- `MediumSecurity` instead of `HighSecurity` is necessary if not all the gems you use are signed.
363
-
364
- NOTE: Be prepared to track down certs for signed gems and add them the same way you added mine.
365
-
366
- </details>
367
-
368
- ## ⚙️ Configuration
369
-
370
- ### SmartMerger Configuration
371
-
372
- The `SmartMerger` class is the main entry point for merging Markdown files:
373
-
374
- ```ruby
375
- require "markdown/merge"
376
-
377
- merger = Markdown::Merge::SmartMerger.new(
378
- template_content,
379
- dest_content,
380
-
381
- # Backend selection (default: :auto)
382
- # :auto - auto-detect available backend (tries commonmarker first, then markly)
383
- # :commonmarker - use Commonmarker (comrak Rust parser)
384
- # :markly - use Markly (cmark-gfm C library)
385
- backend: :auto,
386
-
387
- # Which version to prefer when nodes match but differ
388
- # :destination (default) - keep destination content (preserves customizations)
389
- # :template - use template content (applies updates)
390
- preference: :destination,
391
-
392
- # Whether to add template-only nodes to the result
393
- # false (default) - only include sections that exist in destination
394
- # true - include all template sections
395
- add_template_only_nodes: false,
396
-
397
- # Token for freeze block markers
398
- # Default: "markdown-merge"
399
- # Looks for: <!-- markdown-merge:freeze --> / <!-- markdown-merge:unfreeze -->
400
- freeze_token: "markdown-merge",
401
-
402
- # Enable inner-merge for fenced code blocks
403
- # false (default) - use standard conflict resolution for code blocks
404
- # true - merge code block contents using language-specific mergers
405
- # CodeBlockMerger instance - use custom CodeBlockMerger
406
- inner_merge_code_blocks: false,
407
-
408
- # Match refiner for fuzzy matching of unmatched nodes
409
- # nil (default) - exact matching only
410
- # TableMatchRefiner.new - enable fuzzy table matching
411
- match_refiner: nil,
412
-
413
- # Custom signature generator (optional)
414
- # Receives a node (wrapped with canonical merge_type), returns a signature array or nil
415
- # Return the node itself to fall through to default signature
416
- signature_generator: nil,
417
-
418
- # Backend-specific options (passed through to parser)
419
- # For commonmarker: options: {}
420
- # For markly: flags: Markly::DEFAULT, extensions: [:table]
421
- )
422
- ```
423
-
424
- ### Text Matching Behavior
425
-
426
- **Important**: When matching nodes by text content (such as for anchor patterns in
427
- `PartialTemplateMerger`), the `.text` method returns **plain text without markdown formatting**.
428
-
429
- This means:
430
-
431
- - Markdown: `` ### The `*-merge` Gem Family ``
432
- - `.text` returns: `"The *-merge Gem Family\n"`
433
-
434
- The backticks around `*-merge` are stripped because they are inline formatting, not content.
435
- This is true for both Commonmarker and Markly backends.
436
-
437
- **Anchor pattern examples**:
438
-
439
- ```ruby
440
- # ❌ WRONG - backticks are stripped, so this won't match
441
- anchor: { type: :heading, text: /`\*-merge` Gem Family/ }
442
-
443
- # ✅ CORRECT - match the plain text content
444
- anchor: { type: :heading, text: /\*-merge.*Gem Family/ }
445
-
446
- # ✅ CORRECT - use beginning anchor for exact heading match
447
- anchor: { type: :heading, text: /^The \*-merge Gem Family/ }
448
- ```
449
-
450
- **Other markdown formatting that is stripped from `.text`**:
451
-
452
- - Bold: `**text**` → `text`
453
- - Italic: `*text*` or `_text_` → `text`
454
- - Code: `` `code` `` → `code`
455
- - Links: `[text](url)` → `text`
456
- - Images: `![alt](src)` → `alt`
457
-
458
- **Note**: Different parsers may have other idiosyncrasies. For example:
459
-
460
- - Trailing newlines may or may not be present
461
- - Whitespace normalization may differ
462
- - Entity encoding may vary
463
-
464
- Always test your patterns against actual parsed content when building merge recipes.
465
-
466
- ### Node Type Normalization
467
-
468
- markdown-merge normalizes node types across backends so merge rules are portable:
469
-
470
- ```ruby
471
- # These are equivalent regardless of backend
472
- # Markly's :header becomes :heading
473
- # Markly's :hrule becomes :thematic_break
474
- # etc.
475
-
476
- # Register a custom backend's type mappings
477
- Markdown::Merge::NodeTypeNormalizer.register_backend(:my_parser, {
478
- h1: :heading,
479
- h2: :heading,
480
- para: :paragraph,
481
- # ...
482
- })
483
- ```
484
-
485
- ### Parser-Specific Wrappers
486
-
487
- For convenience, parser-specific wrappers provide backend-specific defaults:
488
-
489
- ```ruby
490
- # commonmarker-merge (freeze_token: "commonmarker-merge", inner_merge_code_blocks: false)
491
- require "commonmarker/merge"
492
- merger = Commonmarker::Merge::SmartMerger.new(template, dest, options: {})
493
-
494
- # markly-merge (freeze_token: "markly-merge", inner_merge_code_blocks: true)
495
- require "markly/merge"
496
- merger = Markly::Merge::SmartMerger.new(template, dest, flags: Markly::DEFAULT, extensions: [:table])
497
- ```
498
-
499
- ````
500
- ### Freeze Blocks
501
-
502
- Freeze blocks protect sections from being modified during merges. They are marked
503
- with HTML comments that are invisible when the Markdown is rendered:
504
-
505
- ```markdown
506
- <!-- markdown-merge:freeze -->
507
- ## This Section Is Protected
508
-
509
- Any content here will be preserved exactly as-is during merges.
510
- The merge tool will not modify, replace, or remove this content.
511
-
512
- <!-- markdown-merge:unfreeze -->
513
- ````
514
-
515
- Add an optional frozen reason to document why:
516
-
517
- ```markdown
518
- <!-- markdown-merge:freeze Custom table - manually maintained -->
519
- | Feature | Status |
520
- |---------|--------|
521
- | Custom | ✅ |
522
- <!-- markdown-merge:unfreeze -->
523
- ```
524
-
525
- ### Inner-Merge Code Blocks
526
-
527
- When enabled, fenced code blocks are merged using language-specific `*-merge` gems:
528
-
529
- ```ruby
530
- merger = SomeParser::Merge::SmartMerger.new(
531
- template,
532
- destination,
533
- inner_merge_code_blocks: true,
534
- )
535
- ```
536
-
537
- Supported languages and their mergers:
538
-
539
- | Language | Fence Info | Merger |
540
- | --- | --- | --- |
541
- | Ruby | `ruby`, `rb` | prism-merge |
542
- | YAML | `yaml`, `yml` | psych-merge |
543
- | JSON | `json` | json-merge |
544
- | TOML | `toml` | toml-merge |
545
-
546
- Example with a Ruby code block:
547
-
548
- ````markdown
549
- ```ruby
550
- # Template
551
- class MyClass
552
- def new_method
553
- puts "from template"
554
- end
555
- end
556
- ```
557
- ````
558
-
559
- When merged(with:
560
-
561
- ````markdown
562
- ```ruby
563
- # Destination
564
- class MyClass
565
- def existing_method
566
- puts "custom"
567
- end
568
- end)
569
- ```
570
- ````
571
-
572
- Result (with `inner_merge_code_blocks: true`):
573
-
574
- ````markdown
575
- ```ruby
576
- class MyClass
577
- def existing_method
578
- puts "custom"
579
- end
580
-
581
- def new_method
582
- puts "from template"
583
- end
584
- end
585
- ```
586
- ````
587
-
588
- ### Table Match Refiner
589
-
590
- When tables don't match by exact signature, the `TableMatchRefiner` uses
591
- fuzzy matching to pair tables with similar structure:
592
-
593
- ```ruby
594
- refiner = Markdown::Merge::TableMatchRefiner.new(
595
- threshold: 0.5, # Minimum similarity (0.0-1.0)
596
- algorithm_options: {
597
- weights: {
598
- header_match: 0.25, # Header cell similarity
599
- first_column: 0.20, # Row label similarity
600
- row_content: 0.25, # Row content overlap
601
- total_cells: 0.15, # Overall cell matching
602
- position: 0.15, # Position distance
603
- },
604
- },
605
- )
606
-
607
- merger = SomeParser::Merge::SmartMerger.new(
608
- template,
609
- destination,
610
- match_refiner: refiner,
611
- )
612
- ```
613
-
614
- ### Debug Logging
615
-
616
- Enable debug logging to see merge decisions:
617
-
618
- ```bash
619
- export MARKDOWN_MERGE_DEBUG=1
620
- ```
621
-
622
- ## 🔧 Basic Usage
623
-
624
- **Note:** This gem provides base classes for implementers. End users should use
625
- [commonmarker-merge][commonmarker-merge] or
626
- [markly-merge][markly-merge] instead.
627
-
628
- ### For End Users
629
-
630
- Use a parser-specific implementation:
631
-
632
- ```ruby
633
- # Option 1: Using commonmarker-merge (Comrak/Rust)
634
- require "commonmarker/merge"
635
-
636
- template = File.read("template.md")
637
- destination = File.read("destination.md")
638
-
639
- merger = Commonmarker::Merge::SmartMerger.new(template, destination)
640
- result = merger.merge
641
-
642
- File.write("merged.md", result.content)
643
- ```
644
-
645
- ```ruby
646
- # Option 2: Using markly-merge (libcmark-gfm/C)
647
- require "markly/merge"
648
-
649
- template = File.read("template.md")
650
- destination = File.read("destination.md")
651
-
652
- merger = Markly::Merge::SmartMerger.new(template, destination)
653
- result = merger.merge
654
-
655
- File.write("merged.md", result.to_markdown)
656
- ```
657
-
658
- ### For Implementers
659
-
660
- Creating a new parser-specific implementation:
661
-
662
- ```ruby
663
- require "markdown/merge"
664
-
665
- module MyParser
666
- module Merge
667
- class FileAnalysis < Markdown::Merge::FileAnalysisBase
668
- def parse_document(source)
669
- # Parse source and return root document node
670
- MyParser.parse(source)
671
- end
672
-
673
- def next_sibling(node)
674
- # Return the next sibling of a node
675
- node.next_sibling
676
- end
677
-
678
- def compute_parser_signature(node)
679
- # Compute signature for parser-specific nodes
680
- # Or call super for default implementation
681
- super
682
- end
683
- end
684
-
685
- class SmartMerger < Markdown::Merge::SmartMergerBase
686
- def create_file_analysis(content, **options)
687
- FileAnalysis.new(content, **options)
688
- end
689
-
690
- def node_to_source(node, analysis)
691
- case node
692
- when Markdown::Merge::FreezeNode
693
- node.full_text
694
- else
695
- # Convert node back to source text
696
- node.to_markdown
697
- end
698
- end
699
- end
700
- end
701
- end
702
- ```
703
-
704
- ### Freeze Block Protection
705
-
706
- Both implementations support freeze blocks for protecting customized sections:
707
-
708
- ```markdown
709
- # My Project
710
-
711
- ## Installation
712
-
713
- <!-- markdown-merge:freeze Custom install instructions -->
714
- This installation section has been customized and will be preserved
715
- during template merges, regardless of what the template contains.
716
- <!-- markdown-merge:unfreeze -->
717
-
718
- ## Usage
719
-
720
- Standard usage section - can be updated from template.
721
- ```
722
-
723
- Content between freeze markers is always preserved from the destination file,
724
- even when the template has different content for that section.
725
-
726
- ## 🦷 FLOSS Funding
727
-
728
- While kettle-rb tools are free software and will always be, the project would benefit immensely from some funding.
729
- Raising a monthly budget of... "dollars" would make the project more sustainable.
730
-
731
- We welcome both individual and corporate sponsors\! We also offer a
732
- wide array of funding channels to account for your preferences
733
- (although currently [Open Collective][🖇osc] is our preferred funding platform).
734
-
735
- **If you're working in a company that's making significant use of kettle-rb tools we'd
736
- appreciate it if you suggest to your company to become a kettle-rb sponsor.**
737
-
738
- You can support the development of kettle-rb tools via
739
- [GitHub Sponsors][🖇sponsor],
740
- [Liberapay][⛳liberapay],
741
- [PayPal][🖇paypal],
742
- [Open Collective][🖇osc]
743
- and [Tidelift][🏙️entsup-tidelift].
744
-
745
- | 📍 NOTE |
746
- | --- |
747
- | If doing a sponsorship in the form of donation is problematic for your company <br/> from an accounting standpoint, we'd recommend the use of Tidelift, <br/> where you can get a support-like subscription instead. |
748
-
749
- ### Open Collective for Individuals
750
-
751
- Support us with a monthly donation and help us continue our activities. \[[Become a backer][🖇osc-backers]\]
752
-
753
- NOTE: [kettle-readme-backers][kettle-readme-backers] updates this list every day, automatically.
754
-
755
- <!-- OPENCOLLECTIVE-INDIVIDUALS:START -->
756
- No backers yet. Be the first!
757
- <!-- OPENCOLLECTIVE-INDIVIDUALS:END -->
758
-
759
- ### Open Collective for Organizations
760
-
761
- Become a sponsor and get your logo on our README on GitHub with a link to your site. \[[Become a sponsor][🖇osc-sponsors]\]
762
-
763
- NOTE: [kettle-readme-backers][kettle-readme-backers] updates this list every day, automatically.
764
-
765
- <!-- OPENCOLLECTIVE-ORGANIZATIONS:START -->
766
- No sponsors yet. Be the first!
767
- <!-- OPENCOLLECTIVE-ORGANIZATIONS:END -->
768
-
769
- [kettle-readme-backers]: https://github.com/kettle-rb/markdown-merge/blob/main/exe/kettle-readme-backers
770
-
771
- ### Another way to support open-source
772
-
773
- I’m driven by a passion to foster a thriving open-source community – a space where people can tackle complex problems, no matter how small. Revitalizing libraries that have fallen into disrepair, and building new libraries focused on solving real-world challenges, are my passions. I was recently affected by layoffs, and the tech jobs market is unwelcoming. I’m reaching out here because your support would significantly aid my efforts to provide for my family, and my farm (11 🐔 chickens, 2 🐶 dogs, 3 🐰 rabbits, 8 🐈‍ cats).
774
-
775
- If you work at a company that uses my work, please encourage them to support me as a corporate sponsor. My work on gems you use might show up in `bundle fund`.
776
-
777
- I’m developing a new library, [floss\_funding][🖇floss-funding-gem], designed to empower open-source developers like myself to get paid for the work we do, in a sustainable way. Please give it a look.
778
-
779
- **[Floss-Funding.dev][🖇floss-funding.dev]: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags**
780
-
781
- [![OpenCollective Backers][🖇osc-backers-i]][🖇osc-backers] [![OpenCollective Sponsors][🖇osc-sponsors-i]][🖇osc-sponsors] [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Donate on PayPal][🖇paypal-img]][🖇paypal] [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate on Polar][🖇polar-img]][🖇polar] [![Donate to my FLOSS efforts at ko-fi.com][🖇kofi-img]][🖇kofi] [![Donate to my FLOSS efforts using Patreon][🖇patreon-img]][🖇patreon]
782
-
783
- ## 🔐 Security
784
-
785
- See [SECURITY.md][🔐security].
786
-
787
- ## 🤝 Contributing
788
-
789
- If you need some ideas of where to help, you could work on adding more code coverage,
790
- or if it is already 💯 (see [below](#code-coverage)) check [reek](REEK), [issues][🤝gh-issues], or [PRs][🤝gh-pulls],
791
- or use the gem and think about how it could be better.
792
-
793
- We [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] so if you make changes, remember to update it.
794
-
795
- See [CONTRIBUTING.md][🤝contributing] for more detailed instructions.
796
-
797
- ### 🚀 Release Instructions
798
-
799
- See [CONTRIBUTING.md][🤝contributing].
800
-
801
- ### Code Coverage
802
-
803
- [![Coverage Graph][🏀codecov-g]][🏀codecov]
804
-
805
- [![Coveralls Test Coverage][🏀coveralls-img]][🏀coveralls]
806
-
807
- [![QLTY Test Coverage][🏀qlty-covi]][🏀qlty-cov]
808
-
809
- ### 🪇 Code of Conduct
810
-
811
- Everyone interacting with this project's codebases, issue trackers,
812
- chat rooms and mailing lists agrees to follow the [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct].
813
-
814
- ## 🌈 Contributors
815
-
816
- [![Contributors][🖐contributors-img]][🖐contributors]
817
-
818
- Made with [contributors-img][🖐contrib-rocks].
819
-
820
- Also see GitLab Contributors: <https://gitlab.com/kettle-rb/markdown-merge/-/graphs/main>
821
-
822
- <details>
823
- <summary>⭐️ Star History</summary>
824
-
825
- <a href="https://star-history.com/#kettle-rb/markdown-merge&Date">
826
- <picture>
827
- <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=kettle-rb/markdown-merge&type=Date&theme=dark" />
828
- <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=kettle-rb/markdown-merge&type=Date" />
829
- <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=kettle-rb/markdown-merge&type=Date" />
830
- </picture>
831
- </a>
832
-
833
- </details>
834
-
835
- ## 📌 Versioning
836
-
837
- This Library adheres to [![Semantic Versioning 2.0.0][📌semver-img]][📌semver].
838
- Violations of this scheme should be reported as bugs.
839
- Specifically, if a minor or patch version is released that breaks backward compatibility,
840
- a new version should be immediately released that restores compatibility.
841
- Breaking changes to the public API will only be introduced with new major versions.
842
-
843
- > dropping support for a platform is both obviously and objectively a breaking change <br/>
844
- > —Jordan Harband ([@ljharb](https://github.com/ljharb), maintainer of SemVer) [in SemVer issue 716][📌semver-breaking]
845
-
846
- I understand that policy doesn't work universally ("exceptions to every rule\!"),
847
- but it is the policy here.
848
- As such, in many cases it is good to specify a dependency on this library using
849
- the [Pessimistic Version Constraint][📌pvc] with two digits of precision.
850
-
851
- For example:
852
-
853
- ```ruby
854
- spec.add_dependency("markdown-merge", "~> 1.0")
855
- ```
856
-
857
- <details markdown="1">
858
- <summary>📌 Is "Platform Support" part of the public API? More details inside.</summary>
859
-
860
- SemVer should, IMO, but doesn't explicitly, say that dropping support for specific Platforms
861
- is a *breaking change* to an API, and for that reason the bike shedding is endless.
862
-
863
- To get a better understanding of how SemVer is intended to work over a project's lifetime,
864
- read this article from the creator of SemVer:
865
-
866
- - ["Major Version Numbers are Not Sacred"][📌major-versions-not-sacred]
867
-
868
- </details>
869
-
870
- See [CHANGELOG.md][📌changelog] for a list of releases.
871
-
872
- ## 📄 License
873
-
874
- The gem is available as open source under the terms of
875
- the [MIT License][📄license] [![License: MIT][📄license-img]][📄license-ref].
876
- See [LICENSE.txt][📄license] for the official [Copyright Notice][📄copyright-notice-explainer].
877
-
878
- ### © Copyright
879
-
880
- <ul>
881
- <li>
882
- Copyright (c) 2025 Peter H. Boling, of
883
- <a href="https://discord.gg/3qme4XHNKN">
884
- Galtzo.com
885
- <picture>
886
- <img src="https://logos.galtzo.com/assets/images/galtzo-floss/avatar-128px-blank.svg" alt="Galtzo.com Logo (Wordless) by Aboling0, CC BY-SA 4.0" width="24">
887
- </picture>
888
- </a>, and markdown-merge contributors.
889
- </li>
890
- </ul>
891
-
892
- ## 🤑 A request for help
893
-
894
- Maintainers have teeth and need to pay their dentists.
895
- After getting laid off in an RIF in March, and encountering difficulty finding a new one,
896
- I began spending most of my time building open source tools.
897
- I'm hoping to be able to pay for my kids' health insurance this month,
898
- so if you value the work I am doing, I need your support.
899
- Please consider sponsoring me or the project.
900
-
901
- To join the community or get help 👇️ Join the Discord.
902
-
903
- [![Live Chat on Discord][✉️discord-invite-img-ftb]][🖼️galtzo-discord]
904
-
905
- To say "thanks\!" ☝️ Join the Discord or 👇️ send money.
906
-
907
- [![Sponsor kettle-rb/markdown-merge on Open Source Collective][🖇osc-all-bottom-img]][🖇osc] 💌 [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal]
908
-
909
- ### Please give the project a star ⭐ ♥.
910
-
911
- Thanks for RTFM. ☺️
912
-
913
- [⛳liberapay-img]: https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay&color=a51611&style=flat
914
- [⛳liberapay-bottom-img]: https://img.shields.io/liberapay/goal/pboling.svg?style=for-the-badge&logo=liberapay&color=a51611
915
- [⛳liberapay]: https://liberapay.com/pboling/donate
916
- [🖇osc-all-img]: https://img.shields.io/opencollective/all/kettle-rb
917
- [🖇osc-sponsors-img]: https://img.shields.io/opencollective/sponsors/kettle-rb
918
- [🖇osc-backers-img]: https://img.shields.io/opencollective/backers/kettle-rb
919
- [🖇osc-backers]: https://opencollective.com/kettle-rb#backer
920
- [🖇osc-backers-i]: https://opencollective.com/kettle-rb/backers/badge.svg?style=flat
921
- [🖇osc-sponsors]: https://opencollective.com/kettle-rb#sponsor
922
- [🖇osc-sponsors-i]: https://opencollective.com/kettle-rb/sponsors/badge.svg?style=flat
923
- [🖇osc-all-bottom-img]: https://img.shields.io/opencollective/all/kettle-rb?style=for-the-badge
924
- [🖇osc-sponsors-bottom-img]: https://img.shields.io/opencollective/sponsors/kettle-rb?style=for-the-badge
925
- [🖇osc-backers-bottom-img]: https://img.shields.io/opencollective/backers/kettle-rb?style=for-the-badge
926
- [🖇osc]: https://opencollective.com/kettle-rb
927
- [🖇sponsor-img]: https://img.shields.io/badge/Sponsor_Me!-pboling.svg?style=social&logo=github
928
- [🖇sponsor-bottom-img]: https://img.shields.io/badge/Sponsor_Me!-pboling-blue?style=for-the-badge&logo=github
929
- [🖇sponsor]: https://github.com/sponsors/pboling
930
- [🖇polar-img]: https://img.shields.io/badge/polar-donate-a51611.svg?style=flat
931
- [🖇polar]: https://polar.sh/pboling
932
- [🖇kofi-img]: https://img.shields.io/badge/ko--fi-%E2%9C%93-a51611.svg?style=flat
933
- [🖇kofi]: https://ko-fi.com/O5O86SNP4
934
- [🖇patreon-img]: https://img.shields.io/badge/patreon-donate-a51611.svg?style=flat
935
- [🖇patreon]: https://patreon.com/galtzo
936
- [🖇buyme-small-img]: https://img.shields.io/badge/buy_me_a_coffee-%E2%9C%93-a51611.svg?style=flat
937
- [🖇buyme-img]: https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20latte&emoji=&slug=pboling&button_colour=FFDD00&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff
938
- [🖇buyme]: https://www.buymeacoffee.com/pboling
939
- [🖇paypal-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=flat&logo=paypal
940
- [🖇paypal-bottom-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=for-the-badge&logo=paypal&color=0A0A0A
941
- [🖇paypal]: https://www.paypal.com/paypalme/peterboling
942
- [🖇floss-funding.dev]: https://floss-funding.dev
943
- [🖇floss-funding-gem]: https://github.com/galtzo-floss/floss_funding
944
- [✉️discord-invite]: https://discord.gg/3qme4XHNKN
945
- [✉️discord-invite-img-ftb]: https://img.shields.io/discord/1373797679469170758?style=for-the-badge&logo=discord
946
- [✉️ruby-friends-img]: https://img.shields.io/badge/daily.dev-%F0%9F%92%8E_Ruby_Friends-0A0A0A?style=for-the-badge&logo=dailydotdev&logoColor=white
947
- [✉️ruby-friends]: https://app.daily.dev/squads/rubyfriends
948
- [✇bundle-group-pattern]: https://gist.github.com/pboling/4564780
949
- [⛳️gem-namespace]: https://github.com/kettle-rb/markdown-merge
950
- [⛳️namespace-img]: https://img.shields.io/badge/namespace-Markdown::Merge-3C2D2D.svg?style=square&logo=ruby&logoColor=white
951
- [⛳️gem-name]: https://bestgems.org/gems/markdown-merge
952
- [⛳️name-img]: https://img.shields.io/badge/name-markdown--merge-3C2D2D.svg?style=square&logo=rubygems&logoColor=red
953
- [⛳️tag-img]: https://img.shields.io/github/tag/kettle-rb/markdown-merge.svg
954
- [⛳️tag]: http://github.com/kettle-rb/markdown-merge/releases
955
- [🚂maint-blog]: http://www.railsbling.com/tags/markdown-merge
956
- [🚂maint-blog-img]: https://img.shields.io/badge/blog-railsbling-0093D0.svg?style=for-the-badge&logo=rubyonrails&logoColor=orange
957
- [🚂maint-contact]: http://www.railsbling.com/contact
958
- [🚂maint-contact-img]: https://img.shields.io/badge/Contact-Maintainer-0093D0.svg?style=flat&logo=rubyonrails&logoColor=red
959
- [💖🖇linkedin]: http://www.linkedin.com/in/peterboling
960
- [💖🖇linkedin-img]: https://img.shields.io/badge/PeterBoling-LinkedIn-0B66C2?style=flat&logo=newjapanprowrestling
961
- [💖✌️wellfound]: https://wellfound.com/u/peter-boling
962
- [💖✌️wellfound-img]: https://img.shields.io/badge/peter--boling-orange?style=flat&logo=wellfound
963
- [💖💲crunchbase]: https://www.crunchbase.com/person/peter-boling
964
- [💖💲crunchbase-img]: https://img.shields.io/badge/peter--boling-purple?style=flat&logo=crunchbase
965
- [💖🐘ruby-mast]: https://ruby.social/@galtzo
966
- [💖🐘ruby-mast-img]: https://img.shields.io/mastodon/follow/109447111526622197?domain=https://ruby.social&style=flat&logo=mastodon&label=Ruby%20@galtzo
967
- [💖🦋bluesky]: https://bsky.app/profile/galtzo.com
968
- [💖🦋bluesky-img]: https://img.shields.io/badge/@galtzo.com-0285FF?style=flat&logo=bluesky&logoColor=white
969
- [💖🌳linktree]: https://linktr.ee/galtzo
970
- [💖🌳linktree-img]: https://img.shields.io/badge/galtzo-purple?style=flat&logo=linktree
971
- [💖💁🏼‍♂️devto]: https://dev.to/galtzo
972
- [💖💁🏼‍♂️devto-img]: https://img.shields.io/badge/dev.to-0A0A0A?style=flat&logo=devdotto&logoColor=white
973
- [💖💁🏼‍♂️aboutme]: https://about.me/peter.boling
974
- [💖💁🏼‍♂️aboutme-img]: https://img.shields.io/badge/about.me-0A0A0A?style=flat&logo=aboutme&logoColor=white
975
- [💖🧊berg]: https://codeberg.org/pboling
976
- [💖🐙hub]: https://github.org/pboling
977
- [💖🛖hut]: https://sr.ht/~galtzo/
978
- [💖🧪lab]: https://gitlab.com/pboling
979
- [👨🏼‍🏫expsup-upwork]: https://www.upwork.com/freelancers/~014942e9b056abdf86?mp_source=share
980
- [👨🏼‍🏫expsup-upwork-img]: https://img.shields.io/badge/UpWork-13544E?style=for-the-badge&logo=Upwork&logoColor=white
981
- [👨🏼‍🏫expsup-codementor]: https://www.codementor.io/peterboling?utm_source=github&utm_medium=button&utm_term=peterboling&utm_campaign=github
982
- [👨🏼‍🏫expsup-codementor-img]: https://img.shields.io/badge/CodeMentor-Get_Help-1abc9c?style=for-the-badge&logo=CodeMentor&logoColor=white
983
- [🏙️entsup-tidelift]: https://tidelift.com/subscription/pkg/rubygems-markdown-merge?utm_source=rubygems-markdown-merge&utm_medium=referral&utm_campaign=readme
984
- [🏙️entsup-tidelift-img]: https://img.shields.io/badge/Tidelift_and_Sonar-Enterprise_Support-FD3456?style=for-the-badge&logo=sonar&logoColor=white
985
- [🏙️entsup-tidelift-sonar]: https://blog.tidelift.com/tidelift-joins-sonar
986
- [💁🏼‍♂️peterboling]: http://www.peterboling.com
987
- [🚂railsbling]: http://www.railsbling.com
988
- [📜src-gl-img]: https://img.shields.io/badge/GitLab-FBA326?style=for-the-badge&logo=Gitlab&logoColor=orange
989
- [📜src-gl]: https://gitlab.com/kettle-rb/markdown-merge/
990
- [📜src-cb-img]: https://img.shields.io/badge/CodeBerg-4893CC?style=for-the-badge&logo=CodeBerg&logoColor=blue
991
- [📜src-cb]: https://codeberg.org/kettle-rb/markdown-merge
992
- [📜src-gh-img]: https://img.shields.io/badge/GitHub-238636?style=for-the-badge&logo=Github&logoColor=green
993
- [📜src-gh]: https://github.com/kettle-rb/markdown-merge
994
- [📜docs-cr-rd-img]: https://img.shields.io/badge/RubyDoc-Current_Release-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white
995
- [📜docs-head-rd-img]: https://img.shields.io/badge/YARD_on_Galtzo.com-HEAD-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white
996
- [📜gl-wiki]: https://gitlab.com/kettle-rb/markdown-merge/-/wikis/home
997
- [📜gh-wiki]: https://github.com/kettle-rb/markdown-merge/wiki
998
- [📜gl-wiki-img]: https://img.shields.io/badge/wiki-examples-943CD2.svg?style=for-the-badge&logo=gitlab&logoColor=white
999
- [📜gh-wiki-img]: https://img.shields.io/badge/wiki-examples-943CD2.svg?style=for-the-badge&logo=github&logoColor=white
1000
- [👽dl-rank]: https://bestgems.org/gems/markdown-merge
1001
- [👽dl-ranki]: https://img.shields.io/gem/rd/markdown-merge.svg
1002
- [👽oss-help]: https://www.codetriage.com/kettle-rb/markdown-merge
1003
- [👽oss-helpi]: https://www.codetriage.com/kettle-rb/markdown-merge/badges/users.svg
1004
- [👽version]: https://bestgems.org/gems/markdown-merge
1005
- [👽versioni]: https://img.shields.io/gem/v/markdown-merge.svg
1006
- [🏀qlty-mnt]: https://qlty.sh/gh/kettle-rb/projects/markdown-merge
1007
- [🏀qlty-mnti]: https://qlty.sh/gh/kettle-rb/projects/markdown-merge/maintainability.svg
1008
- [🏀qlty-cov]: https://qlty.sh/gh/kettle-rb/projects/markdown-merge/metrics/code?sort=coverageRating
1009
- [🏀qlty-covi]: https://qlty.sh/gh/kettle-rb/projects/markdown-merge/coverage.svg
1010
- [🏀codecov]: https://codecov.io/gh/kettle-rb/markdown-merge
1011
- [🏀codecovi]: https://codecov.io/gh/kettle-rb/markdown-merge/graph/badge.svg
1012
- [🏀coveralls]: https://coveralls.io/github/kettle-rb/markdown-merge?branch=main
1013
- [🏀coveralls-img]: https://coveralls.io/repos/github/kettle-rb/markdown-merge/badge.svg?branch=main
1014
- [🖐codeQL]: https://github.com/kettle-rb/markdown-merge/security/code-scanning
1015
- [🖐codeQL-img]: https://github.com/kettle-rb/markdown-merge/actions/workflows/codeql-analysis.yml/badge.svg
1016
- [🚎2-cov-wf]: https://github.com/kettle-rb/markdown-merge/actions/workflows/coverage.yml
1017
- [🚎2-cov-wfi]: https://github.com/kettle-rb/markdown-merge/actions/workflows/coverage.yml/badge.svg
1018
- [🚎3-hd-wf]: https://github.com/kettle-rb/markdown-merge/actions/workflows/heads.yml
1019
- [🚎3-hd-wfi]: https://github.com/kettle-rb/markdown-merge/actions/workflows/heads.yml/badge.svg
1020
- [🚎5-st-wf]: https://github.com/kettle-rb/markdown-merge/actions/workflows/style.yml
1021
- [🚎5-st-wfi]: https://github.com/kettle-rb/markdown-merge/actions/workflows/style.yml/badge.svg
1022
- [🚎6-s-wf]: https://github.com/kettle-rb/markdown-merge/actions/workflows/supported.yml
1023
- [🚎6-s-wfi]: https://github.com/kettle-rb/markdown-merge/actions/workflows/supported.yml/badge.svg
1024
- [🚎9-t-wf]: https://github.com/kettle-rb/markdown-merge/actions/workflows/truffle.yml
1025
- [🚎9-t-wfi]: https://github.com/kettle-rb/markdown-merge/actions/workflows/truffle.yml/badge.svg
1026
- [🚎11-c-wf]: https://github.com/kettle-rb/markdown-merge/actions/workflows/current.yml
1027
- [🚎11-c-wfi]: https://github.com/kettle-rb/markdown-merge/actions/workflows/current.yml/badge.svg
1028
- [🚎12-crh-wf]: https://github.com/kettle-rb/markdown-merge/actions/workflows/dep-heads.yml
1029
- [🚎12-crh-wfi]: https://github.com/kettle-rb/markdown-merge/actions/workflows/dep-heads.yml/badge.svg
1030
- [🚎13-🔒️-wf]: https://github.com/kettle-rb/markdown-merge/actions/workflows/locked_deps.yml
1031
- [🚎13-🔒️-wfi]: https://github.com/kettle-rb/markdown-merge/actions/workflows/locked_deps.yml/badge.svg
1032
- [🚎14-🔓️-wf]: https://github.com/kettle-rb/markdown-merge/actions/workflows/unlocked_deps.yml
1033
- [🚎14-🔓️-wfi]: https://github.com/kettle-rb/markdown-merge/actions/workflows/unlocked_deps.yml/badge.svg
1034
- [🚎15-🪪-wf]: https://github.com/kettle-rb/markdown-merge/actions/workflows/license-eye.yml
1035
- [🚎15-🪪-wfi]: https://github.com/kettle-rb/markdown-merge/actions/workflows/license-eye.yml/badge.svg
1036
- [💎ruby-3.2i]: https://img.shields.io/badge/Ruby-3.2-CC342D?style=for-the-badge&logo=ruby&logoColor=white
1037
- [💎ruby-3.3i]: https://img.shields.io/badge/Ruby-3.3-CC342D?style=for-the-badge&logo=ruby&logoColor=white
1038
- [💎ruby-c-i]: https://img.shields.io/badge/Ruby-current-CC342D?style=for-the-badge&logo=ruby&logoColor=green
1039
- [💎ruby-headi]: https://img.shields.io/badge/Ruby-HEAD-CC342D?style=for-the-badge&logo=ruby&logoColor=blue
1040
- [💎truby-23.1i]: https://img.shields.io/badge/Truffle_Ruby-23.1-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink
1041
- [💎truby-c-i]: https://img.shields.io/badge/Truffle_Ruby-current-34BCB1?style=for-the-badge&logo=ruby&logoColor=green
1042
- [💎truby-headi]: https://img.shields.io/badge/Truffle_Ruby-HEAD-34BCB1?style=for-the-badge&logo=ruby&logoColor=blue
1043
- [💎jruby-c-i]: https://img.shields.io/badge/JRuby-current-FBE742?style=for-the-badge&logo=ruby&logoColor=green
1044
- [💎jruby-headi]: https://img.shields.io/badge/JRuby-HEAD-FBE742?style=for-the-badge&logo=ruby&logoColor=blue
1045
- [🤝gh-issues]: https://github.com/kettle-rb/markdown-merge/issues
1046
- [🤝gh-pulls]: https://github.com/kettle-rb/markdown-merge/pulls
1047
- [🤝gl-issues]: https://gitlab.com/kettle-rb/markdown-merge/-/issues
1048
- [🤝gl-pulls]: https://gitlab.com/kettle-rb/markdown-merge/-/merge_requests
1049
- [🤝cb-issues]: https://codeberg.org/kettle-rb/markdown-merge/issues
1050
- [🤝cb-pulls]: https://codeberg.org/kettle-rb/markdown-merge/pulls
1051
- [🤝cb-donate]: https://donate.codeberg.org/
1052
- [🤝contributing]: CONTRIBUTING.md
1053
- [🏀codecov-g]: https://codecov.io/gh/kettle-rb/markdown-merge/graphs/tree.svg
1054
- [🖐contrib-rocks]: https://contrib.rocks
1055
- [🖐contributors]: https://github.com/kettle-rb/markdown-merge/graphs/contributors
1056
- [🖐contributors-img]: https://contrib.rocks/image?repo=kettle-rb/markdown-merge
1057
- [🚎contributors-gl]: https://gitlab.com/kettle-rb/markdown-merge/-/graphs/main
1058
- [🪇conduct]: CODE_OF_CONDUCT.md
1059
- [🪇conduct-img]: https://img.shields.io/badge/Contributor_Covenant-2.1-259D6C.svg
1060
- [📌pvc]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint
1061
- [📌semver]: https://semver.org/spec/v2.0.0.html
1062
- [📌semver-img]: https://img.shields.io/badge/semver-2.0.0-259D6C.svg?style=flat
1063
- [📌semver-breaking]: https://github.com/semver/semver/issues/716#issuecomment-869336139
1064
- [📌major-versions-not-sacred]: https://tom.preston-werner.com/2022/05/23/major-version-numbers-are-not-sacred.html
1065
- [📌changelog]: CHANGELOG.md
1066
- [📗keep-changelog]: https://keepachangelog.com/en/1.0.0/
1067
- [📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-34495e.svg?style=flat
1068
- [📌gitmoji]: https://gitmoji.dev
1069
- [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
1070
- [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
1071
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-1.730-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1072
- [🔐security]: SECURITY.md
1073
- [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
1074
- [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
1075
- [📄license]: LICENSE.txt
1076
- [📄license-ref]: https://opensource.org/licenses/MIT
1077
- [📄license-img]: https://img.shields.io/badge/License-MIT-259D6C.svg
1078
- [📄license-compat]: https://dev.to/galtzo/how-to-check-license-compatibility-41h0
1079
- [📄license-compat-img]: https://img.shields.io/badge/Apache_Compatible:_Category_A-%E2%9C%93-259D6C.svg?style=flat&logo=Apache
1080
- [📄ilo-declaration]: https://www.ilo.org/declaration/lang--en/index.htm
1081
- [📄ilo-declaration-img]: https://img.shields.io/badge/ILO_Fundamental_Principles-✓-259D6C.svg?style=flat
1082
- [🚎yard-current]: http://rubydoc.info/gems/markdown-merge
1083
- [🚎yard-head]: https://markdown-merge.galtzo.com
1084
- [💎stone_checksums]: https://github.com/galtzo-floss/stone_checksums
1085
- [💎SHA_checksums]: https://gitlab.com/kettle-rb/markdown-merge/-/tree/main/checksums
1086
- [💎rlts]: https://github.com/rubocop-lts/rubocop-lts
1087
- [💎rlts-img]: https://img.shields.io/badge/code_style_&_linting-rubocop--lts-34495e.svg?plastic&logo=ruby&logoColor=white
1088
- [💎appraisal2]: https://github.com/appraisal-rb/appraisal2
1089
- [💎appraisal2-img]: https://img.shields.io/badge/appraised_by-appraisal2-34495e.svg?plastic&logo=ruby&logoColor=white
1090
- [💎d-in-dvcs]: https://railsbling.com/posts/dvcs/put_the_d_in_dvcs/