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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 714f1ef542aa5c01085288e1d02eafb8e405a0bf
4
- data.tar.gz: 40cd2dab04ce2a9c597411cbfc5c22d2c480870d
3
+ metadata.gz: 547ac3de1a9e93cebe5f8c4f1e3f9e41215db41c
4
+ data.tar.gz: cdb2293dccff5c93b383a3696b598cd44b451e5d
5
5
  SHA512:
6
- metadata.gz: 91f560c202ff6675cd68a3f4375072928687201c8dd8733ae23371cd61a2c8e4a1df7fa935846bbedfe7f58da5fd94601be5e4b96b49aa9842db7b1bef080754
7
- data.tar.gz: 8468bd6fea1b6732fa04269f4561b8bc8c354fd1a55856ad2c59032477f8b5e4f6a492c152917ab5107cca79a96225582e90a21a127162ffc90a0ad540dcb533
6
+ metadata.gz: 783844e38b51f0313fef67fdb7b8573d486f38125948a68569b3a072042a1393a9718bcf7ce16d442a55260c22eb55e2b87d039638860716bd2b50d15ad06787
7
+ data.tar.gz: 3276dc9544b290e958a45e509876b4d1e29686316a64ef1f60b23ad5b08b5e1fb96c052b6fa92c3df7d61e987d1cad248a2f80d1019e8e996f2aadbddb689221
@@ -1,7 +1,10 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 2.2.3
4
- before_install: gem install bundler -v 1.10.6
4
+ - 2.5.0
5
+ before_install:
6
+ - gem install bundler -v 1.10.6
7
+ - export TZ=Europe/Berlin
5
8
  script:
6
9
  - rake spec
7
10
  - rake features
@@ -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
- ## 0.2.0 - 2017-10-25
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
- ![Travis](https://api.travis-ci.org/usernam3/meta_commit.svg?branch=master)
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](http://inch-ci.org/github/usernam3/meta_commit.svg?branch=master)](http://inch-ci.org/github/usernam3/meta_commit)
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.2 demonstration](https://asciinema.org/a/svV2TBICPgp7pOWneRI30WDel.png)](https://asciinema.org/a/svV2TBICPgp7pOWneRI30WDel?autoplay=1)
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 in repo and changes between commits on level of programming language.
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
- ## Installing meta_commit
21
+ After you read the 'Description section' you may still have question 'What does meta_commit do ?'
20
22
 
21
- You can install gem with the following command in a terminal:
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 setup ```meta_commit``` gem for your repository you need to add configuration file to root of repo.
29
- Currently meta commit expects configuration file with name `.meta_commit.yml` of this format :
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
- - extension2
44
- - extension3
45
- - extension4
62
+ - markdown_support
46
63
  ```
47
64
 
48
- This config can be used to change gem behavior, currently it supports only `extensions` key.
49
- Elements of `extensions` list are gem names (without `meta_commit_` prefix) that will be loaded.
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
- Prints description of current changes in repository index
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
- Walks over repository commits and writes meta information to git notes
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
- Walks over commits between tags ``` from ``` and ``` to ``` and writes changes to changelog file
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.
@@ -1,4 +1,4 @@
1
- commands :
1
+ commands:
2
2
  changelog:
3
3
  adapter: file
4
4
  formatter: keep_a_changelog
@@ -8,4 +8,4 @@ commands :
8
8
  formatter: commit_message
9
9
 
10
10
  extensions:
11
- - builtin
11
+ #{extensions}
@@ -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
- old_ast_path = @ast_path_factory.create_ast_path(old_file_ast, line.old_lineno)
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
- created_diff = @diff_factory.create_diff_of_type(line.line_origin, {
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
- :old_ast_path => old_ast_path,
40
- :new_ast_path => new_ast_path,
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
  })
@@ -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
- default_configuration = MetaCommit::Configuration.new.fill_from_yaml_file(MetaCommit::ConfigurationStore::DEFAULT_FILE)
78
- configuration_store = MetaCommit::ConfigurationStore.new(default_configuration)
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
- DEFAULT_FILE = File.join(META_COMMIT_HOME, 'config', 'default.yml')
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)
@@ -5,7 +5,7 @@ module MetaCommit::Errors
5
5
  end
6
6
  end
7
7
  class MissingConfigError < StandardError
8
- def initialize(msg='Passed config file does not exist')
8
+ def initialize(msg='Config file can not be found')
9
9
  super(msg)
10
10
  end
11
11
  end
@@ -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 create_ast_path(source_ast, line_number)
9
+ def create_contextual_node(source_ast, line_number)
9
10
  visited_nodes = []
10
- ast_path = MetaCommit::Models::ContextualAstNode.new
11
- ast_path.parser_class = source_ast.parser_class
12
- ast_path.target_node = collect_path_to_ast_at_line(source_ast, line_number, visited_nodes)
13
- ast_path.context_nodes = visited_nodes
14
- ast_path
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 nil if ast.nil? or not covers_line(ast, lineno)
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
- # @param [Integer] lineno
37
- # @return [MetaCommit::Contracts::Ast]
38
- def get_ast_at_line(ast, lineno)
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 :get_ast_at_line
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
- return false if ((ast.first_line.nil?) && (ast.last_line.nil?))
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 create_diff_of_type(type, options)
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?(options[:old_ast_path].parser_class) &&
19
- diff.supports_parser?(options[:new_ast_path].parser_class) &&
20
- diff.supports_change(type, options[:old_file_path], options[:new_file_path], options[:old_ast_path], options[:new_ast_path])
21
- line = options[:line]
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
@@ -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=:replace
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
- system("git --git-dir '#{@git_folder_path}' notes add -f -m '#{message}' #{commit_id}")
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
- old_ast_path = @ast_path_factory.create_ast_path(old_file_ast, line.old_lineno)
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
- created_diff = @diff_factory.create_diff_of_type(line.line_origin, {
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
- :old_ast_path => old_ast_path,
46
- :new_ast_path => new_ast_path,
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
- old_ast_path = @ast_path_factory.create_ast_path(old_file_ast, line.old_lineno)
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
- created_diff = @diff_factory.create_diff_of_type(line.line_origin, {
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
- :old_ast_path => old_ast_path,
38
- :new_ast_path => new_ast_path,
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
- # Stores specific node from ast and all nodes bypassed on the way to target node
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
@@ -1,3 +1,3 @@
1
1
  module MetaCommit
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -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.0"
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.0"
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.2.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: 2017-10-24 00:00:00.000000000 Z
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.0
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.0
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.0
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.0
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.1
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