git-process-lib 2.0.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 (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,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
@@ -0,0 +1,28 @@
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_abstract_merge_error_builder'
14
+
15
+ module GitProc
16
+
17
+ class MergeError < GitProcessError
18
+
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
22
+
23
+ super(msg)
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,159 @@
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_lib'
14
+ require 'git-process/git_rebase_error'
15
+ require 'git-process/git_merge_error'
16
+ require 'highline/import'
17
+
18
+
19
+ module GitProc
20
+
21
+ class Process
22
+
23
+ # @param [GitLib] gitlib
24
+ def initialize(gitlib, opts = {})
25
+ @gitlib = gitlib
26
+ end
27
+
28
+
29
+ def gitlib
30
+ @gitlib
31
+ end
32
+
33
+
34
+ def run
35
+ begin
36
+ verify_preconditions
37
+
38
+ runner
39
+ rescue GitProc::GitProcessError => exp
40
+ puts exp.message
41
+ exit(-1)
42
+ ensure
43
+ cleanup
44
+ end
45
+ end
46
+
47
+
48
+ def runner
49
+ # extension point - does nothing by default
50
+ end
51
+
52
+
53
+ def workdir
54
+ gitlib.workdir
55
+ end
56
+
57
+
58
+ def logger
59
+ gitlib.logger
60
+ end
61
+
62
+
63
+ def config
64
+ gitlib.config
65
+ end
66
+
67
+
68
+ def master_branch
69
+ gitlib.config.master_branch
70
+ end
71
+
72
+
73
+ def remote
74
+ gitlib.remote
75
+ end
76
+
77
+
78
+ def verify_preconditions
79
+ if should_remove_master?
80
+ if ask_about_removing_master
81
+ delete_master_branch!
82
+ end
83
+ end
84
+ end
85
+
86
+
87
+ def cleanup
88
+ # extension point
89
+ end
90
+
91
+
92
+ def fetch_remote_changes(remote_name = nil)
93
+ gitlib.fetch_remote_changes(remote_name)
94
+ end
95
+
96
+
97
+ def is_parked?
98
+ gitlib.is_parked?
99
+ end
100
+
101
+
102
+ private
103
+
104
+
105
+ def delete_master_branch!
106
+ gitlib.branches[config.master_branch].delete!
107
+ end
108
+
109
+
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)
117
+ end
118
+
119
+
120
+ def keep_local_integration_branch?
121
+ keep_local_integration_branch_config_value.to_boolean
122
+ end
123
+
124
+
125
+ #noinspection RubyInstanceMethodNamingConvention
126
+ def keep_local_integration_branch_config_value
127
+ gitlib.config['gitProcess.keepLocalIntegrationBranch']
128
+ end
129
+
130
+
131
+ def ask_about_removing_master
132
+ resp = ask("You should remove your obsolete <%= color('local', [:bold]) %> branch, '#{config.master_branch}'. Should I remove it for you? (Yn) ") do |q|
133
+ q.responses[:not_valid] = 'Please respond with either (y)es or (n)o. Defaults to (y)es.'
134
+ q.case = :down
135
+ q.default = 'Y'
136
+ q.validate = /y|n/i
137
+ end
138
+
139
+ if resp == 'n'
140
+ say("(You can turn off this message using \"git config gitProcess.keepLocalIntegrationBranch true\").")
141
+ false
142
+ else
143
+ true
144
+ end
145
+ end
146
+
147
+
148
+ def proc_rebase(base, opts = {})
149
+ gitlib.proc_rebase(base, opts)
150
+ end
151
+
152
+
153
+ def proc_merge(base, opts = {})
154
+ gitlib.proc_merge(base, opts)
155
+ end
156
+
157
+ end
158
+
159
+ end
@@ -0,0 +1,18 @@
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
+ module GitProc
14
+
15
+ class GitProcessError < RuntimeError
16
+ end
17
+
18
+ end
@@ -0,0 +1,101 @@
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 'optparse'
14
+ require 'trollop'
15
+ require 'git-process/version'
16
+
17
+ module GitProc
18
+
19
+ module GitProcessOptions
20
+
21
+ DEBUG = false
22
+
23
+
24
+ def parse_cli(filename, argv)
25
+ parser = Trollop::Parser.new
26
+ parser.version "#{filename} #{GitProc::Version::STRING}"
27
+
28
+ #parser.banner "#{summary}\n\n"
29
+ command_name = filename.sub(/git-/, '')
30
+ parser.banner "For full documentation, see 'git help #{command_name}'"
31
+ parser.banner ""
32
+
33
+ extend_opts(parser)
34
+ standard_opts(parser)
35
+
36
+ #parser.banner "\n#{description}"
37
+
38
+ opts = Trollop::with_standard_exception_handling parser do
39
+ raise Trollop::HelpNeeded if ARGV.empty? and !empty_argv_ok?
40
+ parser.parse argv
41
+ end
42
+
43
+ opts[:info] = false if opts[:verbose] || opts[:quiet]
44
+ opts[:info] = true if opts[:info_given]
45
+
46
+ post_parse(opts, argv)
47
+
48
+ if DEBUG
49
+ puts "\n\n#{opts.map { |k, v| "#{k}:#{v}" }.join(', ')}"
50
+ puts "\nargs: #{argv.join(', ')}"
51
+ end
52
+
53
+ opts
54
+ end
55
+
56
+
57
+ def standard_opts(parser)
58
+ parser.opt :info, 'Informational messages; show the major things this is doing', :default => true, :short => :none
59
+ parser.opt :quiet, 'Quiet messages; only show errors', :short => :q
60
+ parser.opt :verbose, 'Verbose messages; show lots of details on what this is doing', :short => :v
61
+ parser.opt :version, "Print version (#{GitProc::Version::STRING}) and exit", :short => :none
62
+ parser.opt :help, 'Show this message', :short => :h
63
+
64
+ parser.conflicts :verbose, :info, :quiet
65
+ end
66
+
67
+
68
+ def summary
69
+ 'Default summary'
70
+ end
71
+
72
+
73
+ def usage(filename)
74
+ "#{filename} [options]"
75
+ end
76
+
77
+
78
+ def description
79
+ 'Default description'
80
+ end
81
+
82
+
83
+ def empty_argv_ok?
84
+ true
85
+ end
86
+
87
+
88
+ #noinspection RubyUnusedLocalVariable
89
+ def extend_opts(parser)
90
+ # extension point - does nothing by default
91
+ end
92
+
93
+
94
+ #noinspection RubyUnusedLocalVariable
95
+ def post_parse(opts, argv)
96
+ # extension point - does nothing by default
97
+ end
98
+
99
+ end
100
+
101
+ end
@@ -0,0 +1,30 @@
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_abstract_merge_error_builder'
14
+
15
+ module GitProc
16
+
17
+ class RebaseError < GitProcessError
18
+ attr_reader :error_builder
19
+
20
+
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
24
+
25
+ super(msg)
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,222 @@
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 'addressable/uri'
15
+ #require 'git-process/git_branches'
16
+ #require 'git-process/git_status'
17
+ #require 'git-process/git_process_error'
18
+
19
+
20
+ class String
21
+
22
+ def to_boolean
23
+ return false if self == false || self.nil? || self =~ (/(false|f|no|n|0)$/i)
24
+ return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
25
+ raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
26
+ end
27
+
28
+ end
29
+
30
+
31
+ class NilClass
32
+ def to_boolean
33
+ false
34
+ end
35
+ end
36
+
37
+
38
+ module GitProc
39
+
40
+ #
41
+ # Git Remote configuration
42
+ #
43
+ class GitRemote
44
+
45
+ # @param [GitProc::GitConfig] gitconfig
46
+ def initialize(gitconfig)
47
+ @gitconfig = gitconfig
48
+ end
49
+
50
+
51
+ # @return [#info, #warn, #debug, #error]
52
+ def logger
53
+ @logger ||= @gitconfig.logger
54
+ end
55
+
56
+
57
+ # @return [GitProc::GitConfig]
58
+ def config
59
+ @gitconfig
60
+ end
61
+
62
+
63
+ # @deprecated
64
+ # TODO: Remove
65
+ def server_name
66
+ @server_name ||= self.remote_name
67
+ end
68
+
69
+
70
+ # @return [Boolean] does this have a remote defined?
71
+ def exists?
72
+ if @has_remote.nil?
73
+ @has_remote = (config.gitlib.command(:remote) != '')
74
+ end
75
+ logger.debug { "Does a remote exist? #{@has_remote}" }
76
+ @has_remote
77
+ end
78
+
79
+
80
+ def repo_name
81
+ unless @repo_name
82
+ url = config["remote.#{name}.url"]
83
+ raise GitProcessError.new("There is no #{name} url set up.") if url.nil? or url.empty?
84
+ uri = Addressable::URI.parse(url)
85
+ @repo_name = uri.path.sub(/\.git/, '').sub(/^\//, '')
86
+ end
87
+ @repo_name
88
+ end
89
+
90
+
91
+ def name
92
+ unless @remote_name
93
+ @remote_name = config['gitProcess.remoteName']
94
+ if @remote_name.nil? or @remote_name.empty?
95
+ remotes = self.remote_names
96
+ if remotes.empty?
97
+ @remote_name = nil
98
+ else
99
+ @remote_name = remotes[0]
100
+ raise '!@remote_name.is_a? String' unless @remote_name.is_a? String
101
+ end
102
+ end
103
+ logger.debug { "Using remote name of '#{@remote_name}'" }
104
+ end
105
+ @remote_name
106
+ end
107
+
108
+
109
+ def master_branch_name
110
+ "#{self.name}/#{config.master_branch}"
111
+ end
112
+
113
+
114
+ def remote_names
115
+ remote_str = config.gitlib.command(:remote, [:show])
116
+ if remote_str.nil? or remote_str.empty?
117
+ []
118
+ else
119
+ remote_str.split(/\n/)
120
+ end
121
+ end
122
+
123
+
124
+ #
125
+ # Expands the git configuration server name to a url.
126
+ #
127
+ # Takes into account further expanding an SSH uri that uses SSH aliasing in .ssh/config
128
+ #
129
+ # @param [String] server_name the git configuration server name; defaults to 'origin'
130
+ #
131
+ # @option opts [String] :ssh_config_file the SSH config file to use; defaults to ~/.ssh/config
132
+ #
133
+ # @return the fully expanded URL; never nil
134
+ #
135
+ # @raise [GitHubService::NoRemoteRepository] there is not a URL set for the server name
136
+ # @raise [URI::InvalidURIError] the retrieved URL does not have a schema
137
+ # @raise [GitHubService::NoRemoteRepository] if could not figure out a host for the retrieved URL
138
+ # @raise [::ArgumentError] if a server name is not provided
139
+ def expanded_url(server_name = 'origin', raw_url = nil, opts = {})
140
+ if raw_url.nil?
141
+ raise ArgumentError.new('Need server_name') unless server_name
142
+
143
+ conf_key = "remote.#{server_name}.url"
144
+ url = config[conf_key]
145
+
146
+ raise GitHubService::NoRemoteRepository.new("There is no value set for '#{conf_key}'") if url.nil? or url.empty?
147
+ else
148
+ raise GitHubService::NoRemoteRepository.new("There is no value set for '#{raw_url}'") if raw_url.nil? or raw_url.empty?
149
+ url = raw_url
150
+ end
151
+
152
+ if /^\S+@/ =~ url
153
+ url.sub(/^(\S+@\S+?):(.*)$/, "ssh://\\1/\\2")
154
+ else
155
+ uri = URI.parse(url)
156
+ host = uri.host
157
+ scheme = uri.scheme
158
+
159
+ raise URI::InvalidURIError.new("Need a scheme in URI: '#{url}'") unless scheme
160
+
161
+ if scheme == 'file'
162
+ url
163
+ elsif host.nil?
164
+ # assume that the 'scheme' is the named configuration in ~/.ssh/config
165
+ rv = GitRemote.hostname_and_user_from_ssh_config(scheme, opts[:ssh_config_file] ||= "#{ENV['HOME']}/.ssh/config")
166
+
167
+ raise GitHubService::NoRemoteRepository.new("Could not determine a host from #{url}") if rv.nil?
168
+
169
+ host = rv[0]
170
+ user = rv[1]
171
+ url.sub(/^\S+:(\S+)$/, "ssh://#{user}@#{host}/\\1")
172
+ else
173
+ url
174
+ end
175
+ end
176
+ end
177
+
178
+
179
+ # @return [void]
180
+ def add_remote(remote_name, url)
181
+ config.gitlib.command(:remote, ['add', remote_name, url])
182
+ end
183
+
184
+
185
+ alias :add :add_remote
186
+
187
+
188
+ #noinspection RubyClassMethodNamingConvention
189
+ def self.hostname_and_user_from_ssh_config(host_alias, config_file)
190
+ if File.exists?(config_file)
191
+ config_lines = File.new(config_file).readlines
192
+
193
+ in_host_section = false
194
+ host_name = nil
195
+ user_name = nil
196
+
197
+ config_lines.each do |line|
198
+ line.chop!
199
+ if /^\s*Host\s+#{host_alias}\s*$/ =~ line
200
+ in_host_section = true
201
+ next
202
+ end
203
+
204
+ if in_host_section and (/^\s*HostName\s+\S+\s*$/ =~ line)
205
+ host_name = line.sub(/^\s*HostName\s+(\S+)\s*$/, '\1')
206
+ break unless user_name.nil?
207
+ elsif in_host_section and (/^\s*User\s+\S+\s*$/ =~ line)
208
+ user_name = line.sub(/^\s*User\s+(\S+)\s*$/, '\1')
209
+ break unless host_name.nil?
210
+ elsif in_host_section and (/^\s*Host\s+.*$/ =~ line)
211
+ break
212
+ end
213
+ end
214
+ host_name.nil? ? nil : [host_name, user_name]
215
+ else
216
+ nil
217
+ end
218
+ end
219
+
220
+ end
221
+
222
+ end