- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "zeitwerk"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
Zeitwerk::Loader.new.then do |loader|
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  loader.inflector.inflect "container" => "CONTAINER"
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  loader.ignore "#{__dir__}/gitt/rspec/shared_contexts"
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  loader.tag = File.basename __FILE__, ".rb"
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  loader.push_dir __dir__
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  loader.setup
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
            
              
            
            
# Main namespace.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  SHELL = Shell.new.freeze
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  def self.loader registry = Zeitwerk::Registry
           
        
      
        
          
- 
            
              6
            
            
              
            
            
    @loader ||= registry.loaders.each.find { |loader| loader.tag == File.basename(__FILE__, ".rb") }
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  def self.new(...) = Repository.new(...)
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "dry/monads"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Commands
           
        
      
        
          
- 
            
            
              
            
            
    # A Git branch command wrapper.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    class Branch
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      include Dry::Monads[:result]
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def initialize shell: SHELL
           
        
      
        
          
- 
            
              37
            
            
              
            
            
        @shell = shell
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def default(fallback = "main", *, **)
           
        
      
        
          
- 
            
              6
            
            
              
            
            
        shell.call("config", "init.defaultBranch", *, **)
           
        
      
        
          
- 
            
            
              
            
            
             .fmap(&:chomp)
           
        
      
        
          
- 
            
              5
            
            
              
                
                  then: 3
                
              
                
                  else: 2
                
              
            
            
             .fmap { |name| name.empty? ? fallback : name }
           
        
      
        
          
- 
            
            
              
            
            
             .or(Success(fallback))
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def call(*, **) = shell.call("branch", *, **)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def name(*, **) = shell.call("rev-parse", "--abbrev-ref", "HEAD", *, **).fmap(&:chomp)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      private
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      attr_reader :shell
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "core"
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "dry/monads"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Commands
           
        
      
        
          
- 
            
            
              
            
            
    # A Git config command wrapper.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    class Config
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      include Dry::Monads[:result]
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def initialize shell: SHELL
           
        
      
        
          
- 
            
              41
            
            
              
            
            
        @shell = shell
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def call(*, **) = shell.call("config", *, **)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def get(key, fallback = Core::EMPTY_STRING, *, **)
           
        
      
        
          
- 
            
              11
            
            
              
            
            
        shell.call("config", "--get", key, *, **)
           
        
      
        
          
- 
            
            
              
            
            
             .fmap(&:chomp)
           
        
      
        
          
- 
            
              4
            
            
              
                
                  then: 1
                
              
                
                  else: 3
                
              
            
            
             .or { |error| block_given? ? yield(error) : Success(fallback) }
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def origin? = !get("remote.origin.url").value_or(Core::EMPTY_STRING).empty?
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              6
            
            
              
            
            
      def set(key, value, *, **) = shell.call("config", "--add", key, value, *, **).fmap { value }
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      private
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      attr_reader :shell
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "dry/monads"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Commands
           
        
      
        
          
- 
            
            
              
            
            
    # A Git log command wrapper.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    class Log
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      include Dry::Monads[:result]
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      KEY_MAP = {
           
        
      
        
          
- 
            
            
              
            
            
        author_email: "%ae",
           
        
      
        
          
- 
            
            
              
            
            
        author_name: "%an",
           
        
      
        
          
- 
            
            
              
            
            
        authored_at: "%at",
           
        
      
        
          
- 
            
            
              
            
            
        authored_relative_at: "%ar",
           
        
      
        
          
- 
            
            
              
            
            
        body: "%b",
           
        
      
        
          
- 
            
            
              
            
            
        committed_at: "%ct",
           
        
      
        
          
- 
            
            
              
            
            
        committed_relative_at: "%cr",
           
        
      
        
          
- 
            
            
              
            
            
        committer_email: "%ce",
           
        
      
        
          
- 
            
            
              
            
            
        committer_name: "%cn",
           
        
      
        
          
- 
            
            
              
            
            
        encoding: "%e",
           
        
      
        
          
- 
            
            
              
            
            
        notes: "%N",
           
        
      
        
          
- 
            
            
              
            
            
        raw: "%B",
           
        
      
        
          
- 
            
            
              
            
            
        sha: "%H",
           
        
      
        
          
- 
            
            
              
            
            
        signature: "%G?",
           
        
      
        
          
- 
            
            
              
            
            
        fingerprint: "%GK",
           
        
      
        
          
- 
            
            
              
            
            
        fingerprint_key: "%GF",
           
        
      
        
          
- 
            
            
              
            
            
        subject: "%s",
           
        
      
        
          
- 
            
            
              
            
            
        trailers: "%(trailers)"
           
        
      
        
          
- 
            
            
              
            
            
      }.freeze
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def initialize shell: SHELL, key_map: KEY_MAP, parser: Parsers::Commit.new
           
        
      
        
          
- 
            
              48
            
            
              
            
            
        @shell = shell
           
        
      
        
          
- 
            
              48
            
            
              
            
            
        @key_map = key_map
           
        
      
        
          
- 
            
              48
            
            
              
            
            
        @parser = parser
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def call(*, **) = shell.call("log", *, **)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def index(*arguments, **)
           
        
      
        
          
- 
            
              18
            
            
              
            
            
        arguments.prepend("--shortstat", pretty_format)
           
        
      
        
          
- 
            
              18
            
            
              
            
            
                 .then { |pretty_format| call(*pretty_format, **) }
           
        
      
        
          
- 
            
              17
            
            
              
            
            
                 .fmap { |content| String(content).scrub("?") }
           
        
      
        
          
- 
            
              17
            
            
              
            
            
                 .fmap { |entries| build_records entries }
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def uncommitted path
           
        
      
        
          
