git_spelunk 0.4.1 → 0.5.2
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.
- checksums.yaml +5 -5
- data/bin/git-spelunk +2 -2
- data/lib/git_spelunk/blame.rb +19 -11
- data/lib/git_spelunk/file_context.rb +15 -26
- data/lib/git_spelunk/offset.rb +24 -44
- data/lib/git_spelunk/ui/pager.rb +1 -1
- data/lib/git_spelunk/ui/repo.rb +1 -2
- data/lib/git_spelunk/ui.rb +20 -10
- data/lib/git_spelunk/version.rb +1 -1
- data/lib/git_spelunk.rb +0 -1
- metadata +7 -9
- data/lib/git_spelunk/grit_patches.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ee9a524ce1012243516ff781bc7ab085710b9201b1fa0781745a97727a5c4a13
|
4
|
+
data.tar.gz: ce58f6b2738fa29f294e2c7ef0ee59ced41c642be2ea5f538eb91a4864feec50
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ffb7863801c8fcadc0ded69633bc09f5da9f5859bab2a935aa9b80dba7721720e4549d09737f62b203c6e256dd20b801303f45fdb2fc8ea976625972af24b13c
|
7
|
+
data.tar.gz: c81a7be76968ed1830a9cbe17918daae13d2ac6105d0c694014da575276aeae996eb33ce68d8ed22d04539c8178d6f31fe9b9891f6256258184990dc46ab4310
|
data/bin/git-spelunk
CHANGED
@@ -25,8 +25,8 @@ end
|
|
25
25
|
|
26
26
|
options = parse_options(ARGV)
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
abort "No filename specified. You must provide a file to blame-on." if ARGV.size == 0
|
29
|
+
abort "Too many arguments" if ARGV.size > 1
|
30
30
|
|
31
31
|
file = ARGV[0]
|
32
32
|
|
data/lib/git_spelunk/blame.rb
CHANGED
@@ -1,7 +1,23 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
1
3
|
module GitSpelunk
|
2
4
|
BlameLine = Struct.new(:line_number, :old_line_number, :sha, :commit, :filename, :content)
|
3
5
|
class EmptyBlame < StandardError ; end
|
4
|
-
class Blame
|
6
|
+
class Blame
|
7
|
+
def initialize(repo, file, sha)
|
8
|
+
@lines = nil
|
9
|
+
@repo = repo
|
10
|
+
@file = file
|
11
|
+
@sha = sha
|
12
|
+
end
|
13
|
+
|
14
|
+
def lines
|
15
|
+
return @lines if @lines
|
16
|
+
cmd = ["git", "--git-dir", @repo.path, "blame", "--porcelain", @sha, "--", @file]
|
17
|
+
output, err, status = Open3.capture3(*cmd)
|
18
|
+
@lines ||= process_raw_blame(output)
|
19
|
+
end
|
20
|
+
|
5
21
|
def process_raw_blame(output)
|
6
22
|
lines = []
|
7
23
|
commits = {}
|
@@ -17,8 +33,7 @@ module GitSpelunk
|
|
17
33
|
|
18
34
|
sha, old_lineno, lineno = sha_split[0], sha_split[1].to_i, sha_split[2].to_i
|
19
35
|
|
20
|
-
|
21
|
-
commits[sha] = nil
|
36
|
+
commits[sha] ||= @repo.lookup(sha)
|
22
37
|
|
23
38
|
if rest =~ /^filename (.*)$/
|
24
39
|
commit_file_map[sha] = $1
|
@@ -28,16 +43,9 @@ module GitSpelunk
|
|
28
43
|
{ :data => data, :sha => sha, :filename => commit_file_map[sha], :old_line_number => old_lineno, :line_number => lineno }
|
29
44
|
end
|
30
45
|
|
31
|
-
|
32
|
-
# load all commits in single call
|
33
|
-
@repo.batch(*commits.keys).each do |commit|
|
34
|
-
commits[commit.id] = commit
|
35
|
-
end
|
36
|
-
|
37
|
-
@lines = lines.map do |hash|
|
46
|
+
lines.map do |hash|
|
38
47
|
GitSpelunk::BlameLine.new(hash[:line_number], hash[:old_line_number], hash[:sha], commits[hash[:sha]], hash[:filename], hash[:data])
|
39
48
|
end
|
40
49
|
end
|
41
50
|
end
|
42
51
|
end
|
43
|
-
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'rugged'
|
2
2
|
require 'fileutils'
|
3
3
|
require 'git_spelunk/blame'
|
4
4
|
|
@@ -12,12 +12,10 @@ module GitSpelunk
|
|
12
12
|
@line_number = options[:line_number] || 1
|
13
13
|
|
14
14
|
@repo = options.fetch(:repo) do
|
15
|
-
|
16
|
-
@file = File.expand_path(file).sub(%r{^#{repo_directory}/}, '')
|
17
|
-
Grit::Repo.new(repo_directory)
|
15
|
+
find_repo_from_file(file)
|
18
16
|
end
|
19
17
|
|
20
|
-
@file
|
18
|
+
@file = File.expand_path(file).sub(@repo.workdir, '')
|
21
19
|
@commit_cache = {}
|
22
20
|
end
|
23
21
|
|
@@ -32,18 +30,7 @@ module GitSpelunk
|
|
32
30
|
end
|
33
31
|
|
34
32
|
def find_repo_from_file(file)
|
35
|
-
|
36
|
-
targets = File.expand_path(file).split('/')
|
37
|
-
targets.pop
|
38
|
-
while !File.directory?(targets.join("/") + "/.git")
|
39
|
-
targets.pop
|
40
|
-
end
|
41
|
-
|
42
|
-
if targets.empty?
|
43
|
-
nil
|
44
|
-
else
|
45
|
-
targets.join("/")
|
46
|
-
end
|
33
|
+
Rugged::Repository.discover(file)
|
47
34
|
end
|
48
35
|
|
49
36
|
def get_blame
|
@@ -58,15 +45,17 @@ module GitSpelunk
|
|
58
45
|
get_blame
|
59
46
|
commit = blame_line.commit
|
60
47
|
return nil unless commit
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
[
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
48
|
+
author = commit.author
|
49
|
+
|
50
|
+
author_info = "%s %s" % [author[:name], author[:email]]
|
51
|
+
short_message = commit.message.split("\n").find { |x| !x.strip.empty? } || ''
|
52
|
+
utc = author[:time]
|
53
|
+
{
|
54
|
+
commit: commit.oid,
|
55
|
+
author: author_info,
|
56
|
+
date: author[:time].to_s,
|
57
|
+
message: commit.message
|
58
|
+
}
|
70
59
|
end
|
71
60
|
end
|
72
61
|
end
|
data/lib/git_spelunk/offset.rb
CHANGED
@@ -33,17 +33,16 @@
|
|
33
33
|
|
34
34
|
|
35
35
|
module GitSpelunk
|
36
|
-
require 'grit'
|
37
|
-
|
38
36
|
class Offset
|
39
|
-
attr_reader :repo, :file_name, :sha, :
|
37
|
+
attr_reader :repo, :file_name, :sha, :hunks
|
40
38
|
|
41
39
|
def initialize(repo, file_name, sha, line_number)
|
42
40
|
@repo = repo
|
43
41
|
@file_name = file_name
|
44
42
|
@sha = sha
|
45
43
|
@line_number = line_number
|
46
|
-
@
|
44
|
+
@commit = @repo.lookup(@sha)
|
45
|
+
@parent = @commit.parents[0]
|
47
46
|
true
|
48
47
|
end
|
49
48
|
|
@@ -51,9 +50,13 @@ module GitSpelunk
|
|
51
50
|
class Chunk
|
52
51
|
attr_reader :minus_offset, :minus_length, :plus_offset, :plus_length, :lines
|
53
52
|
|
54
|
-
def initialize(
|
55
|
-
@minus_offset
|
56
|
-
@
|
53
|
+
def initialize(hunk)
|
54
|
+
@minus_offset = hunk.old_start
|
55
|
+
@minus_length = hunk.old_lines
|
56
|
+
@plus_offset = hunk.new_start
|
57
|
+
@plus_length = hunk.new_lines
|
58
|
+
|
59
|
+
@lines = hunk.lines
|
57
60
|
end
|
58
61
|
|
59
62
|
def has_line?(line_number)
|
@@ -66,7 +69,7 @@ module GitSpelunk
|
|
66
69
|
end
|
67
70
|
|
68
71
|
def type
|
69
|
-
data.first
|
72
|
+
data.first.line_origin
|
70
73
|
end
|
71
74
|
|
72
75
|
def size
|
@@ -79,6 +82,7 @@ module GitSpelunk
|
|
79
82
|
end
|
80
83
|
|
81
84
|
def find_parent_line_number(target)
|
85
|
+
return 1 if target == 1
|
82
86
|
# separate in blocks of lines with the same prefix
|
83
87
|
|
84
88
|
old_line_number = minus_offset
|
@@ -86,16 +90,15 @@ module GitSpelunk
|
|
86
90
|
|
87
91
|
blocks = []
|
88
92
|
lines.each do |l|
|
89
|
-
next if l =~ /\/
|
90
93
|
last_block = blocks.last
|
91
94
|
|
92
|
-
if last_block.nil? || last_block.type != l
|
95
|
+
if last_block.nil? || last_block.type != l.line_origin
|
93
96
|
blocks << LineBlock.new(old_line_number, l)
|
94
97
|
else
|
95
98
|
last_block << l
|
96
99
|
end
|
97
100
|
|
98
|
-
if l
|
101
|
+
if l.line_origin == :context || l.line_origin == :addition
|
99
102
|
if new_line_number == target
|
100
103
|
# important: we don't finish building the structure.
|
101
104
|
break
|
@@ -104,7 +107,7 @@ module GitSpelunk
|
|
104
107
|
new_line_number += 1
|
105
108
|
end
|
106
109
|
|
107
|
-
if l
|
110
|
+
if l.line_origin == :deletion || l.line_origin == :context
|
108
111
|
old_line_number += 1
|
109
112
|
end
|
110
113
|
end
|
@@ -112,7 +115,7 @@ module GitSpelunk
|
|
112
115
|
addition_block = blocks.pop
|
113
116
|
last_old_block = blocks.last
|
114
117
|
|
115
|
-
if last_old_block.type ==
|
118
|
+
if last_old_block.type == :context
|
116
119
|
# if the previous context existed in both, just go to the end of that.
|
117
120
|
last_old_block.offset + (last_old_block.size - 1)
|
118
121
|
else
|
@@ -120,37 +123,14 @@ module GitSpelunk
|
|
120
123
|
last_old_block.offset + [addition_block.size - 1, last_old_block.size].min
|
121
124
|
end
|
122
125
|
end
|
123
|
-
|
124
|
-
private
|
125
|
-
|
126
|
-
def extract_stats(l)
|
127
|
-
#@@ -1,355 +1,355 @@
|
128
|
-
l.scan(STATS_PATTERN).first.map(&:to_i)
|
129
|
-
end
|
130
|
-
|
131
|
-
def old_has?(line)
|
132
|
-
# Src line will either have a "+" or will be an unchanged line
|
133
|
-
line[0] == '-' || line[0] == " "
|
134
|
-
end
|
135
|
-
|
136
|
-
def new_has?(line)
|
137
|
-
# Src line will either have a "-" or will be an unchanged line
|
138
|
-
line[0] == '+' || line[0] == " "
|
139
|
-
end
|
140
126
|
end
|
141
127
|
|
142
|
-
def
|
143
|
-
@
|
144
|
-
|
145
|
-
return nil
|
146
|
-
|
147
|
-
chunks = diffs[0].diff.split(/\n/).inject([[]]) do |arr, line|
|
148
|
-
arr.push([]) if line =~ STATS_PATTERN
|
149
|
-
arr.last << line
|
150
|
-
arr
|
151
|
-
end
|
128
|
+
def hunks
|
129
|
+
@hunks ||= begin
|
130
|
+
diff = @parent.diff(@commit, paths: [@file_name])
|
131
|
+
return nil unless diff
|
152
132
|
|
153
|
-
|
133
|
+
diff.patches[0].hunks.map { |h| Chunk.new(h) }
|
154
134
|
end
|
155
135
|
end
|
156
136
|
|
@@ -159,11 +139,11 @@ module GitSpelunk
|
|
159
139
|
end
|
160
140
|
|
161
141
|
def unable_to_trace_lineage?
|
162
|
-
@parent && (@
|
142
|
+
@parent && (@hunks.nil? || target_chunk.nil?)
|
163
143
|
end
|
164
144
|
|
165
145
|
def line_number_to_parent
|
166
|
-
return :at_beginning_of_time unless @parent &&
|
146
|
+
return :at_beginning_of_time unless @parent && hunks
|
167
147
|
chunk = target_chunk(@line_number)
|
168
148
|
return :unable_to_trace unless chunk
|
169
149
|
|
@@ -175,7 +155,7 @@ module GitSpelunk
|
|
175
155
|
private
|
176
156
|
|
177
157
|
def target_chunk(line_number)
|
178
|
-
|
158
|
+
hunks.find { |c| c.has_line?(line_number) }
|
179
159
|
end
|
180
160
|
end
|
181
161
|
end
|
data/lib/git_spelunk/ui/pager.rb
CHANGED
data/lib/git_spelunk/ui/repo.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module GitSpelunk
|
2
2
|
class UI
|
3
3
|
class RepoWindow
|
4
|
-
attr_accessor :content, :command_mode, :command_buffer
|
4
|
+
attr_accessor :content, :command_mode, :command_buffer, :height
|
5
5
|
|
6
6
|
def initialize(height)
|
7
7
|
@height = height
|
@@ -18,7 +18,6 @@ module GitSpelunk
|
|
18
18
|
|
19
19
|
private
|
20
20
|
|
21
|
-
#with_highlighting do
|
22
21
|
def status_line
|
23
22
|
[
|
24
23
|
"navigation: j k CTRL-D CTRL-U",
|
data/lib/git_spelunk/ui.rb
CHANGED
@@ -6,22 +6,23 @@ module GitSpelunk
|
|
6
6
|
class UI
|
7
7
|
def initialize(file_context)
|
8
8
|
Dispel::Screen.open(:colors => true) do |screen|
|
9
|
-
calculate_heights!
|
10
9
|
@file_context = file_context
|
11
10
|
@history = [file_context]
|
12
11
|
|
13
|
-
@pager = PagerWindow.new(
|
12
|
+
@pager = PagerWindow.new(0)
|
14
13
|
@pager.data = @file_context.get_blame
|
15
14
|
|
16
|
-
@repo = RepoWindow.new(
|
15
|
+
@repo = RepoWindow.new(0)
|
17
16
|
set_repo_content
|
18
17
|
|
19
18
|
@status = StatusWindow.new
|
20
19
|
set_status_message
|
21
20
|
|
21
|
+
calculate_heights!(screen)
|
22
22
|
screen.draw *draw
|
23
|
-
Dispel::Keyboard.output
|
23
|
+
Dispel::Keyboard.output(timeout: 0.5) do |key|
|
24
24
|
handle_key(key)
|
25
|
+
calculate_heights!(screen)
|
25
26
|
screen.draw *draw
|
26
27
|
end
|
27
28
|
end
|
@@ -33,7 +34,7 @@ module GitSpelunk
|
|
33
34
|
view3, style3 = @status.draw
|
34
35
|
|
35
36
|
cursor = if typing?
|
36
|
-
[@
|
37
|
+
[@pager.height + @repo.height, @status.command_buffer.size + 1]
|
37
38
|
else
|
38
39
|
[Curses.lines-1, Curses.cols]
|
39
40
|
end
|
@@ -45,10 +46,10 @@ module GitSpelunk
|
|
45
46
|
]
|
46
47
|
end
|
47
48
|
|
48
|
-
def calculate_heights!
|
49
|
-
|
50
|
-
@
|
51
|
-
@
|
49
|
+
def calculate_heights!(screen)
|
50
|
+
status_height = 1
|
51
|
+
@repo.height = [(screen.lines.to_f * 0.20).to_i, 6].max
|
52
|
+
@pager.height = screen.lines - @repo.height - status_height
|
52
53
|
end
|
53
54
|
|
54
55
|
def set_status_message
|
@@ -56,7 +57,16 @@ module GitSpelunk
|
|
56
57
|
end
|
57
58
|
|
58
59
|
def set_repo_content
|
59
|
-
|
60
|
+
info = @file_context.get_line_commit_info(@pager.blame_line)
|
61
|
+
content = <<-EOL
|
62
|
+
commit: #{info[:commit]}
|
63
|
+
Author: #{info[:author]}
|
64
|
+
Date: #{info[:date]}
|
65
|
+
|
66
|
+
EOL
|
67
|
+
message_lines = info[:message].split(/\r?\n/)
|
68
|
+
content += message_lines[0..(@repo.height - 6)].join("\n")
|
69
|
+
@repo.content = content
|
60
70
|
@repo.draw
|
61
71
|
end
|
62
72
|
|
data/lib/git_spelunk/version.rb
CHANGED
data/lib/git_spelunk.rb
CHANGED
metadata
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: git_spelunk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Osheroff
|
8
8
|
- Saroj Yadav
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2023-01-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
15
|
+
name: rugged
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
18
|
- - ">="
|
@@ -68,7 +68,6 @@ files:
|
|
68
68
|
- lib/git_spelunk.rb
|
69
69
|
- lib/git_spelunk/blame.rb
|
70
70
|
- lib/git_spelunk/file_context.rb
|
71
|
-
- lib/git_spelunk/grit_patches.rb
|
72
71
|
- lib/git_spelunk/offset.rb
|
73
72
|
- lib/git_spelunk/ui.rb
|
74
73
|
- lib/git_spelunk/ui/pager.rb
|
@@ -79,7 +78,7 @@ homepage: https://github.com/osheroff/git-spelunk
|
|
79
78
|
licenses:
|
80
79
|
- MIT
|
81
80
|
metadata: {}
|
82
|
-
post_install_message:
|
81
|
+
post_install_message:
|
83
82
|
rdoc_options: []
|
84
83
|
require_paths:
|
85
84
|
- lib
|
@@ -94,9 +93,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
93
|
- !ruby/object:Gem::Version
|
95
94
|
version: '0'
|
96
95
|
requirements: []
|
97
|
-
|
98
|
-
|
99
|
-
signing_key:
|
96
|
+
rubygems_version: 3.4.2
|
97
|
+
signing_key:
|
100
98
|
specification_version: 4
|
101
99
|
summary: A git tool for exploring history and blame
|
102
100
|
test_files: []
|
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'grit'
|
2
|
-
|
3
|
-
if RUBY_VERSION >= "2.0.0"
|
4
|
-
class String
|
5
|
-
def getord(offset); self[offset].ord; end
|
6
|
-
end
|
7
|
-
|
8
|
-
begin
|
9
|
-
old, $VERBOSE = $VERBOSE, nil
|
10
|
-
PACK_IDX_SIGNATURE = "\377tOc".force_encoding(Encoding::ASCII_8BIT)
|
11
|
-
ensure
|
12
|
-
$VERBOSE = old
|
13
|
-
end
|
14
|
-
end
|