git_statistics 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/git-statistics +2 -1
- data/bin/git_statistics +2 -1
- data/lib/git_statistics.rb +69 -21
- data/lib/git_statistics/branches.rb +35 -0
- data/lib/git_statistics/collector.rb +20 -45
- data/lib/git_statistics/commit_line_extractor.rb +8 -8
- data/lib/git_statistics/commits.rb +7 -4
- data/lib/git_statistics/formatters/console.rb +50 -47
- data/lib/git_statistics/initialize.rb +0 -3
- data/lib/git_statistics/log.rb +59 -0
- data/lib/git_statistics/pipe.rb +29 -0
- data/lib/git_statistics/utilities.rb +33 -23
- data/lib/git_statistics/version.rb +1 -1
- data/spec/branches_spec.rb +44 -0
- data/spec/collector_spec.rb +20 -41
- data/spec/commits_spec.rb +51 -47
- data/spec/formatters/console_spec.rb +25 -18
- data/spec/log_spec.rb +54 -0
- data/spec/pipe_spec.rb +59 -0
- data/spec/utilities_spec.rb +65 -63
- metadata +52 -9
- data/lib/git_statistics/core_ext/string.rb +0 -11
    
        data/bin/git-statistics
    CHANGED
    
    
    
        data/bin/git_statistics
    CHANGED
    
    
    
        data/lib/git_statistics.rb
    CHANGED
    
    | @@ -1,50 +1,98 @@ | |
| 1 | 
            +
            require 'ostruct'
         | 
| 2 | 
            +
            require 'optparse'
         | 
| 1 3 | 
             
            require 'git_statistics/initialize'
         | 
| 2 4 |  | 
| 3 5 | 
             
            module GitStatistics
         | 
| 4 6 | 
             
              class GitStatistics
         | 
| 5 | 
            -
                 | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
                     | 
| 9 | 
            -
                     | 
| 10 | 
            -
                     | 
| 11 | 
            -
                     | 
| 12 | 
            -
                     | 
| 13 | 
            -
                     | 
| 14 | 
            -
                     | 
| 15 | 
            -
                     | 
| 16 | 
            -
             | 
| 7 | 
            +
                attr_reader :options
         | 
| 8 | 
            +
                def initialize
         | 
| 9 | 
            +
                  @options = OpenStruct.new(
         | 
| 10 | 
            +
                    email: false,
         | 
| 11 | 
            +
                    merges: false,
         | 
| 12 | 
            +
                    pretty: false,
         | 
| 13 | 
            +
                    update: false,
         | 
| 14 | 
            +
                    sort: "commits",
         | 
| 15 | 
            +
                    top: 0,
         | 
| 16 | 
            +
                    branch: false,
         | 
| 17 | 
            +
                    verbose: false,
         | 
| 18 | 
            +
                    debug: false,
         | 
| 19 | 
            +
                    limit: 100
         | 
| 20 | 
            +
                  )
         | 
| 21 | 
            +
                  parse_options
         | 
| 17 22 | 
             
                end
         | 
| 18 23 |  | 
| 19 24 | 
             
                def execute
         | 
| 25 | 
            +
                  if options.debug
         | 
| 26 | 
            +
                    Log.level = Logger::DEBUG
         | 
| 27 | 
            +
                    Log.use_debug
         | 
| 28 | 
            +
                  elsif options.verbose
         | 
| 29 | 
            +
                    Log.level = Logger::INFO
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 20 32 | 
             
                  # Collect data (incremental or fresh) based on presence of old data
         | 
| 21 | 
            -
                  if  | 
| 33 | 
            +
                  if options.update
         | 
| 22 34 | 
             
                    # Ensure commit directory is present
         | 