- 
            
              5
            
            
              
                
                  else: 4
                
              
                
                  then: 1
                
              
            
            
        return Failure %(Invalid commit message path: "#{path}".) unless path.exist?
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              4
            
            
              
            
            
        shell.call("mktree")
           
        
      
        
          
- 
            
              4
            
            
              
            
            
             .bind { |raw_sha| shell.call "commit-tree", "-F", path.to_s, raw_sha.chomp }
           
        
      
        
          
- 
            
              4
            
            
              
            
            
             .bind { |sha| index "-1", sha.chomp }
           
        
      
        
          
- 
            
            
              
            
            
             .fmap(&:first)
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      private
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      attr_reader :shell, :key_map, :parser
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def pretty_format
           
        
      
        
          
- 
            
              342
            
            
              
            
            
        key_map.reduce(+"") { |format, (key, value)| format << "<#{key}>#{value}</#{key}>%n" }
           
        
      
        
          
- 
            
              18
            
            
              
            
            
               .then { |format| %(--pretty=format:"#{format}") }
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def build_records entries
           
        
      
        
          
- 
            
              17
            
            
              
            
            
        wrap_statistics entries
           
        
      
        
          
- 
            
              17
            
            
              
            
            
        add_empty_statistics entries
           
        
      
        
          
- 
            
              37
            
            
              
            
            
        entries.split("<break/>").map { |entry| parser.call entry }
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
            
              
            
            
      # :reek:UtilityFunction
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def wrap_statistics entries
           
        
      
        
          
