git-process-lib 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/CHANGELOG.md +123 -0
  2. data/Gemfile +21 -0
  3. data/Gemfile.lock +57 -0
  4. data/LICENSE +193 -0
  5. data/README.md +342 -0
  6. data/Rakefile +32 -0
  7. data/bin/git-new-fb +39 -0
  8. data/bin/git-pull-request +63 -0
  9. data/bin/git-sync +38 -0
  10. data/bin/git-to-master +44 -0
  11. data/docs/git-new-fb.1.adoc +83 -0
  12. data/docs/git-process.1.adoc +227 -0
  13. data/docs/git-pull-request.1.adoc +166 -0
  14. data/docs/git-sync.1.adoc +120 -0
  15. data/docs/git-to-master.1.adoc +172 -0
  16. data/git-new-fb.gemspec +20 -0
  17. data/git-process-lib.gemspec +25 -0
  18. data/git-process.gemspec +22 -0
  19. data/git-pull-request.gemspec +20 -0
  20. data/git-sync.gemspec +20 -0
  21. data/git-to-master.gemspec +20 -0
  22. data/lib/git-process/abstract_error_builder.rb +53 -0
  23. data/lib/git-process/changed_file_helper.rb +115 -0
  24. data/lib/git-process/git_abstract_merge_error_builder.rb +130 -0
  25. data/lib/git-process/git_branch.rb +105 -0
  26. data/lib/git-process/git_branches.rb +81 -0
  27. data/lib/git-process/git_config.rb +135 -0
  28. data/lib/git-process/git_lib.rb +646 -0
  29. data/lib/git-process/git_logger.rb +84 -0
  30. data/lib/git-process/git_merge_error.rb +28 -0
  31. data/lib/git-process/git_process.rb +159 -0
  32. data/lib/git-process/git_process_error.rb +18 -0
  33. data/lib/git-process/git_process_options.rb +101 -0
  34. data/lib/git-process/git_rebase_error.rb +30 -0
  35. data/lib/git-process/git_remote.rb +222 -0
  36. data/lib/git-process/git_status.rb +108 -0
  37. data/lib/git-process/github_configuration.rb +298 -0
  38. data/lib/git-process/github_pull_request.rb +165 -0
  39. data/lib/git-process/new_fb.rb +49 -0
  40. data/lib/git-process/parked_changes_error.rb +41 -0
  41. data/lib/git-process/pull_request.rb +136 -0
  42. data/lib/git-process/pull_request_error.rb +25 -0
  43. data/lib/git-process/rebase_to_master.rb +148 -0
  44. data/lib/git-process/sync_process.rb +55 -0
  45. data/lib/git-process/syncer.rb +157 -0
  46. data/lib/git-process/uncommitted_changes_error.rb +23 -0
  47. data/lib/git-process/version.rb +22 -0
  48. data/local-build.rb +24 -0
  49. data/spec/FileHelpers.rb +19 -0
  50. data/spec/GitRepoHelper.rb +123 -0
  51. data/spec/changed_file_helper_spec.rb +127 -0
  52. data/spec/git_abstract_merge_error_builder_spec.rb +64 -0
  53. data/spec/git_branch_spec.rb +123 -0
  54. data/spec/git_config_spec.rb +45 -0
  55. data/spec/git_lib_spec.rb +176 -0
  56. data/spec/git_logger_spec.rb +66 -0
  57. data/spec/git_process_spec.rb +208 -0
  58. data/spec/git_remote_spec.rb +227 -0
  59. data/spec/git_status_spec.rb +122 -0
  60. data/spec/github_configuration_spec.rb +152 -0
  61. data/spec/github_pull_request_spec.rb +117 -0
  62. data/spec/github_test_helper.rb +49 -0
  63. data/spec/new_fb_spec.rb +126 -0
  64. data/spec/pull_request_helper.rb +94 -0
  65. data/spec/pull_request_spec.rb +137 -0
  66. data/spec/rebase_to_master_spec.rb +362 -0
  67. data/spec/spec_helper.rb +21 -0
  68. data/spec/sync_spec.rb +1474 -0
  69. metadata +249 -0
