git-reviewer 0.1.0
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 +7 -0
- data/.solargraph.yml +33 -0
- data/.vscode/launch.json +14 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +109 -0
- data/README.md +37 -0
- data/Rakefile +4 -0
- data/exe/git-reviewer +5 -0
- data/lib/gitreviewer/algorithm/myers.rb +169 -0
- data/lib/gitreviewer/analyze/analyzer.rb +172 -0
- data/lib/gitreviewer/analyze/blame_tree.rb +68 -0
- data/lib/gitreviewer/analyze/builder.rb +141 -0
- data/lib/gitreviewer/analyze/diff_tree.rb +154 -0
- data/lib/gitreviewer/analyze/result_item.rb +31 -0
- data/lib/gitreviewer/command.rb +112 -0
- data/lib/gitreviewer/config/configuration.rb +93 -0
- data/lib/gitreviewer/option/analyze_option.rb +85 -0
- data/lib/gitreviewer/option/init_option.rb +57 -0
- data/lib/gitreviewer/utils/checker.rb +63 -0
- data/lib/gitreviewer/utils/printer.rb +63 -0
- data/lib/gitreviewer/version.rb +3 -0
- data/lib/gitreviewer.rb +9 -0
- data/sig/git/reviewer.rbs +6 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 26093e3ace921d56e1cc429eab08775761614af03564508982f0a5f2b1490b8b
|
4
|
+
data.tar.gz: f7caa02138858499bc212b4ecf3e34d4d66a6a5edb6b11c9a844716c757dc85d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d8dc85d6db503350493a26338cd55666486034a8de07eb630d566629b5267e0a10ef13190f460811a1b8c2ced1a221c6dbca95229aee26d5b324c917684b2edf
|
7
|
+
data.tar.gz: fdcefb88e454e9d49407475975454f25868f96fdd39ad041ef02fd3f9f5009cfdf82b8efcf5cdfe047988f2efe2dddf8ee2dcbddbb1016cece0eed948aa0b84b
|
data/.solargraph.yml
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
---
|
2
|
+
include:
|
3
|
+
- "**/*.rb"
|
4
|
+
exclude:
|
5
|
+
- spec/**/*
|
6
|
+
- test/**/*
|
7
|
+
- vendor/**/*
|
8
|
+
- ".bundle/**/*"
|
9
|
+
require: []
|
10
|
+
domains: []
|
11
|
+
reporters:
|
12
|
+
- rubocop
|
13
|
+
- require_not_found
|
14
|
+
formatter:
|
15
|
+
rubocop:
|
16
|
+
cops: safe
|
17
|
+
except: []
|
18
|
+
only: []
|
19
|
+
extra_args: []
|
20
|
+
require_paths: []
|
21
|
+
plugins: []
|
22
|
+
max_files: 5000
|
23
|
+
|
24
|
+
interpreter:
|
25
|
+
name: "ruby"
|
26
|
+
version: "2.6.3"
|
27
|
+
|
28
|
+
require_paths:
|
29
|
+
- "lib"
|
30
|
+
|
31
|
+
gems:
|
32
|
+
- "claide"
|
33
|
+
- "rspec"
|
data/.vscode/launch.json
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"version": "0.2.0",
|
3
|
+
"configurations": [
|
4
|
+
{
|
5
|
+
"type": "rdbg",
|
6
|
+
"name": "Debug",
|
7
|
+
"request": "launch",
|
8
|
+
"cwd": "${workspaceFolder}",
|
9
|
+
"command": "bundle exec ruby",
|
10
|
+
"script": "${workspaceFolder}/exe/git-reviewer",
|
11
|
+
"args": ["--target=main", "--verbose"]
|
12
|
+
}
|
13
|
+
]
|
14
|
+
}
|
data/Gemfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#source "https://rubygems.org"
|
4
|
+
source "https://gems.ruby-china.com/"
|
5
|
+
|
6
|
+
# Specify your gem's dependencies in git-reviewer.gemspec
|
7
|
+
gemspec
|
8
|
+
|
9
|
+
gem "rake", "~> 13.0"
|
10
|
+
gem "rspec", "~> 3.0"
|
11
|
+
gem 'debug'
|
12
|
+
gem "solargraph"
|
13
|
+
gem "terminal-table"
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
git-reviewer (0.1.0)
|
5
|
+
claide (~> 1.0.3)
|
6
|
+
terminal-table
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://gems.ruby-china.com/
|
10
|
+
specs:
|
11
|
+
ast (2.4.2)
|
12
|
+
backport (1.2.0)
|
13
|
+
benchmark (0.3.0)
|
14
|
+
claide (1.0.3)
|
15
|
+
debug (1.8.0)
|
16
|
+
irb (>= 1.5.0)
|
17
|
+
reline (>= 0.3.1)
|
18
|
+
diff-lcs (1.5.1)
|
19
|
+
e2mmap (0.1.0)
|
20
|
+
io-console (0.7.2)
|
21
|
+
irb (1.6.3)
|
22
|
+
reline (>= 0.3.0)
|
23
|
+
jaro_winkler (1.5.6)
|
24
|
+
json (2.7.1)
|
25
|
+
kramdown (2.4.0)
|
26
|
+
rexml
|
27
|
+
kramdown-parser-gfm (1.1.0)
|
28
|
+
kramdown (~> 2.0)
|
29
|
+
nokogiri (1.13.10-arm64-darwin)
|
30
|
+
racc (~> 1.4)
|
31
|
+
nokogiri (1.13.10-x86_64-darwin)
|
32
|
+
racc (~> 1.4)
|
33
|
+
parallel (1.24.0)
|
34
|
+
parser (3.3.0.5)
|
35
|
+
ast (~> 2.4.1)
|
36
|
+
racc
|
37
|
+
racc (1.7.3)
|
38
|
+
rainbow (3.1.1)
|
39
|
+
rake (13.1.0)
|
40
|
+
rbs (2.8.4)
|
41
|
+
regexp_parser (2.9.0)
|
42
|
+
reline (0.5.0)
|
43
|
+
io-console (~> 0.5)
|
44
|
+
reverse_markdown (2.1.1)
|
45
|
+
nokogiri
|
46
|
+
rexml (3.2.6)
|
47
|
+
rspec (3.13.0)
|
48
|
+
rspec-core (~> 3.13.0)
|
49
|
+
rspec-expectations (~> 3.13.0)
|
50
|
+
rspec-mocks (~> 3.13.0)
|
51
|
+
rspec-core (3.13.0)
|
52
|
+
rspec-support (~> 3.13.0)
|
53
|
+
rspec-expectations (3.13.0)
|
54
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
55
|
+
rspec-support (~> 3.13.0)
|
56
|
+
rspec-mocks (3.13.0)
|
57
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
58
|
+
rspec-support (~> 3.13.0)
|
59
|
+
rspec-support (3.13.1)
|
60
|
+
rubocop (1.50.2)
|
61
|
+
json (~> 2.3)
|
62
|
+
parallel (~> 1.10)
|
63
|
+
parser (>= 3.2.0.0)
|
64
|
+
rainbow (>= 2.2.2, < 4.0)
|
65
|
+
regexp_parser (>= 1.8, < 3.0)
|
66
|
+
rexml (>= 3.2.5, < 4.0)
|
67
|
+
rubocop-ast (>= 1.28.0, < 2.0)
|
68
|
+
ruby-progressbar (~> 1.7)
|
69
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
70
|
+
rubocop-ast (1.30.0)
|
71
|
+
parser (>= 3.2.1.0)
|
72
|
+
ruby-progressbar (1.13.0)
|
73
|
+
solargraph (0.50.0)
|
74
|
+
backport (~> 1.2)
|
75
|
+
benchmark
|
76
|
+
bundler (~> 2.0)
|
77
|
+
diff-lcs (~> 1.4)
|
78
|
+
e2mmap
|
79
|
+
jaro_winkler (~> 1.5)
|
80
|
+
kramdown (~> 2.3)
|
81
|
+
kramdown-parser-gfm (~> 1.1)
|
82
|
+
parser (~> 3.0)
|
83
|
+
rbs (~> 2.0)
|
84
|
+
reverse_markdown (~> 2.0)
|
85
|
+
rubocop (~> 1.38)
|
86
|
+
thor (~> 1.0)
|
87
|
+
tilt (~> 2.0)
|
88
|
+
yard (~> 0.9, >= 0.9.24)
|
89
|
+
terminal-table (3.0.2)
|
90
|
+
unicode-display_width (>= 1.1.1, < 3)
|
91
|
+
thor (1.3.1)
|
92
|
+
tilt (2.3.0)
|
93
|
+
unicode-display_width (2.5.0)
|
94
|
+
yard (0.9.36)
|
95
|
+
|
96
|
+
PLATFORMS
|
97
|
+
arm64-darwin-22
|
98
|
+
x86_64-darwin-22
|
99
|
+
|
100
|
+
DEPENDENCIES
|
101
|
+
debug
|
102
|
+
git-reviewer!
|
103
|
+
rake (~> 13.0)
|
104
|
+
rspec (~> 3.0)
|
105
|
+
solargraph
|
106
|
+
terminal-table
|
107
|
+
|
108
|
+
BUNDLED WITH
|
109
|
+
2.3.9
|
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+

