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.
Files changed (158) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +26 -0
  3. data/History.txt +3 -0
  4. data/README.md +94 -0
  5. data/Rakefile +18 -0
  6. data/lib/rosette/core.rb +110 -0
  7. data/lib/rosette/core/branch_utils.rb +152 -0
  8. data/lib/rosette/core/commands.rb +139 -0
  9. data/lib/rosette/core/commands/errors.rb +17 -0
  10. data/lib/rosette/core/commands/git/commit_command.rb +65 -0
  11. data/lib/rosette/core/commands/git/diff_base_command.rb +301 -0
  12. data/lib/rosette/core/commands/git/diff_command.rb +188 -0
  13. data/lib/rosette/core/commands/git/diff_entry.rb +44 -0
  14. data/lib/rosette/core/commands/git/fetch_command.rb +27 -0
  15. data/lib/rosette/core/commands/git/repo_snapshot_command.rb +40 -0
  16. data/lib/rosette/core/commands/git/show_command.rb +70 -0
  17. data/lib/rosette/core/commands/git/snapshot_command.rb +50 -0
  18. data/lib/rosette/core/commands/git/status_command.rb +128 -0
  19. data/lib/rosette/core/commands/git/with_non_merge_ref.rb +48 -0
  20. data/lib/rosette/core/commands/git/with_ref.rb +92 -0
  21. data/lib/rosette/core/commands/git/with_refs.rb +92 -0
  22. data/lib/rosette/core/commands/git/with_repo_name.rb +50 -0
  23. data/lib/rosette/core/commands/git/with_snapshots.rb +45 -0
  24. data/lib/rosette/core/commands/queuing/enqueue_commit_command.rb +37 -0
  25. data/lib/rosette/core/commands/queuing/requeue_commit_command.rb +46 -0
  26. data/lib/rosette/core/commands/translations/export_command.rb +257 -0
  27. data/lib/rosette/core/commands/translations/translation_lookup_command.rb +66 -0
  28. data/lib/rosette/core/commands/translations/with_locale.rb +47 -0
  29. data/lib/rosette/core/configurator.rb +160 -0
  30. data/lib/rosette/core/error_reporters/buffered_error_reporter.rb +96 -0
  31. data/lib/rosette/core/error_reporters/error_reporter.rb +31 -0
  32. data/lib/rosette/core/error_reporters/nil_error_reporter.rb +25 -0
  33. data/lib/rosette/core/error_reporters/printing_error_reporter.rb +58 -0
  34. data/lib/rosette/core/error_reporters/raising_error_reporter.rb +27 -0
  35. data/lib/rosette/core/errors.rb +93 -0
  36. data/lib/rosette/core/extractor/commit_log.rb +33 -0
  37. data/lib/rosette/core/extractor/commit_log_status.rb +57 -0
  38. data/lib/rosette/core/extractor/commit_processor.rb +109 -0
  39. data/lib/rosette/core/extractor/extractor.rb +72 -0
  40. data/lib/rosette/core/extractor/extractor_config.rb +74 -0
  41. data/lib/rosette/core/extractor/locale.rb +118 -0
  42. data/lib/rosette/core/extractor/phrase.rb +76 -0
  43. data/lib/rosette/core/extractor/phrase/phrase_index_policy.rb +108 -0
  44. data/lib/rosette/core/extractor/phrase/phrase_to_hash.rb +33 -0
  45. data/lib/rosette/core/extractor/repo_config.rb +339 -0
  46. data/lib/rosette/core/extractor/serializer_config.rb +55 -0
  47. data/lib/rosette/core/extractor/static_extractor.rb +44 -0
  48. data/lib/rosette/core/extractor/translation.rb +44 -0
  49. data/lib/rosette/core/extractor/translation/translation_to_hash.rb +28 -0
  50. data/lib/rosette/core/git/diff_finder.rb +131 -0
  51. data/lib/rosette/core/git/ref.rb +116 -0
  52. data/lib/rosette/core/git/repo.rb +378 -0
  53. data/lib/rosette/core/path_matcher_factory.rb +330 -0
  54. data/lib/rosette/core/resolvers/extractor_id.rb +37 -0
  55. data/lib/rosette/core/resolvers/integration_id.rb +37 -0
  56. data/lib/rosette/core/resolvers/preprocessor_id.rb +38 -0
  57. data/lib/rosette/core/resolvers/resolver.rb +115 -0
  58. data/lib/rosette/core/resolvers/serializer_id.rb +37 -0
  59. data/lib/rosette/core/snapshots/cached_head_snapshot_factory.rb +51 -0
  60. data/lib/rosette/core/snapshots/cached_snapshot_factory.rb +67 -0
  61. data/lib/rosette/core/snapshots/head_snapshot_factory.rb +58 -0
  62. data/lib/rosette/core/snapshots/repo_config_path_filter.rb +83 -0
  63. data/lib/rosette/core/snapshots/snapshot_factory.rb +184 -0
  64. data/lib/rosette/core/string_utils.rb +23 -0
  65. data/lib/rosette/core/translation_status.rb +81 -0
  66. data/lib/rosette/core/validators.rb +18 -0
  67. data/lib/rosette/core/validators/commit_validator.rb +62 -0
  68. data/lib/rosette/core/validators/commits_validator.rb +32 -0
  69. data/lib/rosette/core/validators/encoding_validator.rb +32 -0
  70. data/lib/rosette/core/validators/locale_validator.rb +37 -0
  71. data/lib/rosette/core/validators/repo_validator.rb +33 -0
  72. data/lib/rosette/core/validators/serializer_validator.rb +37 -0
  73. data/lib/rosette/core/validators/validator.rb +31 -0
  74. data/lib/rosette/core/version.rb +8 -0
  75. data/lib/rosette/data_stores.rb +11 -0
  76. data/lib/rosette/data_stores/errors.rb +26 -0
  77. data/lib/rosette/data_stores/phrase_status.rb +59 -0
  78. data/lib/rosette/integrations.rb +12 -0
  79. data/lib/rosette/integrations/errors.rb +15 -0
  80. data/lib/rosette/integrations/integratable.rb +58 -0
  81. data/lib/rosette/integrations/integration.rb +23 -0
  82. data/lib/rosette/preprocessors.rb +11 -0
  83. data/lib/rosette/preprocessors/errors.rb +14 -0
  84. data/lib/rosette/preprocessors/preprocessor.rb +48 -0
  85. data/lib/rosette/queuing.rb +14 -0
  86. data/lib/rosette/queuing/commits.rb +19 -0
  87. data/lib/rosette/queuing/commits/commit_conductor.rb +90 -0
  88. data/lib/rosette/queuing/commits/commit_job.rb +93 -0
  89. data/lib/rosette/queuing/commits/commits_queue_configurator.rb +60 -0
  90. data/lib/rosette/queuing/commits/extract_stage.rb +46 -0
  91. data/lib/rosette/queuing/commits/fetch_stage.rb +51 -0
  92. data/lib/rosette/queuing/commits/finalize_stage.rb +76 -0
  93. data/lib/rosette/queuing/commits/phrase_storage_granularity.rb +20 -0
  94. data/lib/rosette/queuing/commits/push_stage.rb +91 -0
  95. data/lib/rosette/queuing/commits/stage.rb +96 -0
  96. data/lib/rosette/queuing/job.rb +74 -0
  97. data/lib/rosette/queuing/queue.rb +28 -0
  98. data/lib/rosette/queuing/queue_configurator.rb +76 -0
  99. data/lib/rosette/queuing/worker.rb +30 -0
  100. data/lib/rosette/serializers.rb +10 -0
  101. data/lib/rosette/serializers/serializer.rb +98 -0
  102. data/lib/rosette/tms.rb +9 -0
  103. data/lib/rosette/tms/repository.rb +95 -0
  104. data/rosette-core.gemspec +24 -0
  105. data/spec/core/branch_utils_spec.rb +110 -0
  106. data/spec/core/commands/git/commit_command_spec.rb +60 -0
  107. data/spec/core/commands/git/diff_command_spec.rb +263 -0
  108. data/spec/core/commands/git/fetch_command_spec.rb +61 -0
  109. data/spec/core/commands/git/repo_snapshot_command_spec.rb +72 -0
  110. data/spec/core/commands/git/show_command_spec.rb +128 -0
  111. data/spec/core/commands/git/snapshot_command_spec.rb +86 -0
  112. data/spec/core/commands/git/status_command_spec.rb +154 -0
  113. data/spec/core/commands/queuing/enqueue_commit_command_spec.rb +34 -0
  114. data/spec/core/commands/queuing/requeue_commit_command_spec.rb +46 -0
  115. data/spec/core/commands/translations/export_command_spec.rb +113 -0
  116. data/spec/core/commands/translations/translation_lookup_command_spec.rb +58 -0
  117. data/spec/core/configurator_spec.rb +47 -0
  118. data/spec/core/error_reporters/buffered_error_reporter_spec.rb +61 -0
  119. data/spec/core/error_reporters/nil_error_reporter_spec.rb +16 -0
  120. data/spec/core/error_reporters/printing_error_reporter_spec.rb +60 -0
  121. data/spec/core/extractor/commit_log_status_spec.rb +216 -0
  122. data/spec/core/extractor/commit_processor_spec.rb +68 -0
  123. data/spec/core/extractor/extractor_config_spec.rb +47 -0
  124. data/spec/core/extractor/extractor_spec.rb +26 -0
  125. data/spec/core/extractor/locale_spec.rb +92 -0
  126. data/spec/core/extractor/phrase/phrase_index_policy_spec.rb +116 -0
  127. data/spec/core/extractor/phrase/phrase_to_hash_spec.rb +18 -0
  128. data/spec/core/extractor/repo_config_spec.rb +147 -0
  129. data/spec/core/extractor/translation/translation_to_hash_spec.rb +25 -0
  130. data/spec/core/git/diff_finder_spec.rb +74 -0
  131. data/spec/core/git/ref_spec.rb +118 -0
  132. data/spec/core/git/repo_spec.rb +216 -0
  133. data/spec/core/path_matcher_factory_spec.rb +139 -0
  134. data/spec/core/resolvers/extractor_id_spec.rb +47 -0
  135. data/spec/core/resolvers/integration_id_spec.rb +47 -0
  136. data/spec/core/resolvers/preprocessor_id_spec.rb +47 -0
  137. data/spec/core/resolvers/serializer_id_spec.rb +47 -0
  138. data/spec/core/snapshots/snapshot_factory_spec.rb +145 -0
  139. data/spec/core/string_utils_spec.rb +19 -0
  140. data/spec/core/translation_status_spec.rb +91 -0
  141. data/spec/core/validators/commit_validator_spec.rb +40 -0
  142. data/spec/core/validators/encoding_validator_spec.rb +30 -0
  143. data/spec/core/validators/locale_validator_spec.rb +31 -0
  144. data/spec/core/validators/repo_validator_spec.rb +30 -0
  145. data/spec/core/validators/serializer_validator_spec.rb +31 -0
  146. data/spec/integrations/integratable_spec.rb +58 -0
  147. data/spec/queuing/commits/commit_conductor_spec.rb +71 -0
  148. data/spec/queuing/commits/commit_job_spec.rb +87 -0
  149. data/spec/queuing/commits/extract_stage_spec.rb +68 -0
  150. data/spec/queuing/commits/fetch_stage_spec.rb +101 -0
  151. data/spec/queuing/commits/finalize_stage_spec.rb +88 -0
  152. data/spec/queuing/commits/push_stage_spec.rb +145 -0
  153. data/spec/queuing/commits/stage_spec.rb +80 -0
  154. data/spec/queuing/job_spec.rb +33 -0
  155. data/spec/queuing/queue_configurator_spec.rb +44 -0
  156. data/spec/spec_helper.rb +90 -0
  157. data/spec/test_helpers/fake_commit_stage.rb +17 -0
  158. 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