git-process 1.0.11 → 1.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 (54) hide show
  1. data/CHANGELOG.md +37 -9
  2. data/Gemfile +2 -2
  3. data/Gemfile.lock +17 -17
  4. data/README.md +14 -7
  5. data/bin/git-new-fb +10 -2
  6. data/bin/git-pull-request +30 -6
  7. data/bin/git-sync +5 -2
  8. data/bin/git-to-master +62 -11
  9. data/git-process.gemspec +15 -15
  10. data/lib/git-process/abstract_error_builder.rb +0 -3
  11. data/lib/git-process/changed_file_helper.rb +30 -24
  12. data/lib/git-process/git_abstract_merge_error_builder.rb +31 -11
  13. data/lib/git-process/git_branch.rb +5 -0
  14. data/lib/git-process/git_config.rb +153 -0
  15. data/lib/git-process/git_lib.rb +212 -164
  16. data/lib/git-process/git_logger.rb +84 -0
  17. data/lib/git-process/git_merge_error.rb +3 -14
  18. data/lib/git-process/git_process.rb +44 -73
  19. data/lib/git-process/git_process_options.rb +6 -6
  20. data/lib/git-process/git_rebase_error.rb +4 -13
  21. data/lib/git-process/git_remote.rb +254 -0
  22. data/lib/git-process/github_configuration.rb +298 -0
  23. data/lib/git-process/github_pull_request.rb +65 -27
  24. data/lib/git-process/new_fb.rb +14 -4
  25. data/lib/git-process/parked_changes_error.rb +1 -1
  26. data/lib/git-process/pull_request.rb +100 -13
  27. data/lib/git-process/pull_request_error.rb +25 -0
  28. data/lib/git-process/rebase_to_master.rb +47 -27
  29. data/lib/git-process/sync.rb +48 -33
  30. data/lib/git-process/uncommitted_changes_error.rb +1 -1
  31. data/lib/git-process/version.rb +2 -2
  32. data/spec/GitRepoHelper.rb +48 -25
  33. data/spec/changed_file_helper_spec.rb +39 -58
  34. data/spec/git_abstract_merge_error_builder_spec.rb +42 -33
  35. data/spec/git_branch_spec.rb +30 -30
  36. data/spec/git_config_spec.rb +45 -0
  37. data/spec/git_lib_spec.rb +103 -122
  38. data/spec/git_logger_spec.rb +66 -0
  39. data/spec/git_process_spec.rb +81 -81
  40. data/spec/git_remote_spec.rb +188 -0
  41. data/spec/git_status_spec.rb +36 -36
  42. data/spec/github_configuration_spec.rb +152 -0
  43. data/spec/github_pull_request_spec.rb +39 -35
  44. data/spec/github_test_helper.rb +49 -0
  45. data/spec/new_fb_spec.rb +65 -24
  46. data/spec/pull_request_helper.rb +94 -0
  47. data/spec/pull_request_spec.rb +128 -0
  48. data/spec/rebase_to_master_spec.rb +241 -145
  49. data/spec/spec_helper.rb +20 -0
  50. data/spec/sync_spec.rb +115 -109
  51. metadata +34 -20
  52. data/lib/git-process/github_client.rb +0 -83
  53. data/lib/git-process/github_service.rb +0 -174
  54. data/spec/github_service_spec.rb +0 -211