| 23 | 
            -
                    collector = Collector.new( | 
| 24 | 
            -
                    commits_directory = collector.repo_path  | 
| 35 | 
            +
                    collector = Collector.new(options.limit, false, options.pretty)
         | 
| 36 | 
            +
                    commits_directory = File.join(collector.repo_path, ".git_statistics")
         | 
| 25 37 | 
             
                    FileUtils.mkdir_p(commits_directory)
         | 
| 26 38 | 
             
                    file_count = Utilities.number_of_matching_files(commits_directory, /\d+\.json/) - 1
         | 
| 27 39 |  | 
| 28 | 
            -
                    # Only use --since if there is data present
         | 
| 29 40 | 
             
                    if file_count >= 0
         | 
| 30 41 | 
             
                      time = Utilities.get_modified_time(commits_directory + "#{file_count}.json")
         | 
| 31 | 
            -
                       | 
| 42 | 
            +
                      # Only use --since if there is data present
         | 
| 43 | 
            +
                      collector.collect(options.branch, "--since=\"#{time}\"")
         | 
| 32 44 | 
             
                      collected = true
         | 
| 33 45 | 
             
                    end
         | 
| 34 46 | 
             
                  end
         | 
| 35 47 |  | 
| 36 48 | 
             
                  # If no data was collected as there was no present data then start fresh
         | 
| 37 49 | 
             
                  unless collected
         | 
| 38 | 
            -
                    collector = Collector.new( | 
| 39 | 
            -
                    collector.collect( | 
| 50 | 
            +
                    collector = Collector.new(options.limit, true, options.pretty)
         | 
| 51 | 
            +
                    collector.collect(options.branch)
         | 
| 40 52 | 
             
                  end
         | 
| 41 53 |  | 
| 42 54 | 
             
                  # Calculate statistics
         | 
| 43 | 
            -
                  collector.commits.calculate_statistics( | 
| 55 | 
            +
                  collector.commits.calculate_statistics(options.email, options.merges)
         | 
| 44 56 |  | 
| 45 57 | 
             
                  # Print results
         | 
| 46 58 | 
             
                  results = Formatters::Console.new(collector.commits)
         | 
| 47 | 
            -
                  puts results.print_summary( | 
| 59 | 
            +
                  puts results.print_summary(options.sort, options.email, options.top)
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                def parse_options
         | 
| 63 | 
            +
                  OptionParser.new do |opt|
         | 
| 64 | 
            +
                    opt.version = VERSION
         | 
| 65 | 
            +
                    opt.on "-e", "--email", "Use author's email instead of name" do
         | 
| 66 | 
            +
                      options.email = true
         | 
| 67 | 
            +
                    end
         | 
| 68 | 
            +
                    opt.on "-m", "--merges", "Factor in merges when calculating statistics" do
         | 
| 69 | 
            +
                      options.merges = true
         | 
| 70 | 
            +
                    end
         | 
| 71 | 
            +
                    opt.on "-p", "--pretty", "Save the commits in git_repo/.git_statistics in pretty print (larger file size)" do
         | 
| 72 | 
            +
                      options.pretty = true
         | 
| 73 | 
            +
                    end
         | 
| 74 | 
            +
                    opt.on "-u", "--update", "Update saved commits with new data" do
         | 
| 75 | 
            +
                      options.update = true
         | 
| 76 | 
            +
                    end
         | 
| 77 | 
            +
                    opt.on "-s", "--sort TYPE", "Sort authors by {commits, additions, deletions, create, delete, rename, copy, merges}" do |type|
         | 
| 78 | 
            +
                      options.sort = type
         | 
| 79 | 
            +
                    end
         | 
| 80 | 
            +
                    opt.on "-t", "--top N", Float,"Show the top N authors in results" do |value|
         | 
| 81 | 
            +
                      options.top = value
         | 
| 82 | 
            +
                    end
         | 
| 83 | 
            +
                    opt.on "-b", "--branch", "Use current branch for statistics (otherwise all branches)" do
         | 
| 84 | 
            +
                      options.branch = true
         | 
| 85 | 
            +
                    end
         | 
| 86 | 
            +
                    opt.on "-v", "--verbose", "Verbose output (shows INFO level log statements)" do
         | 
| 87 | 
            +
                      options.verbose = true
         | 
| 88 | 
            +
                    end
         | 
| 89 | 
            +
                    opt.on "-d", "--debug", "Debug output (shows DEBUG level log statements)" do
         | 
| 90 | 
            +
                      options.debug = true
         | 
| 91 | 
            +
                    end
         | 
| 92 | 
            +
                    opt.on "-l", "--limit MAX_COMMITS", Float, "The maximum limit of commits to hold in memory at a time" do |number|
         | 
| 93 | 
            +
                      options.limit = number
         | 
| 94 | 
            +
                    end
         | 
| 95 | 
            +
                  end.parse!
         | 
| 48 96 | 
             
                end
         | 
| 49 97 | 
             
              end
         | 
| 50 98 |  | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            module GitStatistics
         | 
| 2 | 
            +
              class Branches
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                CURRENT_BRANCH = /\A\*\s/
         | 
| 5 | 
            +
                NO_BRANCH = /no branch/i
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def self.all
         | 
| 8 | 
            +
                  list.collect { |branch| branch.sub(CURRENT_BRANCH, "") }
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def self.current
         | 
| 12 | 
            +
                  return '(none)' if detached?
         | 
| 13 | 
            +
                  list.detect { |branch| branch =~ CURRENT_BRANCH }.sub(CURRENT_BRANCH, "")
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def self.detached?
         | 
| 17 | 
            +
                  stripped.grep(NO_BRANCH).any?
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                private
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  def self.list
         | 
| 23 | 
            +
                    stripped.reject { |b| b =~ NO_BRANCH }
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def self.stripped
         | 
| 27 | 
            +
                    pipe.map(&:strip)
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  def self.pipe
         | 
| 31 | 
            +
                    Pipe.new("git --no-pager branch --no-color")
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
| @@ -1,38 +1,29 @@ | |
| 1 1 | 
             
            module GitStatistics
         | 
| 2 2 | 
             
              class Collector
         | 
| 3 3 |  | 
| 4 | 
            -
                attr_accessor :repo, :repo_path, :commits_path, :commits | 
| 4 | 
            +
                attr_accessor :repo, :repo_path, :commits_path, :commits
         | 
| 5 5 |  | 
| 6 | 
            -
                def initialize( | 
| 7 | 
            -
                  @verbose = verbose
         | 
| 6 | 
            +
                def initialize(limit, fresh, pretty)
         | 
| 8 7 | 
             
                  @repo = Utilities.get_repository
         | 
| 9 | 
            -
             | 
| 10 | 
            -
                   | 
| 11 | 
            -
             | 
| 12 | 
            -
                  @repo_path = File.expand_path("..", @repo.path) + File::Separator
         | 
| 13 | 
            -
                  @commits_path = @repo_path + ".git_statistics" + File::Separator
         | 
| 8 | 
            +
                  @repo_path = File.expand_path("..", @repo.path)
         | 
| 9 | 
            +
                  @commits_path = File.join(@repo_path, ".git_statistics")
         | 
| 14 10 | 
             
                  @commits = Commits.new(@commits_path, fresh, limit, pretty)
         | 
| 15 11 | 
             
                end
         | 
| 16 12 |  | 
| 17 13 | 
             
                def collect(branch, time_since = "", time_until = "")
         | 
| 18 | 
            -
                  # Create pipe for git log to acquire branches
         | 
| 19 | 
            -
                  pipe = open("|git --no-pager branch --no-color")
         | 
| 20 | 
            -
             | 
| 21 14 | 
             
                  # Collect branches to use for git log
         | 
| 22 | 
            -
                  branches = branch ? [ | 
| 15 | 
            +
                  branches = branch ? [] : Branches.all
         | 
| 23 16 |  | 
| 24 17 | 
             
                  # Create pipe for the git log to acquire commits
         | 
| 25 | 
            -
                  pipe =  | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 18 | 
            +
                  pipe = Pipe.new("git --no-pager log #{branches.join(' ')} --date=iso --reverse"\
         | 
| 19 | 
            +
                                  " --no-color --find-copies-harder --numstat --encoding=utf-8"\
         | 
| 20 | 
            +
                                  " --summary #{time_since} #{time_until}"\
         | 
| 21 | 
            +
                                  " --format=\"%H,%an,%ae,%ad,%p\"")
         | 
| 29 22 |  | 
| 30 23 | 
             
                  # Use a buffer approach to queue up lines from the log for each commit
         | 
| 31 24 | 
             
                  buffer = []
         | 
| 32 25 | 
             
                  pipe.each do |line|
         | 
| 33 26 |  | 
| 34 | 
            -
                    line = line.clean_for_authors
         | 
| 35 | 
            -
             | 
| 36 27 | 
             
                    # Extract the buffer (commit) when we match ','x5 in the log format (delimeter)
         | 
| 37 28 | 
             
                    if line.split(',').size == 5
         | 
| 38 29 |  | 
| @@ -44,7 +35,6 @@ module GitStatistics | |
| 44 35 |  | 
| 45 36 | 
             
                      # Save commits to file if size exceeds limit or forced
         | 
| 46 37 | 
             
                      @commits.flush_commits
         | 
| 47 | 
            -
                      @repo = Utilities.get_repository
         | 
| 48 38 | 
             
                    end
         | 
| 49 39 |  | 
| 50 40 | 
             
                    buffer << line
         | 
| @@ -57,33 +47,18 @@ module GitStatistics | |
| 57 47 |  | 
| 58 48 | 
             
                def fall_back_collect_commit(sha)
         | 
| 59 49 | 
             
                  # Create pipe for the git log to acquire commits
         | 
| 60 | 
            -
                  pipe =  | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
                  buffer = pipe.map { |line| line.clean_for_authors }
         | 
| 50 | 
            +
                  pipe = Pipe.new("git --no-pager show #{sha} --date=iso --reverse"\
         | 
| 51 | 
            +
                                  " --no-color --find-copies-harder --numstat --encoding=utf-8 "\
         | 
| 52 | 
            +
                                  "--summary --format=\"%H,%an,%ae,%ad,%p\"")
         | 
| 65 53 |  | 
| 66 54 | 
             
                  # Check that the buffer has valid information (i.e., sha was valid)
         | 
| 67 | 
            -
                  if ! | 
| 68 | 
            -
                     | 
| 55 | 
            +
                  if !pipe.empty? && pipe.first.split(',').first == sha
         | 
| 56 | 
            +
                    pipe.to_a
         | 
| 69 57 | 
             
                  else
         | 
| 70 | 
            -
                     | 
| 58 | 
            +
                    []
         | 
| 71 59 | 
             
                  end
         | 
| 72 60 | 
             
                end
         | 
| 73 61 |  | 
| 74 | 
            -
                def collect_branches(pipe)
         | 
| 75 | 
            -
                  # Acquire all available branches from repository
         | 
| 76 | 
            -
                  branches = []
         | 
| 77 | 
            -
                  pipe.each do |line|
         | 
| 78 | 
            -
             | 
| 79 | 
            -
                    # Remove the '*' leading the current branch
         | 
| 80 | 
            -
                    line = line[1..-1] if line[0] == '*'
         | 
| 81 | 
            -
                    branches << line.clean_for_authors
         | 
| 82 | 
            -
                  end
         | 
| 83 | 
            -
             | 
| 84 | 
            -
                  return branches
         | 
| 85 | 
            -
                end
         | 
| 86 | 
            -
             | 
| 87 62 | 
             
                def acquire_commit_data(line)
         | 
| 88 63 | 
             
                  # Split up formated line
         | 
| 89 64 | 
             
                  commit_info = line.split(',')
         | 
| @@ -109,11 +84,11 @@ module GitStatistics | |
| 109 84 | 
             
                  # Acquire general commit information
         | 
| 110 85 | 
             
                  commit_data = acquire_commit_data(buffer[0])
         | 
| 111 86 |  | 
| 112 | 
            -
                   | 
| 87 | 
            +
                  Log.info "Extracting #{commit_data[:sha]}"
         | 
| 113 88 |  | 
| 114 89 | 
             
                  # Abort if the commit sha extracted form the buffer is invalid
         | 
| 115 90 | 
             
                  if commit_data[:sha].scan(/[\d|a-f]{40}/)[0].nil?
         | 
| 116 | 
            -
                     | 
| 91 | 
            +
                    Log.warn "Invalid buffer containing commit information"
         | 
| 117 92 | 
             
                    return
         | 
| 118 93 | 
             
                  end
         | 
| 119 94 |  | 
| @@ -122,7 +97,7 @@ module GitStatistics | |
| 122 97 |  | 
| 123 98 | 
             
                  # No files were changed in this commit, abort commit
         | 
| 124 99 | 
             
                  if files.nil?
         | 
| 125 | 
            -
                     | 
| 100 | 
            +
                    Log.debug "No files were changed"
         | 
| 126 101 | 
             
                    return
         | 
| 127 102 | 
             
                  end
         | 
| 128 103 |  | 
| @@ -134,9 +109,9 @@ module GitStatistics | |
| 134 109 | 
             
                    if blob.instance_of?(Grit::Blob)
         | 
| 135 110 | 
             
                      process_blob(commit_data[:data], blob, file)
         | 
| 136 111 | 
             
                    elsif blob.instance_of?(Grit::Submodule)
         | 
| 137 | 
            -
                       | 
| 112 | 
            +
                      Log.debug "Ignoring submodule #{blob.name}"
         | 
| 138 113 | 
             
                    else
         | 
| 139 | 
            -
                       | 
| 114 | 
            +
                      Log.warn "Problem processing file #{file[:file]}"
         | 
| 140 115 | 
             
                    end
         | 
| 141 116 | 
             
                  end
         | 
| 142 117 | 
             
                  return commit_data[:data]
         | 
| @@ -17,31 +17,31 @@ module GitStatistics | |
| 17 17 | 
             
                    split_file = Utilities.split_old_new_file(changes[2], changes[3])
         | 
| 18 18 | 
             
                    {:additions => changes[0].to_i,
         | 
| 19 19 | 
             
                      :deletions => changes[1].to_i,
         | 
| 20 | 
            -
                      :file => split_file[:new_file] | 
| 21 | 
            -
                      :old_file => split_file[:old_file] | 
| 20 | 
            +
                      :file => split_file[:new_file],
         | 
| 21 | 
            +
                      :old_file => split_file[:old_file]}
         | 
| 22 22 | 
             
                  end
         | 
| 23 23 | 
             
                  return modified_or_renamed unless modified_or_renamed.empty?
         | 
| 24 24 |  | 
| 25 25 | 
             
                  AdditionsOrDeletions.if_matches(line) do |changes|
         | 
| 26 26 | 
             
                    {:additions => changes[0].to_i,
         | 
| 27 27 | 
             
                      :deletions => changes[1].to_i,
         | 
| 28 | 
            -
                      :file => changes[2] | 
| 28 | 
            +
                      :file => changes[2]}
         | 
| 29 29 | 
             
                  end
         | 
| 30 30 | 
             
                end
         | 
| 31 31 |  | 
| 32 32 | 
             
                def created_or_deleted
         | 
| 33 33 | 
             
                  CreatedOrDeleted.if_matches(line) do |changes|
         | 
| 34 | 
            -
                    {:status => changes[0] | 
| 35 | 
            -
                      :file => changes[1] | 
| 34 | 
            +
                    {:status => changes[0],
         | 
| 35 | 
            +
                      :file => changes[1]}
         | 
| 36 36 | 
             
                  end
         | 
| 37 37 | 
             
                end
         | 
| 38 38 |  | 
| 39 39 | 
             
                def renamed_or_copied
         | 
| 40 40 | 
             
                  RenamedOrCopied.if_matches(line) do |changes|
         | 
| 41 41 | 
             
                    split_file = Utilities.split_old_new_file(changes[1], changes[2])
         | 
| 42 | 
            -
                    {:status => changes[0] | 
| 43 | 
            -
                      :old_file => split_file[:old_file] | 
| 44 | 
            -
                      :new_file => split_file[:new_file] | 
| 42 | 
            +
                    {:status => changes[0],
         | 
| 43 | 
            +
                      :old_file => split_file[:old_file],
         | 
| 44 | 
            +
                      :new_file => split_file[:new_file],
         | 
| 45 45 | 
             
                      :similar => changes[3].to_i}
         | 
| 46 46 | 
             
                  end
         | 
| 47 47 | 
             
                end
         | 
| @@ -14,12 +14,11 @@ module GitStatistics | |
| 14 14 |  | 
| 15 15 | 
             
                def clean
         | 
| 16 16 | 
             
                  # Ensure the path exists
         | 
| 17 | 
            -
                  FileUtils.mkdir_p( | 
| 17 | 
            +
                  FileUtils.mkdir_p(path)
         | 
| 18 18 |  | 
| 19 19 | 
             
                  # Remove all files within path if saving
         | 
| 20 | 
            -
                  if  | 
| 21 | 
            -
                     | 
| 22 | 
            -
                      next if %w[. ..].include? file
         | 
| 20 | 
            +
                  if fresh
         | 
| 21 | 
            +
                    files_in_path.each do |file|
         | 
| 23 22 | 
             
                      File.delete(File.join(path, file))
         | 
| 24 23 | 
             
                    end
         | 
| 25 24 | 
             
                  end
         | 
| @@ -30,6 +29,10 @@ module GitStatistics | |
| 30 29 | 
             
                  @totals[:languages] = {}
         | 
| 31 30 | 
             
                end
         | 
| 32 31 |  | 
| 32 | 
            +
                def files_in_path
         | 
| 33 | 
            +
                  Dir.entries(path).reject { |file| %w[. ..].include?(file) }
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 33 36 | 
             
                def flush_commits(force = false)
         | 
| 34 37 | 
             
                  if size >= limit || force
         | 
| 35 38 | 
             
                    file_count = Utilities.number_of_matching_files(path, /\d+\.json/)
         | 
| @@ -2,7 +2,7 @@ module GitStatistics | |
| 2 2 | 
             
              module Formatters
         | 
| 3 3 | 
             
                class Console
         | 
| 4 4 |  | 
| 5 | 
            -
                  attr_accessor :commits
         | 
| 5 | 
            +
                  attr_accessor :commits, :config
         | 
| 6 6 |  | 
| 7 7 | 
             
                  def initialize(commits)
         | 
| 8 8 | 
             
                    @commits = commits
         | 
| @@ -17,7 +17,7 @@ module GitStatistics | |
| 17 17 | 
             
                    raise "Parameter for --sort is not valid" if data.nil?
         | 
| 18 18 |  | 
| 19 19 | 
             
                    # Create config
         | 
| 20 | 
            -
                    config = {:data => data,
         | 
| 20 | 
            +
                    @config = {:data => data,
         | 
| 21 21 | 
             
                              :author_length => Utilities.max_length_in_list(data.keys, 17),
         | 
| 22 22 | 
             
                              :language_length => Utilities.max_length_in_list(@commits.totals[:languages].keys, 8),
         | 
| 23 23 | 
             
                              :sort => sort,
         | 
| @@ -25,70 +25,73 @@ module GitStatistics | |
| 25 25 | 
             
                              :top_n => top_n}
         | 
| 26 26 |  | 
| 27 27 | 
             
                    # Acquire formatting pattern for output
         | 
| 28 | 
            -
                    pattern = "%-#{config[:author_length]}s | %-#{config[:language_length]}s | %7s | %9s | %9s | %7s | %7s | %7s | %6s | %6s |"
         | 
| 29 | 
            -
                    config | 
| 30 | 
            -
                    return config
         | 
| 28 | 
            +
                    @pattern = "| %-#{config[:author_length]}s | %-#{config[:language_length]}s | %7s | %9s | %9s | %7s | %7s | %7s | %6s | %6s |"
         | 
| 29 | 
            +
                    config
         | 
| 31 30 | 
             
                  end
         | 
| 32 31 |  | 
| 33 32 | 
             
                  def print_summary(sort, email, top_n = 0)
         | 
| 34 33 | 
             
                    # Prepare and determine the config for the result summary based on parameters
         | 
| 34 | 
            +
                    commit_totals = @commits.totals
         | 
| 35 35 | 
             
                    config = prepare_result_summary(sort, email, top_n)
         | 
| 36 36 |  | 
| 37 37 | 
             
                    # Print query/header information
         | 
| 38 | 
            -
                    output = print_header | 
| 38 | 
            +
                    output = print_header
         | 
| 39 39 |  | 
| 40 40 | 
             
                    # Print per author information
         | 
| 41 | 
            -
                    config[:data].each do | | 
| 42 | 
            -
                      output  | 
| 43 | 
            -
             | 
| 44 | 
            -
                                      value[:rename], value[:copy], value[:merges]]
         | 
| 45 | 
            -
                      output += "\n"
         | 
| 46 | 
            -
                      output += print_language_data(config[:pattern], value)
         | 
| 41 | 
            +
                    config[:data].each do |name, commit_data|
         | 
| 42 | 
            +
                      output << print_row(name, commit_data)
         | 
| 43 | 
            +
                      output << print_language_data(commit_data)
         | 
| 47 44 | 
             
                    end
         | 
| 48 | 
            -
             | 
| 49 | 
            -
                     | 
| 50 | 
            -
                    output  | 
| 51 | 
            -
                    output | 
| 52 | 
            -
                    data = @commits.totals
         | 
| 53 | 
            -
                    output += config[:pattern] % ["Repository Totals", "", data[:commits],
         | 
| 54 | 
            -
                                    data[:additions], data[:deletions], data[:create],
         | 
| 55 | 
            -
                                    data[:delete], data[:rename], data[:copy], data[:merges]]
         | 
| 56 | 
            -
                    output += "\n"
         | 
| 57 | 
            -
                    output += print_language_data(config[:pattern], data)
         | 
| 58 | 
            -
                    return output
         | 
| 45 | 
            +
                    output << separator
         | 
| 46 | 
            +
                    output << print_row("Repository Totals", commit_totals)
         | 
| 47 | 
            +
                    output << print_language_data(commit_totals)
         | 
| 48 | 
            +
                    output.flatten.join("\n")
         | 
| 59 49 | 
             
                  end
         | 
| 60 50 |  | 
| 61 | 
            -
                  def print_language_data( | 
| 62 | 
            -
                    output =  | 
| 51 | 
            +
                  def print_language_data(data)
         | 
| 52 | 
            +
                    output = []
         | 
| 63 53 | 
             
                    # Print information of each language for the data
         | 
| 64 | 
            -
                    data[:languages].each do | | 
| 65 | 
            -
                      output  | 
| 66 | 
            -
                                      value[:create], value[:delete], value[:rename],
         | 
| 67 | 
            -
                                      value[:copy], value[:merges]]
         | 
| 68 | 
            -
                      output += "\n"
         | 
| 54 | 
            +
                    data[:languages].each do |language, commit_data|
         | 
| 55 | 
            +
                      output << print_row("", commit_data, language)
         | 
| 69 56 | 
             
                    end
         | 
| 70 | 
            -
                     | 
| 57 | 
            +
                    output
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  def print_row(name, commit_info, language = '')
         | 
| 61 | 
            +
                    format_for_row(name, language, commit_info[:commits],
         | 
| 62 | 
            +
                                    commit_info[:additions], commit_info[:deletions], commit_info[:create],
         | 
| 63 | 
            +
                                    commit_info[:delete], commit_info[:rename], commit_info[:copy], commit_info[:merges])
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  def print_header
         | 
| 67 | 
            +
                    output = []
         | 
| 68 | 
            +
                    output << get_author_info(@commits.stats.size)
         | 
| 69 | 
            +
                    output << get_header_info
         | 
| 70 | 
            +
                    output
         | 
| 71 71 | 
             
                  end
         | 
| 72 72 |  | 
| 73 | 
            -
                  def  | 
| 74 | 
            -
                     | 
| 73 | 
            +
                  def get_header_info
         | 
| 74 | 
            +
                    headers = []
         | 
| 75 | 
            +
                    headers << separator
         | 
| 76 | 
            +
                    headers << format_for_row('Name/Email', 'Language', 'Commits', 'Additions', 'Deletions', 'Creates', 'Deletes', 'Renames', 'Copies', 'Merges')
         | 
| 77 | 
            +
                    headers << separator
         | 
| 78 | 
            +
                    headers
         | 
| 79 | 
            +
                  end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  def format_for_row(*columns)
         | 
| 82 | 
            +
                    @pattern % columns
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  def separator
         | 
| 86 | 
            +
                    "-" * 89 + "-"*config[:author_length] + "-"*config[:language_length]
         | 
| 87 | 
            +
                  end
         | 
| 75 88 |  | 
| 76 | 
            -
             | 
| 77 | 
            -
                     | 
| 78 | 
            -
             | 
| 79 | 
            -
                      output += "Top #{config[:top_n]} authors(#{total_authors}) sorted by #{config[:sort]}\n"
         | 
| 80 | 
            -
                    else
         | 
| 81 | 
            -
                      output += "All authors(#{total_authors}) sorted by #{config[:sort]}\n"
         | 
| 89 | 
            +
                  def get_author_info(total_authors)
         | 
| 90 | 
            +
                    if config[:top_n] > 0 && config[:top_n] < total_authors
         | 
| 91 | 
            +
                      return "Top #{config[:top_n]} authors(#{total_authors}) sorted by #{config[:sort]}"
         | 
| 82 92 | 
             
                    end
         | 
| 83 93 |  | 
| 84 | 
            -
                    #  | 
| 85 | 
            -
                    output += "-"*87 + "-"*config[:author_length] + "-"*config[:language_length]
         | 
| 86 | 
            -
                    output += "\n"
         | 
| 87 | 
            -
                    output += config[:pattern] % ['Name/Email', 'Language', 'Commits', 'Additions', 'Deletions', 'Creates', 'Deletes', 'Renames', 'Copies', 'Merges']
         | 
| 88 | 
            -
                    output += "\n"
         | 
| 89 | 
            -
                    output +=  "-"*87 + "-"*config[:author_length] + "-"*config[:language_length]
         | 
| 90 | 
            -
                    output += "\n"
         | 
| 91 | 
            -
                    return output
         | 
| 94 | 
            +
                    "All authors(#{total_authors}) sorted by #{config[:sort]}"
         | 
| 92 95 | 
             
                  end
         | 
| 93 96 | 
             
                end
         | 
| 94 97 | 
             
              end
         |