pry-git 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
File without changes
data/README.md ADDED
@@ -0,0 +1,116 @@
1
+ pry-git
2
+ ===========
3
+
4
+ (C) John Mair (banisterfiend) 2011
5
+
6
+ _ruby aware git_
7
+
8
+ Retrieve blame, perform diffs, make commits using the natural units of
9
+ Ruby code.
10
+
11
+ pry-git enables you to diff an individual _method_ , it can show you
12
+ the blame for a method and ultimately allow you to commit 'methods' (rather than amorphous
13
+ 'hunks' of code).
14
+
15
+ pry-git is a plugin for the [pry](http://github.com/banister/pry)
16
+ REPL, a powerful IRB alternative.
17
+
18
+ pry-git is very much proof of concept right now, stay tuned!
19
+
20
+ * NOT AVAILABLE: Install the [gem](https://rubygems.org/gems/pry-git): `gem install pry-git`
21
+ * NOT AVAILABLE: Read the [documentation](http://rdoc.info/github/banister/pry-git/master/file/README.md)
22
+ * See the [source code](http://github.com/banister/pry-git)
23
+
24
+ Example: blame
25
+ --------
26
+
27
+ pry(main)> blame Pry#repl
28
+ John Mair def repl(target=TOPLEVEL_BINDING)
29
+ John Mair target = Pry.binding_for(target)
30
+ John Mair target_self = target.eval('self')
31
+ John Mair
32
+ John Mair repl_prologue(target)
33
+ Mon ouïe
34
+ John Mair # cannot rely on nesting.level as
35
+ John Mair # nesting.level changes with new sessions
36
+ John Mair nesting_level = nesting.size
37
+ John Mair
38
+ John Mair break_data = catch(:breakout) do
39
+ John Mair nesting.push [nesting.size, target_self, self]
40
+ John Mair loop do
41
+ John Mair rep(target)
42
+ John Mair end
43
+ John Mair end
44
+ John Mair
45
+ John Mair return_value = repl_epilogue(target, nesting_level, break_data)
46
+ Lee Jarvis return_value || target_self
47
+ John Mair end
48
+
49
+ Example: diff
50
+ --------
51
+
52
+ pry(main)> diff Pry#repl
53
+ def repl(target=TOPLEVEL_BINDING)
54
+ +
55
+ + # hey baby
56
+ target = Pry.binding_for(target)
57
+ target_self = target.eval('self')
58
+
59
+ + # bink
60
+ repl_prologue(target)
61
+
62
+ # cannot rely on nesting.level as
63
+ # nesting.level changes with new sessions
64
+ nesting_level = nesting.size
65
+
66
+ break_data = catch(:breakout) do
67
+ nesting.push [nesting.size, target_self, self]
68
+ loop do
69
+ rep(target)
70
+ end
71
+ end
72
+
73
+ return_value = repl_epilogue(target, nesting_level, break_data)
74
+ return_value || target_self
75
+ end
76
+ -
77
+ - # Perform a read-eval-print.
78
+ - # If no parameter is given, default to top-level (main).
79
+
80
+ Features and limitations
81
+ -------------------------
82
+
83
+ * commit-method not yet implemented
84
+ * BETA software, not guaranteed to work properly yet, stay tuned.
85
+
86
+ Contact
87
+ -------
88
+
89
+ Problems or questions contact me at [github](http://github.com/banister)
90
+
91
+
92
+ License
93
+ -------
94
+
95
+ (The MIT License)
96
+
97
+ Copyright (c) 2011 John Mair (banisterfiend)
98
+
99
+ Permission is hereby granted, free of charge, to any person obtaining
100
+ a copy of this software and associated documentation files (the
101
+ 'Software'), to deal in the Software without restriction, including
102
+ without limitation the rights to use, copy, modify, merge, publish,
103
+ distribute, sublicense, and/or sell copies of the Software, and to
104
+ permit persons to whom the Software is furnished to do so, subject to
105
+ the following conditions:
106
+
107
+ The above copyright notice and this permission notice shall be
108
+ included in all copies or substantial portions of the Software.
109
+
110
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
111
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
112
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
113
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
114
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
115
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
116
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,68 @@
1
+ $:.unshift 'lib'
2
+
3
+ dlext = Config::CONFIG['DLEXT']
4
+ direc = File.dirname(__FILE__)
5
+
6
+ PROJECT_NAME = "pry-git"
7
+
8
+ require 'rake/clean'
9
+ require 'rake/gempackagetask'
10
+ require "#{PROJECT_NAME}/version"
11
+
12
+ CLOBBER.include("**/*~", "**/*#*", "**/*.log")
13
+ CLEAN.include("**/*#*", "**/*#*.*", "**/*_flymake*.*", "**/*_flymake",
14
+ "**/*.rbc", "**/.#*.*")
15
+
16
+ def apply_spec_defaults(s)
17
+ s.name = PROJECT_NAME
18
+ s.summary = "A Ruby-aware git layer"
19
+ s.version = PryGit::VERSION
20
+ s.date = Time.now.strftime '%Y-%m-%d'
21
+ s.author = "John Mair (banisterfiend)"
22
+ s.email = 'jrmair@gmail.com'
23
+ s.description = s.summary
24
+ s.require_path = 'lib'
25
+ s.homepage = "http://github.com/pry/pry-git"
26
+ s.files = Dir["lib/**/*.rb", "test/*.rb", "CHANGELOG", "README.md", "Rakefile"]
27
+ s.add_dependency("diffy")
28
+ s.add_dependency("grit")
29
+ end
30
+
31
+ desc "run pry with plugin enabled"
32
+ task :pry do
33
+ exec("pry -I#{direc}/lib/ -r #{direc}/lib/#{PROJECT_NAME}")
34
+ end
35
+
36
+ desc "run tests"
37
+ task :test do
38
+ sh "bacon -Itest -rubygems -a"
39
+ end
40
+
41
+ namespace :ruby do
42
+ spec = Gem::Specification.new do |s|
43
+ apply_spec_defaults(s)
44
+ s.platform = Gem::Platform::RUBY
45
+ end
46
+
47
+ Rake::GemPackageTask.new(spec) do |pkg|
48
+ pkg.need_zip = false
49
+ pkg.need_tar = false
50
+ end
51
+ end
52
+
53
+ desc "build all platform gems at once"
54
+ task :gems => [:clean, :rmgems, "ruby:gem"]
55
+
56
+ desc "remove all platform gems"
57
+ task :rmgems => ["ruby:clobber_package"]
58
+
59
+ desc "build and push latest gems"
60
+ task :pushgems => :gems do
61
+ chdir("#{File.dirname(__FILE__)}/pkg") do
62
+ Dir["*.gem"].each do |gemfile|
63
+ sh "gem push #{gemfile}"
64
+ end
65
+ end
66
+ end
67
+
68
+
data/lib/pry-git.rb ADDED
@@ -0,0 +1,117 @@
1
+ # pry-git.rb
2
+ # (C) John Mair (banisterfiend); MIT license
3
+
4
+ require "pry-git/version"
5
+ require "pry"
6
+ require 'grit'
7
+ require 'diffy'
8
+ require 'tempfile'
9
+
10
+ module PryGit
11
+ GitCommands = Pry::CommandSet.new do
12
+ command "git blame", "Show blame for a method (for method in HEAD)" do |meth_name|
13
+ if (meth = get_method_object(meth_name, target, {})).nil?
14
+ output.puts "Invalid method name: #{meth_name}."
15
+ next
16
+ end
17
+
18
+ file_name = meth.source_location.first
19
+ code, start_line = method_code_from_head(meth)
20
+
21
+ git_root = find_git_root(File.dirname(file_name))
22
+
23
+ repo = Grit::Repo.new(git_root)
24
+ num_lines = code.lines.count
25
+ authors = repo.blame(relative_path(git_root, file_name), repo.head.commit).lines.select do |v|
26
+ v.lineno >= start_line && v.lineno <= start_line + num_lines
27
+ end.map do |v|
28
+ v.commit.author.output(Time.new).split(/</).first.strip
29
+ end
30
+
31
+ lines_with_blame = []
32
+ code.lines.zip(authors) { |line, author| lines_with_blame << ("#{author}".ljust(10) + colorize_code(line)) }
33
+ output.puts lines_with_blame.join
34
+ end
35
+
36
+ command "git diff", "Show the diff for a method (working directory vs HEAD)" do |meth_name|
37
+
38
+ if (meth = get_method_object(meth_name, target, {})).nil?
39
+ output.puts "Invalid method name: #{meth_name}."
40
+ next
41
+ end
42
+
43
+ output.puts colorize_code(Diffy::Diff.new(method_code_from_head(meth).first, meth.source))
44
+ end
45
+
46
+ command "git add", "Add a method to index" do |meth_name|
47
+ if (meth = get_method_object(meth_name, target, {})).nil?
48
+ output.puts "Invalid method name: #{meth_name}."
49
+ next
50
+ end
51
+
52
+ file_name = meth.source_location.first
53
+
54
+ git_root = find_git_root(File.dirname(file_name))
55
+ repo = Grit::Repo.new(git_root)
56
+
57
+ rel_file_name = relative_path(git_root, meth.source_location.first)
58
+ file_data = get_file_from_commit(file_name)
59
+ code, start_line = method_code_from_head(meth)
60
+ end_line = start_line + code.lines.count
61
+
62
+ before_code = file_data.lines.to_a[0..(start_line - 2)]
63
+ after_code = file_data.lines.to_a[end_line - 1..-1]
64
+
65
+ final_code = before_code << meth.source.lines.to_a << after_code
66
+
67
+ t = Tempfile.new("tmp")
68
+ t.write final_code.join
69
+ t.close
70
+
71
+ sha1 = `git hash-object -w #{t.path}`.chomp
72
+ system("git update-index --cacheinfo 100644 #{sha1} #{rel_file_name}")
73
+ end
74
+
75
+ helpers do
76
+ def get_file_from_commit(path)
77
+ git_root = find_git_root(File.dirname(path))
78
+ repo = Grit::Repo.new(git_root)
79
+ head = repo.commits.first
80
+ tree_names = relative_path(git_root, path).split("/")
81
+ start_tree = head.tree
82
+ blob_name = tree_names.last
83
+ tree = tree_names[0..-2].inject(start_tree) { |a, v| a.trees.find { |t| t.basename == v } }
84
+ blob = tree.blobs.find { |v| v.basename == blob_name }
85
+ blob.data
86
+ end
87
+
88
+ def method_code_from_head(meth)
89
+ code = get_file_from_commit(meth.source_location.first)
90
+ search_line = meth.source.lines.first.strip
91
+ _, start_line = code.lines.to_a.each_with_index.find { |v, i| v.strip == search_line }
92
+ start_line
93
+ [Pry.new(:input => StringIO.new(code.lines.to_a[start_line..-1].join)).r(target), start_line + 1]
94
+ end
95
+
96
+ def relative_path(root, path)
97
+ path =~ /#{root}\/(.*)/
98
+ $1
99
+ end
100
+
101
+ # return the git top level for a give directory
102
+ def find_git_root(dir)
103
+ git_root = "."
104
+ Dir.chdir dir do
105
+ git_root = `git rev-parse --show-toplevel`.chomp
106
+ end
107
+
108
+ raise "No associated git repository found!" if git_root =~ /fatal:/
109
+ git_root
110
+ end
111
+
112
+ end
113
+
114
+ end
115
+ end
116
+
117
+ Pry.commands.import PryGit::GitCommands
@@ -0,0 +1,3 @@
1
+ module PryGit
2
+ VERSION = "0.2.0"
3
+ end
data/test/test.rb ADDED
@@ -0,0 +1,12 @@
1
+ direc = File.dirname(__FILE__)
2
+
3
+ require 'rubygems'
4
+ require "#{direc}/../lib/pry-git"
5
+ require 'bacon'
6
+
7
+ puts "Testing pry-git version #{PryGit::VERSION}..."
8
+ puts "Ruby version: #{RUBY_VERSION}"
9
+
10
+ describe PryGit do
11
+ end
12
+
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pry-git
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.2.0
6
+ platform: ruby
7
+ authors:
8
+ - John Mair (banisterfiend)
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-07-19 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: diffy
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ type: :runtime
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: grit
28
+ prerelease: false
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: "0"
35
+ type: :runtime
36
+ version_requirements: *id002
37
+ description: A Ruby-aware git layer
38
+ email: jrmair@gmail.com
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ extra_rdoc_files: []
44
+
45
+ files:
46
+ - lib/pry-git/version.rb
47
+ - lib/pry-git.rb
48
+ - test/test.rb
49
+ - CHANGELOG
50
+ - README.md
51
+ - Rakefile
52
+ homepage: http://github.com/pry/pry-git
53
+ licenses: []
54
+
55
+ post_install_message:
56
+ rdoc_options: []
57
+
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0"
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.7.2
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: A Ruby-aware git layer
79
+ test_files: []
80
+