鈽笍 bash-merge: Intelligently merge Bash scripts by AST structure
Published on February 21, 2026 by Peter Boling

馃尰 Synopsis


bash-merge is a standalone Ruby module that intelligently merges two versions of a Bash script using tree-sitter AST analysis. It's like a smart "git merge" specifically designed for shell scripts. Built on top of ast-merge, it shares the same architecture as prism-merge for Ruby source files.


Key Features

  • Tree-Sitter Powered: Uses tree-sitter-bash for accurate AST parsing
  • Script-Aware: Understands Bash syntax including functions, variables, and commands
  • Intelligent: Matches functions and variable assignments by name
  • Comment-Preserving: Comments are preserved in their context
  • Shebang Handling: Properly handles #!/bin/bash and similar shebangs
  • Freeze Block Support: Respects freeze markers (default: bash-merge:freeze / bash-merge:unfreeze) for merge control - customizable to match your project's conventions
  • Full Provenance: Tracks origin of every node
  • Standalone: Minimal dependencies - just ast-merge and ruby_tree_sitter
  • Customizable:
    • signature_generator - callable custom signature generators
    • preference - setting of :template, :destination, or a Hash for per-node-type preferences
    • node_splitter - Hash mapping node types to callables for per-node-type merge customization (see ast-merge docs)
    • add_template_only_nodes - setting to retain nodes that do not exist in destination
    • freeze_token - customize freeze block markers (default: "bash-merge")

Supported Node Types


Node Type Signature Format Matching Behavior

  • Function Definition => [:function, name] => Functions match by name
  • Variable Assignment => [:assignment, name] => Variables match by name
  • Command => [:command, name, args...] => Commands match by name and arguments
  • Comment => [:comment, text] => Comments preserved in context
  • Pipeline => [:pipeline, commands...] => Pipelines match by command sequence
  • If Statement => [:if, condition_sig] => Conditionals match by condition signature
  • For Loop => [:for, variable] => For loops match by loop variable
  • While Loop => [:while, condition_sig] => While loops match by condition signature


  • Example


    ```
    require "bash/merge
    "template = File.read("template.sh")
    destination = File.read("destination.sh")
    merger = Bash::Merge::SmartMerger.new(template, destination)
    result = merger.merge
    File.write("merged.sh", result.to_bash)
    ```

    Happy Merging!