@@ -0,0 +1,49 @@
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ require 'git-process/git_process'
14
+
15
+ module GitProc
16
+
17
+ class NewFeatureBranch < Process
18
+
19
+ def initialize(dir, opts)
20
+ @branch_name = opts[:branch_name]
21
+ @local_only = opts[:local]
22
+ super
23
+ end
24
+
25
+
26
+ def runner
27
+ mybranches = gitlib.branches()
28
+ on_parking = (mybranches.parking == mybranches.current)
29
+
30
+ base_branch = if on_parking and not mybranches[config.integration_branch].contains_all_of(mybranches.parking.name)
31
+ '_parking_'
32
+ else
33
+ config.integration_branch
34
+ end
35
+
36
+ gitlib.fetch if gitlib.has_a_remote? and not @local_only
37
+
38
+ logger.info { "Creating #{@branch_name} off of #{base_branch}" }
39
+ new_branch = gitlib.checkout(@branch_name, :new_branch => base_branch)
40
+
41
+ branches = gitlib.branches()
42
+ branches[@branch_name].upstream(config.integration_branch)
43
+ branches.parking.delete! if on_parking
44
+ new_branch
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,41 @@
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ require 'git-process/git_process_error'
14
+
15
+ module GitProc
16
+
17
+ class ParkedChangesError < GitProcessError
18
+ include GitProc::AbstractErrorBuilder
19
+
20
+ attr_reader :error_message, :lib
21
+
22
+
23
+ def initialize(lib)
24
+ @lib = lib
25
+ msg = build_message
26
+ super(msg)
27
+ end
28
+
29
+
30
+ def human_message
31
+ "You made your changes on the the '_parking_' branch instead of a feature branch.\n"+'Please rename the branch to be a feature branch.'
32
+ end
33
+
34
+
35
+ def build_commands
36
+ ['git branch -m _parking_ my_feature_branch']
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,136 @@
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ require 'git-process/git_process'
14
+ require 'git-process/github_pull_request'
15
+ require 'git-process/pull_request_error'
16
+ require 'git-process/syncer'
17
+ require 'highline/import'
18
+
19
+
20
+ module GitProc
21
+
22
+ class PullRequest < Process
23
+
24
+ def initialize(dir, opts)
25
+ super
26
+ @base_branch = opts[:base_branch]
27
+ @head_branch = opts[:head_branch]
28
+ @_repo_name = opts[:repo_name]
29
+ @_remote_name = opts[:server]
30
+ @pr_number = opts[:prNumber]
31
+ @title = opts[:title]
32
+ @description = opts[:description]
33
+ @user = opts[:user]
34
+ @password = opts[:password]
35
+ end
36
+
37
+
38
+ def runner
39
+ if @pr_number.nil? or @pr_number.empty?
40
+ pr = create_pull_request
41
+ logger.info { "Created pull request at #{pr.html_url}" }
42
+ else
43
+ checkout_pull_request
44
+ end
45
+ end
46
+
47
+
48
+ def create_pull_request_client(remote_name, repo_name)
49
+ PullRequest.create_pull_request_client(self, remote_name, repo_name, @user, @password)
50
+ end
51
+
52
+
53
+ def create_pull_request
54
+ current_branch = gitlib.branches.current.name
55
+ base_branch = @base_branch || config.master_branch
56
+ head_branch = @head_branch || current_branch
57
+ title = @title || current_branch
58
+ description = @description || ''
59
+
60
+ PullRequest.create_pull_request(gitlib, remote.name, remote.name, remote.repo_name, current_branch, base_branch, head_branch, title, description, logger, @user, @password)
61
+ end
62
+
63
+
64
+ def checkout_pull_request
65
+ PullRequest.checkout_pull_request(gitlib, @pr_number, remote.name, remote.repo_name, @user, @password, logger)
66
+ end
67
+
68
+
69
+ class << self
70
+
71
+ def create_pull_request_client(lib, remote_name, repo_name, username, password)
72
+ GitHub::PullRequest.new(lib, remote_name, repo_name, {:user => username, :password => password})
73
+ end
74
+
75
+
76
+ def create_pull_request(lib, server_name, remote_name, repo_name, current_branch, base_branch, head_branch, title, description, logger, username, password)
77
+ if base_branch == head_branch
78
+ raise PullRequestError.new("Can not create a pull request where the base branch and head branch are the same: #{base_branch}")
79
+ end
80
+
81
+ lib.push(server_name, current_branch, current_branch, :force => false)
82
+ pr = create_pull_request_client(lib, remote_name, repo_name, username, password)
83
+ pr.create(base_branch, head_branch, title, description)
84
+ end
85
+
86
+
87
+ def checkout_pull_request(lib, pr_number, remote_name, repo_name, username, password, logger)
88
+ logger.info { 'Getting #pr_number' }
89
+
90
+ lib.fetch(remote_name)
91
+
92
+ pr = create_pull_request_client(lib, remote_name, repo_name, username, password)
93
+ json = pr.pull_request(pr_number)
94
+ head_branch_name = json.head.ref
95
+ base_branch_name = json.base.ref
96
+
97
+ remote_head_server_name = match_remote_to_pr_remote(lib, json.head.repo.ssh_url)
98
+ remote_base_server_name = match_remote_to_pr_remote(lib, json.base.repo.ssh_url)
99
+ lib.checkout(head_branch_name, :new_branch => "#{remote_head_server_name}/#{head_branch_name}")
100
+ lib.branch(head_branch_name, :upstream => "#{remote_base_server_name}/#{base_branch_name}")
101
+ #logger.info(json.to_hash)
102
+
103
+ lib.fetch(remote_base_server_name) if remote_head_server_name != remote_base_server_name
104
+ Syncer.rebase_sync(lib, true)
105
+ end
106
+
107
+
108
+ def match_remote_to_pr_remote(lib, pr_remote)
109
+ pr_url = lib.remote.expanded_url(nil, pr_remote)
110
+ servers = lib.remote.remote_names
111
+ server_urls = servers.collect { |s| {:server_name => s, :url => lib.remote.expanded_url(s)} }
112
+
113
+ pair = server_urls.find do |su|
114
+ url = su[:url]
115
+ uri = URI.parse(url)
116
+ host = uri.host
117
+ path = uri.path
118
+
119
+ pr_uri = URI.parse(lib.remote.expanded_url(nil, pr_url))
120
+ pr_host = pr_uri.host
121
+ pr_path = pr_uri.path
122
+
123
+ pr_host == host and pr_path == path
124
+ end
125
+
126
+ if pair.nil?
127
+ raise GitHubService::NoRemoteRepository.new("Could not match pull request url (#{pr_url}) to any of the registered remote urls: #{server_urls.map {|s| '{' + s[:server_name] + ': ' + s[:url] + '}'}.join(', ')}")
128
+ end
129
+
130
+ pair[:server_name]
131
+ end
132
+
133
+ end
134
+
135
+ end
136
+ end
@@ -0,0 +1,25 @@
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ require 'git-process/git_process_error'
14
+
15
+ module GitProc
16
+
17
+ class PullRequestError < GitProcessError
18
+
19
+ def initialize(msg)
20
+ super(msg)
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,148 @@
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ require 'git-process/git_process'
14
+ require 'git-process/git_rebase_error'
15
+ require 'git-process/git_process_error'
16
+ require 'git-process/parked_changes_error'
17
+ require 'git-process/uncommitted_changes_error'
18
+ require 'git-process/github_pull_request'
19
+ require 'git-process/pull_request'
20
+ require 'git-process/syncer'
21
+
22
+
23
+ module GitProc
24
+
25
+ class RebaseToMaster < Process
26
+
27
+ def initialize(dir, opts)
28
+ @keep = opts[:keep]
29
+ @pr_number = opts[:prNumber]
30
+ @user = opts[:user]
31
+ @password = opts[:password]
32
+ super
33
+ end
34
+
35
+
36
+ def verify_preconditions
37
+ super
38
+
39
+ raise UncommittedChangesError.new unless gitlib.status.clean?
40
+ raise ParkedChangesError.new(gitlib) if is_parked?
41
+ end
42
+
43
+
44
+ def runner
45
+ if remote.exists?
46
+ gitlib.fetch(remote.name)
47
+
48
+ unless @pr_number.nil? or @pr_number.empty?
49
+ checkout_pull_request
50
+ end
51
+
52
+ Syncer.rebase_sync(gitlib, true)
53
+ current = gitlib.branches.current.name
54
+ gitlib.push(remote.name, current, config.master_branch)
55
+
56
+ unless @keep
57
+ close_pull_request
58
+ remove_feature_branch
59
+ gitlib.delete_sync_control_file!(current) if gitlib.sync_control_file_exists?(current)
60
+ end
61
+ else
62
+ Syncer.rebase_sync(gitlib, true)
63
+ end
64
+ end
65
+
66
+
67
+ def checkout_pull_request
68
+ PullRequest.checkout_pull_request(gitlib, @pr_number, remote.name, remote.repo_name, @user, @password, logger)
69
+ end
70
+
71
+
72
+ def remove_feature_branch
73
+ mybranches = gitlib.branches
74
+
75
+ remote_master = mybranches[remote.master_branch_name]
76
+ current_branch = mybranches.current
77
+ logger.debug { "Removing feature branch (#{current_branch})" }
78
+
79
+ unless remote_master.contains_all_of(current_branch.name)
80
+ raise GitProcessError.new("Branch '#{current_branch.name}' has not been merged into '#{remote.master_branch_name}'")
81
+ end
82
+
83
+ parking_branch = mybranches['_parking_']
84
+ if parking_branch
85
+ if parking_branch.is_ahead_of(remote_master.name) and
86
+ !current_branch.contains_all_of(parking_branch.name)
87
+
88
+ parking_branch.rename('_parking_OLD_')
89
+
90
+ logger.warn { bad_parking_branch_msg }
91
+ else
92
+ parking_branch.delete!
93
+ end
94
+ end
95
+ remote_master.checkout_to_new('_parking_', :no_track => true)
96
+
97
+ current_branch.delete!(true)
98
+ if mybranches["#{remote.name}/#{current_branch.name}"]
99
+ gitlib.push(remote.name, nil, nil, :delete => current_branch.name)
100
+ end
101
+ end
102
+
103
+
104
+ def close_pull_request
105
+ pr = GitHub::PullRequest.new(gitlib, remote.name, remote.repo_name)
106
+
107
+ # Assume that if we haven't done something that would create the
108
+ # GitHub auth token, then this likely isn't a GitHub-based repo.
109
+ # (Or at least the user isn't using pull requests)
110
+ if pr.configuration.get_config_auth_token
111
+ begin
112
+ if @pr_number
113
+ pr.close(@pr_number)
114
+ else
115
+ mybranches = gitlib.branches()
116
+ pull = pr.find_pull_request(config.master_branch, mybranches.current.name)
117
+ if pull
118
+ pr.close(pull[:number])
119
+ else
120
+ logger.debug { "There is no pull request for #{mybranches.current.name} against #{config.master_branch}" }
121
+ end
122
+ end
123
+ rescue GitHubService::NoRemoteRepository => exp
124
+ logger.debug exp.to_s
125
+ end
126
+ else
127
+ logger.debug 'There is no GitHub auth token defined, so not trying to close a pull request.'
128
+ end
129
+ end
130
+
131
+
132
+ private
133
+
134
+
135
+ def bad_parking_branch_msg
136
+ hl = HighLine.new
137
+ hl.color(
138
+ "\n***********************************************************************************************\n\n"+
139
+ "There is an old '_parking_' branch with unacounted changes in it.\n"+
140
+ "It has been renamed to '_parking_OLD_'.\n"+
141
+ "Please rename the branch to what the changes are about (`git branch -m _parking_OLD_ my_fb_name`),\n"+
142
+ " or remove it altogher (`git branch -D _parking_OLD_`).\n\n"+
143
+ "***********************************************************************************************\n", :red, :bold)
144
+ end
145
+
146
+ end
147
+
148
+ end
@@ -0,0 +1,55 @@
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ require 'git-process/git_process'
14
+ require 'git-process/parked_changes_error'
15
+ require 'git-process/syncer'
16
+ require 'git-process/changed_file_helper'
17
+
18
+
19
+ module GitProc
20
+
21
+ class Sync < Process
22
+
23
+ def initialize(base, opts)
24
+ super
25
+
26
+ @opts = opts
27
+
28
+ self
29
+ end
30
+
31
+
32
+ #noinspection RubyControlFlowConversionInspection
33
+ def verify_preconditions
34
+ super
35
+
36
+ if not gitlib.status.clean?
37
+ change_file_helper.offer_to_help_uncommitted_changes
38
+ end
39
+
40
+ raise ParkedChangesError.new(self) if gitlib.is_parked?
41
+ end
42
+
43
+
44
+ def cleanup
45
+ gitlib.stash_pop if @stash_pushed
46
+ end
47
+
48
+
49
+ def runner
50
+ GitProc::Syncer.do_sync(gitlib, @opts)
51
+ end
52
+
53
+ end
54
+
55
+ end