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