@@ -0,0 +1,84 @@
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 'logger'
14
+
15
+ module GitProc
16
+
17
+ #
18
+ # Provides a Logger for Git commands
19
+ #
20
+ class GitLogger
21
+
22
+ DEBUG = Logger::DEBUG
23
+ INFO = Logger::INFO
24
+ WARN = Logger::WARN
25
+ ERROR = Logger::ERROR
26
+
27
+
28
+ def initialize(log_level = nil, out = STDOUT)
29
+ if out.nil?
30
+ @logger = ::Logger.new(RUBY_PLATFORM =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
31
+ else
32
+ @logger = ::Logger.new(out)
33
+ end
34
+ @logger.level = log_level.nil? ? GitLogger::WARN : log_level
35
+ @logger.datetime_format = '%Y-%m-%d %H:%M:%S'
36
+ @logger.formatter = proc do |_, _, _, msg|
37
+ "#{msg}\n"
38
+ end
39
+ end
40
+
41
+
42
+ def level
43
+ @logger.level
44
+ end
45
+
46
+
47
+ def debug(msg = nil, &block)
48
+ if msg.nil?
49
+ @logger.debug(&block)
50
+ else
51
+ @logger.debug(msg)
52
+ end
53
+ end
54
+
55
+
56
+ def info(msg = nil, &block)
57
+ if msg.nil?
58
+ @logger.info(&block)
59
+ else
60
+ @logger.info(msg)
61
+ end
62
+ end
63
+
64
+
65
+ def warn(msg = nil, &block)
66
+ if msg.nil?
67
+ @logger.send(:warn, &block)
68
+ else
69
+ @logger.send(:warn, msg)
70
+ end
71
+ end
72
+
73
+
74
+ def error(msg = nil, &block)
75
+ if msg.nil?
76
+ @logger.error(&block)
77
+ else
78
+ @logger.error(msg)
79
+ end
80
+ end
81
+
82
+ end
83
+
84
+ end
@@ -15,25 +15,14 @@ require 'git-process/git_abstract_merge_error_builder'
15
15
  module GitProc
16
16
 
17
17
  class MergeError < GitProcessError
18
- include AbstractMergeErrorBuilder
19
18
 
20
- attr_reader :error_message, :lib
21
-
22
-
23
- def initialize(merge_error_message, lib)
24
- @lib = lib
25
- @error_message = merge_error_message
26
-
27
- msg = build_message
19
+ def initialize(merge_error_message, gitlib)
20
+ @error_builder = GitProc::AbstractMergeErrorBuilder.new(gitlib, merge_error_message, 'git commit')
21
+ msg = error_builder.build_message
28
22
 
29
23
  super(msg)
30
24
  end
31
25
 
32
-
33
- def continue_command
34
- 'git commit'
35
- end
36
-
37
26
  end
38
27
 
39
28
  end
@@ -19,13 +19,15 @@ require 'highline/import'
19
19
  module GitProc
20
20
 
21
21
  class Process
22
- include GitLib
23
22
 
23
+ # @param [GitLib] gitlib
24
+ def initialize(gitlib, opts = {})
25
+ @gitlib = gitlib
26
+ end
24
27
 
25
- def initialize(dir, opts = {})
26
- @log_level = Process.log_level(opts)
27
28
 
28
- set_workdir(dir)
29
+ def gitlib
30
+ @gitlib
29
31
  end
30
32
 
31
33
 
@@ -48,51 +50,30 @@ module GitProc
48
50
  end
49
51
 
50
52
 
51
- def set_workdir(dir)
52
- unless dir.nil?
53
- @workdir = find_workdir(dir)
54
- if @workdir.nil?
55
- @workdir = dir
56
- logger.info { "Initializing new repository at #{workdir}" }
57
- command(:init)
58
- else
59
- logger.debug { "Opening existing repository at #{workdir}" }
60
- end
61
- else
62
- logger.debug "Process dir is nil"
63
- end
64
- end
65
-
66
-
67
53
  def workdir
68
- @workdir
54
+ gitlib.workdir
69
55
  end
70
56
 
71
57
 
72
- def log_level
73
- @log_level
58
+ def logger
59
+ gitlib.logger
74
60
  end
75
61
 
76
62
 
77
- def log_level=(ll)
78
- @log_level = ll
63
+ def config
64
+ gitlib.config
79
65
  end
80
66
 
81
67
 
82
- def remote_master_branch
83
- "#{server_name}/#{master_branch}"
84
- end
85
-
86
-
87
- def integration_branch
88
- has_a_remote? ? remote_master_branch : master_branch
68
+ def remote
69
+ gitlib.remote
89
70
  end
90
71
 
91
72
 
92
73
  def verify_preconditions
93
- if should_remove_master
74
+ if should_remove_master?
94
75
  if ask_about_removing_master
95
- branches[master_branch].delete!
76
+ delete_master_branch!
96
77
  end
97
78
  end
98
79
  end
@@ -103,46 +84,47 @@ module GitProc
103
84
  end
104
85
 
105
86
 
106
- def should_remove_master
107
- my_branches = branches()
108
- has_a_remote? and
109
- my_branches.include?(master_branch) and
110
- my_branches.current.name != master_branch and
111
- !keep_local_integration_branch? and
112
- my_branches[integration_branch].contains_all_of(master_branch)
87
+ def fetch_remote_changes(remote_name = nil)
88
+ if remote.exists?
89
+ gitlib.fetch(remote_name || remote.name)
90
+ else
91
+ logger.debug 'Can not fetch latest changes because there is no remote defined'
92
+ end
113
93
  end
114
94
 
115
95
 
116
- def keep_local_integration_branch?
117
- keep_local_integration_branch_config_value.to_boolean
96
+ def is_parked?
97
+ mybranches = gitlib.branches
98
+ mybranches.parking == mybranches.current
118
99
  end
119
100
 
120
101
 
121
- def Process.log_level(opts)
122
- if opts[:log_level]
123
- opts[:log_level]
124
- elsif opts[:quiet]
125
- Logger::ERROR
126
- elsif opts[:verbose]
127
- Logger::DEBUG
128
- else
129
- Logger::INFO
130
- end
102
+ private
103
+
104
+
105
+ def delete_master_branch!
106
+ gitlib.branches[config.master_branch].delete!
131
107
  end
132
108
 
133
109
 
134
- def is_parked?
135
- mybranches = branches
136
- mybranches.parking == mybranches.current
110
+ def should_remove_master?
111
+ my_branches = gitlib.branches()
112
+ gitlib.has_a_remote? and
113
+ my_branches.include?(config.master_branch) and
114
+ my_branches.current.name != config.master_branch and
115
+ !keep_local_integration_branch? and
116
+ my_branches[config.integration_branch].contains_all_of(config.master_branch)
137
117
  end
138
118
 
139
119
 
140
- private
120
+ def keep_local_integration_branch?
121
+ keep_local_integration_branch_config_value.to_boolean
122
+ end
141
123
 
142
124
 
143
125
  #noinspection RubyInstanceMethodNamingConvention
144
126
  def keep_local_integration_branch_config_value
145
- config('gitProcess.keepLocalIntegrationBranch')
127
+ gitlib.config['gitProcess.keepLocalIntegrationBranch']
146
128
  end
147
129
 
148
130
 
@@ -163,31 +145,20 @@ module GitProc
163
145
  end
164
146
 
165
147
 
166
- def find_workdir(dir)
167
- if dir == File::SEPARATOR
168
- nil
169
- elsif File.directory?(File.join(dir, '.git'))
170
- dir
171
- else
172
- find_workdir(File.expand_path("#{dir}/.."))
173
- end
174
- end
175
-
176
-
177
148
  def proc_rebase(base, opts = {})
178
149
  begin
179
- rebase(base, opts)
150
+ gitlib.rebase(base, opts)
180
151
  rescue GitExecuteError => rebase_error
181
- raise RebaseError.new(rebase_error.message, self)
152
+ raise RebaseError.new(rebase_error.message, gitlib)
182
153
  end
183
154
  end
184
155
 
185
156
 
186
157
  def proc_merge(base)
187
158
  begin
188
- merge(base)
159
+ gitlib.merge(base)
189
160
  rescue GitExecuteError => merge_error
190
- raise MergeError.new(merge_error.message, self)
161
+ raise MergeError.new(merge_error.message, gitlib)
191
162
  end
192
163
  end
193
164
 
@@ -53,18 +53,18 @@ module GitProc
53
53
 
54
54
 
55
55
  def standard_opts(parser)
56
- parser.opt :info, "Informational messages; show the major things this is doing", :default => true, :short => :none
57
- parser.opt :quiet, "Quiet messages; only show errors", :short => :q
58
- parser.opt :verbose, "Verbose messages; show lots of details on what this is doing", :short => :v
56
+ parser.opt :info, 'Informational messages; show the major things this is doing', :default => true, :short => :none
57
+ parser.opt :quiet, 'Quiet messages; only show errors', :short => :q
58
+ parser.opt :verbose, 'Verbose messages; show lots of details on what this is doing', :short => :v
59
59
  parser.opt :version, "Print version (#{GitProc::Version::STRING}) and exit", :short => :none
60
- parser.opt :help, "Show this message", :short => :h
60
+ parser.opt :help, 'Show this message', :short => :h
61
61
 
62
62
  parser.conflicts :verbose, :info, :quiet
63
63
  end
64
64
 
65
65
 
66
66
  def summary
67
- "Default summary"
67
+ 'Default summary'
68
68
  end
69
69
 
70
70
 
@@ -74,7 +74,7 @@ module GitProc
74
74
 
75
75
 
76
76
  def description
77
- "Default description"
77
+ 'Default description'
78
78
  end
79
79
 
80
80
 
@@ -15,25 +15,16 @@ require 'git-process/git_abstract_merge_error_builder'
15
15
  module GitProc
16
16
 
17
17
  class RebaseError < GitProcessError
18
- include GitProc::AbstractMergeErrorBuilder
18
+ attr_reader :error_builder
19
19
 
20
- attr_reader :error_message, :lib
21
20
 
22
-
23
- def initialize(rebase_error_message, lib)
24
- @lib = lib
25
- @error_message = rebase_error_message
26
-
27
- msg = build_message
21
+ def initialize(rebase_error_message, gitlib)
22
+ @error_builder = GitProc::AbstractMergeErrorBuilder.new(gitlib, rebase_error_message, 'git rebase --continue')
23
+ msg = error_builder.build_message
28
24
 
29
25
  super(msg)
30
26
  end
31
27
 
32
-
33
- def continue_command
34
- 'git rebase --continue'
35
- end
36
-
37
28
  end
38
29
 
39
30
  end
@@ -0,0 +1,254 @@
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_config'
14
+ #require 'git-process/git_branches'
15
+ #require 'git-process/git_status'
16
+ #require 'git-process/git_process_error'
17
+
18
+
19
+ class String
20
+
21
+ def to_boolean
22
+ return false if self == false || self.nil? || self =~ (/(false|f|no|n|0)$/i)
23
+ return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
24
+ raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
25
+ end
26
+
27
+ end
28
+
29
+
30
+ class NilClass
31
+ def to_boolean
32
+ false
33
+ end
34
+ end
35
+
36
+
37
+ module GitProc
38
+
39
+ #
40
+ # Git Remote configuration
41
+ #
42
+ class GitRemote
43
+
44
+ # @param [GitProc::GitConfig] gitconfig
45
+ def initialize(gitconfig)
46
+ @gitconfig = gitconfig
47
+ end
48
+
49
+
50
+ # @return [#info, #warn, #debug, #error]
51
+ def logger
52
+ @logger ||= @gitconfig.logger
53
+ end
54
+
55
+
56
+ # @return [GitProc::GitConfig]
57
+ def config
58
+ @gitconfig
59
+ end
60
+
61
+
62
+ # @deprecated
63
+ # TODO: Remove
64
+ def server_name
65
+ @server_name ||= self.remote_name
66
+ end
67
+
68
+
69
+ # @return [Boolean] does this have a remote defined?
70
+ def exists?
71
+ if @has_remote.nil?
72
+ @has_remote = (config.gitlib.command(:remote) != '')
73
+ end
74
+ logger.debug { "Does a remote exist? #{@has_remote}" }
75
+ @has_remote
76
+ end
77
+
78
+
79
+ def repo_name
80
+ unless @repo_name
81
+ url = config["remote.#{name}.url"]
82
+ raise GitProcessError.new("There is no #{name} url set up.") if url.nil? or url.empty?
83
+ @repo_name = url.sub(/^.*:(.*?)(.git)?$/, '\1')
84
+ end
85
+ @repo_name
86
+ end
87
+
88
+
89
+ def name
90
+ unless @remote_name
91
+ @remote_name = config['gitProcess.remoteName']
92
+ if @remote_name.nil? or @remote_name.empty?
93
+ remotes = self.remote_names
94
+ if remotes.empty?
95
+ @remote_name = nil
96
+ else
97
+ @remote_name = remotes[0]
98
+ raise '!@remote_name.is_a? String' unless @remote_name.is_a? String
99
+ end
100
+ end
101
+ logger.debug { "Using remote name of '#{@remote_name}'" }
102
+ end
103
+ @remote_name
104
+ end
105
+
106
+
107
+ def master_branch_name
108
+ "#{self.name}/#{config.master_branch}"
109
+ end
110
+
111
+
112
+ def remote_names
113
+ remote_str = config.gitlib.command(:remote, [:show])
114
+ if remote_str.nil? or remote_str.empty?
115
+ []
116
+ else
117
+ remote_str.split(/\n/)
118
+ end
119
+ end
120
+
121
+
122
+ #
123
+ # Expands the git configuration server name to a url.
124
+ #
125
+ # Takes into account further expanding an SSH uri that uses SSH aliasing in .ssh/config
126
+ #
127
+ # @param [String] server_name the git configuration server name; defaults to 'origin'
128
+ #
129
+ # @option opts [String] :ssh_config_file the SSH config file to use; defaults to ~/.ssh/config
130
+ #
131
+ # @return the fully expanded URL; never nil
132
+ #
133
+ # @raise [GitHubService::NoRemoteRepository] there is not a URL set for the server name
134
+ # @raise [URI::InvalidURIError] the retrieved URL does not have a schema
135
+ # @raise [GitHubService::NoRemoteRepository] if could not figure out a host for the retrieved URL
136
+ # @raise [::ArgumentError] if a server name is not provided
137
+ def expanded_url(server_name = 'origin', raw_url = nil, opts = {})
138
+ if raw_url.nil?
139
+ raise ArgumentError.new('Need server_name') unless server_name
140
+
141
+ conf_key = "remote.#{server_name}.url"
142
+ url = config[conf_key]
143
+
144
+ raise GitHubService::NoRemoteRepository.new("There is no value set for '#{conf_key}'") if url.nil? or url.empty?
145
+ else
146
+ raise GitHubService::NoRemoteRepository.new("There is no value set for '#{raw_url}'") if raw_url.nil? or raw_url.empty?
147
+ url = raw_url
148
+ end
149
+
150
+ if /^\S+@/ =~ url
151
+ url.sub(/^(\S+@\S+?):(.*)$/, "ssh://\\1/\\2")
152
+ else
153
+ uri = URI.parse(url)
154
+ host = uri.host
155
+ scheme = uri.scheme
156
+
157
+ raise URI::InvalidURIError.new("Need a scheme in URI: '#{url}'") unless scheme
158
+
159
+ if scheme == 'file'
160
+ url
161
+ elsif host.nil?
162
+ # assume that the 'scheme' is the named configuration in ~/.ssh/config
163
+ rv = GitRemote.hostname_and_user_from_ssh_config(scheme, opts[:ssh_config_file] ||= "#{ENV['HOME']}/.ssh/config")
164
+
165
+ raise GitHubService::NoRemoteRepository.new("Could not determine a host from #{url}") if rv.nil?
166
+
167
+ host = rv[0]
168
+ user = rv[1]
169
+ url.sub(/^\S+:(\S+)$/, "ssh://#{user}@#{host}/\\1")
170
+ else
171
+ url
172
+ end
173
+ end
174
+ end
175
+
176
+
177
+ # @return [boolean]
178
+ def rerere_enabled?
179
+ re = config['rerere.enabled']
180
+ !re.nil? && re.to_boolean
181
+ end
182
+
183
+
184
+ # @return [void]
185
+ def rerere_enabled(re, global = true)
186
+ if global
187
+ config.set_global('rerere.enabled', re)
188
+ else
189
+ config['rerere.enabled'] = re
190
+ end
191
+ end
192
+
193
+
194
+ # @return [boolean]
195
+ def rerere_autoupdate?
196
+ re = config['rerere.autoupdate']
197
+ !re.nil? && re.to_boolean
198
+ end
199
+
200
+
201
+ # @return [void]
202
+ def rerere_autoupdate(re, global = true)
203
+ if global
204
+ config.set_global('rerere.autoupdate', re)
205
+ else
206
+ config['rerere.autoupdate'] = re
207
+ end
208
+ end
209
+
210
+
211
+ # @return [void]
212
+ def add_remote(remote_name, url)
213
+ config.gitlib.command(:remote, ['add', remote_name, url])
214
+ end
215
+
216
+
217
+ alias :add :add_remote
218
+
219
+
220
+ #noinspection RubyClassMethodNamingConvention
221
+ def self.hostname_and_user_from_ssh_config(host_alias, config_file)
222
+ if File.exists?(config_file)
223
+ config_lines = File.new(config_file).readlines
224
+
225
+ in_host_section = false
226
+ host_name = nil
227
+ user_name = nil
228
+
229
+ config_lines.each do |line|
230
+ line.chop!
231
+ if /^\s*Host\s+#{host_alias}\s*$/ =~ line
232
+ in_host_section = true
233
+ next
234
+ end
235
+
236
+ if in_host_section and (/^\s*HostName\s+\S+\s*$/ =~ line)
237
+ host_name = line.sub(/^\s*HostName\s+(\S+)\s*$/, '\1')
238
+ break unless user_name.nil?
239
+ elsif in_host_section and (/^\s*User\s+\S+\s*$/ =~ line)
240
+ user_name = line.sub(/^\s*User\s+(\S+)\s*$/, '\1')
241
+ break unless host_name.nil?
242
+ elsif in_host_section and (/^\s*Host\s+.*$/ =~ line)
243
+ break
244
+ end
245
+ end
246
+ host_name.nil? ? nil : [host_name, user_name]
247
+ else
248
+ nil
249
+ end
250
+ end
251
+
252
+ end
253
+
254
+ end