rosette-core 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +26 -0
- data/History.txt +3 -0
- data/README.md +94 -0
- data/Rakefile +18 -0
- data/lib/rosette/core.rb +110 -0
- data/lib/rosette/core/branch_utils.rb +152 -0
- data/lib/rosette/core/commands.rb +139 -0
- data/lib/rosette/core/commands/errors.rb +17 -0
- data/lib/rosette/core/commands/git/commit_command.rb +65 -0
- data/lib/rosette/core/commands/git/diff_base_command.rb +301 -0
- data/lib/rosette/core/commands/git/diff_command.rb +188 -0
- data/lib/rosette/core/commands/git/diff_entry.rb +44 -0
- data/lib/rosette/core/commands/git/fetch_command.rb +27 -0
- data/lib/rosette/core/commands/git/repo_snapshot_command.rb +40 -0
- data/lib/rosette/core/commands/git/show_command.rb +70 -0
- data/lib/rosette/core/commands/git/snapshot_command.rb +50 -0
- data/lib/rosette/core/commands/git/status_command.rb +128 -0
- data/lib/rosette/core/commands/git/with_non_merge_ref.rb +48 -0
- data/lib/rosette/core/commands/git/with_ref.rb +92 -0
- data/lib/rosette/core/commands/git/with_refs.rb +92 -0
- data/lib/rosette/core/commands/git/with_repo_name.rb +50 -0
- data/lib/rosette/core/commands/git/with_snapshots.rb +45 -0
- data/lib/rosette/core/commands/queuing/enqueue_commit_command.rb +37 -0
- data/lib/rosette/core/commands/queuing/requeue_commit_command.rb +46 -0
- data/lib/rosette/core/commands/translations/export_command.rb +257 -0
- data/lib/rosette/core/commands/translations/translation_lookup_command.rb +66 -0
- data/lib/rosette/core/commands/translations/with_locale.rb +47 -0
- data/lib/rosette/core/configurator.rb +160 -0
- data/lib/rosette/core/error_reporters/buffered_error_reporter.rb +96 -0
- data/lib/rosette/core/error_reporters/error_reporter.rb +31 -0
- data/lib/rosette/core/error_reporters/nil_error_reporter.rb +25 -0
- data/lib/rosette/core/error_reporters/printing_error_reporter.rb +58 -0
- data/lib/rosette/core/error_reporters/raising_error_reporter.rb +27 -0
- data/lib/rosette/core/errors.rb +93 -0
- data/lib/rosette/core/extractor/commit_log.rb +33 -0
- data/lib/rosette/core/extractor/commit_log_status.rb +57 -0
- data/lib/rosette/core/extractor/commit_processor.rb +109 -0
- data/lib/rosette/core/extractor/extractor.rb +72 -0
- data/lib/rosette/core/extractor/extractor_config.rb +74 -0
- data/lib/rosette/core/extractor/locale.rb +118 -0
- data/lib/rosette/core/extractor/phrase.rb +76 -0
- data/lib/rosette/core/extractor/phrase/phrase_index_policy.rb +108 -0
- data/lib/rosette/core/extractor/phrase/phrase_to_hash.rb +33 -0
- data/lib/rosette/core/extractor/repo_config.rb +339 -0
- data/lib/rosette/core/extractor/serializer_config.rb +55 -0
- data/lib/rosette/core/extractor/static_extractor.rb +44 -0
- data/lib/rosette/core/extractor/translation.rb +44 -0
- data/lib/rosette/core/extractor/translation/translation_to_hash.rb +28 -0
- data/lib/rosette/core/git/diff_finder.rb +131 -0
- data/lib/rosette/core/git/ref.rb +116 -0
- data/lib/rosette/core/git/repo.rb +378 -0
- data/lib/rosette/core/path_matcher_factory.rb +330 -0
- data/lib/rosette/core/resolvers/extractor_id.rb +37 -0
- data/lib/rosette/core/resolvers/integration_id.rb +37 -0
- data/lib/rosette/core/resolvers/preprocessor_id.rb +38 -0
- data/lib/rosette/core/resolvers/resolver.rb +115 -0
- data/lib/rosette/core/resolvers/serializer_id.rb +37 -0
- data/lib/rosette/core/snapshots/cached_head_snapshot_factory.rb +51 -0
- data/lib/rosette/core/snapshots/cached_snapshot_factory.rb +67 -0
- data/lib/rosette/core/snapshots/head_snapshot_factory.rb +58 -0
- data/lib/rosette/core/snapshots/repo_config_path_filter.rb +83 -0
- data/lib/rosette/core/snapshots/snapshot_factory.rb +184 -0
- data/lib/rosette/core/string_utils.rb +23 -0
- data/lib/rosette/core/translation_status.rb +81 -0
- data/lib/rosette/core/validators.rb +18 -0
- data/lib/rosette/core/validators/commit_validator.rb +62 -0
- data/lib/rosette/core/validators/commits_validator.rb +32 -0
- data/lib/rosette/core/validators/encoding_validator.rb +32 -0
- data/lib/rosette/core/validators/locale_validator.rb +37 -0
- data/lib/rosette/core/validators/repo_validator.rb +33 -0
- data/lib/rosette/core/validators/serializer_validator.rb +37 -0
- data/lib/rosette/core/validators/validator.rb +31 -0
- data/lib/rosette/core/version.rb +8 -0
- data/lib/rosette/data_stores.rb +11 -0
- data/lib/rosette/data_stores/errors.rb +26 -0
- data/lib/rosette/data_stores/phrase_status.rb +59 -0
- data/lib/rosette/integrations.rb +12 -0
- data/lib/rosette/integrations/errors.rb +15 -0
- data/lib/rosette/integrations/integratable.rb +58 -0
- data/lib/rosette/integrations/integration.rb +23 -0
- data/lib/rosette/preprocessors.rb +11 -0
- data/lib/rosette/preprocessors/errors.rb +14 -0
- data/lib/rosette/preprocessors/preprocessor.rb +48 -0
- data/lib/rosette/queuing.rb +14 -0
- data/lib/rosette/queuing/commits.rb +19 -0
- data/lib/rosette/queuing/commits/commit_conductor.rb +90 -0
- data/lib/rosette/queuing/commits/commit_job.rb +93 -0
- data/lib/rosette/queuing/commits/commits_queue_configurator.rb +60 -0
- data/lib/rosette/queuing/commits/extract_stage.rb +46 -0
- data/lib/rosette/queuing/commits/fetch_stage.rb +51 -0
- data/lib/rosette/queuing/commits/finalize_stage.rb +76 -0
- data/lib/rosette/queuing/commits/phrase_storage_granularity.rb +20 -0
- data/lib/rosette/queuing/commits/push_stage.rb +91 -0
- data/lib/rosette/queuing/commits/stage.rb +96 -0
- data/lib/rosette/queuing/job.rb +74 -0
- data/lib/rosette/queuing/queue.rb +28 -0
- data/lib/rosette/queuing/queue_configurator.rb +76 -0
- data/lib/rosette/queuing/worker.rb +30 -0
- data/lib/rosette/serializers.rb +10 -0
- data/lib/rosette/serializers/serializer.rb +98 -0
- data/lib/rosette/tms.rb +9 -0
- data/lib/rosette/tms/repository.rb +95 -0
- data/rosette-core.gemspec +24 -0
- data/spec/core/branch_utils_spec.rb +110 -0
- data/spec/core/commands/git/commit_command_spec.rb +60 -0
- data/spec/core/commands/git/diff_command_spec.rb +263 -0
- data/spec/core/commands/git/fetch_command_spec.rb +61 -0
- data/spec/core/commands/git/repo_snapshot_command_spec.rb +72 -0
- data/spec/core/commands/git/show_command_spec.rb +128 -0
- data/spec/core/commands/git/snapshot_command_spec.rb +86 -0
- data/spec/core/commands/git/status_command_spec.rb +154 -0
- data/spec/core/commands/queuing/enqueue_commit_command_spec.rb +34 -0
- data/spec/core/commands/queuing/requeue_commit_command_spec.rb +46 -0
- data/spec/core/commands/translations/export_command_spec.rb +113 -0
- data/spec/core/commands/translations/translation_lookup_command_spec.rb +58 -0
- data/spec/core/configurator_spec.rb +47 -0
- data/spec/core/error_reporters/buffered_error_reporter_spec.rb +61 -0
- data/spec/core/error_reporters/nil_error_reporter_spec.rb +16 -0
- data/spec/core/error_reporters/printing_error_reporter_spec.rb +60 -0
- data/spec/core/extractor/commit_log_status_spec.rb +216 -0
- data/spec/core/extractor/commit_processor_spec.rb +68 -0
- data/spec/core/extractor/extractor_config_spec.rb +47 -0
- data/spec/core/extractor/extractor_spec.rb +26 -0
- data/spec/core/extractor/locale_spec.rb +92 -0
- data/spec/core/extractor/phrase/phrase_index_policy_spec.rb +116 -0
- data/spec/core/extractor/phrase/phrase_to_hash_spec.rb +18 -0
- data/spec/core/extractor/repo_config_spec.rb +147 -0
- data/spec/core/extractor/translation/translation_to_hash_spec.rb +25 -0
- data/spec/core/git/diff_finder_spec.rb +74 -0
- data/spec/core/git/ref_spec.rb +118 -0
- data/spec/core/git/repo_spec.rb +216 -0
- data/spec/core/path_matcher_factory_spec.rb +139 -0
- data/spec/core/resolvers/extractor_id_spec.rb +47 -0
- data/spec/core/resolvers/integration_id_spec.rb +47 -0
- data/spec/core/resolvers/preprocessor_id_spec.rb +47 -0
- data/spec/core/resolvers/serializer_id_spec.rb +47 -0
- data/spec/core/snapshots/snapshot_factory_spec.rb +145 -0
- data/spec/core/string_utils_spec.rb +19 -0
- data/spec/core/translation_status_spec.rb +91 -0
- data/spec/core/validators/commit_validator_spec.rb +40 -0
- data/spec/core/validators/encoding_validator_spec.rb +30 -0
- data/spec/core/validators/locale_validator_spec.rb +31 -0
- data/spec/core/validators/repo_validator_spec.rb +30 -0
- data/spec/core/validators/serializer_validator_spec.rb +31 -0
- data/spec/integrations/integratable_spec.rb +58 -0
- data/spec/queuing/commits/commit_conductor_spec.rb +71 -0
- data/spec/queuing/commits/commit_job_spec.rb +87 -0
- data/spec/queuing/commits/extract_stage_spec.rb +68 -0
- data/spec/queuing/commits/fetch_stage_spec.rb +101 -0
- data/spec/queuing/commits/finalize_stage_spec.rb +88 -0
- data/spec/queuing/commits/push_stage_spec.rb +145 -0
- data/spec/queuing/commits/stage_spec.rb +80 -0
- data/spec/queuing/job_spec.rb +33 -0
- data/spec/queuing/queue_configurator_spec.rb +44 -0
- data/spec/spec_helper.rb +90 -0
- data/spec/test_helpers/fake_commit_stage.rb +17 -0
- metadata +257 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Rosette
|
4
|
+
module Core
|
5
|
+
module Commands
|
6
|
+
|
7
|
+
# Custom errors that can be raised during command execution.
|
8
|
+
module Errors
|
9
|
+
# Raised whenever Rosette is asked to return information about
|
10
|
+
# a commit that has not been processed (i.e. committed) yet.
|
11
|
+
# Rosette knows nothing about commits it hasn't seen yet.
|
12
|
+
class UnprocessedCommitError < StandardError; end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Rosette
|
4
|
+
module Core
|
5
|
+
module Commands
|
6
|
+
|
7
|
+
# Inspects the given commit and extracts translatable phrases using the configured
|
8
|
+
# extractors. By design, {CommitCommand} (and the rest of Rosette) will not process
|
9
|
+
# merge commits. Generally, a snapshot of the repository will be necessary to
|
10
|
+
# find the commits where individual files were last changed (see the
|
11
|
+
# {RepoSnapshotCommand} or {SnapshotFactory} class to take snapshots).
|
12
|
+
#
|
13
|
+
# @see Rosette::Core::Commands::RepoSnapshotCommand
|
14
|
+
# @see Rosette::Core::SnapshotFactory
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
# CommitCommand.new(configuration)
|
18
|
+
# .set_repo_name('my_repo')
|
19
|
+
# .set_ref('master')
|
20
|
+
# .execute
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# CommitCommand.new(configuration)
|
24
|
+
# .set_repo_name('my_repo')
|
25
|
+
# .set_commit_id('67f0e9a60dfe39430b346086f965e6c94a8ddd24')
|
26
|
+
# .execute
|
27
|
+
class CommitCommand < GitCommand
|
28
|
+
include WithRepoName
|
29
|
+
include WithRef
|
30
|
+
|
31
|
+
# Executes the command. Causes phrases to be extracted from the given git ref or
|
32
|
+
# commit id and written to the configured data store, triggering hooks in the process.
|
33
|
+
#
|
34
|
+
# @return [void]
|
35
|
+
def execute
|
36
|
+
commit_processor.process_each_phrase(repo_name, commit_id) do |phrase|
|
37
|
+
begin
|
38
|
+
datastore.store_phrase(repo_name, phrase)
|
39
|
+
rescue ActiveRecord::RecordNotUnique => e
|
40
|
+
configuration.error_reporter.report_warning(e)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
trigger_hooks(:after)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def commit_processor
|
50
|
+
@commit_processor ||= Rosette::Core::CommitProcessor.new(
|
51
|
+
configuration, configuration.error_reporter
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
def trigger_hooks(stage)
|
56
|
+
repo_config = get_repo(repo_name)
|
57
|
+
repo_config.hooks.fetch(stage, {}).fetch(:commit, []).each do |hook_proc|
|
58
|
+
hook_proc.call(configuration, repo_config, commit_id)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,301 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Rosette
|
4
|
+
module Core
|
5
|
+
module Commands
|
6
|
+
|
7
|
+
# The base class for commands that need to calculate diffs.
|
8
|
+
class DiffBaseCommand < GitCommand
|
9
|
+
attr_reader :strict
|
10
|
+
alias_method :strict?, :strict
|
11
|
+
|
12
|
+
def initialize(*args)
|
13
|
+
super
|
14
|
+
@strict = true
|
15
|
+
end
|
16
|
+
|
17
|
+
# Sets a boolean value that determines if the diff should be made
|
18
|
+
# against true parents of the given commit or the most recently
|
19
|
+
# processed parents. If set to +true+, the diff will be computed
|
20
|
+
# between the given commit and its actual (true) parents, and an error
|
21
|
+
# will be raised if any of the parents have not been processed by
|
22
|
+
# Rosette (i.e. don't exist in the commit log). If set to +false+, the
|
23
|
+
# diff will be computed between the given commit and the most recent
|
24
|
+
# parents that have been processed. For each parent of the given commit,
|
25
|
+
# +ShowCommand+ will traverse the commit history looking for a processed
|
26
|
+
# commit and use it in place of the true parent (assuming the true
|
27
|
+
# parent hasn't been processed yet). It's worth noting that setting
|
28
|
+
# +strict+ to +false+ may potentially produce results that contain
|
29
|
+
# phrases that were not introduced exclusively in the given commit.
|
30
|
+
# Since such behavior is different from what git clients return, and
|
31
|
+
# therefore possibly unexpected, strictness is enabled (set to +true+)
|
32
|
+
# by default.
|
33
|
+
#
|
34
|
+
# @param [Boolean] strict
|
35
|
+
# @return [self]
|
36
|
+
def set_strict(strict)
|
37
|
+
@strict = strict
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
include WithSnapshots
|
44
|
+
|
45
|
+
def diff_between(commit_id, parent_commit_ids, paths = nil)
|
46
|
+
# handle the case where this is the first commit (or there are no
|
47
|
+
# processed parents)
|
48
|
+
if parent_commit_ids.empty?
|
49
|
+
entries = retrieve_child_phrases(commit_id, []).map do |p|
|
50
|
+
DiffEntry.new(p, :added)
|
51
|
+
end
|
52
|
+
|
53
|
+
{ added: entries, removed: [], modified: [] }
|
54
|
+
else
|
55
|
+
calculate_phrase_diff_against(commit_id, parent_commit_ids, paths)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def ensure_commits_have_been_processed(commits)
|
60
|
+
commits.uniq.each do |commit_id|
|
61
|
+
if commit_id
|
62
|
+
unless commit_exists?(repo_name, commit_id)
|
63
|
+
raise Errors::UnprocessedCommitError,
|
64
|
+
"Commit #{commit_id} has not been processed yet."
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def commit_exists?(repo_name, commit_id)
|
71
|
+
datastore.commit_log_exists?(repo_name, commit_id)
|
72
|
+
end
|
73
|
+
|
74
|
+
def compare(head_phrases, diff_point_phrases)
|
75
|
+
partitioned_head_phrases = partition_phrases(head_phrases)
|
76
|
+
partitioned_diff_point_phrases = partition_phrases(diff_point_phrases)
|
77
|
+
join_diffs(
|
78
|
+
key_diff(partitioned_head_phrases, partitioned_diff_point_phrases),
|
79
|
+
meta_key_diff(partitioned_head_phrases, partitioned_diff_point_phrases)
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
def join_diffs(diff1, diff2)
|
84
|
+
# we don't care about :unmodified, so leave it out
|
85
|
+
[:added, :removed, :modified].each_with_object({}) do |state, ret|
|
86
|
+
ret[state] = diff1[state] + diff2[state]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def key_diff(partitioned_head_phrases, partitioned_diff_point_phrases)
|
91
|
+
diff = Hash.new { |hash, key| hash[key] = [] }
|
92
|
+
|
93
|
+
key_head_to_diff_point(
|
94
|
+
partitioned_head_phrases.first,
|
95
|
+
partitioned_diff_point_phrases.first
|
96
|
+
) do |phrase, state, old_phrase|
|
97
|
+
diff[state] << DiffEntry.new(phrase, state, old_phrase)
|
98
|
+
end
|
99
|
+
|
100
|
+
key_diff_point_to_head(
|
101
|
+
partitioned_head_phrases.first,
|
102
|
+
partitioned_diff_point_phrases.first
|
103
|
+
) do |phrase, state, old_phrase|
|
104
|
+
diff[state] << DiffEntry.new(phrase, state, old_phrase)
|
105
|
+
end
|
106
|
+
|
107
|
+
diff
|
108
|
+
end
|
109
|
+
|
110
|
+
def key_head_to_diff_point(head_phrases, diff_point_phrases)
|
111
|
+
if block_given?
|
112
|
+
head_phrases.each do |head_phrase|
|
113
|
+
phrase = diff_point_phrases.find do |diff_point_phrase|
|
114
|
+
diff_point_phrase.key == head_phrase.key &&
|
115
|
+
diff_point_phrase.file == head_phrase.file
|
116
|
+
end
|
117
|
+
|
118
|
+
state = phrase ? :unmodified : :added
|
119
|
+
yield head_phrase, state
|
120
|
+
end
|
121
|
+
else
|
122
|
+
to_enum(__method__, head_phrases, diff_point_phrases)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def key_diff_point_to_head(head_phrases, diff_point_phrases)
|
127
|
+
if block_given?
|
128
|
+
diff_point_phrases.each do |diff_point_phrase|
|
129
|
+
phrase = head_phrases.find do |head_phrase|
|
130
|
+
head_phrase.key == diff_point_phrase.key &&
|
131
|
+
head_phrase.file == diff_point_phrase.file
|
132
|
+
end
|
133
|
+
|
134
|
+
unless phrase
|
135
|
+
yield diff_point_phrase, :removed
|
136
|
+
end
|
137
|
+
end
|
138
|
+
else
|
139
|
+
to_enum(__method__, head_phrases, diff_point_phrases)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def meta_key_diff(partitioned_head_phrases, partitioned_diff_point_phrases)
|
144
|
+
diff = Hash.new { |hash, key| hash[key] = [] }
|
145
|
+
|
146
|
+
meta_key_head_to_diff_point(
|
147
|
+
partitioned_head_phrases.last,
|
148
|
+
partitioned_diff_point_phrases.last
|
149
|
+
) do |phrase, state, old_phrase|
|
150
|
+
diff[state] << DiffEntry.new(phrase, state, old_phrase)
|
151
|
+
end
|
152
|
+
|
153
|
+
meta_key_diff_point_to_head(
|
154
|
+
partitioned_head_phrases.last,
|
155
|
+
partitioned_diff_point_phrases.last
|
156
|
+
) do |phrase, state, old_phrase|
|
157
|
+
diff[state] << DiffEntry.new(phrase, state, old_phrase)
|
158
|
+
end
|
159
|
+
|
160
|
+
diff
|
161
|
+
end
|
162
|
+
|
163
|
+
# identifies phrases in head that:
|
164
|
+
# are not in diff point ('added')
|
165
|
+
# have the same meta key but different keys as a phrase in diff point ('modified')
|
166
|
+
# are identical to a phrase in diff point ('unmodified')
|
167
|
+
def meta_key_head_to_diff_point(head_phrases, diff_point_phrases)
|
168
|
+
if block_given?
|
169
|
+
# iterate over all head phrases that have meta keys
|
170
|
+
head_phrases.each do |head_phrase|
|
171
|
+
idx = diff_point_phrases.find_index do |diff_point_phrase|
|
172
|
+
diff_point_phrase.meta_key == head_phrase.meta_key &&
|
173
|
+
diff_point_phrase.file == head_phrase.file
|
174
|
+
end
|
175
|
+
|
176
|
+
state = if idx
|
177
|
+
if diff_point_phrases[idx].key == head_phrase.key
|
178
|
+
:unmodified
|
179
|
+
else
|
180
|
+
:modified
|
181
|
+
end
|
182
|
+
else
|
183
|
+
:added
|
184
|
+
end
|
185
|
+
|
186
|
+
if state == :modified
|
187
|
+
yield head_phrase, state, diff_point_phrases[idx]
|
188
|
+
else
|
189
|
+
yield head_phrase, state
|
190
|
+
end
|
191
|
+
end
|
192
|
+
else
|
193
|
+
to_enum(__method__, head_phrases, diff_point_phrases)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# identifies phrases in diff point that are not in head ('removed')
|
198
|
+
def meta_key_diff_point_to_head(head_phrases, diff_point_phrases)
|
199
|
+
if block_given?
|
200
|
+
diff_point_phrases.each do |diff_point_phrase|
|
201
|
+
idx = head_phrases.find_index do |head_phrase|
|
202
|
+
head_phrase.meta_key == diff_point_phrase.meta_key &&
|
203
|
+
head_phrase.file == diff_point_phrase.file
|
204
|
+
end
|
205
|
+
|
206
|
+
unless idx
|
207
|
+
yield diff_point_phrase, :removed
|
208
|
+
end
|
209
|
+
end
|
210
|
+
else
|
211
|
+
to_enum(__method__, head_phrases, diff_point_phrases)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def partition_phrases(phrases)
|
216
|
+
phrases.partition { |ph| ph.index_key == :key }
|
217
|
+
end
|
218
|
+
|
219
|
+
# if parent_commit_id is processed, it will get returned (i.e. it will
|
220
|
+
# start looking at parent_commit_id, not parent_commit_id - 1).
|
221
|
+
def get_closest_processed_parent(parent_commit_id)
|
222
|
+
parent = repo.each_commit_starting_at(parent_commit_id).find do |rev|
|
223
|
+
commit_exists?(repo_name, rev.getId.name)
|
224
|
+
end
|
225
|
+
|
226
|
+
parent.getId.name if parent
|
227
|
+
end
|
228
|
+
|
229
|
+
def calculate_phrase_diff_against(commit_id, parent_commit_ids, paths)
|
230
|
+
{ added: [], modified: [], removed: [] }.tap do |final_diff|
|
231
|
+
parent_commit_ids.each do |parent_commit_id|
|
232
|
+
git_diff = compute_git_diff_between(commit_id, parent_commit_id)
|
233
|
+
paths ||= compute_paths(git_diff)
|
234
|
+
child_phrases = retrieve_child_phrases(commit_id, paths)
|
235
|
+
parent_phrases = retrieve_parent_phrases(parent_commit_id, paths)
|
236
|
+
phrase_diff = compare(child_phrases, parent_phrases)
|
237
|
+
|
238
|
+
phrase_diff.each_pair do |state, phrases|
|
239
|
+
final_diff[state].concat(phrases)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def compute_git_diff_between(commit_id, parent_commit_id)
|
246
|
+
rev_walker = RevWalk.new(repo.jgit_repo)
|
247
|
+
diff_finder = DiffFinder.new(repo.jgit_repo, rev_walker)
|
248
|
+
repo.diff([parent_commit_id], commit_id, [], diff_finder)
|
249
|
+
end
|
250
|
+
|
251
|
+
def compute_paths(diff)
|
252
|
+
child_paths = []
|
253
|
+
|
254
|
+
diff.each_with_object([]) do |(_, diff_entries), ret|
|
255
|
+
diff_entries.each do |diff_entry|
|
256
|
+
path = get_path(diff_entry)
|
257
|
+
|
258
|
+
if repo_config.extractor_configs.any? { |ext| ext.matches?(path) }
|
259
|
+
ret << path
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def get_path(diff_entry)
|
266
|
+
new_path = diff_entry.getNewPath
|
267
|
+
|
268
|
+
if new_path == '/dev/null'
|
269
|
+
diff_entry.getOldPath
|
270
|
+
else
|
271
|
+
new_path
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def retrieve_child_phrases(commit_id, paths)
|
276
|
+
snapshot = take_snapshot(repo_config, commit_id, paths)
|
277
|
+
datastore.phrases_by_commits(repo_name, snapshot).to_a
|
278
|
+
end
|
279
|
+
|
280
|
+
def retrieve_parent_phrases(parent_commit_id, paths)
|
281
|
+
ensure_commits_have_been_processed([parent_commit_id])
|
282
|
+
|
283
|
+
parent_snapshot = take_snapshot(
|
284
|
+
repo_config, parent_commit_id, paths
|
285
|
+
)
|
286
|
+
|
287
|
+
datastore.phrases_by_commits(repo_name, parent_snapshot).to_a
|
288
|
+
end
|
289
|
+
|
290
|
+
def repo_config
|
291
|
+
get_repo(repo_name)
|
292
|
+
end
|
293
|
+
|
294
|
+
def repo
|
295
|
+
repo_config.repo
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Rosette
|
4
|
+
module Core
|
5
|
+
module Commands
|
6
|
+
|
7
|
+
# Detects phrase changes between two git refs or commit ids. Identifies phrases
|
8
|
+
# that have been added, removed, or changed. The refs used in the comparison
|
9
|
+
# are referred to as a "head" and a "diff point". In Rosette/git parlance, a "head"
|
10
|
+
# generally means a git ref that is currently being modified, i.e. a branch. A
|
11
|
+
# "diff point" is some common ref to compare "head" to, often the repository's
|
12
|
+
# "master" branch.
|
13
|
+
#
|
14
|
+
# Perhaps an easier way to visualize these concepts is through a git command-line
|
15
|
+
# example. Imagine you're developing a feature to add widget support to your app.
|
16
|
+
# You start work by switching to a new git branch (i.e. +git+ +checkout+ +-b+ +add_widgets+).
|
17
|
+
# After adding a few files, changing some others, and deleting old code, you want
|
18
|
+
# to see a complete set of all your changes. To do this, you run +git+ +diff+. At
|
19
|
+
# this point, your "head" is your branch "add_widgets" and your "diff point" is
|
20
|
+
# "master" (or whichever branch you were on when you ran +git+ +checkout+). You
|
21
|
+
# could also have run +git+ +diff+ +master+ or +git+ +diff+ +master+ +HEAD+ to
|
22
|
+
# get the same result. You can think of {DiffCommand} like a +git+ +diff+ command
|
23
|
+
# of the form +git+ +diff+ +<diff+ +point>+ +<head>+.
|
24
|
+
#
|
25
|
+
# @see Rosette::Core::DiffFinder
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# cmd = DiffCommand.new(configuration)
|
29
|
+
# .set_repo_name('my_repo')
|
30
|
+
# .set_head_ref('my_branch')
|
31
|
+
# .set_diff_point_ref('master')
|
32
|
+
# .set_paths(['config/locales/en.yml'])
|
33
|
+
#
|
34
|
+
# cmd.execute
|
35
|
+
# # {
|
36
|
+
# # added: [
|
37
|
+
# # { key: 'Foo', ... },
|
38
|
+
# # ],
|
39
|
+
# # removed: [
|
40
|
+
# # { key: 'I got deleted', ... }
|
41
|
+
# # ],
|
42
|
+
# # modified: [
|
43
|
+
# # { key: 'New value', old_key: 'Old value', ... }
|
44
|
+
# # ]
|
45
|
+
# # }
|
46
|
+
#
|
47
|
+
# @!attribute [r] head_commit_str
|
48
|
+
# @return [String] the raw head ref or commit id as set via {#set_head_ref}
|
49
|
+
# or {#set_head_commit_id}.
|
50
|
+
#
|
51
|
+
# @!attribute [r] diff_point_commit_str
|
52
|
+
# @return [String] the raw diff point ref or commit id as set via
|
53
|
+
# {#set_diff_point_ref} or {#set_diff_point_commit_id}.
|
54
|
+
#
|
55
|
+
# @!attribute [r] paths
|
56
|
+
# @return [Array] the list of paths to include in the diff. The diff will not
|
57
|
+
# contain phrases that were added/removed/modified in files that are not
|
58
|
+
# contained within this list.
|
59
|
+
class DiffCommand < DiffBaseCommand
|
60
|
+
attr_reader :head_commit_str, :diff_point_commit_str, :paths
|
61
|
+
|
62
|
+
include WithRepoName
|
63
|
+
|
64
|
+
validate :head_commit_str, type: :commit
|
65
|
+
validate :diff_point_commit_str, type: :commit
|
66
|
+
|
67
|
+
# Set the head commit id. Calling this method after {#set_head_ref} will
|
68
|
+
# overwrite the head ref value. In other words, it's generally a good idea
|
69
|
+
# to only call one of {#set_head_commit_id} or {#set_head_ref} but not both.
|
70
|
+
#
|
71
|
+
# @param [String] head_commit_id The head commit id.
|
72
|
+
# @return [self]
|
73
|
+
def set_head_commit_id(head_commit_id)
|
74
|
+
@head_commit_str = head_commit_id
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
# Set the head ref (i.e. a branch name). Calling this method after
|
79
|
+
# {#set_head_commit_id} will overwrite the head commit id value. In other
|
80
|
+
# words, it's generally a good idea to only call one of {#set_head_commit_id}
|
81
|
+
# or {#set_head_ref} but not both.
|
82
|
+
#
|
83
|
+
# @param [String] head_ref The head ref.
|
84
|
+
# @return [self]
|
85
|
+
def set_head_ref(head_ref)
|
86
|
+
@head_commit_str = head_ref
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
# Set the diff point commit id. Calling this method after {#set_head_ref}
|
91
|
+
# will overwrite the head ref value. In other words, it's generally a good
|
92
|
+
# idea to only call one of {#set_head_commit_id} or {#set_head_ref} but
|
93
|
+
# not both.
|
94
|
+
#
|
95
|
+
# @param [String] diff_point_commit_id The diff point commit id.
|
96
|
+
# @return [self]
|
97
|
+
def set_diff_point_commit_id(diff_point_commit_id)
|
98
|
+
@diff_point_commit_str = diff_point_commit_id
|
99
|
+
self
|
100
|
+
end
|
101
|
+
|
102
|
+
# Set the diff point ref (i.e. master). Calling this method after
|
103
|
+
# {#set_diff_point_commit_id} will overwrite the diff point commit id value.
|
104
|
+
# In other words, it's generally a good idea to only call one of
|
105
|
+
# {#set_diff_point_commit_id} or {#set_diff_point_ref} but not both.
|
106
|
+
#
|
107
|
+
# @param [String] diff_point_ref The diff point ref.
|
108
|
+
# @return [self]
|
109
|
+
def set_diff_point_ref(diff_point_ref)
|
110
|
+
@diff_point_commit_str = diff_point_ref
|
111
|
+
self
|
112
|
+
end
|
113
|
+
|
114
|
+
# Set the list of paths to consider when computing the diff. Any paths not
|
115
|
+
# in this list will not appear in the diff.
|
116
|
+
#
|
117
|
+
# @param [Array] paths The list of paths.
|
118
|
+
# @return [self]
|
119
|
+
def set_paths(paths)
|
120
|
+
@paths = paths
|
121
|
+
self
|
122
|
+
end
|
123
|
+
|
124
|
+
# Resolves the given head git ref or commit id and returns the corresponding
|
125
|
+
# commit id. If {#set_head_ref} was used to set a git ref (i.e. branch name),
|
126
|
+
# this method looks up and returns the corresponding commit id. If
|
127
|
+
# {#set_head_commit_id} was used to set a commit id, then that commit id is
|
128
|
+
# validated and returned.
|
129
|
+
#
|
130
|
+
# @return [String] The commit id set via either {#set_head_ref} or
|
131
|
+
# {#set_head_commit_id}.
|
132
|
+
#
|
133
|
+
# @raise [Java::OrgEclipseJgitErrors::MissingObjectException, Java::JavaLang::IllegalArgumentException]
|
134
|
+
# If either the commit id doesn't exist or the ref can't be found.
|
135
|
+
def head_commit_id
|
136
|
+
@head_commit_id ||= get_repo(repo_name)
|
137
|
+
.repo.get_rev_commit(head_commit_str)
|
138
|
+
.getId
|
139
|
+
.name
|
140
|
+
end
|
141
|
+
|
142
|
+
# Resolves the given diff point git ref or commit id and returns the corresponding
|
143
|
+
# commit id. If {#set_diff_point_ref} was used to set a git ref (i.e. branch name),
|
144
|
+
# this method looks up and returns the corresponding commit id. If
|
145
|
+
# {#set_diff_point_commit_id} was used to set a commit id, then that commit id is
|
146
|
+
# validated and returned.
|
147
|
+
#
|
148
|
+
# @return [String] The commit id set via either {#set_diff_point_ref} or
|
149
|
+
# {#set_diff_point_commit_id}.
|
150
|
+
#
|
151
|
+
# @raise [Java::OrgEclipseJgitErrors::MissingObjectException, Java::JavaLang::IllegalArgumentException]
|
152
|
+
# If either the commit id doesn't exist or the ref can't be found.
|
153
|
+
def diff_point_commit_id
|
154
|
+
@diff_point_commit_id ||= get_repo(repo_name)
|
155
|
+
.repo.get_rev_commit(diff_point_commit_str)
|
156
|
+
.getId
|
157
|
+
.name
|
158
|
+
end
|
159
|
+
|
160
|
+
# Computes the diff.
|
161
|
+
# @return [Hash] a hash of differences, grouped by added/removed/modified keys. Each
|
162
|
+
# value is an array of phrases. For added and removed phrases, the phrase hashes
|
163
|
+
# will contain normal phrase attributes. For changed phrases, the phrase hashes
|
164
|
+
# will contain normal phrase attributes plus a special +old_key+ attribute that
|
165
|
+
# contains the previous key of the phrase. See the example above for a visual
|
166
|
+
# representation of the diff hash.
|
167
|
+
def execute
|
168
|
+
diff_between(head_commit_id, Array(parent_commit_id))
|
169
|
+
end
|
170
|
+
|
171
|
+
protected
|
172
|
+
|
173
|
+
def diff_point_exists?
|
174
|
+
commit_exists?(repo_name, diff_point_commit_id)
|
175
|
+
end
|
176
|
+
|
177
|
+
def parent_commit_id
|
178
|
+
parent_commit_id = if strict? || diff_point_exists?
|
179
|
+
diff_point_commit_id
|
180
|
+
else
|
181
|
+
get_closest_processed_parent(diff_point_commit_id)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|