├── lib ├── git_pissed │ ├── version.rb │ ├── formats │ │ ├── csv.rb │ │ ├── html.rb │ │ └── templates │ │ │ └── index.html │ ├── git.rb │ ├── format.rb │ ├── cli.rb │ ├── word_finder.rb │ └── options.rb └── git_pissed.rb ├── img ├── graph-100res.gif ├── graph-100res.png ├── graph-15res.png └── graph-love-hate-rainbow.png ├── Gemfile ├── Rakefile ├── .gitignore ├── Gemfile.lock ├── bin └── git-pissed ├── CONTRIBUTING.md ├── CHANGELOG.md ├── git_pissed.gemspec ├── LICENSE.txt └── README.md /lib/git_pissed/version.rb: -------------------------------------------------------------------------------- 1 | module GitPissed 2 | VERSION = '1.1.0' 3 | end 4 | -------------------------------------------------------------------------------- /img/graph-100res.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrishunt/git-pissed/HEAD/img/graph-100res.gif -------------------------------------------------------------------------------- /img/graph-100res.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrishunt/git-pissed/HEAD/img/graph-100res.png -------------------------------------------------------------------------------- /img/graph-15res.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrishunt/git-pissed/HEAD/img/graph-15res.png -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in git_pissed.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /img/graph-love-hate-rainbow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrishunt/git-pissed/HEAD/img/graph-love-hate-rainbow.png -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | desc 'Run all tests' 4 | task :test do 5 | abort ":trollface:" 6 | end 7 | 8 | task :default => :test 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | InstalledFiles 7 | _yardoc 8 | coverage 9 | doc/ 10 | lib/bundler/man 11 | pkg 12 | rdoc 13 | spec/reports 14 | test/tmp 15 | test/version_tmp 16 | tmp 17 | /git-pissed.html 18 | -------------------------------------------------------------------------------- /lib/git_pissed.rb: -------------------------------------------------------------------------------- 1 | require 'optparse' 2 | require 'ruby-progressbar' 3 | 4 | require 'git_pissed/version' 5 | require 'git_pissed/git' 6 | require 'git_pissed/word_finder' 7 | require 'git_pissed/format' 8 | require 'git_pissed/formats/csv' 9 | require 'git_pissed/formats/html' 10 | require 'git_pissed/options' 11 | require 'git_pissed/cli' 12 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | git_pissed (1.1.0) 5 | ruby-progressbar (~> 1.7.5) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | rake (10.4.2) 11 | ruby-progressbar (1.7.5) 12 | 13 | PLATFORMS 14 | ruby 15 | 16 | DEPENDENCIES 17 | bundler (~> 1.10.0) 18 | git_pissed! 19 | rake (~> 10.4.2) 20 | 21 | BUNDLED WITH 22 | 1.10.6 23 | -------------------------------------------------------------------------------- /bin/git-pissed: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # git-pissed 4 | # 5 | # gitting pissed about your code 6 | # 7 | # usage: 8 | # 9 | # # track default set of words (shit, fuck, crap) 10 | # $ git-pissed 11 | # 12 | # # track custom set of words, max of 40 revisions, format as HTML 13 | # $ git-pissed --words=love,hate --max-revisions=40 --format=html 14 | 15 | require 'git_pissed' 16 | 17 | GitPissed::CLI.new.execute 18 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Want to contribute? Awesome! Thank you so much. 4 | 5 | ## How? 6 | 7 | 1. [Fork it](https://help.github.com/articles/fork-a-repo) 8 | 2. Create a feature branch (`git checkout -b my-new-feature`) 9 | 3. Commit changes (`git commit -am 'Add some feature'`) 10 | 4. Push to the branch (`git push origin my-new-feature`) 11 | 5. Create new [Pull 12 | Request](https://help.github.com/articles/using-pull-requests) 13 | -------------------------------------------------------------------------------- /lib/git_pissed/formats/csv.rb: -------------------------------------------------------------------------------- 1 | module GitPissed 2 | class CSV < Format 3 | def name 4 | 'CSV' 5 | end 6 | 7 | def formatted 8 | table.join "\n" 9 | end 10 | 11 | def table 12 | [["date", *options.words].join(',')].tap do |table| 13 | words_by_date.each do |date, words| 14 | table << [date, words.values_at(*options.words)].join(',') 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/git_pissed/formats/html.rb: -------------------------------------------------------------------------------- 1 | module GitPissed 2 | class HTML < Format 3 | def name 4 | 'HTML' 5 | end 6 | 7 | def formatted 8 | File.read( 9 | File.expand_path('../templates/index.html', __FILE__) 10 | ).gsub("%CSV%", csv).gsub("%PATH%", File.basename(Dir.pwd)) 11 | end 12 | 13 | def csv 14 | CSV.new(words_by_date, options). 15 | table. 16 | map { |r| "'#{r.strip}\\n'" }. 17 | join('+') 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/git_pissed/git.rb: -------------------------------------------------------------------------------- 1 | module GitPissed 2 | Git = Struct.new(:options) do 3 | def revisions 4 | revs = `git rev-list --all`.split 5 | 6 | if revs.count > options.max_revisions 7 | revs = revs.each_slice(revs.count / options.max_revisions).map(&:first) 8 | end 9 | 10 | revs 11 | end 12 | 13 | def date_for(revision) 14 | `git show #{revision} --format=format:"%ad" --date=short | head -n1`.strip 15 | end 16 | 17 | def count_for(word, revision) 18 | `git grep -iw #{word} #{revision} | wc -l`.strip.to_i 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/git_pissed/format.rb: -------------------------------------------------------------------------------- 1 | module GitPissed 2 | Format = Struct.new(:words_by_date, :options) do 3 | def to_s 4 | [ "#{name.upcase} has been written to #{file_path}", 5 | "To open:", 6 | " $ open #{file_path}" 7 | ].join("\n") 8 | end 9 | 10 | def file_path 11 | @file_path ||= begin 12 | file = File.open("git-pissed.#{name.downcase}", 'w') 13 | file.write formatted 14 | file.close 15 | file.path 16 | end 17 | end 18 | 19 | def name 20 | raise 'Implement me!' 21 | end 22 | 23 | def formatted 24 | raise 'Implement me!' 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/git_pissed/cli.rb: -------------------------------------------------------------------------------- 1 | module GitPissed 2 | class CLI 3 | def execute 4 | puts "Measuring #{options.words.join(', ')}..." 5 | puts "\n#{formatted_output}" 6 | end 7 | 8 | def options 9 | @options ||= Options.new.parse! 10 | end 11 | 12 | def formatted_output 13 | send options.format 14 | end 15 | 16 | def csv 17 | @csv ||= CSV.new(words_by_date, options) 18 | end 19 | 20 | def html 21 | @html ||= HTML.new(words_by_date, options) 22 | end 23 | 24 | def words_by_date 25 | WordFinder.new( 26 | Git.new(options).revisions, 27 | options 28 | ).by_date 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/git_pissed/word_finder.rb: -------------------------------------------------------------------------------- 1 | module GitPissed 2 | WordFinder = Struct.new(:revisions, :options) do 3 | def by_date 4 | with_sorted_dates do |dates| 5 | revisions.each do |revision| 6 | date = git.date_for(revision) 7 | 8 | options.words.each do |word| 9 | dates[date][word] = [ 10 | dates[date][word], git.count_for(word, revision) 11 | ].max 12 | 13 | progress_bar.increment 14 | end 15 | end 16 | end 17 | end 18 | 19 | def with_sorted_dates 20 | dates = Hash.new { |hash, key| hash[key] = Hash.new(0) } 21 | yield dates 22 | dates.sort { |(k1, v1), (k2, v2)| k1 <=> k2 } 23 | end 24 | 25 | def progress_bar 26 | @progress_bar ||= begin 27 | ProgressBar.create \ 28 | :total => (revisions.count * options.words.count), 29 | :format => '%e <%B> %p%% %t' 30 | end 31 | end 32 | 33 | def git 34 | Git.new(options) 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ##v1.1.0 4 | *2015-01-05* 5 | 6 | - [#15](https://github.com/chrishunt/git-pissed/pull/15) Add current directory to graph title 7 | 8 | ##v1.0.7 9 | *2014-03-02* 10 | 11 | - [#11](https://github.com/chrishunt/git-pissed/pull/11) Update gems 12 | 13 | ##v1.0.6 14 | *2014-01-02* 15 | 16 | - [#8](https://github.com/chrishunt/git-pissed/pull/8) Fix typo in css 17 | - [#9](https://github.com/chrishunt/git-pissed/pull/9) Specify gem versions in gemspec 18 | 19 | ##v1.0.5 20 | *2013-09-06* 21 | 22 | - [#4](https://github.com/chrishunt/git-pissed/pull/4): Fix word ordering for Ruby `1.8.7` 23 | 24 | ##v1.0.4 25 | *2013-09-06* 26 | 27 | - Pass the final data to each formatter instead of the revisions 28 | 29 | ##v1.0.3 30 | *2013-09-03* 31 | 32 | - Ruby 1.8 compatibility 33 | 34 | ##v1.0.2 35 | *2013-09-03* 36 | 37 | - Fix syntax for some early versions of Ruby 1.9 38 | 39 | ##v1.0.1 40 | *2013-09-03* 41 | 42 | - Change project url to GitHub 43 | 44 | ##v1.0.0 45 | *2013-09-03* 46 | 47 | - Initial release 48 | -------------------------------------------------------------------------------- /git_pissed.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'git_pissed/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'git_pissed' 8 | spec.version = GitPissed::VERSION 9 | spec.authors = ['Chris Hunt'] 10 | spec.email = ['c@chrishunt.co'] 11 | spec.description = %q{gitting pissed about your code} 12 | spec.summary = %q{gitting pissed about your code} 13 | spec.homepage = 'https://github.com/chrishunt/git-pissed' 14 | spec.license = 'MIT' 15 | 16 | spec.files = `git ls-files`.split($/) 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ['lib'] 20 | 21 | spec.add_dependency 'ruby-progressbar', '~> 1.7.5' 22 | 23 | spec.add_development_dependency 'bundler', '~> 1.10.0' 24 | spec.add_development_dependency 'rake', '~> 10.4.2' 25 | end 26 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Chris Hunt 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /lib/git_pissed/options.rb: -------------------------------------------------------------------------------- 1 | module GitPissed 2 | class Options 3 | MAX_REVISIONS = 30 4 | 5 | DEFAULT_WORDS = %w( 6 | shit 7 | fuck 8 | crap 9 | ).freeze 10 | 11 | FORMATS = %w( 12 | html 13 | csv 14 | ).freeze 15 | 16 | def words 17 | @words ||= DEFAULT_WORDS 18 | end 19 | 20 | def max_revisions 21 | @max_revisions ||= MAX_REVISIONS 22 | end 23 | 24 | def format 25 | @format ||= FORMATS.first 26 | end 27 | 28 | def parse! 29 | options = OptionParser.new do |opts| 30 | opts.banner = [ 31 | 'usage: git-pissed', 32 | '[--words=]', 33 | '[--max-revisions=]', 34 | "[--format=<#{FORMATS.join('|')}>]", 35 | '[--version]' 36 | ].join(' ') 37 | 38 | opts.separator "\noptions:" 39 | 40 | opts.on( 41 | "--words=#{DEFAULT_WORDS.join(',')}", Array, 42 | 'Words to track across entire history' 43 | ) do |words| 44 | raise OptionParser::InvalidArgument if words.empty? 45 | @words = words 46 | end 47 | 48 | opts.on( 49 | "--max-revisions=#{MAX_REVISIONS}", Integer, 50 | 'Number of revisions to track, spread equally across entire history' 51 | ) do |max_revisions| 52 | @max_revisions = max_revisions 53 | end 54 | 55 | opts.on( 56 | "--format=#{FORMATS.first}", String, 57 | "Output format. Supported formats: #{FORMATS.join(', ')}" 58 | ) do |format| 59 | raise OptionParser::InvalidArgument unless FORMATS.include?(format) 60 | @format = format 61 | end 62 | 63 | opts.on('--version', 'Show version') do 64 | puts VERSION 65 | exit 66 | end 67 | end 68 | 69 | options.parse! 70 | 71 | self 72 | rescue OptionParser::MissingArgument, OptionParser::InvalidArgument 73 | abort options.help 74 | end 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # git-pissed 2 | 3 | ### gitting pissed about your code. 4 | 5 | Are the developers on your team frustrated? Do you wish you had better tests? 6 | Do rainy days get you down? Do you have enough stackoverflow links in your 7 | code? Answer these questions *today* with `git pissed` 8 | 9 | `git pissed` tracks any number of words across your entire git history. The 10 | defaults are **wildly offensive** and inspired by [Vidar Holen's Linux Kernel 11 | Swear Counts](http://www.vidarholen.net/contents/wordcount). 12 | 13 | ![](https://raw.github.com/chrishunt/git-pissed/master/img/graph-100res.gif) 14 | 15 | ## Usage 16 | 17 | Install the gem, open a git repo, and git pissed. 18 | 19 | ```bash 20 | $ gem install git_pissed 21 | $ cd my/git/repo 22 | $ git pissed && open git-pissed.html 23 | ``` 24 | 25 | Sometimes we want to track happiness instead of **ANGER!!** `git pissed` allows 26 | the tracking of any set of words. 27 | 28 | ```bash 29 | $ git pissed --words=love,hate,rainbow 30 | ``` 31 | 32 | ![](https://raw.github.com/chrishunt/git-pissed/master/img/graph-love-hate-rainbow.png) 33 | 34 | If you are a graphing wizard and just want the data, generate a CSV instead. 35 | 36 | ```bash 37 | $ git pissed --format=csv 38 | $ cat git-pissed.csv 39 | date,love,hate,rainbow 40 | 2010-12-27,37,3,2 41 | 2012-03-17,29,5,3 42 | 2012-11-08,35,4,3 43 | 2013-04-08,38,9,3 44 | 2013-09-02,42,9,3 45 | ``` 46 | 47 | Got time to spare and want a really cool graph? Increase the resolution. The 48 | amount specified in `max-revisions` (defaults to 30) will be scanned evenly 49 | throughout the entire git history. 50 | 51 | ```bash 52 | $ git pissed --max-revisions=100 53 | ``` 54 | 55 | ![](https://raw.github.com/chrishunt/git-pissed/master/img/graph-100res.png) 56 | 57 | ```bash 58 | $ git pissed --max-revisions=15 59 | ``` 60 | 61 | ![](https://raw.github.com/chrishunt/git-pissed/master/img/graph-15res.png) 62 | 63 | ## Options 64 | 65 | ```bash 66 | $ git-pissed --help 67 | usage: git-pissed [--words=] [--max-revisions=] [--format=] [--version] 68 | 69 | options: 70 | --words=shit,fuck,crap Words to track across entire history 71 | --max-revisions=30 Number of revisions to track, spread equally across entire history 72 | --format=html Output format. Supported formats: html, csv 73 | --version Show version 74 | ``` 75 | 76 | ## Requirements 77 | `git pissed` requires Ruby `1.8.7` or later. 78 | 79 | ## Installation 80 | 81 | ```bash 82 | $ gem install git_pissed 83 | ``` 84 | 85 | ## Contributing 86 | Please see the [Contributing 87 | Document](https://github.com/chrishunt/git-pissed/blob/master/CONTRIBUTING.md) 88 | 89 | ## Changelog 90 | Please see the [Changelog 91 | Document](https://github.com/chrishunt/git-pissed/blob/master/CHANGELOG.md) 92 | 93 | ## License 94 | Copyright (C) 2013 Chris Hunt, [MIT 95 | License](https://github.com/chrishunt/git-pissed/blob/master/LICENSE.txt) 96 | -------------------------------------------------------------------------------- /lib/git_pissed/formats/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | git pissed 7 | 30 | 31 | 32 | 36 | 37 |
38 | 39 | 61 | 62 | 63 | --------------------------------------------------------------------------------