- 
            
              17
            
            
              
            
            
        entries.gsub!(/\n"\n\s\d+\sfile.+\d+\s(insertion|deletion).+\n/) do |match|
           
        
      
        
          
- 
            
              14
            
            
              
            
            
          match.delete_prefix!("\n\"\n").strip!
           
        
      
        
          
- 
            
              14
            
            
              
            
            
          "\n<statistics>#{match}</statistics>\n<break/>"
           
        
      
        
          
- 
            
            
              
            
            
        end
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
            
              
            
            
      # :reek:UtilityFunction
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def add_empty_statistics entries
           
        
      
        
          
- 
            
              17
            
            
              
            
            
        entries.gsub! %(</trailers>\n"\n"<author_email>),
           
        
      
        
          
- 
            
            
              
            
            
                      "</trailers>\n<statistics></statistics><break/>\n<author_email>"
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "core"
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "dry/monads"
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "tempfile"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Commands
           
        
      
        
          
- 
            
            
              
            
            
    # A Git tag command wrapper.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    class Tag
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      include Dry::Monads[:result]
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      KEY_MAP = {
           
        
      
        
          
- 
            
            
              
            
            
        author_email: "%(*authoremail)",
           
        
      
        
          
- 
            
            
              
            
            
        author_name: "%(*authorname)",
           
        
      
        
          
- 
            
            
              
            
            
        authored_at: "%(*authordate:raw)",
           
        
      
        
          
- 
            
            
              
            
            
        authored_relative_at: "%(*authordate:relative)",
           
        
      
        
          
- 
            
            
              
            
            
        body: "%(body)",
           
        
      
        
          
- 
            
            
              
            
            
        committed_at: "%(*committerdate:raw)",
           
        
      
        
          
- 
            
            
              
            
            
        committed_relative_at: "%(*committerdate:relative)",
           
        
      
        
          
- 
            
            
              
            
            
        committer_email: "%(*committeremail)",
           
        
      
        
          
- 
            
            
              
            
            
        committer_name: "%(*committername)",
           
        
      
        
          
- 
            
            
              
            
            
        sha: "%(objectname)",
           
        
      
        
          
- 
            
            
              
            
            
        signature: "%(contents:signature)",
           
        
      
        
          
- 
            
            
              
            
            
        subject: "%(subject)",
           
        
      
        
          
- 
            
            
              
            
            
        trailers: "%(trailers)",
           
        
      
        
          
- 
            
            
              
            
            
        version: "%(refname)"
           
        
      
        
          
- 
            
            
              
            
            
      }.freeze
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def initialize shell: SHELL, key_map: KEY_MAP, parser: Parsers::Tag.new
           
        
      
        
          
- 
            
              55
            
            
              
            
            
        @shell = shell
           
        
      
        
          
- 
            
              55
            
            
              
            
            
        @key_map = key_map
           
        
      
        
          
- 
            
              55
            
            
              
            
            
        @parser = parser
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def call(*, **) = shell.call("tag", *, **)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def create version, body = Core::EMPTY_STRING, *flags
           
        
      
        
          
- 
            
              7
            
            
              
                
                  else: 6
                
              
                
                  then: 1
                
              
            
            
        return Failure "Unable to create Git tag without version." unless version
           
        
      
        
          
- 
            
              6
            
            
              
                
                  then: 1
                
              
                
                  else: 5
                
              
            
            
        return Failure "Tag exists: #{version}." if exist? version
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              5
            
            
              
            
            
        Tempfile.open "gitt" do |file|
           
        
      
        
          
- 
            
              5
            
            
              
            
            
          file.write body
           
        
      
        
          
- 
            
              5
            
            
              
            
            
          write version, file.tap(&:rewind), *flags
           
        
      
        
          
- 
            
            
              
            
            
        end
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def delete_local(version, *, **)
           
        
      
        
          
- 
            
              7
            
            
              
            
            
        call("--delete", version, *, **).fmap { |text| text[/\d+\.\d+\.\d+/] }
           
        
      
        
          
- 
            
            
              
            
            
                                        .alt_map do |error|
           
        
      
        
          
- 
            
              1
            
            
              
            
            
                                          error.delete_prefix("error: tag ").chomp
           
        
      
        
          
- 
            
            
              
            
            
                                        end
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def delete_remote(version, *, **)
           
        
      
        
          
- 
            
              3
            
            
              
            
            
        shell.call("push", "--delete", "origin", version, *, **)
           
        
      
        
          
- 
            
              2
            
            
              
            
            
             .fmap { version }
           
        
      
        
          
- 
            
              1
            
            
              
            
            
             .alt_map { |error| error.gsub("error: ", "").chomp }
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def exist?(version) = local?(version) || remote?(version)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def index(*arguments, **)
           
        
      
        
          
- 
            
              2
            
            
              
            
            
        arguments.prepend(pretty_format, "--list")
           
        
      
        
          
- 
            
              2
            
            
              
            
            
                 .then { |flags| call(*flags, **) }
           
        
      
        
          
- 
            
              2
            
            
              
            
            
                 .fmap { |content| String(content).scrub("?").split %("\n") }
           
        
      
        
          
- 
            
              2
            
            
              
            
            
                 .fmap { |entries| build_records entries }
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def last(*, **)
           
        
      
        
          
- 
            
              5
            
            
              
            
            
        shell.call("describe", "--abbrev=0", "--tags", *, **)
           
        
      
        
          
- 
            
            
              
            
            
             .fmap(&:strip)
           
        
      
        
          
- 
            
            
              
            
            
             .or do |error|
           
        
      
        
          
- 
            
              2
            
            
              
                
                  then: 1
                
              
            
            
               if error.match?(/no names found/i)
           
        
      
        
          
- 
            
              1
            
            
              
            
            
                 Failure "No tags found."
           
        
      
        
          
- 
            
            
              
                
                  else: 1
                
              
            
            
               else
           
        
      
        
          
- 
            
              1
            
            
              
            
            
                 Failure error.delete_prefix("fatal: ").chomp
           
        
      
        
          
- 
            
            
              
            
            
               end
           
        
      
        
          
- 
            
            
              
            
            
             end
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def local? version
           
        
      
        
          
- 
            
              14
            
            
              
            
            
        call("--list", version).value_or(Core::EMPTY_STRING).match?(/\A#{version}\Z/)
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def push(*, **) = shell.call("push", "--tags", *, **)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def remote? version
           
        
      
        
          
- 
            
              11
            
            
              
            
            
        shell.call("ls-remote", "--tags", "origin", version)
           
        
      
        
          
- 
            
            
              
            
            
             .value_or(Core::EMPTY_STRING)
           
        
      
        
          
- 
            
            
              
            
            
             .match?(%r(.+tags/#{version}\Z))
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def show(version, *, **)
           
        
      
        
          
- 
            
              4
            
            
              
            
            
        call(pretty_format, "--list", version, *, **).fmap { |content| parser.call content }
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def tagged? = !call.value_or(Core::EMPTY_STRING).empty?
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      private
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      attr_reader :shell, :key_map, :parser
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def pretty_format
           
        
      
        
          
- 
            
              60
            
            
              
            
            
        key_map.reduce(+"") { |format, (key, value)| format << "<#{key}>#{value}</#{key}>%n" }
           
        
      
        
          
- 
            
              4
            
            
              
            
            
               .then { |format| %(--format="#{format}") }
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              3
            
            
              
            
            
      def build_records(entries) = entries.map { |entry| parser.call entry }
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def write version, file, *flags
           
        
      
        
          
- 
            
              5
            
            
              
            
            
        arguments = ["--annotate", version, "--cleanup", "verbatim", *flags, "--file", file.path]
           
        
      
        
          
- 
            
              9
            
            
              
            
            
        call(*arguments).fmap { version }
           
        
      
        
          
- 
            
              1
            
            
              
            
            
                        .or { Failure "Unable to create tag: #{version}." }
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
            
              
            
            
  # Provides shared behavior for objects that can act like a commit.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Directable
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def directive? = amend? || fixup? || squash?
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def amend? = subject.match?(/\Aamend!\s/)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def fixup? = subject.match?(/\Afixup!\s/)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def squash? = subject.match?(/\Asquash!\s/)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def prefix = subject[/\A[\w!]+/]
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "dry/monads"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Models
           
        
      
        
          
- 
            
            
              
            
            
    # Represents commit details.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    Commit = Struct.new(
           
        
      
        
          
- 
            
            
              
            
            
      :author_email,
           
        
      
        
          
- 
            
            
              
            
            
      :author_name,
           
        
      
        
          
- 
            
            
              
            
            
      :authored_at,
           
        
      
        
          
- 
            
            
              
            
            
      :authored_relative_at,
           
        
      
        
          
- 
            
            
              
            
            
      :body,
           
        
      
        
          
- 
            
            
              
            
            
      :body_lines,
           
        
      
        
          
- 
            
            
              
            
            
      :body_paragraphs,
           
        
      
        
          
- 
            
            
              
            
            
      :committed_at,
           
        
      
        
          
- 
            
            
              
            
            
      :committed_relative_at,
           
        
      
        
          
- 
            
            
              
            
            
      :committer_email,
           
        
      
        
          
- 
            
            
              
            
            
      :committer_name,
           
        
      
        
          
- 
            
            
              
            
            
      :deletions,
           
        
      
        
          
- 
            
            
              
            
            
      :encoding,
           
        
      
        
          
- 
            
            
              
            
            
      :files_changed,
           
        
      
        
          
- 
            
            
              
            
            
      :fingerprint,
           
        
      
        
          
- 
            
            
              
            
            
      :fingerprint_key,
           
        
      
        
          
- 
            
            
              
            
            
      :insertions,
           
        
      
        
          
- 
            
            
              
            
            
      :lines,
           
        
      
        
          
- 
            
            
              
            
            
      :notes,
           
        
      
        
          
- 
            
            
              
            
            
      :raw,
           
        
      
        
          
- 
            
            
              
            
            
      :sha,
           
        
      
        
          
- 
            
            
              
            
            
      :signature,
           
        
      
        
          
- 
            
            
              
            
            
      :subject,
           
        
      
        
          
- 
            
            
              
            
            
      :trailers
           
        
      
        
          
- 
            
            
              
            
            
    ) do
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      include Directable
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      include Trailable
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      include Dry::Monads[:result]
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def initialize(**)
           
        
      
        
          
- 
            
              65
            
            
              
            
            
        super
           
        
      
        
          
- 
            
              65
            
            
              
            
            
        freeze
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "core"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Models
           
        
      
        
          
- 
            
            
              
            
            
    # Represents a person within a repository.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    Person = Data.define :name, :delimiter, :email do
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def self.for(string, parser: Parsers::Person.new) = parser.call string
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def initialize name: nil, delimiter: " ", email: nil
           
        
      
        
          
- 
            
              21
            
            
              
            
            
        super
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def to_s
           
        
      
        
          
- 
            
              6
            
            
              
                
                  in: 2
                
              
            
            
        case self
           
        
      
        
          
- 
            
              2
            
            
              
                
                  in: 1
                
              
            
            
          in String, String, String then "#{name}#{delimiter}<#{email}>"
           
        
      
        
          
- 
            
              1
            
            
              
                
                  in: 1
                
              
            
            
          in String, String, nil then name
           
        
      
        
          
- 
            
              1
            
            
              
                
                  else: 2
                
              
            
            
          in nil, String, String then "<#{email}>"
           
        
      
        
          
- 
            
              2
            
            
              
            
            
          else Core::EMPTY_STRING
           
        
      
        
          
- 
            
            
              
            
            
        end
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Models
           
        
      
        
          
- 
            
            
              
            
            
    # Represents tag details.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    Tag = Struct.new(
           
        
      
        
          
- 
            
            
              
            
            
      :author_email,
           
        
      
        
          
- 
            
            
              
            
            
      :author_name,
           
        
      
        
          
- 
            
            
              
            
            
      :authored_at,
           
        
      
        
          
- 
            
            
              
            
            
      :authored_relative_at,
           
        
      
        
          
- 
            
            
              
            
            
      :body,
           
        
      
        
          
- 
            
            
              
            
            
      :committed_at,
           
        
      
        
          
- 
            
            
              
            
            
      :committed_relative_at,
           
        
      
        
          
- 
            
            
              
            
            
      :committer_email,
           
        
      
        
          
- 
            
            
              
            
            
      :committer_name,
           
        
      
        
          
- 
            
            
              
            
            
      :sha,
           
        
      
        
          
- 
            
            
              
            
            
      :signature,
           
        
      
        
          
- 
            
            
              
            
            
      :subject,
           
        
      
        
          
- 
            
            
              
            
            
      :trailers,
           
        
      
        
          
- 
            
            
              
            
            
      :version
           
        
      
        
          
- 
            
            
              
            
            
    ) do
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      include Trailable
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def initialize(**)
           
        
      
        
          
- 
            
              20
            
            
              
            
            
        super
           
        
      
        
          
- 
            
              20
            
            
              
            
            
        freeze
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Models
           
        
      
        
          
- 
            
            
              
            
            
    # Represents commit trailer details.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    Trailer = Data.define :key, :delimiter, :space, :value do
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def self.for(string, parser: Parsers::Trailer.new) = parser.call string
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def initialize key:, value:, delimiter: ":", space: " "
           
        
      
        
          
- 
            
              107
            
            
              
            
            
        super
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def empty? = String(key).empty? || String(value).empty?
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def to_s = to_h.values.join
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "core"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Parsers
           
        
      
        
          
- 
            
            
              
            
            
    # Extracts attributes from XML formatted content.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    class Attributer
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def initialize keys = Core::EMPTY_ARRAY
           
        
      
        
          
- 
            
              130
            
            
              
            
            
        @keys = keys
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def call content
           
        
      
        
          
- 
            
              51
            
            
              
            
            
        build String(content)
           
        
      
        
          
- 
            
            
              
            
            
      rescue ArgumentError => error
           
        
      
        
          
- 
            
              2
            
            
              
                
                  then: 1
                
              
                
                  else: 1
                
              
            
            
        error.message.include?("invalid byte") ? build(content.scrub("?")) : raise
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      private
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      attr_reader :keys
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def build content
           
        
      
        
          
- 
            
              51
            
            
              
            
            
        keys.each.with_object({}) do |key, attributes|
           
        
      
        
          
- 
            
              797
            
            
              
            
            
          attributes[key] = content[%r(<#{key}>(?<value>.*?)</#{key}>)m, :value]
           
        
      
        
          
- 
            
            
              
            
            
        end
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "refinements/hash"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Parsers
           
        
      
        
          
- 
            
            
              
            
            
    # Parses raw commit to produce a commit record.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    class Commit
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      using Refinements::Hash
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def initialize attributer: Attributer.new(Commands::Log::KEY_MAP.keys.append(:statistics)),
           
        
      
        
          
- 
            
            
              
            
            
                     sanitizers: Sanitizers::CONTAINER,
           
        
      
        
          
- 
            
            
              
            
            
                     model: Models::Commit
           
        
      
        
          
- 
            
              61
            
            
              
            
            
        @attributer = attributer
           
        
      
        
          
- 
            
              61
            
            
              
            
            
        @sanitizers = sanitizers
           
        
      
        
          
- 
            
              61
            
            
              
            
            
        @model = model
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def call content
           
        
      
        
          
- 
            
              33
            
            
              
            
            
        attributer.call(content)
           
        
      
        
          
- 
            
              33
            
            
              
            
            
                  .then { |attributes| mutate attributes }
           
        
      
        
          
- 
            
              33
            
            
              
            
            
                  .then { |attributes| model[**attributes] }
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      private
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      attr_reader :attributer, :sanitizers, :model
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
            
              
            
            
      # :reek:TooManyStatements
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def mutate attributes
           
        
      
        
          
- 
            
              33
            
            
              
            
            
        body, trailers = attributes.values_at :body, :trailers
           
        
      
        
          
- 
            
              33
            
            
              
            
            
        body = scissors_sanitizer.call body
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              33
            
            
              
            
            
        attributes.transform_with! signature: signature_sanitizer, trailers: trailers_sanitizer
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              33
            
            
              
            
            
        attributes[:body] =
           
        
      
        
          
- 
            
              33
            
            
              
                
                  then: 25
                
              
                
                  else: 8
                
              
            
            
          (trailers ? body.sub(/\n??#{Regexp.escape trailers}\n??/, "") : body).chomp
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              165
            
            
              
            
            
        private_methods.grep(/\Aprocess_/).sort.each { |method| __send__ method, attributes }
           
        
      
        
          
- 
            
              33
            
            
              
            
            
        attributes
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
            
              
            
            
      # :reek:FeatureEnvy
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def process_body_lines attributes
           
        
      
        
          
- 
            
              33
            
            
              
            
            
        attributes[:body_lines] = lines_sanitizer.call attributes[:body]
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
            
              
            
            
      # :reek:FeatureEnvy
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def process_body_paragraphs attributes
           
        
      
        
          
- 
            
              33
            
            
              
            
            
        attributes[:body_paragraphs] = paragraphs_sanitizer.call attributes[:body]
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
            
              
            
            
      # :reek:FeatureEnvy
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def process_lines attributes
           
        
      
        
          
- 
            
              33
            
            
              
            
            
        attributes[:lines] = lines_sanitizer.call attributes[:raw]
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
            
              
            
            
      # :reek:FeatureEnvy
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def process_statistics attributes
           
        
      
        
          
- 
            
              33
            
            
              
            
            
        attributes.merge! statistics_sanitizer.call(attributes.delete(:statistics))
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def lines_sanitizer = sanitizers.fetch :lines
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def paragraphs_sanitizer = sanitizers.fetch :paragraphs
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def scissors_sanitizer = sanitizers.fetch :scissors
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def statistics_sanitizer = sanitizers.fetch :statistics
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def signature_sanitizer = sanitizers.fetch :signature
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def trailers_sanitizer = sanitizers.fetch :trailers
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Parsers
           
        
      
        
          
- 
            
            
              
            
            
    # Parses trailer to produce a person.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    class Person
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def initialize email_start: "<", email_end: ">", model: Models::Person
           
        
      
        
          
- 
            
              6
            
            
              
            
            
        @email_start = email_start
           
        
      
        
          
- 
            
              6
            
            
              
            
            
        @email_end = email_end
           
        
      
        
          
- 
            
              6
            
            
              
            
            
        @model = model
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def call content
           
        
      
        
          
- 
            
              6
            
            
              
                
                  then: 1
                
              
            
            
        if content.start_with? email_start
           
        
      
        
          
- 
            
              1
            
            
              
            
            
          model[email: content.delete_prefix(email_start).delete_suffix(email_end)]
           
        
      
        
          
- 
            
            
              
                
                  else: 5
                
              
            
            
        else
           
        
      
        
          
- 
            
              5
            
            
              
            
            
          name, email = content.split " #{email_start}"
           
        
      
        
          
- 
            
              5
            
            
              
                
                  then: 2
                
              
                
                  else: 3
                
              
            
            
          email.delete_suffix! email_end if email
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              5
            
            
              
            
            
          model[name:, email:]
           
        
      
        
          
- 
            
            
              
            
            
        end
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      private
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      attr_reader :email_start, :email_end, :model
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "refinements/hash"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Parsers
           
        
      
        
          
- 
            
            
              
            
            
    # Parses raw tag information to produce a tag record.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    class Tag
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      using Refinements::Hash
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def initialize attributer: Attributer.new(Commands::Tag::KEY_MAP.keys),
           
        
      
        
          
- 
            
            
              
            
            
                     sanitizers: Sanitizers::CONTAINER,
           
        
      
        
          
- 
            
            
              
            
            
                     model: Models::Tag
           
        
      
        
          
- 
            
              62
            
            
              
            
            
        @attributer = attributer
           
        
      
        
          
- 
            
              62
            
            
              
            
            
        @sanitizers = sanitizers
           
        
      
        
          
- 
            
              62
            
            
              
            
            
        @model = model
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
            
              
            
            
      # :reek:TooManyStatements
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def call content
           
        
      
        
          
- 
            
              11
            
            
              
            
            
        attributes = attributer.call content
           
        
      
        
          
- 
            
              11
            
            
              
            
            
        body, trailers = attributes.values_at :body, :trailers
           
        
      
        
          
- 
            
              11
            
            
              
            
            
        sanitize attributes
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              11
            
            
              
            
            
        attributes[:body] = (
           
        
      
        
          
- 
            
              11
            
            
              
                
                  then: 6
                
              
                
                  else: 5
                
              
            
            
          trailers ? body.sub(/\n??#{Regexp.escape trailers}\n??/, "") : body
           
        
      
        
          
- 
            
            
              
            
            
        ).to_s.chomp
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              11
            
            
              
            
            
        model[**attributes]
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      private
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      attr_reader :attributer, :sanitizers, :model
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def sanitize attributes
           
        
      
        
          
- 
            
              11
            
            
              
            
            
        attributes.transform_with! author_email: email_sanitizer,
           
        
      
        
          
- 
            
            
              
            
            
                                   authored_at: date_sanitizer,
           
        
      
        
          
- 
            
            
              
            
            
                                   committed_at: date_sanitizer,
           
        
      
        
          
- 
            
            
              
            
            
                                   committer_email: email_sanitizer,
           
        
      
        
          
- 
            
            
              
            
            
                                   trailers: trailers_sanitizer,
           
        
      
        
          
- 
            
            
              
            
            
                                   version: version_serializer
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def date_sanitizer = sanitizers.fetch :date
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def email_sanitizer = sanitizers.fetch :email
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def trailers_sanitizer = sanitizers.fetch :trailers
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def version_serializer = sanitizers.fetch :version
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Parsers
           
        
      
        
          
- 
            
            
              
            
            
    # Parses raw trailer data to produce a trailer record.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    class Trailer
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      PATTERN = /
           
        
      
        
          
- 
            
            
              
            
            
        \A                  # Start of line.
           
        
      
        
          
- 
            
            
              
            
            
        (?<key>[a-zA-Z-]+)  # Key.
           
        
      
        
          
- 
            
            
              
            
            
        (?<delimiter>:)     # Delimiter (colon).
           
        
      
        
          
- 
            
            
              
            
            
        (?<space>\s?)       # Space (optional).
           
        
      
        
          
- 
            
            
              
            
            
        (?<value>.*?)       # Value.
           
        
      
        
          
- 
            
            
              
            
            
        \Z                  # End of line.
           
        
      
        
          
- 
            
            
              
            
            
      /x
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      EMPTY = Models::Trailer[key: nil, value: nil]
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def initialize pattern: PATTERN, model: Models::Trailer, empty: EMPTY
           
        
      
        
          
- 
            
              12
            
            
              
            
            
        @pattern = pattern
           
        
      
        
          
- 
            
              12
            
            
              
            
            
        @model = model
           
        
      
        
          
- 
            
              12
            
            
              
            
            
        @empty = empty
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def call content
           
        
      
        
          
- 
            
              25
            
            
              
                
                  then: 2
                
              
                
                  else: 23
                
              
            
            
        return empty if content.start_with? "#"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              23
            
            
              
            
            
        content.match(pattern)
           
        
      
        
          
- 
            
              23
            
            
              
                
                  then: 21
                
              
                
                  else: 2
                
              
            
            
               .then { |data| data ? model[**data.named_captures(symbolize_names: true)] : empty }
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      private
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      attr_reader :pattern, :model, :empty
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "core"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
            
              
            
            
  # Primary object/wrapper for processing all Git related commands.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  class Repository
           
        
      
        
          
- 
            
            
              
            
            
    COMMANDS = {
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      branch: Commands::Branch,
           
        
      
        
          
- 
            
            
              
            
            
      config: Commands::Config,
           
        
      
        
          
- 
            
            
              
            
            
      log: Commands::Log,
           
        
      
        
          
- 
            
            
              
            
            
      tag: Commands::Tag
           
        
      
        
          
- 
            
            
              
            
            
    }.freeze
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def initialize shell: SHELL, commands: COMMANDS
           
        
      
        
          
- 
            
              28
            
            
              
            
            
      @shell = shell
           
        
      
        
          
- 
            
              140
            
            
              
            
            
      @commands = commands.transform_values { |command| command.new shell: }
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def branch(...) = commands.fetch(__method__).call(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def branch_default(...) = commands.fetch(:branch).default(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def branch_name(...) = commands.fetch(:branch).name(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def call(...) = shell.call(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def commits(...) = commands.fetch(:log).index(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def config(...) = commands.fetch(__method__).call(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def exist? = shell.call("rev-parse", "--git-dir").value_or(Core::EMPTY_STRING).chomp == ".git"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def get(...) = commands.fetch(:config).get(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def inspect
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      "#<#{self.class}:#{object_id} @shell=#{shell.inspect} " \
           
        
      
        
          
- 
            
            
              
            
            
      "@commands=#{commands.values.map(&:class).inspect}>"
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def log(...) = commands.fetch(__method__).call(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def origin? = commands.fetch(:config).origin?
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def set(...) = commands.fetch(:config).set(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def tag(...) = commands.fetch(__method__).call(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def tags(...) = commands.fetch(:tag).index(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def tag?(...) = commands.fetch(:tag).exist?(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def tag_create(...) = commands.fetch(:tag).create(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def tag_delete_local(...) = commands.fetch(:tag).delete_local(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def tag_delete_remote(...) = commands.fetch(:tag).delete_remote(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def tag_last(...) = commands.fetch(:tag).last(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def tag_local?(...) = commands.fetch(:tag).local?(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def tag_remote?(...) = commands.fetch(:tag).remote?(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def tag_show(...) = commands.fetch(:tag).show(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def tagged? = commands.fetch(:tag).tagged?
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def tags_push(...) = commands.fetch(:tag).push(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def uncommitted(...) = commands.fetch(:log).uncommitted(...)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    private
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    attr_reader :shell, :commands
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Sanitizers
           
        
      
        
          
- 
            
            
              
            
            
    CONTAINER = {
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      date: Date,
           
        
      
        
          
- 
            
            
              
            
            
      email: Email,
           
        
      
        
          
- 
            
            
              
            
            
      lines: Lines,
           
        
      
        
          
- 
            
            
              
            
            
      paragraphs: Paragraphs.new,
           
        
      
        
          
- 
            
            
              
            
            
      scissors: Scissors,
           
        
      
        
          
- 
            
            
              
            
            
      signature: Signature,
           
        
      
        
          
- 
            
            
              
            
            
      statistics: Statistics.new,
           
        
      
        
          
- 
            
            
              
            
            
      trailers: Trailers.new,
           
        
      
        
          
- 
            
            
              
            
            
      version: Version
           
        
      
        
          
- 
            
            
              
            
            
    }.freeze
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "core"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Sanitizers
           
        
      
        
          
- 
            
              25
            
            
              
                
                  then: 13
                
              
                
                  else: 11
                
              
            
            
    Date = -> text { text.sub(/\s.+\Z/, Core::EMPTY_STRING) if text }
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "core"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Sanitizers
           
        
      
        
          
- 
            
              25
            
            
              
                
                  then: 13
                
              
                
                  else: 11
                
              
            
            
    Email = -> text { text.tr "<>", Core::EMPTY_STRING if text }
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "core"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Sanitizers
           
        
      
        
          
- 
            
              69
            
            
              
                
                  then: 57
                
              
                
                  else: 11
                
              
            
            
    Lines = -> text { text ? text.split("\n") : Core::EMPTY_ARRAY }
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "core"
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "refinements/array"
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "strscan"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Sanitizers
           
        
      
        
          
- 
            
            
              
            
            
    # Detects and parses paragraphs (including code blocks).
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    class Paragraphs
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      using Refinements::Array
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      PATTERN = /
           
        
      
        
          
- 
            
            
              
            
            
        (              # Condition start.
           
        
      
        
          
- 
            
            
              
            
            
        (?:\..*?\n)?   # Optional ASCII Doc label.
           
        
      
        
          
- 
            
            
              
            
            
        (?:\[.*\]\n)?  # Optional ASCII Doc directive.
           
        
      
        
          
- 
            
            
              
            
            
        [-_=+.*]{4}    # ASCII Doc block start.
           
        
      
        
          
- 
            
            
              
            
            
        [\s\S]*?       # Lazy block content of any character.
           
        
      
        
          
- 
            
            
              
            
            
        [-_=+.*]{4}    # ASCII Doc block end.
           
        
      
        
          
- 
            
            
              
            
            
        |              # Or.
           
        
      
        
          
- 
            
            
              
            
            
        ```            # Markdown start.
           
        
      
        
          
- 
            
            
              
            
            
        [\s\S]*?       # Lazy block content of any character.
           
        
      
        
          
- 
            
            
              
            
            
        ```            # Markdown end.
           
        
      
        
          
- 
            
            
              
            
            
        )              # Condition end.
           
        
      
        
          
- 
            
            
              
            
            
      /mx
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def initialize pattern: PATTERN, client: StringScanner
           
        
      
        
          
- 
            
              28
            
            
              
            
            
        @pattern = pattern
           
        
      
        
          
- 
            
              28
            
            
              
            
            
        @client = client
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def call(text) = scan(client.new(text.to_s))
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      private
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      attr_reader :pattern, :client
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
            
              
            
            
      # :reek:FeatureEnvy
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def scan scanner, collection = []
           
        
      
        
          
- 
            
              60
            
            
              
                
                  body: 67
                
              
            
            
        until scanner.eos?
           
        
      
        
          
- 
            
              67
            
            
              
            
            
          match = scanner.scan_until pattern
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              67
            
            
              
                
                  else: 24
                
              
                
                  then: 43
                
              
            
            
          break collection << scanner.string[scanner.rest].tap(&:strip!).split("\n\n") unless match
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              24
            
            
              
            
            
          collection << scanner.pre_match.strip
           
        
      
        
          
- 
            
              24
            
            
              
            
            
          collection << scanner.captures
           
        
      
        
          
- 
            
            
              
            
            
        end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              60
            
            
              
            
            
        collection.tap(&:flatten!).tap(&:compress!)
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "core"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Sanitizers
           
        
      
        
          
- 
            
              36
            
            
              
                
                  then: 34
                
              
                
                  else: 1
                
              
            
            
    Scissors = -> text { text.sub(/^#\s-.+\s>8\s-.+/m, Core::EMPTY_STRING) if text }
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Sanitizers
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    Signature = lambda do |text|
           
        
      
        
          
- 
            
              42
            
            
              
                
                  when: 1
                
              
            
            
      case text
           
        
      
        
          
- 
            
              1
            
            
              
                
                  when: 1
                
              
            
            
        when "B" then "Bad"
           
        
      
        
          
- 
            
              1
            
            
              
                
                  when: 1
                
              
            
            
        when "E" then "Error"
           
        
      
        
          
- 
            
              1
            
            
              
                
                  when: 23
                
              
            
            
        when "G" then "Good"
           
        
      
        
          
- 
            
              23
            
            
              
                
                  when: 1
                
              
            
            
        when "N" then "None"
           
        
      
        
          
- 
            
              1
            
            
              
                
                  when: 1
                
              
            
            
        when "R" then "Revoked"
           
        
      
        
          
- 
            
              1
            
            
              
                
                  when: 1
                
              
            
            
        when "U" then "Unknown"
           
        
      
        
          
- 
            
              1
            
            
              
                
                  when: 1
                
              
            
            
        when "X" then "Expired"
           
        
      
        
          
- 
            
              1
            
            
              
                
                  else: 12
                
              
            
            
        when "Y" then "Expired Key"
           
        
      
        
          
- 
            
              12
            
            
              
            
            
        else "Invalid"
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Sanitizers
           
        
      
        
          
- 
            
            
              
            
            
    # Converts raw text into a statistics hash.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    class Statistics
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      EMPTY = {files_changed: 0, insertions: 0, deletions: 0}.freeze
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      PATTERN = /
           
        
      
        
          
- 
            
            
              
            
            
        (?<total>\d+)                     # Total capture group.
           
        
      
        
          
- 
            
            
              
            
            
        \s                                # Space delimiter.
           
        
      
        
          
- 
            
            
              
            
            
        (?<kind>file|insertion|deletion)  # Kind capture group.
           
        
      
        
          
- 
            
            
              
            
            
      /x
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def self.update attributes, kind, total
           
        
      
        
          
- 
            
              56
            
            
              
                
                  when: 20
                
              
            
            
        case kind
           
        
      
        
          
- 
            
              20
            
            
              
                
                  when: 18
                
              
            
            
          when "file" then attributes[:files_changed] = total
           
        
      
        
          
- 
            
              18
            
            
              
                
                  when: 17
                
              
            
            
          when "insertion" then attributes[:insertions] = total
           
        
      
        
          
- 
            
              17
            
            
              
                
                  else: 1
                
              
            
            
          when "deletion" then attributes[:deletions] = total
           
        
      
        
          
- 
            
              1
            
            
              
            
            
          else fail StandardError, "Invalid kind: #{kind.inspect}."
           
        
      
        
          
- 
            
            
              
            
            
        end
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def initialize attributes = EMPTY, pattern: PATTERN
           
        
      
        
          
- 
            
              7
            
            
              
            
            
        @attributes = attributes
           
        
      
        
          
- 
            
              7
            
            
              
            
            
        @pattern = pattern
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def call text
           
        
      
        
          
- 
            
              39
            
            
              
                
                  else: 22
                
              
                
                  then: 17
                
              
            
            
        return attributes unless text
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              22
            
            
              
            
            
        text.scan(pattern).each.with_object(attributes.dup) do |(number, kind), aggregate|
           
        
      
        
          
- 
            
              52
            
            
              
            
            
          self.class.update aggregate, kind, number.to_i
           
        
      
        
          
- 
            
            
              
            
            
        end
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      private
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      attr_reader :attributes, :pattern
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Sanitizers
           
        
      
        
          
- 
            
            
              
            
            
    # Sanitizes content by turning it into an array of trailer records.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    class Trailers
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def initialize parser: Parsers::Trailer.new
           
        
      
        
          
- 
            
              5
            
            
              
            
            
        @parser = parser
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      def call text
           
        
      
        
          
- 
            
              48
            
            
              
            
            
        String(text).split("\n")
           
        
      
        
          
- 
            
              18
            
            
              
            
            
                    .map { |line| parser.call line }
           
        
      
        
          
- 
            
            
              
            
            
                    .reject(&:empty?)
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      private
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
      attr_reader :parser
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "core"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Sanitizers
           
        
      
        
          
- 
            
              14
            
            
              
                
                  then: 7
                
              
                
                  else: 6
                
              
            
            
    Version = -> text { text.delete_prefix "refs/tags/" if text }
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "dry/monads"
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "open3"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
            
              
            
            
  # A low-level shell client.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  class Shell
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    include Dry::Monads[:result]
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def initialize client: Open3
           
        
      
        
          
- 
            
              7
            
            
              
            
            
      @client = client
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def call(*all, **)
           
        
      
        
          
- 
            
              489
            
            
              
            
            
      environment, arguments = all.partition { it.is_a? Hash }
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              120
            
            
              
            
            
      client.capture3(*environment, "git", *arguments, **).then do |stdout, stderr, status|
           
        
      
        
          
- 
            
              120
            
            
              
                
                  then: 104
                
              
                
                  else: 16
                
              
            
            
        status.success? ? Success(stdout) : Failure(stderr)
           
        
      
        
          
- 
            
            
              
            
            
      end
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    private
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    attr_reader :client
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end
           
        
      
    
  
 
      
        
  
  
    
      
        
          
- 
            
            
              
            
            
# frozen_string_literal: true
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
require "dry/monads"
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
module Gitt
           
        
      
        
          
- 
            
            
              
            
            
  # Provides shared behavior for objects that have trailers.
           
        
      
        
          
- 
            
              1
            
            
              
            
            
  module Trailable
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    include Dry::Monads[:result]
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def find_trailer key
           
        
      
        
          
- 
            
              24
            
            
              
            
            
      trailers.find { |trailer| trailer.key == key }
           
        
      
        
          
- 
            
            
              
            
            
              .then do |trailer|
           
        
      
        
          
- 
            
              12
            
            
              
                
                  then: 6
                
              
                
                  else: 6
                
              
            
            
                return Success trailer if trailer
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              6
            
            
              
            
            
                Failure "Unable to find trailer for key: #{key.inspect}."
           
        
      
        
          
- 
            
            
              
            
            
              end
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def find_trailers key
           
        
      
        
          
- 
            
              48
            
            
              
            
            
      trailers.select { |trailer| trailer.key == key }
           
        
      
        
          
- 
            
              12
            
            
              
            
            
              .then { |trailers| Success trailers }
           
        
      
        
          
- 
            
            
              
            
            
    end
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              1
            
            
              
            
            
    def trailer_value_for(key) = find_trailer(key).fmap(&:value)
           
        
      
        
          
- 
            
            
              
            
            
           
        
      
        
          
- 
            
              7
            
            
              
            
            
    def trailer_values_for(key) = find_trailers(key).fmap { |trailers| trailers.map(&:value) }
           
        
      
        
          
- 
            
            
              
            
            
  end
           
        
      
        
          
- 
            
            
              
            
            
end