hangover 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rvmrc ADDED
@@ -0,0 +1,7 @@
1
+ if [[ -s "$rvm_path/environments/ruby-1.9.2-p136@hangover" ]] ; then
2
+ . "$rvm_path/environments/ruby-1.9.2-p136@hangover"
3
+ else
4
+ rvm --create use "ruby-1.9.2-p136@hangover"
5
+ fi
6
+
7
+ rvm wrapper ruby-1.9.2-p136@hangover textmate
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rb-fsevent"
4
+ gem "i18n"
5
+ gem "activesupport"
6
+ gem "jeweler"
data/Gemfile.lock ADDED
@@ -0,0 +1,21 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activesupport (3.0.3)
5
+ git (1.2.5)
6
+ i18n (0.5.0)
7
+ jeweler (1.5.2)
8
+ bundler (~> 1.0.0)
9
+ git (>= 1.2.5)
10
+ rake
11
+ rake (0.8.7)
12
+ rb-fsevent (0.4.0)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ activesupport
19
+ i18n
20
+ jeweler
21
+ rb-fsevent
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Norman Timmler
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,19 @@
1
+ = hangover
2
+
3
+ Description goes here.
4
+
5
+ == Contributing to hangover
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
9
+ * Fork the project
10
+ * Start a feature/bugfix branch
11
+ * Commit and push until you are happy with your contribution
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2011 Norman Timmler. See LICENSE.txt for
18
+ further details.
19
+
data/Rakefile ADDED
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "hangover"
16
+ gem.homepage = "http://github.com/unnu/hangover"
17
+ gem.license = "MIT"
18
+ gem.summary = %Q{Tracks any single change of a complete directory in a git repository.}
19
+ gem.description = %Q{Later.}
20
+ gem.email = "norman.timmler@gmail.com"
21
+ gem.authors = ["unnu"]
22
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
23
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
+ gem.add_runtime_dependency 'rb-fsevent', '>= 0.4.0'
25
+ gem.add_runtime_dependency 'activesupport', '>= 3.0.3'
26
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
27
+ end
28
+ Jeweler::RubygemsDotOrgTasks.new
29
+
30
+ require 'rake/testtask'
31
+ Rake::TestTask.new(:test) do |test|
32
+ test.libs << 'lib' << 'test'
33
+ test.pattern = 'test/**/*_test.rb'
34
+ test.verbose = true
35
+ end
36
+
37
+ task :default => :test
38
+
39
+ require 'rake/rdoctask'
40
+ Rake::RDocTask.new do |rdoc|
41
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
42
+
43
+ rdoc.rdoc_dir = 'rdoc'
44
+ rdoc.title = "hangover #{version}"
45
+ rdoc.rdoc_files.include('README*')
46
+ rdoc.rdoc_files.include('lib/**/*.rb')
47
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/bin/hangover ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ require 'fileutils'
3
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/hangover')
4
+
5
+ command = ARGV.shift
6
+
7
+ begin
8
+ Hangover.new(`pwd`.chomp).__send__(command, *ARGV)
9
+ #rescue => e
10
+ # $stderr.puts "ERROR: #{e.message}"
11
+ # exit(1)
12
+ end
data/foo ADDED
@@ -0,0 +1,5 @@
1
+ Das ist Zeile eins.
2
+ Das ist Zeile zwei.
3
+ Zeile drei ist weg.
4
+
5
+ Mal sehen yo!
@@ -0,0 +1,11 @@
1
+ class CommitMessageBuilder
2
+ attr_reader :message
3
+
4
+ def initialize(adds, subs)
5
+ message_parts = []
6
+ message_parts << "ADDS: #{adds.join(', ')}" unless adds.empty?
7
+ message_parts << "SUBS: #{subs.join(', ')}" unless subs.empty?
8
+
9
+ @message = message_parts.empty? ? 'Minor changes.' : message_parts.join(' - ')
10
+ end
11
+ end
@@ -0,0 +1,47 @@
1
+ class DiffTokenizer
2
+
3
+ def initialize(text)
4
+ lines = text.split("\n")
5
+
6
+ add_lines = select_lines_starting_with(lines, '+')
7
+ substract_lines = select_lines_starting_with(lines, '-')
8
+
9
+ added_tokens = extract_tokens(add_lines)
10
+ substracted_tokens = extract_tokens(substract_lines)
11
+
12
+ cleaned_added_tokens = added_tokens - substracted_tokens.uniq
13
+ cleaned_substracted_tokens = substracted_tokens - added_tokens.uniq
14
+
15
+ @top = {}
16
+ @top[:adds] = count_tokens(cleaned_added_tokens)
17
+ @top[:subs] = count_tokens(cleaned_substracted_tokens)
18
+ end
19
+
20
+ def top(count, type)
21
+ @top[type][0, count].map { |token_and_count| token_and_count.first }
22
+ end
23
+
24
+ def top_adds(count = 5)
25
+ top(count, :adds)
26
+ end
27
+
28
+ def top_subs(count = 5)
29
+ top(count, :subs)
30
+ end
31
+
32
+ private
33
+ def select_lines_starting_with(lines, char)
34
+ escaped_char = Regexp.escape(char)
35
+ lines.select { |line| line =~ Regexp.new("^#{escaped_char}[^#{escaped_char}]") }
36
+ end
37
+
38
+ def extract_tokens(lines)
39
+ lines.map { |line| line.scan(/[\w_]{2,}/) }.flatten
40
+ end
41
+
42
+ def count_tokens(tokens)
43
+ counts = Hash.new(0)
44
+ tokens.each { |token| counts[token] += 1 }
45
+ counts.sort_by { |a, b| b }.reverse
46
+ end
47
+ end
@@ -0,0 +1,67 @@
1
+ require 'pathname'
2
+
3
+ class Repository
4
+
5
+ NAME = '.hangover'
6
+
7
+ class << self
8
+ def find(dir)
9
+ path = Pathname.new(dir).expand_path.realpath
10
+
11
+ begin
12
+ try_path = path + NAME
13
+ return new(path.to_s) if try_path.directory?
14
+ end while (path = path.parent).to_s != '/'
15
+
16
+ nil
17
+ end
18
+ end
19
+
20
+ def initialize(dir)
21
+ @repository = "#{dir}/#{NAME}"
22
+ ENV['GIT_DIR'] = @repository
23
+ ENV['GIT_WORK_TREE'] = dir
24
+ end
25
+
26
+ def exists!
27
+ return if File.exists?(@repository)
28
+
29
+ # TODO: get name of repo from repo dir
30
+ p "Initializing new hangover repo at #{@repository}"
31
+ init
32
+ add
33
+ commit
34
+ end
35
+
36
+ def gitk
37
+ `gitk`
38
+ end
39
+
40
+ def init
41
+ `git init`
42
+ end
43
+
44
+ def add
45
+ `git add .`
46
+ end
47
+
48
+ def commit(message, args = '')
49
+ `git commit #{args} -m "#{message}"`
50
+ end
51
+
52
+ def commit_a(message)
53
+ commit(message, '-a')
54
+ end
55
+
56
+ def diff
57
+ `git diff --unified=0`
58
+ end
59
+
60
+ def clean
61
+ `git clean`
62
+ end
63
+
64
+ def git(args_string)
65
+ `git #{args_string}`
66
+ end
67
+ end
@@ -0,0 +1,16 @@
1
+ class WatchDir
2
+
3
+ def initialize(dir)
4
+ @dir = dir
5
+ @fsevent = FSEvent.new
6
+ end
7
+
8
+ def on_change(&block)
9
+ @fsevent.watch(@dir, :latency => 0.5, :no_defer => true) do |directories|
10
+ directories.select { |dir| dir !~ /\.git|\.hangover/ }.each do |dir|
11
+ yield dir
12
+ end
13
+ end
14
+ @fsevent.run
15
+ end
16
+ end
data/lib/hangover.rb ADDED
@@ -0,0 +1,116 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'rb-fsevent'
4
+ require 'active_support/core_ext'
5
+
6
+ $:.push(File.expand_path(File.dirname(__FILE__)))
7
+
8
+ require 'hangover/repository'
9
+ require 'hangover/diff_tokenizer'
10
+ require 'hangover/commit_message_builder'
11
+ require 'hangover/watch_dir'
12
+
13
+ class Hangover
14
+
15
+ def initialize(base_dir)
16
+ @base_dir = expand_dir(base_dir)
17
+ end
18
+
19
+ def start
20
+ exit_if_running!
21
+ $stderr.puts "Hangover starting..."
22
+ daemonize!
23
+
24
+ WatchDir.new(@base_dir).on_change do |dir|
25
+ repository = Repository.find(dir)
26
+ diff = repository.diff
27
+ next if diff.blank?
28
+
29
+ tokenizer = DiffTokenizer.new(diff)
30
+
31
+ message = CommitMessageBuilder.new(tokenizer.top_adds, tokenizer.top_subs).message
32
+ repository.add
33
+ repository.commit_a(message)
34
+ end
35
+ end
36
+
37
+ def stop
38
+ if running?
39
+ Process.kill(15, pid)
40
+ $stderr.puts "Hangover stopped."
41
+ else
42
+ $stderr.puts "Hangover not running."
43
+ end
44
+ ensure
45
+ remove_pid if File.exist?(pid_file)
46
+ end
47
+
48
+ def create
49
+ Repository.new(@base_dir).exists!
50
+ end
51
+
52
+ def gitk
53
+ Repository.new(@base_dir).gitk
54
+ end
55
+
56
+ def status
57
+ if running?
58
+ $stderr.puts "Hangover is running and watching #{@base_dir}"
59
+ else
60
+ $stderr.puts "Hangover NOT running."
61
+ end
62
+ end
63
+
64
+ def git(*args)
65
+ args_string = args.join(' ')
66
+ $stderr.puts Repository.new(@base_dir).git(args_string)
67
+ end
68
+
69
+ private
70
+ def expand_dir(dir)
71
+ raise ArgumentError, "No dir given!" unless dir
72
+
73
+ dir.replace("#{FileUtils.pwd}/#{dir}") if dir !~ /^\//
74
+ raise ArgumentError, "Dir '#{dir}' does not exist" unless File.directory?(dir)
75
+
76
+ dir
77
+ end
78
+
79
+ def pid
80
+ IO.read(pid_file).to_i if File.exist?(pid_file)
81
+ end
82
+
83
+ def write_pid
84
+ File.open(pid_file, "w") { |f| f.puts Process.pid }
85
+ end
86
+
87
+ def remove_pid
88
+ FileUtils.rm(pid_file) if File.exist?(pid_file)
89
+ end
90
+
91
+ def pid_file
92
+ "#{ENV['HOME']}/.hangover.pid"
93
+ end
94
+
95
+ def running?
96
+ Process.kill(0, pid) == 1 if pid
97
+ rescue Errno::ESRCH
98
+ if File.exist?(pid_file)
99
+ $stderr.puts "Removing stale pid file."
100
+ remove_pid
101
+ end
102
+ false
103
+ end
104
+
105
+ def exit_if_running!
106
+ return unless running?
107
+
108
+ $stderr.puts "Hangover already running."
109
+ exit(0)
110
+ end
111
+
112
+ def daemonize!
113
+ Process.daemon(true, true)
114
+ write_pid
115
+ end
116
+ end
@@ -0,0 +1,17 @@
1
+ require 'test_helper'
2
+
3
+ class CommitMessageBuilderTest < ActiveSupport::TestCase
4
+
5
+ test "show adds if any" do
6
+ assert_equal "ADDS: foo, bar, baz", CommitMessageBuilder.new(%w(foo bar baz), []).message
7
+ end
8
+
9
+ test "show subs if any" do
10
+ assert_equal "SUBS: foo, bar, baz", CommitMessageBuilder.new([], %w(foo bar baz)).message
11
+ end
12
+
13
+ test "show both if any" do
14
+ assert_equal "ADDS: one, two - SUBS: foo, bar, baz", CommitMessageBuilder.new(%w(one two), %w(foo bar baz)).message
15
+ end
16
+ end
17
+
@@ -0,0 +1,22 @@
1
+ require 'test_helper'
2
+
3
+ class DiffTokenizerTest < ActiveSupport::TestCase
4
+
5
+ test "extract subs with token count order ('Das ist Zeile' was extracted three times)" do
6
+ assert_equal %w(Das ist Zeile eins zwei drei), DiffTokenizer.new(diff(:only_subs)).top(100, :subs)
7
+ end
8
+
9
+ test "extract adds" do
10
+ assert_equal %w(dazu Eine), DiffTokenizer.new(diff(:only_one_add)).top(100, :adds)
11
+ end
12
+
13
+ test "extract both only returning tokens that were not in both (adds and subs)" do
14
+ assert_equal %w(weg), DiffTokenizer.new(diff(:one_line_changed)).top(100, :adds)
15
+ assert_equal %w(Das), DiffTokenizer.new(diff(:one_line_changed)).top(100, :subs)
16
+ end
17
+
18
+ private
19
+ def diff(name)
20
+ IO.read(File.dirname(__FILE__) + "/fixtures/#{name}.diff")
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ diff --git a/foo b/foo
2
+ index 0619a69..ed1881e 100644
3
+ --- a/foo
4
+ +++ b/foo
5
+ @@ -1,3 +1,3 @@
6
+ Das ist Zeile eins.
7
+ Das ist Zeile zwei.
8
+ -Das ist Zeile drei.
9
+
10
+ +Zeile drei ist weg.
11
+
@@ -0,0 +1,11 @@
1
+ tika:ruby-1.9.2-p136@hangover hangover[master*]$ git diff foo
2
+ diff --git a/foo b/foo
3
+ index 0619a69..8ce98f0 100644
4
+ --- a/foo
5
+ +++ b/foo
6
+ @@ -1,3 +1,4 @@
7
+ Das ist Zeile eins.
8
+ Das ist Zeile zwei.
9
+ +Eine dazu.
10
+ Das ist Zeile drei.
11
+
@@ -0,0 +1,9 @@
1
+ diff --git a/foo b/foo
2
+ index 0619a69..e69de29 100644
3
+ --- a/foo
4
+ +++ b/foo
5
+ @@ -1,3 +0,0 @@
6
+ -Das ist Zeile eins.
7
+ -Das ist Zeile zwei.
8
+ -Das ist Zeile drei.
9
+
@@ -0,0 +1,4 @@
1
+ require 'test_helper'
2
+
3
+ class HangoverTest < ActiveSupport::TestCase
4
+ end
@@ -0,0 +1,3 @@
1
+ require 'test/unit'
2
+ require 'active_support/test_case'
3
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/hangover')
metadata ADDED
@@ -0,0 +1,171 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hangover
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - unnu
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-03-26 00:00:00 +01:00
18
+ default_executable: hangover
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rb-fsevent
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ prerelease: false
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: i18n
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ segments:
41
+ - 0
42
+ version: "0"
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: *id002
46
+ - !ruby/object:Gem::Dependency
47
+ name: activesupport
48
+ requirement: &id003 !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ segments:
54
+ - 0
55
+ version: "0"
56
+ type: :runtime
57
+ prerelease: false
58
+ version_requirements: *id003
59
+ - !ruby/object:Gem::Dependency
60
+ name: jeweler
61
+ requirement: &id004 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ segments:
67
+ - 0
68
+ version: "0"
69
+ type: :runtime
70
+ prerelease: false
71
+ version_requirements: *id004
72
+ - !ruby/object:Gem::Dependency
73
+ name: rb-fsevent
74
+ requirement: &id005 !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ segments:
80
+ - 0
81
+ - 4
82
+ - 0
83
+ version: 0.4.0
84
+ type: :runtime
85
+ prerelease: false
86
+ version_requirements: *id005
87
+ - !ruby/object:Gem::Dependency
88
+ name: activesupport
89
+ requirement: &id006 !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ segments:
95
+ - 3
96
+ - 0
97
+ - 3
98
+ version: 3.0.3
99
+ type: :runtime
100
+ prerelease: false
101
+ version_requirements: *id006
102
+ description: Later.
103
+ email: norman.timmler@gmail.com
104
+ executables:
105
+ - hangover
106
+ extensions: []
107
+
108
+ extra_rdoc_files:
109
+ - LICENSE.txt
110
+ - README.rdoc
111
+ files:
112
+ - .document
113
+ - .rvmrc
114
+ - Gemfile
115
+ - Gemfile.lock
116
+ - LICENSE.txt
117
+ - README.rdoc
118
+ - Rakefile
119
+ - VERSION
120
+ - bin/hangover
121
+ - foo
122
+ - lib/hangover.rb
123
+ - lib/hangover/commit_message_builder.rb
124
+ - lib/hangover/diff_tokenizer.rb
125
+ - lib/hangover/repository.rb
126
+ - lib/hangover/watch_dir.rb
127
+ - test/commit_message_builder_test.rb
128
+ - test/diff_tokenizer_test.rb
129
+ - test/fixtures/one_line_changed.diff
130
+ - test/fixtures/only_one_add.diff
131
+ - test/fixtures/only_subs.diff
132
+ - test/hangover_test.rb
133
+ - test/test_helper.rb
134
+ has_rdoc: true
135
+ homepage: http://github.com/unnu/hangover
136
+ licenses:
137
+ - MIT
138
+ post_install_message:
139
+ rdoc_options: []
140
+
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ none: false
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ hash: 567810515
149
+ segments:
150
+ - 0
151
+ version: "0"
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ segments:
158
+ - 0
159
+ version: "0"
160
+ requirements: []
161
+
162
+ rubyforge_project:
163
+ rubygems_version: 1.3.7
164
+ signing_key:
165
+ specification_version: 3
166
+ summary: Tracks any single change of a complete directory in a git repository.
167
+ test_files:
168
+ - test/commit_message_builder_test.rb
169
+ - test/diff_tokenizer_test.rb
170
+ - test/hangover_test.rb
171
+ - test/test_helper.rb