avm-git 0.1.0

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 (50) hide show
  1. checksums.yaml +7 -0
  2. data/lib/avm/git/auto_commit/commit_info.rb +23 -0
  3. data/lib/avm/git/auto_commit/rules/base.rb +39 -0
  4. data/lib/avm/git/auto_commit/rules/last.rb +19 -0
  5. data/lib/avm/git/auto_commit/rules/manual.rb +45 -0
  6. data/lib/avm/git/auto_commit/rules/new.rb +24 -0
  7. data/lib/avm/git/auto_commit/rules/nth.rb +31 -0
  8. data/lib/avm/git/auto_commit/rules/unique.rb +21 -0
  9. data/lib/avm/git/auto_commit/rules.rb +31 -0
  10. data/lib/avm/git/auto_commit_path/ruby.rb +20 -0
  11. data/lib/avm/git/auto_commit_path.rb +28 -0
  12. data/lib/avm/git/commit/class_methods.rb +31 -0
  13. data/lib/avm/git/commit/deploy.rb +38 -0
  14. data/lib/avm/git/commit/deploy_methods.rb +19 -0
  15. data/lib/avm/git/commit/diff_tree_line.rb +32 -0
  16. data/lib/avm/git/commit/file.rb +46 -0
  17. data/lib/avm/git/commit.rb +59 -0
  18. data/lib/avm/git/file_auto_fixup.rb +83 -0
  19. data/lib/avm/git/issue/complete/commits.rb +42 -0
  20. data/lib/avm/git/issue/complete/git_subrepos.rb +23 -0
  21. data/lib/avm/git/issue/complete/local_branch.rb +54 -0
  22. data/lib/avm/git/issue/complete/local_tag.rb +39 -0
  23. data/lib/avm/git/issue/complete/push.rb +54 -0
  24. data/lib/avm/git/issue/complete/remote.rb +33 -0
  25. data/lib/avm/git/issue/complete/test.rb +45 -0
  26. data/lib/avm/git/issue/complete/tracker.rb +28 -0
  27. data/lib/avm/git/issue/complete/validation.rb +31 -0
  28. data/lib/avm/git/issue/complete/validations.rb +53 -0
  29. data/lib/avm/git/issue/complete/working_tree.rb +19 -0
  30. data/lib/avm/git/issue/complete.rb +51 -0
  31. data/lib/avm/git/issue/deliver.rb +56 -0
  32. data/lib/avm/git/issue.rb +11 -0
  33. data/lib/avm/git/organize/reference_update.rb +34 -0
  34. data/lib/avm/git/organize/repository.rb +76 -0
  35. data/lib/avm/git/organize.rb +11 -0
  36. data/lib/avm/git/revision_test.rb +105 -0
  37. data/lib/avm/git/scms/git/change_tracker.rb +35 -0
  38. data/lib/avm/git/scms/git/commit.rb +55 -0
  39. data/lib/avm/git/scms/git.rb +65 -0
  40. data/lib/avm/git/scms/git_subrepo.rb +41 -0
  41. data/lib/avm/git/scms/provider.rb +19 -0
  42. data/lib/avm/git/scms.rb +11 -0
  43. data/lib/avm/git/subrepo_check/parent.rb +51 -0
  44. data/lib/avm/git/subrepo_check/remote.rb +89 -0
  45. data/lib/avm/git/subrepo_check/show_result.rb +32 -0
  46. data/lib/avm/git/subrepo_check.rb +38 -0
  47. data/lib/avm/git/subrepo_checks.rb +59 -0
  48. data/lib/avm/git/version.rb +7 -0
  49. data/lib/avm/git.rb +11 -0
  50. metadata +165 -0
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+ require 'eac_ruby_utils/ruby'
5
+
6
+ module Avm
7
+ module Git
8
+ class RevisionTest
9
+ enable_simple_cache
10
+ enable_speaker
11
+ common_constructor :git, :sha1, :options
12
+
13
+ def banner
14
+ infov 'Revision to test', sha1
15
+ ::EacRubyUtils::Speaker.context.on(::EacCli::Speaker.new(err_line_prefix: ' ')) do
16
+ revision_banner
17
+ end
18
+ end
19
+
20
+ def successful_label
21
+ successful?.to_s.send((successful? ? :green : :red))
22
+ end
23
+
24
+ def to_s
25
+ sha1
26
+ end
27
+
28
+ def successful?
29
+ successful
30
+ end
31
+
32
+ private
33
+
34
+ def checkout_revision
35
+ infom 'Checking out revision...'
36
+ git.execute!('checkout', sha1)
37
+ end
38
+
39
+ def commit_uncached
40
+ ::Avm::Git::Commit.new(git, sha1)
41
+ end
42
+
43
+ def git_absolute_path
44
+ ::File.expand_path(git.to_s)
45
+ end
46
+
47
+ def revision_banner
48
+ infov '* Subject', commit.subject
49
+ infov '* Success?', successful_label
50
+ infov '* STDOUT', stdout_cache.content_path
51
+ infov '* STDERR', stderr_cache.content_path
52
+ end
53
+
54
+ def root_cache
55
+ fs_cache.child(git_absolute_path.parameterize, sha1,
56
+ options.fetch(:test_command).to_s.parameterize)
57
+ end
58
+
59
+ def run_test
60
+ infom "Running test command \"#{::Shellwords.join(test_command_args)}\"" \
61
+ " on \"#{git_absolute_path}\"..."
62
+ result = ::EacRubyUtils::Ruby.on_clean_environment { test_command.execute }
63
+ infom 'Test done'
64
+ write_result_cache(result)
65
+ end
66
+
67
+ def stdout_cache
68
+ root_cache.child('stdout')
69
+ end
70
+
71
+ def stderr_cache
72
+ root_cache.child('stderr')
73
+ end
74
+
75
+ def successful_cache
76
+ root_cache.child('successful')
77
+ end
78
+
79
+ def successful_uncached
80
+ if options.fetch(:no_cache) || !successful_cache.cached?
81
+ checkout_revision
82
+ run_test
83
+ end
84
+ successful_cache.read == 'true'
85
+ end
86
+
87
+ def test_command
88
+ ::EacRubyUtils::Envs.local.command(*test_command_args).chdir(git.to_s)
89
+ end
90
+
91
+ def test_command_args
92
+ r = ::Shellwords.split(options.fetch(:test_command).to_s)
93
+ return r if r.any?
94
+
95
+ raise 'No command found'
96
+ end
97
+
98
+ def write_result_cache(result)
99
+ stdout_cache.write(result[:stdout])
100
+ stderr_cache.write(result[:stderr])
101
+ successful_cache.write(result[:exit_code].zero? ? 'true' : 'false')
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ module Avm
6
+ module Git
7
+ module Scms
8
+ class Git < ::Avm::Scms::Base
9
+ class ChangeTracker
10
+ common_constructor :git_scm, :message
11
+ attr_reader :starting_commit
12
+ delegate :git_repo, to: :git_scm
13
+
14
+ def start
15
+ raise 'Repository is dirty' if git_repo.dirty?
16
+
17
+ self.starting_commit = git_repo.head
18
+ end
19
+
20
+ # @return [Avm::Git::Scms::Git::Commit, nil]
21
+ def stop
22
+ git_scm.commit_dirty
23
+ return nil if starting_commit == git_repo.head
24
+
25
+ git_scm.reset_and_commit(starting_commit, message)
26
+ end
27
+
28
+ private
29
+
30
+ attr_writer :starting_commit
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'avm/scms/commit'
4
+ require 'eac_ruby_utils/core_ext'
5
+
6
+ module Avm
7
+ module Git
8
+ module Scms
9
+ class Git < ::Avm::Scms::Base
10
+ class Commit < ::Avm::Scms::Commit
11
+ common_constructor :git_scm, :git_commit do
12
+ git_commit.assert_argument(::EacGit::Local::Commit, 'git_commit')
13
+ end
14
+ delegate :git_repo, to: :git_scm
15
+
16
+ # @return [Array<Pathname>]
17
+ def changed_files
18
+ git_commit.changed_files.map { |cf| cf.path.to_pathname }
19
+ end
20
+
21
+ # @param other [Avm::Git::Scms::Git::Commit]
22
+ # @return [Avm::Git::Scms::Git::Commit]
23
+ def merge_with(other)
24
+ validate_clean_and_head
25
+ raise 'Implemented for only if other is parent' unless
26
+ other.git_commit == git_commit.parent
27
+
28
+ git_scm.reset_and_commit(other.git_commit.parent, other.git_commit.raw_body)
29
+ end
30
+
31
+ def reword(new_message)
32
+ validate_clean_and_head
33
+
34
+ git_repo.command('commit', '--amend', '-m', new_message).execute!
35
+ self.class.new(git_scm, git_repo.head)
36
+ end
37
+
38
+ # @param path [Pathname]
39
+ # @return [TrueClass,FalseClass]
40
+ def scm_file?(path)
41
+ %w[.gitrepo .gitmodules].any? { |file| file.include?(path.basename.to_path) }
42
+ end
43
+
44
+ private
45
+
46
+ def validate_clean_and_head
47
+ raise 'Implemented for only if repository is no dirty' if git_repo.dirty?
48
+ raise 'Implemented for only if self is HEAD' unless
49
+ git_commit == git_repo.head
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'avm/scms/base'
4
+ require 'eac_ruby_utils/core_ext'
5
+
6
+ module Avm
7
+ module Git
8
+ module Scms
9
+ class Git < ::Avm::Scms::Base
10
+ require_sub __FILE__
11
+ include ::Comparable
12
+
13
+ COMMIT_DIRTY_DEFAULT_MESSAGE = 'Dirty files.'
14
+
15
+ def <=>(other)
16
+ git_repo <=> other.git_repo
17
+ end
18
+
19
+ # @return [Avm::Git::Scms::Git::Commit,nil]
20
+ def commitize(source)
21
+ if source.is_a?(::Avm::Git::Scms::Git::Commit)
22
+ return source if source.git_repo == self
23
+
24
+ raise 'Not same Git repository'
25
+ end
26
+ git_repo.commitize(source).if_present do |v|
27
+ ::Avm::Git::Scms::Git::Commit.new(self, v)
28
+ end
29
+ end
30
+
31
+ # @return [Avm::Git::Scms::Git::Commit,nil]
32
+ def commit_dirty(message = nil)
33
+ return nil unless git_repo.dirty?
34
+
35
+ git_repo.command('add', '.').execute!
36
+ git_repo.command('commit', '-m',
37
+ message.call_if_proc.if_present(COMMIT_DIRTY_DEFAULT_MESSAGE)).execute!
38
+ commitize(git_repo.head)
39
+ end
40
+
41
+ # @return [Avm::Git::Scms::Git::Commit,nil]
42
+ def commit_if_change(message = nil)
43
+ tracker = ::Avm::Git::Scms::Git::ChangeTracker.new(self, message)
44
+ tracker.start
45
+ yield
46
+ tracker.stop
47
+ end
48
+
49
+ def git_repo
50
+ @git_repo ||= ::EacGit::Local.new(path)
51
+ end
52
+
53
+ # @return [Avm::Git::Scms::Git::Commit]
54
+ def reset_and_commit(commit_to_reset, message)
55
+ git_repo.command('reset', '--soft', commitize(commit_to_reset).git_commit.id).execute!
56
+ commit_dirty(message)
57
+ end
58
+
59
+ def valid?
60
+ path.join('.git').exist?
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'avm/scms/base'
4
+ require 'eac_git/local'
5
+ require 'eac_ruby_utils/core_ext'
6
+
7
+ module Avm
8
+ module Git
9
+ module Scms
10
+ class GitSubrepo < ::Avm::Scms::Base
11
+ def commit_if_change(_message)
12
+ nyi
13
+ end
14
+
15
+ def update
16
+ git_subrepo.command('clean').execute!
17
+ git_subrepo.command('pull').execute!
18
+ end
19
+
20
+ # @return [EacGit::Local]
21
+ def git_repo
22
+ @git_repo ||= ::EacGit::Local.find(path)
23
+ end
24
+
25
+ # @return [EacGit::Local::Subrepo]
26
+ def git_subrepo
27
+ @git_subrepo ||= git_repo.subrepo(subpath)
28
+ end
29
+
30
+ # @return [Pathname]
31
+ def subpath
32
+ path.expand_path.relative_path_from(git_repo.root_path.expand_path)
33
+ end
34
+
35
+ def valid?
36
+ path.join('.gitrepo').file?
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'avm/git/scms/git'
4
+ require 'avm/git/scms/git_subrepo'
5
+ require 'eac_ruby_utils/core_ext'
6
+
7
+ module Avm
8
+ module Git
9
+ module Scms
10
+ class Provider
11
+ SCMS = [::Avm::Git::Scms::Git, ::Avm::Git::Scms::GitSubrepo].freeze
12
+
13
+ def all
14
+ SCMS
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ module Avm
6
+ module Git
7
+ module Scms
8
+ require_sub __FILE__
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'avm/result'
4
+ require 'eac_ruby_utils/core_ext'
5
+
6
+ module Avm
7
+ module Git
8
+ class SubrepoCheck
9
+ module Parent
10
+ def fix_parent
11
+ return if parent_result.success?
12
+
13
+ info(' Fixing...')
14
+ self.parent_hash = expected_parent_hash
15
+ info_banner
16
+ end
17
+
18
+ private
19
+
20
+ def expected_parent_hash_uncached
21
+ subrepo.local.rev_parse("#{last_file_change_rev}^")
22
+ end
23
+
24
+ def last_file_change_rev
25
+ subrepo.local.command('log', '-n', '1', '--pretty=format:%H', '--',
26
+ subrepo.config_relative_path.to_path).execute!.strip
27
+ end
28
+
29
+ def parent_hash
30
+ subrepo.parent_commit_id
31
+ end
32
+
33
+ def parent_hash=(new_hash)
34
+ subrepo.config.parent_commit_id = new_hash
35
+ subrepo.write_config
36
+ end
37
+
38
+ def parent_hash_ok?
39
+ return false if expected_parent_hash.blank? || parent_hash.blank?
40
+
41
+ expected_parent_hash == parent_hash
42
+ end
43
+
44
+ def parent_result_uncached
45
+ ::Avm::Result.success_or_error(parent_hash_ok?,
46
+ parent_hash.presence || blank_text)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'avm/result'
4
+ require 'eac_ruby_utils/core_ext'
5
+
6
+ module Avm
7
+ module Git
8
+ class SubrepoCheck
9
+ module Remote
10
+ private
11
+
12
+ def fetch_uncached
13
+ subrepo.command('clean').execute!
14
+ subrepo.command('fetch').execute!
15
+ end
16
+
17
+ def check_remote_disabled?
18
+ !check_remote?
19
+ end
20
+
21
+ def check_remote_disabled_result
22
+ ::Avm::Result.neutral('Check remote disabled')
23
+ end
24
+
25
+ def local_descend_remote?
26
+ local_id.present? && remote_id.present? && subrepo.local.descendant?(local_id, remote_id)
27
+ end
28
+
29
+ def local_descend_remote_result
30
+ ::Avm::Result.pending(remote_result_value)
31
+ end
32
+
33
+ def local_id_uncached
34
+ fetch
35
+ subrepo.command('branch', '--force').execute!
36
+ subrepo.local.rev_parse("subrepo/#{subrepo.subpath}")
37
+ end
38
+
39
+ def remote_descend_local?
40
+ local_id.present? && remote_id.present? && subrepo.local.descendant?(remote_id, local_id)
41
+ end
42
+
43
+ def remote_descend_local_result
44
+ ::Avm::Result.outdated(remote_result_value)
45
+ end
46
+
47
+ def remote_branches
48
+ ['', 'refs/heads/', 'refs/tags/'].map { |prefix| "#{prefix}#{subrepo.remote_branch}" }
49
+ end
50
+
51
+ def remote_id_uncached
52
+ ls_result = subrepo.remote.ls
53
+ remote_branches.each do |b|
54
+ return ls_result[b] if ls_result[b].present?
55
+ end
56
+ nil
57
+ end
58
+
59
+ def remote_result_uncached
60
+ %w[check_remote_disabled same_ids local_descend_remote remote_descend_local]
61
+ .each do |condition|
62
+ return send("#{condition}_result") if send("#{condition}?")
63
+ end
64
+
65
+ ::Avm::Result.error(remote_result_value)
66
+ end
67
+
68
+ def remote_result_value
69
+ local_s = local_id.presence || blank_text
70
+ remote_s = remote_id.presence || blank_text
71
+
72
+ if local_s == remote_s
73
+ "[L/R=#{local_s}]"
74
+ else
75
+ "[L=#{local_s}, R=#{remote_s}]"
76
+ end
77
+ end
78
+
79
+ def same_ids?
80
+ local_id.present? && remote_id.present? && local_id == remote_id
81
+ end
82
+
83
+ def same_ids_result
84
+ ::Avm::Result.success(remote_result_value)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ module Avm
6
+ module Git
7
+ class SubrepoCheck
8
+ module ShowResult
9
+ def show_result
10
+ out(subrepo.subpath.to_path.cyan)
11
+ out_attr('parent', parent_result.label)
12
+ run_fix_parent
13
+ out_attr('remote', remote_result.label)
14
+ out("\n")
15
+ end
16
+
17
+ def run_fix_parent
18
+ return unless fix_parent?
19
+ return unless parent_result.error?
20
+
21
+ out('|Fixing...'.white)
22
+ self.parent_hash = expected_parent_hash
23
+ out_attr('new parent', parent_result.label)
24
+ end
25
+
26
+ def out_attr(key, value)
27
+ out('|' + "#{key}=".white + value)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ module Avm
6
+ module Git
7
+ class SubrepoCheck
8
+ require_sub __FILE__, include_modules: true
9
+ enable_speaker
10
+ enable_simple_cache
11
+
12
+ BLANK_TEXT = 'BLANK'
13
+
14
+ common_constructor :subrepo, :options
15
+
16
+ def blank_text
17
+ BLANK_TEXT
18
+ end
19
+
20
+ def check_remote?
21
+ options.fetch(:check_remote) ? true : false
22
+ end
23
+
24
+ def fix_parent?
25
+ options.fetch(:fix_parent) ? true : false
26
+ end
27
+
28
+ private
29
+
30
+ def result_uncached
31
+ return ::Avm::Result.error('Parent failed') if parent_result.error?
32
+ return ::Avm::Result.error('Remote failed') if remote_result.error?
33
+
34
+ ::Avm::Result.success('Parent and remote ok')
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'avm/git/subrepo_check'
4
+ require 'eac_ruby_utils/core_ext'
5
+
6
+ module Avm
7
+ module Git
8
+ class SubrepoChecks
9
+ enable_speaker
10
+ enable_simple_cache
11
+ attr_accessor :check_remote, :fix_parent
12
+ common_constructor :repository
13
+
14
+ def add_all_subrepos
15
+ add_subrepos(
16
+ *repository.command('subrepo', '-q', 'status').execute!.split("\n").map(&:strip)
17
+ .select(&:present?)
18
+ )
19
+ end
20
+
21
+ def add_subrepos(*subpath_list)
22
+ subpath_list.each do |subpath|
23
+ subpaths.add(subpath)
24
+ end
25
+ reset_cache
26
+ self
27
+ end
28
+
29
+ def check_options
30
+ { fix_parent: fix_parent, check_remote: check_remote }
31
+ end
32
+
33
+ def show_result
34
+ checks.each(&:show_result)
35
+ infov 'Result', result.label
36
+ end
37
+
38
+ private
39
+
40
+ def checks_uncached
41
+ subpaths.map do |subpath|
42
+ ::Avm::Git::SubrepoCheck.new(repository.subrepo(subpath), check_options)
43
+ end
44
+ end
45
+
46
+ def result_uncached
47
+ error_count = checks.count { |check| check.result.error? }
48
+ ::Avm::Result.success_or_error(
49
+ error_count.zero?,
50
+ "#{error_count} of #{checks.count} subrepos failed"
51
+ )
52
+ end
53
+
54
+ def subpaths
55
+ @subpaths ||= ::Set.new
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Avm
4
+ module Git
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
data/lib/avm/git.rb ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ module Avm
6
+ module Git
7
+ require_sub __FILE__
8
+
9
+ DEFAULT_REMOTE_NAME = 'origin'
10
+ end
11
+ end