git_spelunk 0.4.1 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|