|
2
|
+
|
3
|
+

|
4
|
+

|
5
|
+

|
6
|
+

|
7
|
+

|
8
|
+

|
9
|
+
# Git::Reviewer
|
10
|
+
|
11
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/git/reviewer`. To experiment with that code, run `bin/console` for an interactive prompt.
|
12
|
+
|
13
|
+
TODO: Delete this and the text above, and describe your gem
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Install the gem and add to the application's Gemfile by executing:
|
18
|
+
|
19
|
+
$ bundle add git-reviewer
|
20
|
+
|
21
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
22
|
+
|
23
|
+
$ gem install git-reviewer
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
TODO: Write usage instructions here
|
28
|
+
|
29
|
+
## Development
|
30
|
+
|
31
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
32
|
+
|
33
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
34
|
+
|
35
|
+
## Contributing
|
36
|
+
|
37
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/git-reviewer.
|
data/Rakefile
ADDED
data/exe/git-reviewer
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
require_relative '../analyze/blame_tree'
|
2
|
+
require_relative '../analyze/diff_tree'
|
3
|
+
|
4
|
+
module GitReviewer
|
5
|
+
class Myers
|
6
|
+
# Type: BlameFile
|
7
|
+
attr_accessor :source, :target
|
8
|
+
|
9
|
+
def initialize(source, target)
|
10
|
+
self.source = source
|
11
|
+
self.target = target
|
12
|
+
end
|
13
|
+
|
14
|
+
def resolve
|
15
|
+
# 字符串 a 和 b 的长度,分别为 n 和 m
|
16
|
+
m = source.blame_lines.count
|
17
|
+
n = target.blame_lines.count
|
18
|
+
|
19
|
+
# 用于存储每条 K 线上最佳位置的 Map
|
20
|
+
v = { 1 => 0 }
|
21
|
+
# 用于存储所有深度的所有最佳位置的 Map,用于回溯编辑路径
|
22
|
+
vs = { 0 => { 1 => 0 } }
|
23
|
+
|
24
|
+
loop do
|
25
|
+
# 外层循环,遍历深度
|
26
|
+
(0..m + n).each do |d|
|
27
|
+
tmp = {}
|
28
|
+
# 内层循环,宽度优先搜索,遍历 K 线
|
29
|
+
(-d..d).step(2) do |k|
|
30
|
+
down = ((k == -d) || ((k != d) && v[k + 1] > v[k - 1]))
|
31
|
+
k_prev = down ? k + 1 : k - 1
|
32
|
+
# 获取移动的起点位置
|
33
|
+
x_start = v[k_prev]
|
34
|
+
y_start = x_start - k_prev
|
35
|
+
# 获取移动一步的中间位置,向右或向下
|
36
|
+
x_mid = down ? x_start : x_start + 1
|
37
|
+
y_mid = x_mid - k
|
38
|
+
# 获取移动的终点位置,后续可能会向右下移动。
|
39
|
+
x_end = x_mid
|
40
|
+
y_end = y_mid
|
41
|
+
|
42
|
+
# 向右下移动,深度始终不变
|
43
|
+
while x_end < m && y_end < n && source.blame_lines[x_end] == target.blame_lines[y_end]
|
44
|
+
x_end += 1
|
45
|
+
y_end += 1
|
46
|
+
end
|
47
|
+
|
48
|
+
# 记录对应 K 线所能达到的最佳位置
|
49
|
+
v[k] = x_end
|
50
|
+
|
51
|
+
tmp[k] = x_end
|
52
|
+
|
53
|
+
# 如果两个字符串均到达末端,表示找到了终点,可以结束查找
|
54
|
+
if x_end == m && y_end == n
|
55
|
+
vs[d] = tmp
|
56
|
+
# 生成最短编辑路径
|
57
|
+
snakes = route(vs, m, n, d)
|
58
|
+
# 打印最短编辑路径
|
59
|
+
result = build_diff(snakes)
|
60
|
+
return result
|
61
|
+
end
|
62
|
+
end
|
63
|
+
# 记录深度为 D 的所有 K 线的最佳位置
|
64
|
+
vs[d] = tmp
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def route(vs, m, n, d)
|
70
|
+
snakes = []
|
71
|
+
# 定义位置结构
|
72
|
+
pos = { x: m, y: n }
|
73
|
+
|
74
|
+
# 回溯最短编辑路径
|
75
|
+
while d > 0
|
76
|
+
v = vs[d]
|
77
|
+
v_prev = vs[d - 1]
|
78
|
+
|
79
|
+
k = pos[:x] - pos[:y]
|
80
|
+
# 判断之前位置到当前位置最开始移动的方式,向下或向右
|
81
|
+
down = ((k == -d) || ((k != d) && (v_prev[k + 1] > v_prev[k - 1])))
|
82
|
+
k_prev = down ? k + 1 : k - 1
|
83
|
+
|
84
|
+
# 当前位置
|
85
|
+
x_end = v[k]
|
86
|
+
y_end = x_end - k
|
87
|
+
|
88
|
+
# 之前位置
|
89
|
+
x_start = v_prev[k_prev]
|
90
|
+
y_start = x_start - k_prev
|
91
|
+
|
92
|
+
# 中间走斜线时的起始位置
|
93
|
+
x_mid = down ? x_start : x_start + 1
|
94
|
+
y_mid = x_mid - k
|
95
|
+
|
96
|
+
snakes.unshift([x_start, x_mid, x_end])
|
97
|
+
|
98
|
+
pos[:x] = x_start
|
99
|
+
pos[:y] = y_start
|
100
|
+
|
101
|
+
d -= 1
|
102
|
+
end
|
103
|
+
snakes
|
104
|
+
end
|
105
|
+
|
106
|
+
def build_diff(snakes)
|
107
|
+
diff_result = []
|
108
|
+
y_offset = 0
|
109
|
+
|
110
|
+
snakes.each_with_index do |snake, index|
|
111
|
+
s = snake[0]
|
112
|
+
m = snake[1]
|
113
|
+
e = snake[2]
|
114
|
+
|
115
|
+
# 如果是第一个差异,并且差异的开始点不是字符串头(即两字符串在开始部分有相同子字符串)
|
116
|
+
if index === 0 && s != 0
|
117
|
+
# 所有相同字符,直到s
|
118
|
+
(0..s - 1).each do |j|
|
119
|
+
diff_line = DiffLine.new(source.blame_lines[s], target.blame_lines[y_offset], DiffLine::UNCHANGE)
|
120
|
+
diff_result.append(diff_line)
|
121
|
+
y_offset += 1
|
122
|
+
end
|
123
|
+
end
|
124
|
+
if m - s == 1
|
125
|
+
# 用红色打印删除的字符
|
126
|
+
diff_line = DiffLine.new(source.blame_lines[s], nil, DiffLine::DELETE)
|
127
|
+
diff_result.append(diff_line)
|
128
|
+
else
|
129
|
+
# 用绿色打印插入的字符
|
130
|
+
diff_line = DiffLine.new(nil, target.blame_lines[y_offset], DiffLine::ADD)
|
131
|
+
diff_result.append(diff_line)
|
132
|
+
y_offset += 1
|
133
|
+
end
|
134
|
+
# 相同的字符
|
135
|
+
(0..e - m - 1).each do |i|
|
136
|
+
diff_line = DiffLine.new(source.blame_lines[s], target.blame_lines[y_offset], DiffLine::UNCHANGE)
|
137
|
+
diff_result.append(diff_line)
|
138
|
+
y_offset += 1
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# 确定属性
|
143
|
+
operation = DiffFile::UNKNOWN
|
144
|
+
binary = false
|
145
|
+
if source.exist? && target.exist?
|
146
|
+
operation = DiffFile::MODIFY
|
147
|
+
binary = target.binary?
|
148
|
+
elsif source.exist? && !target.exist?
|
149
|
+
operation = DiffFile::DELETE
|
150
|
+
binary = source.binary?
|
151
|
+
elsif !source.exist? && target.exist?
|
152
|
+
operation = DiffFile::ADD
|
153
|
+
binary = target.binary?
|
154
|
+
else
|
155
|
+
operation = DiffFile::UNKNOWN
|
156
|
+
binary = false
|
157
|
+
end
|
158
|
+
|
159
|
+
# 确定结果
|
160
|
+
result = DiffFile.new(source.file_name, diff_result, operation, binary)
|
161
|
+
|
162
|
+
# # 打印内容
|
163
|
+
result.print_meta_info
|
164
|
+
Printer.verbose_put "#{result.format_line_diff}"
|
165
|
+
|
166
|
+
return result
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require_relative 'blame_tree'
|
3
|
+
require_relative 'builder'
|
4
|
+
require_relative '../utils/printer'
|
5
|
+
require_relative '../utils/checker'
|
6
|
+
require_relative '../config/configuration'
|
7
|
+
require_relative 'result_item'
|
8
|
+
|
9
|
+
module GitReviewer
|
10
|
+
|
11
|
+
|
12
|
+
class Analyzer
|
13
|
+
attr_accessor :source_branch # source branch
|
14
|
+
attr_accessor :target_branch # target branch
|
15
|
+
attr_accessor :builder # blame builder
|
16
|
+
|
17
|
+
attr_accessor :author_results #
|
18
|
+
attr_accessor :reviewer_results #
|
19
|
+
attr_accessor :configuration # 配置信息 Configuration
|
20
|
+
|
21
|
+
def initialize(source_branch, target_branch)
|
22
|
+
@source_branch = source_branch
|
23
|
+
@target_branch = target_branch
|
24
|
+
@author_results = Hash.new
|
25
|
+
@reviewer_results = Hash.new
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def setup_builder
|
30
|
+
# 构建 BlameTree
|
31
|
+
@builder = Builder.new(@source_branch, @target_branch)
|
32
|
+
@builder.build
|
33
|
+
|
34
|
+
if @builder.source_blame.blame_files.count == 0
|
35
|
+
Printer.warning "Warning: no blame file for source blame branch<#{@source_branch}>"
|
36
|
+
exit 1
|
37
|
+
end
|
38
|
+
|
39
|
+
if @builder.target_blame.blame_files.count == 0
|
40
|
+
Printer.warning "Warning: no blame file for target blame branch<#{@target_branch}>"
|
41
|
+
exit 1
|
42
|
+
end
|
43
|
+
|
44
|
+
if @builder.source_blame.blame_files.count != @builder.target_blame.blame_files.count
|
45
|
+
Printer.red "Error: internal error. The number of files is not equal."
|
46
|
+
exit 1
|
47
|
+
end
|
48
|
+
|
49
|
+
if @builder.diff_files == nil
|
50
|
+
Printer.red "Error: internal error. The diff files of builder is nil."
|
51
|
+
exit 1
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def setup_configuration
|
56
|
+
file_name = ".gitreviewer.yml"
|
57
|
+
file_exist = File.exist?(file_name)
|
58
|
+
# 检测配置文件
|
59
|
+
if !file_exist
|
60
|
+
Printer.red "Error: `.gitreviewer.yml` not exist in current directory. Please execute `git reviewer --init` first."
|
61
|
+
exit 1
|
62
|
+
end
|
63
|
+
# 解析配置文件
|
64
|
+
data = YAML.load_file(file_name)
|
65
|
+
@configuration = Configuration.new(data['project_owner'], data['folder_owner'], data['file_owner'], data['ignore_files'], data['ignore_folders'])
|
66
|
+
end
|
67
|
+
|
68
|
+
def execute
|
69
|
+
setup_builder
|
70
|
+
setup_configuration
|
71
|
+
analyze_author
|
72
|
+
analyze_reviewer
|
73
|
+
end
|
74
|
+
|
75
|
+
def analyze_author
|
76
|
+
@builder.diff_files.each do |fdiff|
|
77
|
+
fdiff.diff_lines.each_with_index do |ldiff, index|
|
78
|
+
record_author(fdiff, ldiff)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def analyze_reviewer
|
84
|
+
@builder.diff_files.each do |fdiff|
|
85
|
+
reviewer = nil
|
86
|
+
lines = 0
|
87
|
+
|
88
|
+
# 其他操作按行计算权重
|
89
|
+
fdiff.diff_lines.each_with_index do |ldiff, index|
|
90
|
+
if ldiff.operation == DiffLine::DELETE
|
91
|
+
# 删除行: 由原作者 review
|
92
|
+
reviewer = ldiff.source_line.user
|
93
|
+
record_reviewer(fdiff, reviewer, 1)
|
94
|
+
elsif ldiff.operation == DiffLine::ADD
|
95
|
+
# 新增行
|
96
|
+
if reviewer != nil
|
97
|
+
# 紧随删除,由删除行的原作者 review
|
98
|
+
lines += 1
|
99
|
+
else
|
100
|
+
# 非紧随删除,由 configuration 决定谁来 review
|
101
|
+
reviewer = @configuration.reviewer_of_file(fdiff.file_name)
|
102
|
+
record_reviewer(fdiff, reviewer, 1)
|
103
|
+
end
|
104
|
+
else
|
105
|
+
# 未变化
|
106
|
+
if reviewer != nil
|
107
|
+
# 处理编辑类型
|
108
|
+
record_reviewer(fdiff, reviewer, lines)
|
109
|
+
reviewer = nil
|
110
|
+
lines = 0
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# 最后一行
|
115
|
+
if index == fdiff.diff_lines.count - 1 && reviewer != nil
|
116
|
+
record_reviewer(fdiff, reviewer, lines)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def record_author(fdiff, ldiff)
|
123
|
+
file_name = fdiff.file_name
|
124
|
+
if @configuration.is_ignore?(file_name)
|
125
|
+
return
|
126
|
+
end
|
127
|
+
|
128
|
+
author = ""
|
129
|
+
if ldiff.operation == DiffLine::DELETE
|
130
|
+
# 删除类型,记录为原始作者
|
131
|
+
author = ldiff.source_line.user
|
132
|
+
else
|
133
|
+
# 新增类型,记录为最新作者
|
134
|
+
author = ldiff.target_line.user
|
135
|
+
end
|
136
|
+
|
137
|
+
item = @author_results[author]
|
138
|
+
if item == nil
|
139
|
+
item = ResultItem.new(author)
|
140
|
+
end
|
141
|
+
item.add_file_name(file_name)
|
142
|
+
item.add_line_count(1)
|
143
|
+
@author_results[author] = item
|
144
|
+
end
|
145
|
+
|
146
|
+
def record_reviewer(fdiff, reviewer, lines)
|
147
|
+
if reviewer == nil
|
148
|
+
return
|
149
|
+
end
|
150
|
+
file_name = fdiff.file_name
|
151
|
+
if @configuration.is_ignore?(file_name)
|
152
|
+
return
|
153
|
+
end
|
154
|
+
|
155
|
+
item = @reviewer_results[reviewer]
|
156
|
+
if item == nil
|
157
|
+
item = ResultItem.new(reviewer)
|
158
|
+
end
|
159
|
+
item.add_file_name(file_name)
|
160
|
+
item.add_line_count(lines)
|
161
|
+
@reviewer_results[reviewer] = item
|
162
|
+
end
|
163
|
+
|
164
|
+
def print_author_result
|
165
|
+
print @author_results
|
166
|
+
end
|
167
|
+
|
168
|
+
def print_reviewer_result
|
169
|
+
print @reviewer_results
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'digest'
|
2
|
+
|
3
|
+
module GitReviewer
|
4
|
+
# BlameBranch
|
5
|
+
class BlameBranch
|
6
|
+
attr_accessor :branch
|
7
|
+
attr_accessor :blame_files
|
8
|
+
|
9
|
+
def initialize(branch, blame_files)
|
10
|
+
@branch = branch
|
11
|
+
@blame_files = blame_files
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# BlameFile
|
16
|
+
class BlameFile
|
17
|
+
attr_accessor :file_name
|
18
|
+
attr_accessor :blame_lines
|
19
|
+
|
20
|
+
attr_writer :exist
|
21
|
+
attr_writer :binary
|
22
|
+
|
23
|
+
# 文件是否存在
|
24
|
+
def exist?
|
25
|
+
@exist
|
26
|
+
end
|
27
|
+
|
28
|
+
# 文件是否是二进制
|
29
|
+
def binary?
|
30
|
+
@binary
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(file_name, blame_lines, exist, binary)
|
34
|
+
@file_name = file_name
|
35
|
+
@blame_lines = blame_lines
|
36
|
+
@exist = exist
|
37
|
+
@binary = binary
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# BlameLine
|
42
|
+
class BlameLine
|
43
|
+
attr_accessor :hash
|
44
|
+
attr_accessor :user
|
45
|
+
attr_accessor :date
|
46
|
+
attr_accessor :line
|
47
|
+
attr_accessor :code
|
48
|
+
attr_accessor :description
|
49
|
+
|
50
|
+
def initialize(hash, user, date, line, code)
|
51
|
+
@hash = hash
|
52
|
+
@user = user
|
53
|
+
@date = date
|
54
|
+
@line = line
|
55
|
+
@code = code
|
56
|
+
@description = code
|
57
|
+
end
|
58
|
+
|
59
|
+
# 用于 Myers 中进行判等操作
|
60
|
+
def ==(other)
|
61
|
+
other.is_a?(BlameLine) && other.code == @code
|
62
|
+
end
|
63
|
+
|
64
|
+
def format_line
|
65
|
+
format('%5d', line)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|