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,55 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Rosette
|
4
|
+
module Core
|
5
|
+
|
6
|
+
# Configuration for a serializer. Should generally be configured using
|
7
|
+
# an instance of {RepoConfig}.
|
8
|
+
#
|
9
|
+
# @see RepoConfig
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# RepoConfig.new('my_repo')
|
13
|
+
# .add_serializer('rails', 'yaml/rails') do |ser|
|
14
|
+
# ser.add_preprocessor('normalization') do |pre|
|
15
|
+
# pre.set_normalization_form(:nfc)
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# @!attribute [r] name
|
20
|
+
# @return [String] the semantic name of this serializer.
|
21
|
+
# @!attribute [r] klass
|
22
|
+
# @return [Class] the serializer's class.
|
23
|
+
# @!attribute [r] serializer_id
|
24
|
+
# @return [String] the id of the serializer.
|
25
|
+
# @!attribute [r] preprocessors
|
26
|
+
# @return [Array] a list of preprocessor configurations.
|
27
|
+
class SerializerConfig
|
28
|
+
attr_reader :name, :klass, :serializer_id, :preprocessors
|
29
|
+
|
30
|
+
# Creates a new serializer config.
|
31
|
+
#
|
32
|
+
# @param [String] name The semantic name of this serializer.
|
33
|
+
# @param [Class] klass The serializer's class.
|
34
|
+
# @param [String] serializer_id The id of the serializer.
|
35
|
+
def initialize(name, klass, serializer_id)
|
36
|
+
@name = name
|
37
|
+
@klass = klass
|
38
|
+
@serializer_id = serializer_id
|
39
|
+
@preprocessors = []
|
40
|
+
end
|
41
|
+
|
42
|
+
# Adds a pre-processor to this serializer config. The given block
|
43
|
+
# will be passed to the pre-processor's configurator, which will
|
44
|
+
# in turn yield the configurator to you.
|
45
|
+
#
|
46
|
+
# @param [String] preprocessor_id The id of the preprocessor to add.
|
47
|
+
# @return [void]
|
48
|
+
def add_preprocessor(preprocessor_id, &block)
|
49
|
+
klass = PreprocessorId.resolve(preprocessor_id)
|
50
|
+
preprocessors << klass.configure(&block)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Rosette
|
4
|
+
module Core
|
5
|
+
|
6
|
+
# Base class for extractors that extract entries from flat files,
|
7
|
+
# eg. XML, YAML, json, etc.
|
8
|
+
#
|
9
|
+
# @!attribute [r] config
|
10
|
+
# @return [Configurator] the Rosette config to use.
|
11
|
+
class StaticExtractor
|
12
|
+
attr_reader :config
|
13
|
+
|
14
|
+
# Creates a new extractor.
|
15
|
+
#
|
16
|
+
# @param [Configurator] config The Rosette config to use.
|
17
|
+
def initialize(config = nil)
|
18
|
+
@config = config
|
19
|
+
end
|
20
|
+
|
21
|
+
# Extracts each translatable phrase from the given flat file
|
22
|
+
# contents. Must be implemented by derived classes
|
23
|
+
#
|
24
|
+
# @param [String] file_contents The flat file contents to extract
|
25
|
+
# phrases from.
|
26
|
+
# @return [void, Enumerator] If passed a block, this method yields
|
27
|
+
# each consecutive phrase found in +file_contents+. If no block is
|
28
|
+
# passed, it returns an +Enumerator+.
|
29
|
+
# @yield [phrase] a single extracted phrase.
|
30
|
+
# @yieldparam phrase [Phrase]
|
31
|
+
def extract_each_from(file_contents)
|
32
|
+
raise NotImplementedError,
|
33
|
+
"#{__method__} must be implemented by derived classes."
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def make_phrase(key, meta_key = nil, file = nil)
|
39
|
+
Phrase.new(key, meta_key, file)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Rosette
|
4
|
+
module Core
|
5
|
+
|
6
|
+
# Represents a translation. Translations always have an associated phrase,
|
7
|
+
# a locale, and some translated text.
|
8
|
+
#
|
9
|
+
# @!attribute [r] phrase
|
10
|
+
# @return [Phrase] the associated phrase object.
|
11
|
+
# @!attribute [r] locale
|
12
|
+
# @return [String] the locale code.
|
13
|
+
# @!attribute [r] translation
|
14
|
+
# @return [String] the translation text.
|
15
|
+
class Translation
|
16
|
+
include TranslationToHash
|
17
|
+
|
18
|
+
attr_reader :phrase, :locale, :translation
|
19
|
+
|
20
|
+
# Creates a new translation object.
|
21
|
+
#
|
22
|
+
# @param [Phrase] phrase The associated phrase object.
|
23
|
+
# @param [String] locale The locale code.
|
24
|
+
# @param [String] translation The translated text.
|
25
|
+
def initialize(phrase, locale, translation)
|
26
|
+
@phrase = phrase
|
27
|
+
@locale = locale
|
28
|
+
@translation = translation
|
29
|
+
end
|
30
|
+
|
31
|
+
# Turns this translation object into a hash.
|
32
|
+
#
|
33
|
+
# @return [Hash] a hash with +phrase+ as a hash, +locale+, and
|
34
|
+
# +translation+.
|
35
|
+
def self.from_h(hash)
|
36
|
+
new(
|
37
|
+
Phrase.from_h(hash[:phrase]),
|
38
|
+
hash[:locale], hash[:translation]
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Rosette
|
4
|
+
module Core
|
5
|
+
|
6
|
+
# Turns a {Translation} into a hash. Must be mixed into a {Translation}-like
|
7
|
+
# class.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# t = Translation.new
|
11
|
+
# t.translation = 'foó'
|
12
|
+
# t.locale = 'fr-FR'
|
13
|
+
# t.phrase = Phrase.new
|
14
|
+
#
|
15
|
+
# t.to_h # => { translation: 'foó', locale: 'fr-FR', phrase: { ... } }
|
16
|
+
module TranslationToHash
|
17
|
+
# Converts the attributes of a {Translation} into a hash of attributes.
|
18
|
+
# This includes the attributes of the associated {Phrase} object, which
|
19
|
+
# is also converted to a hash via the {PhraseToHash} module.
|
20
|
+
#
|
21
|
+
# @return [Hash] a hash of translation attributes.
|
22
|
+
def to_h
|
23
|
+
{ locale: locale, translation: translation, phrase: phrase.to_h }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
java_import 'org.eclipse.jgit.util.io.NullOutputStream'
|
4
|
+
java_import 'org.eclipse.jgit.diff.DiffFormatter'
|
5
|
+
java_import 'org.eclipse.jgit.treewalk.CanonicalTreeParser'
|
6
|
+
java_import 'org.eclipse.jgit.treewalk.EmptyTreeIterator'
|
7
|
+
java_import 'org.eclipse.jgit.treewalk.filter.PathFilterGroup'
|
8
|
+
|
9
|
+
module Rosette
|
10
|
+
module Core
|
11
|
+
|
12
|
+
# Used to compute diffs between two git refs. Can also read file
|
13
|
+
# contents from diff entries.
|
14
|
+
#
|
15
|
+
# @!attribute [r] jgit_repo
|
16
|
+
# @return [Java::OrgEclipseJgitStorageFile::FileRepository] the git
|
17
|
+
# repository.
|
18
|
+
# @!attribute [r] rev_walker
|
19
|
+
# @return [Java::OrgEclipseJgitRevwalk::RevWalk] the RevWalk instance
|
20
|
+
# to use.
|
21
|
+
class DiffFinder
|
22
|
+
attr_reader :jgit_repo, :rev_walker
|
23
|
+
|
24
|
+
# Creates a new diff finder instance.
|
25
|
+
#
|
26
|
+
# @param [Java::OrgEclipseJgitStorageFile::FileRepository] jgit_repo
|
27
|
+
# The git repository.
|
28
|
+
# @param [Java::OrgEclipseJgitRevwalk::RevWalk] rev_walker The RevWalk
|
29
|
+
# instance to use.
|
30
|
+
def initialize(jgit_repo, rev_walker)
|
31
|
+
@jgit_repo = jgit_repo
|
32
|
+
@rev_walker = rev_walker
|
33
|
+
end
|
34
|
+
|
35
|
+
# Computes a diff between two revs.
|
36
|
+
#
|
37
|
+
# @param [Java::OrgEclipseJgitRevwalk::RevCommit] rev_parents The first
|
38
|
+
# commit or commits to use in the diff (the parent, i.e. the commit that
|
39
|
+
# occurred earlier in time).
|
40
|
+
# @param [Java::OrgEclipseJgitRevwalk::RevCommit] rev_child The second
|
41
|
+
# commit to use in the diff (the child of the parent, i.e. the commit
|
42
|
+
# that occurred later in time).
|
43
|
+
# @param [Array<String>] paths The paths to include in the diff. If given
|
44
|
+
# an empty array, this method will return a diff for all paths.
|
45
|
+
# @return [Hash<String, Java::OrgEclipseJgitDiff::DiffEntry>] A hash of
|
46
|
+
# commit ids to diff entries for the diff between +rev_parents+ and
|
47
|
+
# +rev_child+. There will be one diff entry for each file that changed.
|
48
|
+
def diff(rev_parents, rev_child, paths = [])
|
49
|
+
rev_parents = Array(rev_parents)
|
50
|
+
diff_formatter.setPathFilter(construct_filter(Array(paths)))
|
51
|
+
|
52
|
+
rev_parents.each_with_object({}) do |rev_parent, ret|
|
53
|
+
ret[rev_parent.getId.name] = diff_formatter.scan(
|
54
|
+
rev_walker.parseCommit(rev_parent.getId).getTree,
|
55
|
+
rev_child.getTree
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Computes a diff between a rev and its parent.
|
61
|
+
#
|
62
|
+
# @param [Java::OrgEclipseJgitRevwalk::RevCommit] rev The rev to use.
|
63
|
+
# @return [Hash<String, Java::OrgEclipseJgitDiff::DiffEntry>] A hash of
|
64
|
+
# commit ids to diff entries for the diff between +rev+ and its parents.
|
65
|
+
# There will be one diff entry for each file that changed.
|
66
|
+
def diff_with_parents(rev)
|
67
|
+
if rev.getParentCount > 0
|
68
|
+
rev.getParentCount.times.each_with_object({}) do |i, ret|
|
69
|
+
parent = rev.getParent(i)
|
70
|
+
|
71
|
+
ret[parent.getId.name] = diff_formatter.scan(
|
72
|
+
rev_walker.parseCommit(parent.getId).getTree,
|
73
|
+
rev.getTree
|
74
|
+
)
|
75
|
+
end
|
76
|
+
else
|
77
|
+
{
|
78
|
+
rev.getId.name => diff_formatter.scan(
|
79
|
+
EmptyTreeIterator.new,
|
80
|
+
CanonicalTreeParser.new(
|
81
|
+
nil, rev_walker.getObjectReader, rev.getTree
|
82
|
+
)
|
83
|
+
)
|
84
|
+
}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Reads the "new" contents of a diff entry. Diff entries contain a
|
89
|
+
# reference to both the new and old files. The "new" contents means
|
90
|
+
# the contents of the changed file, not the original.
|
91
|
+
#
|
92
|
+
# @param [Java::OrgEclipseJgitDiff::DiffEntry] entry The diff entry
|
93
|
+
# to read from.
|
94
|
+
# @param [Encoding] encoding The encoding to expect the contents
|
95
|
+
# to be in.
|
96
|
+
# @return [String] The file contents, encoded in +encoding+.
|
97
|
+
def read_new_entry(entry, encoding = Encoding::UTF_8)
|
98
|
+
Java::JavaLang::String.new(
|
99
|
+
object_reader.open(entry.newId.toObjectId).getBytes, encoding.to_s
|
100
|
+
)
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def object_reader
|
106
|
+
@object_reader ||= jgit_repo.newObjectReader
|
107
|
+
end
|
108
|
+
|
109
|
+
def construct_filter(paths)
|
110
|
+
paths = fix_paths(paths)
|
111
|
+
PathFilterGroup.createFromStrings(paths) if paths.size > 0
|
112
|
+
end
|
113
|
+
|
114
|
+
def fix_paths(paths)
|
115
|
+
# paths can't begin with a dot or dot slash (jgit limitation)
|
116
|
+
paths.map do |path|
|
117
|
+
path.gsub(/\A(\.(?:\/|\z))/, '')
|
118
|
+
end.select do |path|
|
119
|
+
!path.strip.empty?
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def diff_formatter
|
124
|
+
@diff_formatter ||= DiffFormatter.new(NullOutputStream::INSTANCE).tap do |formatter|
|
125
|
+
formatter.setRepository(jgit_repo)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Rosette
|
4
|
+
module Core
|
5
|
+
|
6
|
+
class Ref
|
7
|
+
DELIMITER = '/'
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def parse(ref_name)
|
11
|
+
chunks = ref_name.split(DELIMITER)
|
12
|
+
|
13
|
+
if chunks.first == 'refs'
|
14
|
+
create_from(chunks[1..-1])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def inherited(subclass)
|
19
|
+
descendants << subclass
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def create_from(chunks)
|
25
|
+
descendants.each do |descendant|
|
26
|
+
if ref = descendant.create_from(chunks)
|
27
|
+
return ref
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def descendants
|
33
|
+
@descendants ||= []
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_reader :name
|
38
|
+
|
39
|
+
def remote?
|
40
|
+
type == :remote
|
41
|
+
end
|
42
|
+
|
43
|
+
def head?
|
44
|
+
type == :head
|
45
|
+
end
|
46
|
+
|
47
|
+
def tag?
|
48
|
+
type == :tag
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class Remote < Ref
|
53
|
+
def self.create_from(chunks)
|
54
|
+
if chunks.first == 'remotes'
|
55
|
+
new(chunks[1], chunks[2..-1].join(DELIMITER))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
attr_reader :remote
|
60
|
+
|
61
|
+
def initialize(remote, name)
|
62
|
+
@remote = remote
|
63
|
+
@name = name
|
64
|
+
end
|
65
|
+
|
66
|
+
def type
|
67
|
+
:remote
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_s
|
71
|
+
"refs/remotes/#{remote}/#{name}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Head < Ref
|
76
|
+
def self.create_from(chunks)
|
77
|
+
if chunks.first == 'heads'
|
78
|
+
new(chunks[1..-1].join(DELIMITER))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def initialize(name)
|
83
|
+
@name = name
|
84
|
+
end
|
85
|
+
|
86
|
+
def type
|
87
|
+
:head
|
88
|
+
end
|
89
|
+
|
90
|
+
def to_s
|
91
|
+
"refs/heads/#{name}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class Tag < Ref
|
96
|
+
def self.create_from(chunks)
|
97
|
+
if chunks.first == 'tags'
|
98
|
+
new(chunks[1..-1].join(DELIMITER))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def initialize(name)
|
103
|
+
@name = name
|
104
|
+
end
|
105
|
+
|
106
|
+
def type
|
107
|
+
:tag
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_s
|
111
|
+
"refs/tags/#{name}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,378 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'thread'
|
4
|
+
|
5
|
+
java_import 'org.eclipse.jgit.api.BlameCommand'
|
6
|
+
java_import 'org.eclipse.jgit.api.CloneCommand'
|
7
|
+
java_import 'org.eclipse.jgit.api.FetchCommand'
|
8
|
+
java_import 'org.eclipse.jgit.api.Git'
|
9
|
+
java_import 'org.eclipse.jgit.diff.RawTextComparator'
|
10
|
+
java_import 'org.eclipse.jgit.internal.storage.file.FileRepository'
|
11
|
+
java_import 'org.eclipse.jgit.lib.Constants'
|
12
|
+
java_import 'org.eclipse.jgit.lib.ObjectId'
|
13
|
+
java_import 'org.eclipse.jgit.lib.RefDatabase'
|
14
|
+
java_import 'org.eclipse.jgit.revwalk.RevSort'
|
15
|
+
java_import 'org.eclipse.jgit.revwalk.RevWalk'
|
16
|
+
java_import 'org.eclipse.jgit.revwalk.RevWalkUtils'
|
17
|
+
|
18
|
+
module Rosette
|
19
|
+
module Core
|
20
|
+
|
21
|
+
# Wraps a git repository and can perform operations on it via jgit.
|
22
|
+
# NOTE: This class is NOT thread safe.
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# repo = Repo.from_path('/path/to/my_repo/.git')
|
26
|
+
# repo.get_rev_commit('master') # => <RevCommit #5234ab6>
|
27
|
+
#
|
28
|
+
# @!attribute [r] jgit_repo
|
29
|
+
# @return [Java::OrgEclipseJgitStorageFile::FileRepository] The jgit
|
30
|
+
# repository object.
|
31
|
+
class Repo
|
32
|
+
attr_reader :jgit_repo
|
33
|
+
|
34
|
+
# Creates a repo instance from the given path.
|
35
|
+
#
|
36
|
+
# @param [String] path The path to the .git directory of a git
|
37
|
+
# repository.
|
38
|
+
# @return [Repo] The new repo instance.
|
39
|
+
def self.from_path(path)
|
40
|
+
new(FileRepository.new(path))
|
41
|
+
end
|
42
|
+
|
43
|
+
# Creates a new repo instance from the given jgit repo object.
|
44
|
+
#
|
45
|
+
# @param [Java::OrgEclipseJgitStorageFile::FileRepository] jgit_repo
|
46
|
+
# The jgit repo object to wrap.
|
47
|
+
def initialize(jgit_repo)
|
48
|
+
@jgit_repo = jgit_repo
|
49
|
+
end
|
50
|
+
|
51
|
+
# Retrieves a jgit commit object for the given ref or commit id. If
|
52
|
+
# +ref_or_commit_id+ cannot be directly looked up as a commit id or ref,
|
53
|
+
# heads are searched as a fallback. If no head ref can be found, remotes
|
54
|
+
# are searched as a secondary fallback. If no remote can be found, an
|
55
|
+
# error is raised.
|
56
|
+
#
|
57
|
+
# @param [String] ref_or_commit_id The git ref (i.e. a branch name) or
|
58
|
+
# git commit id of the commit to retrieve.
|
59
|
+
# @param [Java::OrgEclipseJgitRevwalk::RevWalk] walker The +RevWalk+ to
|
60
|
+
# use. Since +RevCommits+ returned from different +RevWalk+s aren't
|
61
|
+
# equivalent, callers may want to pass in an instance of their own.
|
62
|
+
# By default, an internally created +RevWalk+ is used.
|
63
|
+
# @return [Java::OrgEclipseJgitRevwalk::RevCommit] The identified
|
64
|
+
# +RevCommit+.
|
65
|
+
def get_rev_commit(ref_or_commit_id, walker = rev_walker)
|
66
|
+
walker.parseCommit(
|
67
|
+
ObjectId.fromString(ref_or_commit_id)
|
68
|
+
)
|
69
|
+
rescue Java::OrgEclipseJgitErrors::MissingObjectException, Java::JavaLang::IllegalArgumentException => e
|
70
|
+
found_ref = get_ref(ref_or_commit_id)
|
71
|
+
|
72
|
+
unless found_ref
|
73
|
+
[Constants::R_HEADS, Constants::R_REMOTES + 'origin/'].each do |prefix|
|
74
|
+
ref_candidates = jgit_repo.getRefDatabase.getRefs(prefix)
|
75
|
+
|
76
|
+
found_ref_name, found_ref = ref_candidates.find do |ref_name, ref|
|
77
|
+
ref_name == ref_or_commit_id
|
78
|
+
end
|
79
|
+
|
80
|
+
break if found_ref
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
if found_ref
|
85
|
+
walker.parseCommit(found_ref.getObjectId)
|
86
|
+
else
|
87
|
+
raise e
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Calculates a diff between two git refs or commit ids. Returns a hash of
|
92
|
+
# commit ids to +DiffEntry+ objects.
|
93
|
+
#
|
94
|
+
# @see DiffFinder
|
95
|
+
# @param [String] ref_parent The parent git ref or commit id.
|
96
|
+
# @param [String] ref_child The child git ref or commit id.
|
97
|
+
# @param [Array<String>] paths The paths to include in the diff. If an
|
98
|
+
# empty array is given, returns a diff for all paths.
|
99
|
+
# @return [Hash<String, Java::OrgEclipseJgitDiff::DiffEntry>]
|
100
|
+
def diff(ref_parents, ref_child, paths = [], finder = diff_finder)
|
101
|
+
rev_child = get_rev_commit(ref_child, finder.rev_walker)
|
102
|
+
|
103
|
+
rev_parents = Array(ref_parents).map do |ref_parent|
|
104
|
+
get_rev_commit(ref_parent, finder.rev_walker)
|
105
|
+
end
|
106
|
+
|
107
|
+
finder.diff(rev_parents, rev_child, paths)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Calculates a diff for the given ref against its parent.
|
111
|
+
#
|
112
|
+
# @param [String] ref The ref to diff with.
|
113
|
+
# @param [DiffFinder] finder The diff finder to use when calculating
|
114
|
+
# diffs in a multi-threaded environment
|
115
|
+
# @return [Array<Java::OrgEclipseJgitDiff::DiffEntry>]
|
116
|
+
def ref_diff_with_parents(ref, finder = diff_finder)
|
117
|
+
rev_diff_with_parents(get_rev_commit(ref), finder)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Calculates a diff for the given rev against its parent.
|
121
|
+
#
|
122
|
+
# @param [Java::OrgEclipseJgitRevwalk::RevCommit] rev The rev to diff with.
|
123
|
+
# @param [DiffFinder] finder The diff finder to use when calculating
|
124
|
+
# diffs in a multi-threaded environment.
|
125
|
+
# @return [Array<Java::OrgEclipseJgitDiff::DiffEntry>]
|
126
|
+
def rev_diff_with_parents(rev, finder = diff_finder)
|
127
|
+
finder.diff_with_parents(rev)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Retrieves the parent commits for the given rev.
|
131
|
+
#
|
132
|
+
# @param [Java::OrgEclipseJgitRevwalk::RevCommit] rev The rev to get
|
133
|
+
# parents for.
|
134
|
+
# @return [Array<Java::OrgEclipseJgitRevwalk::RevCommit>] A list of
|
135
|
+
# parent commits.
|
136
|
+
def parents_of(rev)
|
137
|
+
rev.getParentCount.times.map do |i|
|
138
|
+
rev.getParent(i)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Retrieves the parent commit ids for the given rev.
|
143
|
+
#
|
144
|
+
# @param [Java::OrgEclipseJgitRevwalk::RevCommit] rev The rev to get
|
145
|
+
# parent ids for.
|
146
|
+
# @return [Array<Java::OrgEclipseJgitLib::ObjectId>] An array of object
|
147
|
+
# id instances (the commit ids for the parents of +rev+).
|
148
|
+
def parent_ids_of(rev)
|
149
|
+
parents_of(rev).map { |parent| parent.getName }
|
150
|
+
end
|
151
|
+
|
152
|
+
# Retrieves the path for the repository's working directory.
|
153
|
+
#
|
154
|
+
# @return [String] The path to the working directory.
|
155
|
+
def path
|
156
|
+
jgit_repo.workTree.path
|
157
|
+
end
|
158
|
+
|
159
|
+
# Reads the git entry for the given object id and returns the bytes.
|
160
|
+
#
|
161
|
+
# @param [Java::OrgEclipseJgitLib::ObjectId] object_id The object id
|
162
|
+
# to retrieve bytes for.
|
163
|
+
# @return [Array<Fixnum>] An array of bytes.
|
164
|
+
def read_object_bytes(object_id)
|
165
|
+
object_reader.open(object_id).getBytes
|
166
|
+
end
|
167
|
+
|
168
|
+
# Iterates over and yields each commit in the repo.
|
169
|
+
#
|
170
|
+
# @return [void, Enumerator] If no block is given, returns an
|
171
|
+
# +Enumerator+.
|
172
|
+
# @yield [rev]
|
173
|
+
# @yieldparam rev [Java::OrgEclipseJgitRevwalk::RevCommit]
|
174
|
+
def each_commit
|
175
|
+
if block_given?
|
176
|
+
commit_walker = RevWalk.new(jgit_repo).tap do |walker|
|
177
|
+
walker.markStart(all_heads(walker))
|
178
|
+
walker.sort(RevSort::REVERSE)
|
179
|
+
end
|
180
|
+
|
181
|
+
commit_walker.each { |cur_rev| yield cur_rev }
|
182
|
+
commit_walker.dispose
|
183
|
+
else
|
184
|
+
to_enum(__method__)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# Iterates over and yields each commit, starting at the given git ref
|
189
|
+
# or commit id.
|
190
|
+
#
|
191
|
+
# @param [String] start_ref The ref to start at.
|
192
|
+
# @return [void, Enumerator] If no block is given, returns an
|
193
|
+
# +Enumerator+.
|
194
|
+
def each_commit_starting_at(start_ref)
|
195
|
+
if block_given?
|
196
|
+
commit_walker = RevWalk.new(jgit_repo).tap do |walker|
|
197
|
+
walker.markStart(get_rev_commit(start_ref, walker))
|
198
|
+
end
|
199
|
+
|
200
|
+
commit_walker.each { |cur_rev| yield cur_rev }
|
201
|
+
commit_walker.dispose
|
202
|
+
else
|
203
|
+
to_enum(__method__, start_ref)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Iterates over and yields each commit, starting at the given git ref
|
208
|
+
# or commit id and ending at the other.
|
209
|
+
#
|
210
|
+
# @param [String] start_ref The beginning of the commit range.
|
211
|
+
# @param [String] end_ref The end of the commit range (inclusive).
|
212
|
+
# @return [void, Enumerator] If no block is given, returns an
|
213
|
+
# +Enumerator+.
|
214
|
+
def each_commit_in_range(start_ref, end_ref)
|
215
|
+
if block_given?
|
216
|
+
commit_walker = RevWalk.new(jgit_repo).tap do |walker|
|
217
|
+
walker.markStart(get_rev_commit(start_ref, walker))
|
218
|
+
end
|
219
|
+
|
220
|
+
end_rev = get_rev_commit(end_ref, commit_walker)
|
221
|
+
|
222
|
+
commit_walker.each do |cur_rev|
|
223
|
+
yield cur_rev
|
224
|
+
break if cur_rev.getId.name == end_rev.getId.name
|
225
|
+
end
|
226
|
+
|
227
|
+
commit_walker.dispose
|
228
|
+
else
|
229
|
+
to_enum(__method__, start_ref, end_ref)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Counts the number of commits in the repo.
|
234
|
+
#
|
235
|
+
# @return [Fixnum] The number of commits in the repo.
|
236
|
+
def commit_count
|
237
|
+
commit_walker = RevWalk.new(jgit_repo).tap do |walker|
|
238
|
+
walker.markStart(all_heads(walker))
|
239
|
+
end
|
240
|
+
|
241
|
+
count = commit_walker.count
|
242
|
+
commit_walker.dispose
|
243
|
+
count
|
244
|
+
end
|
245
|
+
|
246
|
+
# Fetches the repository.
|
247
|
+
#
|
248
|
+
# @param [String] remote The remote to fetch from.
|
249
|
+
# @return [void]
|
250
|
+
def fetch(remote = 'origin')
|
251
|
+
git.fetch.setRemote(remote).call
|
252
|
+
end
|
253
|
+
|
254
|
+
# Clones a repository
|
255
|
+
#
|
256
|
+
# @param [String] repo_uri The URI of the repo to be cloned.
|
257
|
+
# @param [String] repo_dir The directory to store the local copy.
|
258
|
+
# @return [void]
|
259
|
+
def self.clone(repo_uri, repo_dir)
|
260
|
+
CloneCommand.new
|
261
|
+
.setDirectory(Java::JavaIo::File.new(repo_dir))
|
262
|
+
.setURI(repo_uri)
|
263
|
+
.call
|
264
|
+
end
|
265
|
+
|
266
|
+
# Retrieves the first non-merge parent of the given ref or commit id.
|
267
|
+
#
|
268
|
+
# @param [String] ref The git ref or commit id.
|
269
|
+
# @return [Java::OrgEclipseJgitRevwalk::RevCommit] The first non-merge
|
270
|
+
# parent of +ref+.
|
271
|
+
def find_first_non_merge_parent(ref)
|
272
|
+
each_commit_starting_at(ref).with_index do |prev_rev, idx|
|
273
|
+
next if idx == 0
|
274
|
+
break prev_rev if prev_rev.getParentCount <= 1
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
# Finds git authors per source line for the given file and commit.
|
279
|
+
#
|
280
|
+
# @param [String] path The file path.
|
281
|
+
# @param [String] commit_id The commit id.
|
282
|
+
# @return [Hash<Fixnum, Java::OrgEclipseJgitLib::PersonIdent>]
|
283
|
+
# A hash of line numbers to git authors.
|
284
|
+
def blame(path, commit_id)
|
285
|
+
blame_result = BlameCommand.new(jgit_repo)
|
286
|
+
.setFilePath(path)
|
287
|
+
.setFollowFileRenames(true)
|
288
|
+
.setTextComparator(RawTextComparator::WS_IGNORE_ALL)
|
289
|
+
.setStartCommit(ObjectId.fromString(commit_id))
|
290
|
+
.call
|
291
|
+
|
292
|
+
lines_to_authors = {}
|
293
|
+
line_number = 0
|
294
|
+
|
295
|
+
loop do
|
296
|
+
begin
|
297
|
+
lines_to_authors[line_number] = blame_result.getSourceAuthor(line_number)
|
298
|
+
line_number += 1
|
299
|
+
rescue Java::JavaLang::ArrayIndexOutOfBoundsException
|
300
|
+
break
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
lines_to_authors
|
305
|
+
end
|
306
|
+
|
307
|
+
# Gets a reference to the given git ref.
|
308
|
+
#
|
309
|
+
# @param [String] ref_str The ref to get.
|
310
|
+
# @return [Java::OrgEclipseJgitLib::Ref] A reference to the commit found
|
311
|
+
# for +ref_str+.
|
312
|
+
def get_ref(ref_str)
|
313
|
+
jgit_repo.getRef(ref_str)
|
314
|
+
end
|
315
|
+
|
316
|
+
# Get all refs in the repo.
|
317
|
+
#
|
318
|
+
# @return [Hash<String, Java::OrgEclipseJgitLib::Ref>] A hash of ref
|
319
|
+
# strings to jgit ref objects.
|
320
|
+
def all_refs
|
321
|
+
jgit_repo.all_refs
|
322
|
+
end
|
323
|
+
|
324
|
+
# Get all head refs in the repo.
|
325
|
+
#
|
326
|
+
# @return [Array<String>] A list of all head refs.
|
327
|
+
def all_head_refs
|
328
|
+
all_refs = jgit_repo.refDatabase.getRefs(RefDatabase::ALL).keys
|
329
|
+
all_refs.select do |ref|
|
330
|
+
ref =~ /\Arefs\/(?:heads|remotes)/
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
# Get a list of commits for all the heads in the repo.
|
335
|
+
#
|
336
|
+
# @param [Java::OrgEclipseJgitRevwalk::RevWalk] walker The walker to use.
|
337
|
+
# @return [Array<Java::OrgEclipseJgitRevwalk::RevCommit>] A list of
|
338
|
+
# commits, one for each of the heads in the repo.
|
339
|
+
def all_heads(walker = rev_walker)
|
340
|
+
all_head_refs.map { |ref| get_rev_commit(ref, walker) }
|
341
|
+
end
|
342
|
+
|
343
|
+
# Get a list of refs (i.e. branches) that contain the given commit id.
|
344
|
+
#
|
345
|
+
# @param [String] commit_id_or_ref The git commit id or ref to get refs
|
346
|
+
# for. This method returns all the refs that contain this commit.
|
347
|
+
# @param [Java::OrgEclipseJgitRevwalk::RevWalk] walker The walker to use.
|
348
|
+
# @return [Array<Java::OrgEclipseJgitLib::Ref>] The list of refs that
|
349
|
+
# contain +commit_id_or_ref+.
|
350
|
+
def refs_containing(commit_id_or_ref, walker = rev_walker, refs = nil)
|
351
|
+
refs ||= jgit_repo.refDatabase.getRefs(RefDatabase::ALL).values
|
352
|
+
commit = get_rev_commit(commit_id_or_ref, walker)
|
353
|
+
RevWalkUtils.findBranchesReachableFrom(commit, walker, refs)
|
354
|
+
rescue Java::OrgEclipseJgitErrors::MissingObjectException
|
355
|
+
[]
|
356
|
+
end
|
357
|
+
|
358
|
+
private
|
359
|
+
|
360
|
+
def git
|
361
|
+
@git ||= Git.new(jgit_repo)
|
362
|
+
end
|
363
|
+
|
364
|
+
def diff_finder
|
365
|
+
@diff_finder ||= DiffFinder.new(jgit_repo, rev_walker)
|
366
|
+
end
|
367
|
+
|
368
|
+
def rev_walker
|
369
|
+
@rev_walker ||= RevWalk.new(jgit_repo)
|
370
|
+
end
|
371
|
+
|
372
|
+
def object_reader
|
373
|
+
@object_reader ||= jgit_repo.newObjectReader
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
end
|
378
|
+
end
|