meta_commit 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -1
- data/CHANGELOG.md +13 -1
- data/README.md +200 -18
- data/config/default.yml +2 -2
- data/exe/meta_commit +3 -0
- data/lib/meta_commit/changelog/commands/commit_diff_examiner.rb +13 -5
- data/lib/meta_commit/cli.rb +30 -3
- data/lib/meta_commit/configuration_store.rb +1 -1
- data/lib/meta_commit/container.rb +14 -0
- data/lib/meta_commit/errors.rb +1 -1
- data/lib/meta_commit/extensions/builtin.rb +80 -0
- data/lib/meta_commit/factories/contextual_ast_node_factory.rb +19 -22
- data/lib/meta_commit/factories/diff_factory.rb +26 -15
- data/lib/meta_commit/git/repo.rb +8 -6
- data/lib/meta_commit/index/adapters/git_notes.rb +3 -1
- data/lib/meta_commit/index/commands/diff_examiner.rb +14 -5
- data/lib/meta_commit/message/commands/diff_index_examiner.rb +13 -5
- data/lib/meta_commit/models/contextual_ast_node.rb +1 -6
- data/lib/meta_commit/models/line.rb +26 -1
- data/lib/meta_commit/version.rb +1 -1
- data/meta_commit.gemspec +2 -3
- metadata +8 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 547ac3de1a9e93cebe5f8c4f1e3f9e41215db41c
|
4
|
+
data.tar.gz: cdb2293dccff5c93b383a3696b598cd44b451e5d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 783844e38b51f0313fef67fdb7b8573d486f38125948a68569b3a072042a1393a9718bcf7ce16d442a55260c22eb55e2b87d039638860716bd2b50d15ad06787
|
7
|
+
data.tar.gz: 3276dc9544b290e958a45e509876b4d1e29686316a64ef1f60b23ad5b08b5e1fb96c052b6fa92c3df7d61e987d1cad248a2f80d1019e8e996f2aadbddb689221
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -4,7 +4,19 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
5
5
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
-
##
|
7
|
+
## [v0.3.0] - 2018-03-12
|
8
|
+
### Added
|
9
|
+
- `meta_commit init` command
|
10
|
+
- `meta_commit version` command
|
11
|
+
- Column where diff starts
|
12
|
+
- `whole_file_changed` flag
|
13
|
+
### Changed
|
14
|
+
- Configuration file is required to run command
|
15
|
+
- Diff now expects changes using `ChangeContext` DTO
|
16
|
+
### Removed
|
17
|
+
- Default config usage from `MetaCommit::ConfigurationStore`
|
18
|
+
|
19
|
+
## [v0.2.0] - 2017-10-25
|
8
20
|
### Added
|
9
21
|
- Code documentation
|
10
22
|
- Contribution guide
|
data/README.md
CHANGED
@@ -1,32 +1,51 @@
|
|
1
1
|
# meta commit
|
2
2
|
[![Gem Version](https://badge.fury.io/rb/meta_commit.svg)](https://badge.fury.io/rb/meta_commit)
|
3
|
-
![
|
3
|
+
[![Build Status](https://travis-ci.org/usernam3/meta_commit.svg?branch=master)](https://travis-ci.org/usernam3/meta_commit)
|
4
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/ff142cfbcd634e8ad8f1/maintainability)](https://codeclimate.com/github/usernam3/meta_commit/maintainability)
|
4
5
|
[![Coverage Status](https://coveralls.io/repos/github/usernam3/meta_commit/badge.svg?branch=master)](https://coveralls.io/github/usernam3/meta_commit?branch=master)
|
5
|
-
[![Inline docs](
|
6
|
+
[![Inline docs](https://inch-ci.org/github/usernam3/meta_commit.svg?branch=master)](http://inch-ci.org/github/usernam3/meta_commit)
|
6
7
|
|
7
8
|
> Enrich commit diffs with programing language insights
|
8
9
|
|
9
10
|
|
10
11
|
## Description
|
11
12
|
|
12
|
-
[![v0.
|
13
|
+
[![v0.3 demonstration](https://asciinema.org/a/P6kWCq2S4eOpMzEjOAKSHYmyv.png)](https://asciinema.org/a/P6kWCq2S4eOpMzEjOAKSHYmyv?autoplay=1)
|
13
14
|
|
14
|
-
meta commit - is set of commands for git repository, which extracts useful information from commits and allows to get more insights from usual actions with repository.
|
15
|
+
[meta commit](https://usernam3.github.io/meta_commit/) - is set of commands for git repository, which extracts useful information from commits and allows to get more insights from usual actions with repository.
|
15
16
|
|
16
|
-
Git is universal distributed version control system and works with minimal common units which exist in any file - strings. Meta commit is utility which gives git information about content of files
|
17
|
+
Git is universal distributed version control system and works with minimal common units which exist in any file - strings. Meta commit is utility which gives git information about content of repository files and changes between commits on level of programming language.
|
17
18
|
|
19
|
+
## What does meta_commit do ?
|
18
20
|
|
19
|
-
|
21
|
+
After you read the 'Description section' you may still have question 'What does meta_commit do ?'
|
20
22
|
|
21
|
-
|
23
|
+
The answer is - it takes diff of each commit in the repository, parse diff content using programming language parser (taken from list of extensions) and try to understand what changes commit introduced on programming language level (not lines level as git does it).
|
24
|
+
|
25
|
+
|
26
|
+
## Installation
|
27
|
+
|
28
|
+
To install gem you need to run:
|
22
29
|
|
23
30
|
$ gem install meta_commit
|
24
31
|
|
32
|
+
> You may need to additionally install the cmake to install rugged (ruby bindings to libgit). It is [known issue](https://github.com/usernam3/meta_commit/issues/19).
|
33
|
+
|
25
34
|
|
26
35
|
## Setup
|
27
36
|
|
28
|
-
To
|
29
|
-
|
37
|
+
To start using meta_commit, first need to add configuration file to the root of the repository.
|
38
|
+
To add it, you need to run init command.
|
39
|
+
|
40
|
+
meta_commit init
|
41
|
+
|
42
|
+
Or
|
43
|
+
|
44
|
+
meta_commit init --extensions=ruby_support markdown_support
|
45
|
+
|
46
|
+
(*If you want to use [ruby](https://github.com/meta-commit/ruby_support) and [markdown](https://github.com/meta-commit/markdown_support) extensions you will need also to install them by running `gem install meta_commit_ruby_support meta_commit_markdown_support`*)
|
47
|
+
|
48
|
+
This command will add `.meta_commit.yml` configuration file with this contents to the repository
|
30
49
|
|
31
50
|
```YAML
|
32
51
|
commands :
|
@@ -40,32 +59,195 @@ commands :
|
|
40
59
|
|
41
60
|
extensions:
|
42
61
|
- ruby_support
|
43
|
-
-
|
44
|
-
- extension3
|
45
|
-
- extension4
|
62
|
+
- markdown_support
|
46
63
|
```
|
47
64
|
|
48
|
-
This config can be used to
|
49
|
-
|
50
|
-
[Here](https://github.com/usernam3/meta_commit/blob/master/CONTRIBUTING.md) you can get more information about how to create new extension.
|
65
|
+
This config can be used to customize gem behavior, currently only `extensions` key is used.
|
66
|
+
Items of `extensions` list are gem names (without `meta_commit_` prefix) that will be loaded.
|
67
|
+
[Here](https://github.com/usernam3/meta_commit/blob/master/CONTRIBUTING.md#creating-new-extension) you can get more information about how to create the new extension.
|
51
68
|
|
52
69
|
|
53
70
|
## Usage
|
54
71
|
|
72
|
+
Here you can see usage examples of main meta_commit commands
|
73
|
+
|
55
74
|
### Message
|
56
75
|
|
57
76
|
meta_commit message [--directory=$(pwd)]
|
58
77
|
|
59
|
-
|
78
|
+
Message command prints description of repository working tree staged changes.
|
79
|
+
Result of execution of this command is string, that is printed to the console and which represents summary of staged changes.
|
80
|
+
|
81
|
+
Example :
|
82
|
+
|
83
|
+
```zsh
|
84
|
+
> /repository_folder git diff --cached
|
85
|
+
|
86
|
+
diff --git a/lib/meta_commit/changelog/commands/commit_diff_examiner.rb b/lib/meta_commit/changelog/commands/commit_diff_examiner.rb
|
87
|
+
index 49a8ae1..e8a2bc3 100644
|
88
|
+
--- a/lib/meta_commit/changelog/commands/commit_diff_examiner.rb
|
89
|
+
+++ b/lib/meta_commit/changelog/commands/commit_diff_examiner.rb
|
90
|
+
@@ -8,6 +8,7 @@ module MetaCommit::Changelog
|
91
|
+
@parse_command = parse_command
|
92
|
+
@ast_path_factory = ast_path_factory
|
93
|
+
@diff_factory = diff_factory
|
94
|
+
+ @some_property = 2 + 2
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
@@ -16,7 +17,7 @@ module MetaCommit::Changelog
|
99
|
+
# @param [String] left_commit commit id
|
100
|
+
# @param [String] right_commit commit id
|
101
|
+
# @return [Array<MetaCommit::Contracts::Diff>]
|
102
|
+
- def meta(repo, left_commit, right_commit)
|
103
|
+
+ def examine_meta(repo, left_commit, right_commit)
|
104
|
+
diffs = []
|
105
|
+
commit_id_old = left_commit.oid
|
106
|
+
+ def examine_meta(repo, left_commit, right_commit)
|
107
|
+
diffs = []
|
108
|
+
commit_id_old = left_commit.oid
|
109
|
+
commit_id_new = right_commit.oid
|
110
|
+
@@ -53,6 +54,10 @@ module MetaCommit::Changelog
|
111
|
+
end
|
112
|
+
diffs
|
113
|
+
end
|
114
|
+
+
|
115
|
+
+ def examine
|
116
|
+
+ 'method to test'
|
117
|
+
+ end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
> dev/my_projects/meta_commit meta_commit message
|
124
|
+
- changes in method initialize
|
125
|
+
- changes in CommitDiffExaminer#examine_meta
|
126
|
+
- changes in method examine
|
127
|
+
```
|
60
128
|
|
61
129
|
### Index
|
62
130
|
|
63
131
|
meta_commit index [--directory=$(pwd)]
|
64
132
|
|
65
|
-
|
133
|
+
Index command walks over the repository commits and writes meta information to storage.
|
134
|
+
Currently the only supported storage adapter is git notes.
|
135
|
+
So after execution of index command the new git notes will be added to your repository, you will be able to see them using standard `git log` command.
|
136
|
+
|
137
|
+
Example :
|
138
|
+
|
139
|
+
```zsh
|
140
|
+
> /repository_folder git log
|
141
|
+
|
142
|
+
commit 1a8ba80e79e7c96fc06d3daf35bbad31cdaab5a4
|
143
|
+
Author: Stanislav Dobrovolskiy <stasdobrovolskiy@gmail.com>
|
144
|
+
Date: Sat Mar 3 02:44:12 2018 +0100
|
145
|
+
|
146
|
+
`init` command implementation #9
|
147
|
+
- remove default config usage #9
|
148
|
+
|
149
|
+
commit 352b0b3734cc8a940f2133ac73720fc7728e59e7
|
150
|
+
Author: Stanislav Dobrovolskiy <stasdobrovolskiy@gmail.com>
|
151
|
+
Date: Sun Feb 25 22:15:52 2018 +0100
|
152
|
+
|
153
|
+
replace double quotes from note body
|
154
|
+
|
155
|
+
|
156
|
+
> /repository_folder meta_commit index
|
157
|
+
repository successfully indexed
|
158
|
+
> /repository_folder git log
|
159
|
+
|
160
|
+
commit 1a8ba80e79e7c96fc06d3daf35bbad31cdaab5a4
|
161
|
+
Author: Stanislav Dobrovolskiy <stasdobrovolskiy@gmail.com>
|
162
|
+
Date: Sat Mar 3 02:44:12 2018 +0100
|
163
|
+
|
164
|
+
`init` command implementation #9
|
165
|
+
- remove default config usage #9
|
166
|
+
|
167
|
+
Notes:
|
168
|
+
- changes in method fixture_configuration_file
|
169
|
+
- changes in method init
|
170
|
+
- changes in method boot_container_with_config
|
171
|
+
- change initialization of Errors::MissingConfigError
|
172
|
+
|
173
|
+
commit 352b0b3734cc8a940f2133ac73720fc7728e59e7
|
174
|
+
Author: Stanislav Dobrovolskiy <stasdobrovolskiy@gmail.com>
|
175
|
+
Date: Sun Feb 25 22:15:52 2018 +0100
|
176
|
+
|
177
|
+
replace double quotes from note body
|
178
|
+
|
179
|
+
Notes:
|
180
|
+
- changes in GitNotes#write_to_notes
|
181
|
+
- changes in method write_to_notes
|
182
|
+
```
|
66
183
|
|
67
184
|
### Changelog
|
68
185
|
|
69
186
|
meta_commit changelog [--from-tag] [--to-tag] [--directory=$(pwd)] [--filename='CHANGELOG.md']
|
70
187
|
|
71
|
-
|
188
|
+
This command walks over commits between ``` from ``` tag and ``` to ``` tag, examine changes and write them to changelog file
|
189
|
+
|
190
|
+
Example :
|
191
|
+
|
192
|
+
```zsh
|
193
|
+
|
194
|
+
> /repository_folder meta_commit changelog v0.2.0 v0.3.0
|
195
|
+
added version [v0.3.0] to CHANGELOG.md
|
196
|
+
|
197
|
+
> /repository_folder cat CHANGELOG.md
|
198
|
+
...
|
199
|
+
|
200
|
+
## [v0.3.0] - 2018-03-03
|
201
|
+
### Added
|
202
|
+
- add Maintainability
|
203
|
+
- create class SomeServiceNEWNAME
|
204
|
+
- Configuration,DataObject added to SomeModule
|
205
|
+
- changes in method meta
|
206
|
+
- changes in method init
|
207
|
+
- changes in method version
|
208
|
+
- changes in method boot_container_with_config
|
209
|
+
- changes in method load_builtin_extension
|
210
|
+
- changes in method create_contextual_node
|
211
|
+
- changes in method collect_path_to_ast_at_line
|
212
|
+
- changes in method lines?
|
213
|
+
- changes in method covers_line?
|
214
|
+
- changes in method create_diff
|
215
|
+
- changes in method change_context
|
216
|
+
- changes in method organize_lines
|
217
|
+
- changes in method write_to_notes
|
218
|
+
- changes in method examine_diff
|
219
|
+
- changes in method index_meta
|
220
|
+
- changes in method compute_column
|
221
|
+
### Changed
|
222
|
+
- change README.md
|
223
|
+
- change Inline docs
|
224
|
+
- changes in CommitDiffExaminer#meta
|
225
|
+
- change initialization of Errors::MissingConfigError
|
226
|
+
- changes in ContextualAstNodeFactory#create_contextual_node
|
227
|
+
- changes in ContextualAstNodeFactory#covers_line?
|
228
|
+
- changes in DiffFactory#create_diff
|
229
|
+
- changes in GitNotes#write_to_notes
|
230
|
+
- changes in DiffExaminer#examine_diff
|
231
|
+
- changes in DiffIndexExaminer#index_meta
|
232
|
+
### Removed
|
233
|
+
- changes in method meta
|
234
|
+
- changes in method boot_container_with_config
|
235
|
+
- changes in method create_ast_path
|
236
|
+
- changes in method get_ast_at_line
|
237
|
+
- changes in method covers_line
|
238
|
+
- changes in method examine_diff
|
239
|
+
- changes in method index_meta
|
240
|
+
- remove module Models
|
241
|
+
|
242
|
+
...
|
243
|
+
|
244
|
+
```
|
245
|
+
|
246
|
+
|
247
|
+
## Extensions
|
248
|
+
|
249
|
+
Extensions are very important part of meta_commit utility, they are used to parse source files and see difference between commits.
|
250
|
+
Idea is that each extension should add new capabilities and knowledge on how to work with the new file extensions.
|
251
|
+
Without extensions the meta_commit is just a script which traverses git repository and read files.
|
252
|
+
|
253
|
+
[Here](https://github.com/usernam3/meta_commit/blob/master/CONTRIBUTING.md#creating-new-extension) you can get more information about how to create new extension.
|
data/config/default.yml
CHANGED
data/exe/meta_commit
CHANGED
@@ -6,4 +6,7 @@ begin
|
|
6
6
|
MetaCommit::ApplicationInterface.start( ARGV )
|
7
7
|
rescue MetaCommit::Errors::MissingRepoError => e
|
8
8
|
Thor.new.say(e.message)
|
9
|
+
rescue MetaCommit::Errors::MissingConfigError => e
|
10
|
+
Thor.new.say(e.message)
|
11
|
+
Thor.new.say('Please, run `meta_commit init` to create configuration file')
|
9
12
|
end
|
@@ -29,15 +29,23 @@ module MetaCommit::Changelog
|
|
29
29
|
new_file_ast = @parse_command.execute(new_file_path, new_file_content)
|
30
30
|
next if new_file_ast.nil?
|
31
31
|
|
32
|
-
|
33
|
-
new_ast_path = @ast_path_factory.create_ast_path(new_file_ast, line.new_lineno)
|
32
|
+
column_where_changes_start = line.compute_column(old_file_content, new_file_content)
|
34
33
|
|
35
|
-
|
34
|
+
old_file_deleted = (patch.delta.new_file[:oid] == MetaCommit::Git::Repo::FILE_NOT_EXISTS_OID)
|
35
|
+
old_line_number = (old_file_deleted) ? (MetaCommit::Factories::ContextualAstNodeFactory::WHOLE_FILE) : (line.old_lineno)
|
36
|
+
old_contextual_ast = @ast_path_factory.create_contextual_node(old_file_ast, old_line_number)
|
37
|
+
|
38
|
+
new_file_created = (patch.delta.old_file[:oid] == MetaCommit::Git::Repo::FILE_NOT_EXISTS_OID)
|
39
|
+
new_line_number = (new_file_created) ? (MetaCommit::Factories::ContextualAstNodeFactory::WHOLE_FILE) : (line.new_lineno)
|
40
|
+
new_contextual_ast = @ast_path_factory.create_contextual_node(new_file_ast, new_line_number)
|
41
|
+
|
42
|
+
created_diff = @diff_factory.create_diff({
|
36
43
|
:line => line,
|
44
|
+
:column => column_where_changes_start,
|
37
45
|
:commit_id_old => commit_id_old,
|
38
46
|
:commit_id_new => commit_id_new,
|
39
|
-
:
|
40
|
-
:
|
47
|
+
:old_contextual_ast => old_contextual_ast,
|
48
|
+
:new_contextual_ast => new_contextual_ast,
|
41
49
|
:old_file_path => old_file_path,
|
42
50
|
:new_file_path => new_file_path,
|
43
51
|
})
|
data/lib/meta_commit/cli.rb
CHANGED
@@ -70,13 +70,40 @@ module MetaCommit
|
|
70
70
|
say('repository successfully indexed')
|
71
71
|
end
|
72
72
|
|
73
|
+
desc 'init', 'add configuration file to project'
|
74
|
+
option :directory, :type => :string, :default => Dir.pwd
|
75
|
+
option :extensions, :type => :array, :default => ['builtin']
|
76
|
+
|
77
|
+
def init
|
78
|
+
repository_path = options[:directory]
|
79
|
+
extensions = options[:extensions]
|
80
|
+
|
81
|
+
config_file = File.join(repository_path, MetaCommit::ConfigurationStore::META_COMMIT_CONFIG_FILENAME)
|
82
|
+
|
83
|
+
return say('Configuration file exists. You repository is already meta_commit compatible.') if File.exist?(config_file)
|
84
|
+
|
85
|
+
template = File.read(MetaCommit::ConfigurationStore::TEMPLATE_FILE)
|
86
|
+
configuration = template.gsub(/\#{extensions}/, extensions.map {|extension| " - #{extension}"}.join("\n"))
|
87
|
+
|
88
|
+
out_file = File.new(config_file, 'w')
|
89
|
+
out_file.puts(configuration)
|
90
|
+
out_file.close
|
91
|
+
|
92
|
+
say("The configuration file #{MetaCommit::ConfigurationStore::META_COMMIT_CONFIG_FILENAME} added")
|
93
|
+
end
|
94
|
+
|
95
|
+
desc 'version', 'prints meta_commit gem version'
|
96
|
+
|
97
|
+
def version
|
98
|
+
say(MetaCommit::VERSION)
|
99
|
+
end
|
100
|
+
|
73
101
|
no_commands do
|
74
102
|
# @param [String] configuration_path
|
75
103
|
# @return [MetaCommit::Container]
|
76
104
|
def boot_container_with_config(configuration_path)
|
77
|
-
|
78
|
-
configuration_store = MetaCommit::ConfigurationStore.new(
|
79
|
-
configuration_store.merge(MetaCommit::Configuration.new.fill_from_yaml_file(configuration_path)) if File.exist?(configuration_path)
|
105
|
+
project_configuration = MetaCommit::Configuration.new.fill_from_yaml_file(configuration_path)
|
106
|
+
configuration_store = MetaCommit::ConfigurationStore.new(project_configuration)
|
80
107
|
|
81
108
|
container = MetaCommit::Container.new
|
82
109
|
container.boot(configuration_store)
|
@@ -3,7 +3,7 @@ module MetaCommit
|
|
3
3
|
# @attr [MetaCommit::Configuration] configuration
|
4
4
|
class ConfigurationStore
|
5
5
|
META_COMMIT_HOME = File.realpath(File.join(File.dirname(__FILE__), '..', '..'))
|
6
|
-
|
6
|
+
TEMPLATE_FILE = File.join(META_COMMIT_HOME, 'config', 'default.yml')
|
7
7
|
META_COMMIT_CONFIG_FILENAME = '.meta_commit.yml'
|
8
8
|
|
9
9
|
# @param [MetaCommit::Configuration] configuration
|
@@ -3,6 +3,8 @@ module MetaCommit
|
|
3
3
|
class Container
|
4
4
|
include Dry::Container::Mixin
|
5
5
|
|
6
|
+
BUILTIN_EXTENSION = 'builtin'
|
7
|
+
|
6
8
|
def initialize
|
7
9
|
super
|
8
10
|
register :parser_classes, []
|
@@ -37,10 +39,22 @@ module MetaCommit
|
|
37
39
|
self[:parser_classes].concat(extension.parsers)
|
38
40
|
self[:diff_classes].concat(extension.diffs)
|
39
41
|
end
|
42
|
+
load_builtin_extension if extensions_to_load.include?(BUILTIN_EXTENSION)
|
40
43
|
end
|
41
44
|
|
42
45
|
protected :load_packages
|
43
46
|
|
47
|
+
def load_builtin_extension
|
48
|
+
require 'meta_commit/extensions/builtin'
|
49
|
+
|
50
|
+
extension = extension_class(camelize(without_extension_prefix('builtin'))).new
|
51
|
+
|
52
|
+
self[:parser_classes].concat(extension.parsers)
|
53
|
+
self[:diff_classes].concat(extension.diffs)
|
54
|
+
end
|
55
|
+
|
56
|
+
protected :load_builtin_extension
|
57
|
+
|
44
58
|
# @param [Array<String>] extensions_to_load
|
45
59
|
# @return [Array<String>]
|
46
60
|
def required_extensions(extensions_to_load)
|
data/lib/meta_commit/errors.rb
CHANGED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'meta_commit_contracts'
|
2
|
+
|
3
|
+
module MetaCommit::Extension::Builtin
|
4
|
+
class Locator < MetaCommit::Contracts::Locator
|
5
|
+
def parsers
|
6
|
+
[Parser]
|
7
|
+
end
|
8
|
+
|
9
|
+
def diffs
|
10
|
+
[Diff]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Ast < MetaCommit::Contracts::Ast
|
15
|
+
attr_reader :first_line, :last_line, :lines
|
16
|
+
|
17
|
+
def initialize(first_lineno, last_lineno, lines)
|
18
|
+
@first_line =first_lineno
|
19
|
+
@last_line = last_lineno
|
20
|
+
@lines = lines
|
21
|
+
end
|
22
|
+
|
23
|
+
def children
|
24
|
+
return [Ast.new(@first_line, @last_line, [])] if @lines.empty?
|
25
|
+
[Ast.new(@first_line+1, @last_line, @lines)]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Parser < MetaCommit::Contracts::Parser
|
30
|
+
def self.supported_file_extensions
|
31
|
+
%w(txt md rb)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Boolean]
|
35
|
+
def self.supports_syntax?(source_code)
|
36
|
+
true
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse(source_code)
|
40
|
+
lines = source_code.split("\n")
|
41
|
+
Ast.new(0, lines.length-1, lines)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Diff < MetaCommit::Contracts::Diff
|
46
|
+
def string_representation
|
47
|
+
prefix = 'all file ' if change_context.new_contextual_ast.whole_file_change
|
48
|
+
column = " (C#{change_context.column})" unless change_context.column.nil?
|
49
|
+
|
50
|
+
"#{prefix}#{change_context.type} | in files #{change_context.old_file_path}:#{change_context.old_lineno} #{change_context.new_file_path}:#{change_context.new_lineno}#{column} | between commits #{change_context.commit_id_old} and #{change_context.commit_id_new}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def supports_change(context)
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
def supports_parser?(parser)
|
58
|
+
[Parser].include?(parser)
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_s
|
62
|
+
string_representation
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [Boolean]
|
66
|
+
def type_addition?
|
67
|
+
change_context.type == MetaCommit::Contracts::Diff::TYPE_ADDITION
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [Boolean]
|
71
|
+
def type_deletion?
|
72
|
+
change_context.type == MetaCommit::Contracts::Diff::TYPE_DELETION
|
73
|
+
end
|
74
|
+
|
75
|
+
# @return [Boolean]
|
76
|
+
def type_replace?
|
77
|
+
change_context.type == MetaCommit::Contracts::Diff::TYPE_REPLACE
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -1,17 +1,19 @@
|
|
1
1
|
module MetaCommit::Factories
|
2
2
|
# Factory that builds ContextualAstNode from ast nodes
|
3
3
|
class ContextualAstNodeFactory
|
4
|
+
WHOLE_FILE = :whole_file_change
|
4
5
|
|
5
6
|
# @param [MetaCommit::Contracts::Ast] source_ast
|
6
7
|
# @param [Integer] line_number
|
7
8
|
# @return [MetaCommit::Models::ContextualAstNode]
|
8
|
-
def
|
9
|
+
def create_contextual_node(source_ast, line_number)
|
9
10
|
visited_nodes = []
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
contextual_node = MetaCommit::Models::ContextualAstNode.new
|
12
|
+
contextual_node.parser_class = source_ast.parser_class
|
13
|
+
contextual_node.target_node = collect_path_to_ast_at_line(source_ast, line_number, visited_nodes)
|
14
|
+
contextual_node.context_nodes = visited_nodes
|
15
|
+
contextual_node.whole_file_change = (line_number == WHOLE_FILE)
|
16
|
+
contextual_node
|
15
17
|
end
|
16
18
|
|
17
19
|
# @param [MetaCommit::Contracts::Ast] ast
|
@@ -19,7 +21,9 @@ module MetaCommit::Factories
|
|
19
21
|
# @param [Array<MetaCommit::Contracts::Ast>] accumulator
|
20
22
|
# @return [MetaCommit::Contracts::Ast]
|
21
23
|
def collect_path_to_ast_at_line(ast, lineno, accumulator)
|
22
|
-
return
|
24
|
+
return ast if lineno == WHOLE_FILE
|
25
|
+
return nil if ast.nil? || (lines?(ast) && !covers_line?(ast, lineno))
|
26
|
+
|
23
27
|
closest_ast = ast
|
24
28
|
accumulator.push(closest_ast)
|
25
29
|
ast.children.each do |child|
|
@@ -33,29 +37,22 @@ module MetaCommit::Factories
|
|
33
37
|
|
34
38
|
|
35
39
|
# @param [MetaCommit::Contracts::Ast] ast
|
36
|
-
# @
|
37
|
-
|
38
|
-
|
39
|
-
return nil unless covers_line(ast, lineno)
|
40
|
-
closest_ast = ast
|
41
|
-
ast.children.each do |child|
|
42
|
-
found_ast = get_ast_at_line(child, lineno)
|
43
|
-
closest_ast = found_ast unless found_ast.nil?
|
44
|
-
end
|
45
|
-
closest_ast
|
40
|
+
# @return [Boolean]
|
41
|
+
def lines?(ast)
|
42
|
+
!(ast.first_line.nil? && ast.last_line.nil?)
|
46
43
|
end
|
47
44
|
|
48
|
-
protected :
|
45
|
+
protected :lines?
|
49
46
|
|
50
47
|
|
51
48
|
# @param [MetaCommit::Contracts::Ast] ast
|
52
49
|
# @param [Integer] line
|
53
50
|
# @return [Boolean]
|
54
|
-
def covers_line(ast, line)
|
55
|
-
|
56
|
-
((ast.first_line <= line) && (ast.last_line >= line))
|
51
|
+
def covers_line?(ast, line)
|
52
|
+
(ast.first_line <= line) && (ast.last_line >= line)
|
57
53
|
end
|
58
54
|
|
59
|
-
protected :covers_line
|
55
|
+
protected :covers_line?
|
56
|
+
|
60
57
|
end
|
61
58
|
end
|
@@ -4,34 +4,45 @@ module MetaCommit::Factories
|
|
4
4
|
class DiffFactory
|
5
5
|
attr_accessor :available_diff_classes
|
6
6
|
|
7
|
+
# @param [Array<Class>] diff_classes
|
7
8
|
def initialize(diff_classes)
|
8
9
|
@available_diff_classes = diff_classes
|
9
10
|
end
|
10
11
|
|
11
12
|
# Factory method
|
12
|
-
# @param [Symbol] type
|
13
13
|
# @param [Hash] options
|
14
14
|
# @return [Diff, nil] created diff or nil if matched diff not found
|
15
|
-
def
|
15
|
+
def create_diff(options)
|
16
|
+
context = change_context(options)
|
16
17
|
@available_diff_classes.each do |diff_class|
|
17
18
|
diff = diff_class.new
|
18
|
-
if diff.supports_parser?(
|
19
|
-
diff.supports_parser?(
|
20
|
-
diff.supports_change(
|
21
|
-
|
22
|
-
diff.diff_type = line.line_origin
|
23
|
-
diff.commit_old = options[:commit_id_old]
|
24
|
-
diff.commit_new = options[:commit_id_new]
|
25
|
-
diff.old_file = options[:old_file_path]
|
26
|
-
diff.new_file = options[:new_file_path]
|
27
|
-
diff.old_lineno = line.old_lineno
|
28
|
-
diff.new_lineno = line.new_lineno
|
29
|
-
diff.old_ast_path = options[:old_ast_path]
|
30
|
-
diff.new_ast_path = options[:new_ast_path]
|
19
|
+
if diff.supports_parser?(context.old_contextual_ast.parser_class) &&
|
20
|
+
diff.supports_parser?(context.new_contextual_ast.parser_class) &&
|
21
|
+
diff.supports_change(context)
|
22
|
+
diff.change_context = context
|
31
23
|
return diff
|
32
24
|
end
|
33
25
|
end
|
34
26
|
nil
|
35
27
|
end
|
28
|
+
|
29
|
+
private
|
30
|
+
# Convert hash to context object
|
31
|
+
# @param [Hash] options
|
32
|
+
# @return [MetaCommit::Contracts::ChangeContext]
|
33
|
+
def change_context(options)
|
34
|
+
context = MetaCommit::Contracts::ChangeContext.new
|
35
|
+
context.type = options[:line].line_origin
|
36
|
+
context.old_lineno = options[:line].old_lineno
|
37
|
+
context.new_lineno = options[:line].new_lineno
|
38
|
+
context.column = options[:column]
|
39
|
+
context.commit_id_old = options[:commit_id_old]
|
40
|
+
context.commit_id_new = options[:commit_id_new]
|
41
|
+
context.old_contextual_ast = options[:old_contextual_ast]
|
42
|
+
context.new_contextual_ast = options[:new_contextual_ast]
|
43
|
+
context.old_file_path = options[:old_file_path]
|
44
|
+
context.new_file_path = options[:new_file_path]
|
45
|
+
context
|
46
|
+
end
|
36
47
|
end
|
37
48
|
end
|
data/lib/meta_commit/git/repo.rb
CHANGED
@@ -159,13 +159,15 @@ module MetaCommit::Git
|
|
159
159
|
|
160
160
|
line_to_walk = MetaCommit::Models::Line.new
|
161
161
|
if is_replace_change
|
162
|
-
line_to_walk.line_origin
|
163
|
-
line_to_walk.old_lineno=line.old_lineno
|
164
|
-
line_to_walk.new_lineno=next_line.new_lineno
|
162
|
+
line_to_walk.line_origin = :replace
|
163
|
+
line_to_walk.old_lineno = line.old_lineno
|
164
|
+
line_to_walk.new_lineno = next_line.new_lineno
|
165
|
+
line_to_walk.content_offset = next_line.content_offset
|
165
166
|
else
|
166
|
-
line_to_walk.line_origin=line.line_origin
|
167
|
-
line_to_walk.old_lineno=line.old_lineno
|
168
|
-
line_to_walk.new_lineno=line.new_lineno
|
167
|
+
line_to_walk.line_origin = line.line_origin
|
168
|
+
line_to_walk.old_lineno = line.old_lineno
|
169
|
+
line_to_walk.new_lineno = line.new_lineno
|
170
|
+
line_to_walk.content_offset = line.content_offset
|
169
171
|
end
|
170
172
|
if is_replace_change
|
171
173
|
skip_next_line = true
|
@@ -25,7 +25,9 @@ module MetaCommit::Index
|
|
25
25
|
# @param [String] commit_id
|
26
26
|
# @param [String] message
|
27
27
|
def write_to_notes(commit_id, message)
|
28
|
-
|
28
|
+
msg = message.gsub('"', '')
|
29
|
+
return if msg.empty?
|
30
|
+
system("git --git-dir '#{@git_folder_path}' notes add -f -m \"#{msg}\" #{commit_id}")
|
29
31
|
end
|
30
32
|
|
31
33
|
protected :write_to_notes
|
@@ -12,6 +12,7 @@ module MetaCommit::Index
|
|
12
12
|
|
13
13
|
# Creates diff objects with meta information of changes in repository commits
|
14
14
|
# @param [MetaCommit::Git::Repo] repo
|
15
|
+
# @return [MetaCommit::Models::Changes::Repository]
|
15
16
|
def meta(repo)
|
16
17
|
repo_changes = MetaCommit::Models::Changes::Repository.new(repo.path)
|
17
18
|
repo.walk_by_commits do |left_commit, right_commit|
|
@@ -35,15 +36,23 @@ module MetaCommit::Index
|
|
35
36
|
new_file_ast = @parse_command.execute(new_file_path, new_file_content)
|
36
37
|
next if new_file_ast.nil?
|
37
38
|
|
38
|
-
|
39
|
-
new_ast_path = @ast_path_factory.create_ast_path(new_file_ast, line.new_lineno)
|
39
|
+
column_where_changes_start = line.compute_column(old_file_content, new_file_content)
|
40
40
|
|
41
|
-
|
41
|
+
old_file_deleted = (patch.delta.new_file[:oid] == MetaCommit::Git::Repo::FILE_NOT_EXISTS_OID)
|
42
|
+
old_line_number = (old_file_deleted) ? (MetaCommit::Factories::ContextualAstNodeFactory::WHOLE_FILE) : (line.old_lineno)
|
43
|
+
old_contextual_ast = @ast_path_factory.create_contextual_node(old_file_ast, old_line_number)
|
44
|
+
|
45
|
+
new_file_created = (patch.delta.old_file[:oid] == MetaCommit::Git::Repo::FILE_NOT_EXISTS_OID)
|
46
|
+
new_line_number = (new_file_created) ? (MetaCommit::Factories::ContextualAstNodeFactory::WHOLE_FILE) : (line.new_lineno)
|
47
|
+
new_contextual_ast = @ast_path_factory.create_contextual_node(new_file_ast, new_line_number)
|
48
|
+
|
49
|
+
created_diff = @diff_factory.create_diff({
|
42
50
|
:line => line,
|
51
|
+
:column => column_where_changes_start,
|
43
52
|
:commit_id_old => left_commit.oid,
|
44
53
|
:commit_id_new => right_commit.oid,
|
45
|
-
:
|
46
|
-
:
|
54
|
+
:old_contextual_ast => old_contextual_ast,
|
55
|
+
:new_contextual_ast => new_contextual_ast,
|
47
56
|
:old_file_path => old_file_path,
|
48
57
|
:new_file_path => new_file_path,
|
49
58
|
})
|
@@ -27,15 +27,23 @@ module MetaCommit::Message
|
|
27
27
|
new_file_ast = @parse_command.execute(new_file_path, new_file_content)
|
28
28
|
next if new_file_ast.nil?
|
29
29
|
|
30
|
-
|
31
|
-
new_ast_path = @ast_path_factory.create_ast_path(new_file_ast, line.new_lineno)
|
30
|
+
column_where_changes_start = line.compute_column(old_file_content, new_file_content)
|
32
31
|
|
33
|
-
|
32
|
+
old_file_deleted = (patch.delta.new_file[:oid] == MetaCommit::Git::Repo::FILE_NOT_EXISTS_OID)
|
33
|
+
old_line_number = (old_file_deleted) ? (MetaCommit::Factories::ContextualAstNodeFactory::WHOLE_FILE) : (line.old_lineno)
|
34
|
+
old_contextual_ast = @ast_path_factory.create_contextual_node(old_file_ast, old_line_number)
|
35
|
+
|
36
|
+
new_file_created = (patch.delta.old_file[:oid] == MetaCommit::Git::Repo::FILE_NOT_EXISTS_OID)
|
37
|
+
new_line_number = (new_file_created) ? (MetaCommit::Factories::ContextualAstNodeFactory::WHOLE_FILE) : (line.new_lineno)
|
38
|
+
new_contextual_ast = @ast_path_factory.create_contextual_node(new_file_ast, new_line_number)
|
39
|
+
|
40
|
+
created_diff = @diff_factory.create_diff({
|
34
41
|
:line => line,
|
42
|
+
:column => column_where_changes_start,
|
35
43
|
:commit_id_old => commit_id_old,
|
36
44
|
:commit_id_new => commit_id_new,
|
37
|
-
:
|
38
|
-
:
|
45
|
+
:old_contextual_ast => old_contextual_ast,
|
46
|
+
:new_contextual_ast => new_contextual_ast,
|
39
47
|
:old_file_path => old_file_path,
|
40
48
|
:new_file_path => new_file_path,
|
41
49
|
})
|
@@ -1,9 +1,4 @@
|
|
1
1
|
module MetaCommit::Models
|
2
|
-
|
3
|
-
# @attr [MetaCommit::Contracts::Ast] target_node Target node from AST
|
4
|
-
# @attr [Array<MetaCommit::Contracts::Ast>] context_nodes Nodes bypassed on the way to target node
|
5
|
-
# @attr [Class] parser_class
|
6
|
-
class ContextualAstNode
|
7
|
-
attr_accessor :target_node, :context_nodes, :parser_class
|
2
|
+
class ContextualAstNode < MetaCommit::Contracts::ContextualAst
|
8
3
|
end
|
9
4
|
end
|
@@ -1,5 +1,30 @@
|
|
1
1
|
module MetaCommit::Models
|
2
2
|
class Line
|
3
|
-
attr_accessor :line_origin, :old_lineno, :new_lineno
|
3
|
+
attr_accessor :line_origin, :old_lineno, :new_lineno, :content_offset
|
4
|
+
|
5
|
+
# Compares old_lineno line of old_file_content and new_lineno of new_file_content
|
6
|
+
# @param [String] old_file_content
|
7
|
+
# @param [String] new_file_content
|
8
|
+
# @return [Numeric, Nil] index of first different symbol in string
|
9
|
+
def compute_column(old_file_content, new_file_content)
|
10
|
+
return if old_file_content.empty? || new_file_content.empty?
|
11
|
+
return if old_lineno == -1 || new_lineno == -1
|
12
|
+
|
13
|
+
old_file_lines = old_file_content.lines.map(&:chomp)
|
14
|
+
old_file_lines = old_file_lines.push('') if old_file_content.end_with?("\n")
|
15
|
+
old_line = old_file_lines[old_lineno - 1]
|
16
|
+
|
17
|
+
new_file_lines = new_file_content.lines.map(&:chomp)
|
18
|
+
new_file_lines = new_file_lines.push('') if new_file_content.end_with?("\n")
|
19
|
+
new_line = new_file_lines[new_lineno - 1]
|
20
|
+
|
21
|
+
return if old_line.empty? || new_line.empty?
|
22
|
+
|
23
|
+
old_line.each_char.with_index do |char, index|
|
24
|
+
return index if (char != new_line[index])
|
25
|
+
end
|
26
|
+
|
27
|
+
old_line.length
|
28
|
+
end
|
4
29
|
end
|
5
30
|
end
|
data/lib/meta_commit/version.rb
CHANGED
data/meta_commit.gemspec
CHANGED
@@ -18,16 +18,15 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_runtime_dependency "meta_commit_contracts", "~> 0.1
|
21
|
+
spec.add_runtime_dependency "meta_commit_contracts", "~> 0.1"
|
22
22
|
spec.add_runtime_dependency "rugged", "~> 0.25"
|
23
23
|
spec.add_runtime_dependency "dry-container", "~> 0.6.0"
|
24
|
-
spec.add_runtime_dependency "parser", "~> 2.4"
|
25
24
|
spec.add_runtime_dependency "thor", "~> 0.19"
|
26
25
|
|
27
26
|
spec.add_development_dependency "bundler", "~> 1.10"
|
28
27
|
spec.add_development_dependency "rake", "~> 10.0"
|
29
28
|
spec.add_development_dependency "rspec", "~> 3.6"
|
30
|
-
spec.add_development_dependency "cucumber", "~> 2.4
|
29
|
+
spec.add_development_dependency "cucumber", "~> 2.4"
|
31
30
|
spec.add_development_dependency "aruba", "~> 0.14.2"
|
32
31
|
spec.add_development_dependency "rspec-mocks", "~> 3.6"
|
33
32
|
spec.add_development_dependency "byebug", "~> 9.0"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: meta_commit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stanislav Dobrovolskiy
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2018-03-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: meta_commit_contracts
|
@@ -17,14 +17,14 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: 0.1
|
20
|
+
version: '0.1'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: 0.1
|
27
|
+
version: '0.1'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: rugged
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,20 +53,6 @@ dependencies:
|
|
53
53
|
- - "~>"
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: 0.6.0
|
56
|
-
- !ruby/object:Gem::Dependency
|
57
|
-
name: parser
|
58
|
-
requirement: !ruby/object:Gem::Requirement
|
59
|
-
requirements:
|
60
|
-
- - "~>"
|
61
|
-
- !ruby/object:Gem::Version
|
62
|
-
version: '2.4'
|
63
|
-
type: :runtime
|
64
|
-
prerelease: false
|
65
|
-
version_requirements: !ruby/object:Gem::Requirement
|
66
|
-
requirements:
|
67
|
-
- - "~>"
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
version: '2.4'
|
70
56
|
- !ruby/object:Gem::Dependency
|
71
57
|
name: thor
|
72
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -129,14 +115,14 @@ dependencies:
|
|
129
115
|
requirements:
|
130
116
|
- - "~>"
|
131
117
|
- !ruby/object:Gem::Version
|
132
|
-
version: 2.4
|
118
|
+
version: '2.4'
|
133
119
|
type: :development
|
134
120
|
prerelease: false
|
135
121
|
version_requirements: !ruby/object:Gem::Requirement
|
136
122
|
requirements:
|
137
123
|
- - "~>"
|
138
124
|
- !ruby/object:Gem::Version
|
139
|
-
version: 2.4
|
125
|
+
version: '2.4'
|
140
126
|
- !ruby/object:Gem::Dependency
|
141
127
|
name: aruba
|
142
128
|
requirement: !ruby/object:Gem::Requirement
|
@@ -224,6 +210,7 @@ files:
|
|
224
210
|
- lib/meta_commit/configuration_store.rb
|
225
211
|
- lib/meta_commit/container.rb
|
226
212
|
- lib/meta_commit/errors.rb
|
213
|
+
- lib/meta_commit/extensions/builtin.rb
|
227
214
|
- lib/meta_commit/factories/contextual_ast_node_factory.rb
|
228
215
|
- lib/meta_commit/factories/diff_factory.rb
|
229
216
|
- lib/meta_commit/factories/parser_factory.rb
|
@@ -260,7 +247,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
260
247
|
version: '0'
|
261
248
|
requirements: []
|
262
249
|
rubyforge_project:
|
263
|
-
rubygems_version: 2.5.
|
250
|
+
rubygems_version: 2.5.2
|
264
251
|
signing_key:
|
265
252
|
specification_version: 4
|
266
253
|
summary: Enrich commit diffs with programing language insights
|