avm-git 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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