Class: Git

Inherits:
Object
  • Object
show all
Defined in:
lib/git-stat.rb

Overview

Provides a quick wrapper around git commands in order to analyze and provide interesting statistics around a git repository

Constant Summary collapse

UNKNOWN_TYPE =
'UNKNOWN'

Class Method Summary collapse

Class Method Details

.all_statsObject

Does Git::files_stat and then formats everything. Output will look like this:

{
  'project_name' => Git::project_name(),
  'total_files' => stats[:number_of_files],
  'total_lines' => stats[:total_line_counts],
  'file_types' => stats[:file_types],
  'authors_line_count' => stats[:authors_influence],
  'line_counts_by_type' => stats[:line_counts_by_type],
  'branches' => Git::remote_branches(),
  'tags' => Git::tags()
}


194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/git-stat.rb', line 194

def self.all_stats
  stats = files_stat()
  {
    'project_name' => Git::project_name(),
    'total_files' => stats[:number_of_files],
    'total_lines' => stats[:total_line_counts],
    'file_types' => stats[:file_types],
    'authors_line_count' => stats[:authors_influence],
    'line_counts_by_type' => stats[:line_counts_by_type],
    'branches' => Git::remote_branches(),
    'tags' => Git::tags()
  }
end

.blame(file) ⇒ Object

Executes the blame command and returns the String output



54
55
56
# File 'lib/git-stat.rb', line 54

def self.blame(file)
  `git blame "#{file}"`
end

.file_stat(file) ⇒ Object

Returns a hash containing some interesting stats about a file including:

  1. The file

  2. The file type

  3. line count

  4. and all the authors and the number of lines they were responsible for

It will look something like this:

{
  :file => file,
  :type => type,
  :line_count => line_count,
  :authors_influence => authors
}


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/git-stat.rb', line 82

def self.file_stat(file)
  authors = {}
  line_count = 0
  type = File.extname(file).empty? ? UNKNOWN_TYPE : File.extname(file)
  begin
    lines = blame(file).split(/\n/)
    line_count = lines.length
    lines.each do |line|
      if (line.match(/\((.*?)\s+[0-9]{4}-[0-9]{2}-[0-9]{2}\s+[0-9]{2}:[0-9]{2}:[0-9]{2}\s.[0-9]*\s+[0-9]*\)/))
        authors[$1] += 1 if authors.has_key?($1)
        authors[$1] = 1 unless authors.has_key?($1)
      else
        $stderr.puts "[WARN] Did not find whom to blame on this line in file: #{file}"
        $stderr.flush
      end
    end
  rescue Exception
    line_count = Git::line_count(file)
  end
  {
    :file => file,
    :type => type,
    :line_count => line_count,
    :authors_influence => authors
  }
end

.files_statObject

This iterates over files and calls Git::file_stat on each of them. Output will look like this:

{
  :number_of_files => number_of_files,
  :total_line_counts => total_line_counts,
  :file_types => sort_hash(types_count),
  :line_counts_by_type => sort_hash(line_counts_by_type),
  :authors_influence => sort_hash(authors_influence)
}


144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/git-stat.rb', line 144

def self.files_stat
  files = Git::ls_files()
  number_of_files = files.length
  types_count = {}
  line_counts = {}
  line_counts_by_type = {}
  authors_influence = {}
  total_line_counts = 0

  file_index = 0
  files.each do |file|
    file_index += 1
    file_stat = file_stat(file)
    authors_influence = merge_hash_value_counts(authors_influence, file_stat[:authors_influence])
    total_line_counts += file_stat[:line_count]

    if types_count.has_key?(file_stat[:type])
      types_count[file_stat[:type]] += 1
      line_counts_by_type[file_stat[:type]] += file_stat[:line_count]
    else
      types_count[file_stat[:type]] = 1
      line_counts_by_type[file_stat[:type]] = file_stat[:line_count]
    end
    line_counts[file] = Git::line_count(file)
  end

  {
    :number_of_files => number_of_files,
    :total_line_counts => total_line_counts,
    :file_types => sort_hash(types_count),
    :line_counts_by_type => sort_hash(line_counts_by_type),
    :authors_influence => sort_hash(authors_influence)
  }
end

.is_git_project?Boolean

Checks if the current directory is a git project

Returns:

  • (Boolean)


18
19
20
21
# File 'lib/git-stat.rb', line 18

def self.is_git_project?
  `git status`
  $?.success?
end

.line_count(file) ⇒ Object

Returns the number of lines in a file



61
62
63
# File 'lib/git-stat.rb', line 61

def self.line_count(file)
  `git blame "#{file}" | wc -l`.to_i
end

.ls_filesObject

Returns a list of files in the git project



33
34
35
# File 'lib/git-stat.rb', line 33

def self.ls_files
  `git ls-files`.split(/\n/)
end

.merge_hash_value_counts(h1, h2) ⇒ Object

Merges two hashes containing counts together



112
113
114
115
116
117
118
# File 'lib/git-stat.rb', line 112

def self.merge_hash_value_counts(h1, h2)
  h2.each_key.inject(h1) do |result,key|
    result[key] += h2[key] if result.has_key?(key)
    result[key] = h2[key] unless result.has_key?(key)
    result
  end
end

.project_nameObject

Returns the project name, which is assumed to be the current directory’s name



26
27
28
# File 'lib/git-stat.rb', line 26

def self.project_name
  `printf '%s\n' "${PWD##*/}"`.chomp
end

.remote_branchesObject

Returns a list of all remote branches



40
41
42
# File 'lib/git-stat.rb', line 40

def self.remote_branches
  `git branch -a | grep remotes | awk '{print $1}'`.split(/\n/)
end

.sort_hash(hash) ⇒ Object

Creates a “sorted” hash by the value counts. This is simply for easier visual output when serialized to yaml



124
125
126
127
128
129
130
# File 'lib/git-stat.rb', line 124

def self.sort_hash(hash)
  sorted_array = hash.sort_by {|k,v| v}.reverse
  sorted_array.inject({}) do |result,element|
    result[element[0]] = element[1]
    result
  end
end

.tagsObject

Returns a list of tags



47
48
49
# File 'lib/git-stat.rb', line 47

def self.tags
  `git tag -l`.split(/\n/)
end