git-reviewer 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![](https://chuquan-public-r-001.oss-cn-shanghai.aliyuncs.com/sketch-images/git-reviewer-02.png)
|
2
|
+
|
3
|
+
![Platform](http://img.shields.io/badge/platform-macOS-blue.svg?style=flat)
|
4
|
+
![Platform](http://img.shields.io/badge/platform-Linux-blue.svg?style=flat)
|
5
|
+
![Platform](http://img.shields.io/badge/platform-Windows-blue.svg?style=flat)
|
6
|
+
![Language](http://img.shields.io/badge/language-ruby-brightgreen.svg?style=flat)
|
7
|
+
![Tool](http://img.shields.io/badge/tool-homebrew-orange.svg?style=flat)
|
8
|
+
![License](http://img.shields.io/badge/license-MIT-red.svg?style=flat)
|
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
|