git-glimmer 1.7.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.
@@ -0,0 +1 @@
1
+ require_relative 'git'
@@ -0,0 +1,167 @@
1
+ # Add the directory containing this file to the start of the load path if it
2
+ # isn't there already.
3
+ $:.unshift(File.dirname(__FILE__)) unless
4
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
5
+
6
+ require 'git/author'
7
+ require 'git/base'
8
+ require 'git/branch'
9
+ require 'git/branches'
10
+ require 'git/config'
11
+ require 'git/diff'
12
+ require 'git/index'
13
+ require 'git/lib'
14
+ require 'git/log'
15
+ require 'git/object'
16
+ require 'git/path'
17
+ require 'git/remote'
18
+ require 'git/repository'
19
+ require 'git/status'
20
+ require 'git/stash'
21
+ require 'git/stashes'
22
+ require 'git/version'
23
+ require 'git/working_directory'
24
+
25
+ lib = Git::Lib.new(nil, nil)
26
+ unless lib.meets_required_version?
27
+ $stderr.puts "[WARNING] The git gem requires git #{lib.required_command_version.join('.')} or later, but only found #{lib.current_command_version.join('.')}. You should probably upgrade."
28
+ end
29
+
30
+ # Git/Ruby Library
31
+ #
32
+ # This provides bindings for working with git in complex
33
+ # interactions, including branching and merging, object
34
+ # inspection and manipulation, history, patch generation
35
+ # and more. You should be able to do most fundamental git
36
+ # operations with this library.
37
+ #
38
+ # This module provides the basic functions to open a git
39
+ # reference to work with. You can open a working directory,
40
+ # open a bare repository, initialize a new repo or clone an
41
+ # existing remote repository.
42
+ #
43
+ # Author:: Scott Chacon (mailto:schacon@gmail.com)
44
+ # License:: MIT License
45
+ module Git
46
+
47
+ #g.config('user.name', 'Scott Chacon') # sets value
48
+ #g.config('user.email', 'email@email.com') # sets value
49
+ #g.config('user.name') # returns 'Scott Chacon'
50
+ #g.config # returns whole config hash
51
+ def config(name = nil, value = nil)
52
+ lib = Git::Lib.new
53
+ if(name && value)
54
+ # set value
55
+ lib.config_set(name, value)
56
+ elsif (name)
57
+ # return value
58
+ lib.config_get(name)
59
+ else
60
+ # return hash
61
+ lib.config_list
62
+ end
63
+ end
64
+
65
+ def self.configure
66
+ yield Base.config
67
+ end
68
+
69
+ def self.config
70
+ return Base.config
71
+ end
72
+
73
+ def global_config(name = nil, value = nil)
74
+ self.class.global_config(name, value)
75
+ end
76
+
77
+ # open a bare repository
78
+ #
79
+ # this takes the path to a bare git repo
80
+ # it expects not to be able to use a working directory
81
+ # so you can't checkout stuff, commit things, etc.
82
+ # but you can do most read operations
83
+ def self.bare(git_dir, options = {})
84
+ Base.bare(git_dir, options)
85
+ end
86
+
87
+ # clones a remote repository
88
+ #
89
+ # options
90
+ # :bare => true (does a bare clone)
91
+ # :repository => '/path/to/alt_git_dir'
92
+ # :index => '/path/to/alt_index_file'
93
+ #
94
+ # example
95
+ # Git.clone('git://repo.or.cz/rubygit.git', 'clone.git', :bare => true)
96
+ #
97
+ def self.clone(repository, name, options = {})
98
+ Base.clone(repository, name, options)
99
+ end
100
+
101
+ # Export the current HEAD (or a branch, if <tt>options[:branch]</tt>
102
+ # is specified) into the +name+ directory, then remove all traces of git from the
103
+ # directory.
104
+ #
105
+ # See +clone+ for options. Does not obey the <tt>:remote</tt> option,
106
+ # since the .git info will be deleted anyway; always uses the default
107
+ # remote, 'origin.'
108
+ def self.export(repository, name, options = {})
109
+ options.delete(:remote)
110
+ repo = clone(repository, name, {:depth => 1}.merge(options))
111
+ repo.checkout("origin/#{options[:branch]}") if options[:branch]
112
+ Dir.chdir(repo.dir.to_s) { FileUtils.rm_r '.git' }
113
+ end
114
+
115
+ # Same as g.config, but forces it to be at the global level
116
+ #
117
+ #g.config('user.name', 'Scott Chacon') # sets value
118
+ #g.config('user.email', 'email@email.com') # sets value
119
+ #g.config('user.name') # returns 'Scott Chacon'
120
+ #g.config # returns whole config hash
121
+ def self.global_config(name = nil, value = nil)
122
+ lib = Git::Lib.new(nil, nil)
123
+ if(name && value)
124
+ # set value
125
+ lib.global_config_set(name, value)
126
+ elsif (name)
127
+ # return value
128
+ lib.global_config_get(name)
129
+ else
130
+ # return hash
131
+ lib.global_config_list
132
+ end
133
+ end
134
+
135
+ # initialize a new git repository, defaults to the current working directory
136
+ #
137
+ # options
138
+ # :repository => '/path/to/alt_git_dir'
139
+ # :index => '/path/to/alt_index_file'
140
+ def self.init(working_dir = '.', options = {})
141
+ Base.init(working_dir, options)
142
+ end
143
+
144
+ # returns a Hash containing information about the references
145
+ # of the target repository
146
+ #
147
+ # @param [String|NilClass] location the target repository location or nil for '.'
148
+ # @return [{String=>Hash}] the available references of the target repo.
149
+ def self.ls_remote(location=nil)
150
+ Git::Lib.new.ls_remote(location)
151
+ end
152
+
153
+ # open an existing git working directory
154
+ #
155
+ # this will most likely be the most common way to create
156
+ # a git reference, referring to a working directory.
157
+ # if not provided in the options, the library will assume
158
+ # your git_dir and index are in the default place (.git/, .git/index)
159
+ #
160
+ # options
161
+ # :repository => '/path/to/alt_git_dir'
162
+ # :index => '/path/to/alt_index_file'
163
+ def self.open(working_dir, options = {})
164
+ Base.open(working_dir, options)
165
+ end
166
+
167
+ end
@@ -0,0 +1,14 @@
1
+ module Git
2
+ class Author
3
+ attr_accessor :name, :email, :date
4
+
5
+ def initialize(author_string)
6
+ if m = /(.*?) <(.*?)> (\d+) (.*)/.match(author_string)
7
+ @name = m[1]
8
+ @email = m[2]
9
+ @date = Time.at(m[3].to_i)
10
+ end
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,551 @@
1
+ require 'git/base/factory'
2
+
3
+ module Git
4
+
5
+ class Base
6
+
7
+ include Git::Base::Factory
8
+
9
+ # opens a bare Git Repository - no working directory options
10
+ def self.bare(git_dir, opts = {})
11
+ self.new({:repository => git_dir}.merge(opts))
12
+ end
13
+
14
+ # clones a git repository locally
15
+ #
16
+ # repository - http://repo.or.cz/w/sinatra.git
17
+ # name - sinatra
18
+ #
19
+ # options:
20
+ # :repository
21
+ #
22
+ # :bare
23
+ # or
24
+ # :working_directory
25
+ # :index_file
26
+ #
27
+ def self.clone(repository, name, opts = {})
28
+ # run git-clone
29
+ self.new(Git::Lib.new.clone(repository, name, opts))
30
+ end
31
+
32
+ # Returns (and initialize if needed) a Git::Config instance
33
+ #
34
+ # @return [Git::Config] the current config instance.
35
+ def self.config
36
+ return @@config ||= Config.new
37
+ end
38
+
39
+ # initializes a git repository
40
+ #
41
+ # options:
42
+ # :bare
43
+ # :index
44
+ # :repository
45
+ #
46
+ def self.init(working_dir, opts = {})
47
+ opts[:working_directory] ||= working_dir
48
+ opts[:repository] ||= File.join(opts[:working_directory], '.git')
49
+
50
+ FileUtils.mkdir_p(opts[:working_directory]) if opts[:working_directory] && !File.directory?(opts[:working_directory])
51
+
52
+ init_opts = {
53
+ :bare => opts[:bare]
54
+ }
55
+
56
+ opts.delete(:working_directory) if opts[:bare]
57
+
58
+ # Submodules have a .git *file* not a .git folder.
59
+ # This file's contents point to the location of
60
+ # where the git refs are held (In the parent repo)
61
+ if File.file?('.git')
62
+ git_file = File.open('.git').read[8..-1].strip
63
+ opts[:repository] = git_file
64
+ opts[:index] = git_file + '/index'
65
+ end
66
+
67
+ Git::Lib.new(opts).init(init_opts)
68
+
69
+ self.new(opts)
70
+ end
71
+
72
+ # opens a new Git Project from a working directory
73
+ # you can specify non-standard git_dir and index file in the options
74
+ def self.open(working_dir, opts={})
75
+ self.new({:working_directory => working_dir}.merge(opts))
76
+ end
77
+
78
+ def initialize(options = {})
79
+ if working_dir = options[:working_directory]
80
+ options[:repository] ||= File.join(working_dir, '.git')
81
+ options[:index] ||= File.join(working_dir, '.git', 'index')
82
+ end
83
+ if options[:log]
84
+ @logger = options[:log]
85
+ @logger.info("Starting Git")
86
+ else
87
+ @logger = nil
88
+ end
89
+
90
+ @working_directory = options[:working_directory] ? Git::WorkingDirectory.new(options[:working_directory]) : nil
91
+ @repository = options[:repository] ? Git::Repository.new(options[:repository]) : nil
92
+ @index = options[:index] ? Git::Index.new(options[:index], false) : nil
93
+ end
94
+
95
+ # changes current working directory for a block
96
+ # to the git working directory
97
+ #
98
+ # example
99
+ # @git.chdir do
100
+ # # write files
101
+ # @git.add
102
+ # @git.commit('message')
103
+ # end
104
+ def chdir # :yields: the Git::Path
105
+ Dir.chdir(dir.path) do
106
+ yield dir.path
107
+ end
108
+ end
109
+
110
+ #g.config('user.name', 'Scott Chacon') # sets value
111
+ #g.config('user.email', 'email@email.com') # sets value
112
+ #g.config('user.name') # returns 'Scott Chacon'
113
+ #g.config # returns whole config hash
114
+ def config(name = nil, value = nil)
115
+ if(name && value)
116
+ # set value
117
+ lib.config_set(name, value)
118
+ elsif (name)
119
+ # return value
120
+ lib.config_get(name)
121
+ else
122
+ # return hash
123
+ lib.config_list
124
+ end
125
+ end
126
+
127
+ # returns a reference to the working directory
128
+ # @git.dir.path
129
+ # @git.dir.writeable?
130
+ def dir
131
+ @working_directory
132
+ end
133
+
134
+ # returns reference to the git index file
135
+ def index
136
+ @index
137
+ end
138
+
139
+ # returns reference to the git repository directory
140
+ # @git.dir.path
141
+ def repo
142
+ @repository
143
+ end
144
+
145
+ # returns the repository size in bytes
146
+ def repo_size
147
+ Dir.chdir(repo.path) do
148
+ return `du -s`.chomp.split.first.to_i
149
+ end
150
+ end
151
+
152
+ def set_index(index_file, check = true)
153
+ @lib = nil
154
+ @index = Git::Index.new(index_file.to_s, check)
155
+ end
156
+
157
+ def set_working(work_dir, check = true)
158
+ @lib = nil
159
+ @working_directory = Git::WorkingDirectory.new(work_dir.to_s, check)
160
+ end
161
+
162
+ # returns +true+ if the branch exists locally
163
+ def is_local_branch?(branch)
164
+ branch_names = self.branches.local.map {|b| b.name}
165
+ branch_names.include?(branch)
166
+ end
167
+
168
+ # returns +true+ if the branch exists remotely
169
+ def is_remote_branch?(branch)
170
+ branch_names = self.branches.remote.map {|b| b.name}
171
+ branch_names.include?(branch)
172
+ end
173
+
174
+ # returns +true+ if the branch exists
175
+ def is_branch?(branch)
176
+ branch_names = self.branches.map {|b| b.name}
177
+ branch_names.include?(branch)
178
+ end
179
+
180
+ # this is a convenience method for accessing the class that wraps all the
181
+ # actual 'git' forked system calls. At some point I hope to replace the Git::Lib
182
+ # class with one that uses native methods or libgit C bindings
183
+ def lib
184
+ @lib ||= Git::Lib.new(self, @logger)
185
+ end
186
+
187
+ # will run a grep for 'string' on the HEAD of the git repository
188
+ #
189
+ # to be more surgical in your grep, you can call grep() off a specific
190
+ # git object. for example:
191
+ #
192
+ # @git.object("v2.3").grep('TODO')
193
+ #
194
+ # in any case, it returns a hash of arrays of the type:
195
+ # hsh[tree-ish] = [[line_no, match], [line_no, match2]]
196
+ # hsh[tree-ish] = [[line_no, match], [line_no, match2]]
197
+ #
198
+ # so you might use it like this:
199
+ #
200
+ # @git.grep("TODO").each do |sha, arr|
201
+ # puts "in blob #{sha}:"
202
+ # arr.each do |match|
203
+ # puts "\t line #{match[0]}: '#{match[1]}'"
204
+ # end
205
+ # end
206
+ def grep(string, path_limiter = nil, opts = {})
207
+ self.object('HEAD').grep(string, path_limiter, opts)
208
+ end
209
+
210
+ # updates the repository index using the working directory content
211
+ #
212
+ # @git.add('path/to/file')
213
+ # @git.add(['path/to/file1','path/to/file2'])
214
+ # @git.add(:all => true)
215
+ #
216
+ # options:
217
+ # :all => true
218
+ #
219
+ # @param [String,Array] paths files paths to be added (optional, default='.')
220
+ # @param [Hash] options
221
+ def add(*args)
222
+ if args[0].instance_of?(String) || args[0].instance_of?(Array)
223
+ self.lib.add(args[0],args[1]||{})
224
+ else
225
+ self.lib.add('.', args[0]||{})
226
+ end
227
+ end
228
+
229
+ # removes file(s) from the git repository
230
+ def remove(path = '.', opts = {})
231
+ self.lib.remove(path, opts)
232
+ end
233
+
234
+ # resets the working directory to the provided commitish
235
+ def reset(commitish = nil, opts = {})
236
+ self.lib.reset(commitish, opts)
237
+ end
238
+
239
+ # resets the working directory to the commitish with '--hard'
240
+ def reset_hard(commitish = nil, opts = {})
241
+ opts = {:hard => true}.merge(opts)
242
+ self.lib.reset(commitish, opts)
243
+ end
244
+
245
+ # cleans the working directory
246
+ #
247
+ # options:
248
+ # :force
249
+ # :d
250
+ #
251
+ def clean(opts = {})
252
+ self.lib.clean(opts)
253
+ end
254
+
255
+ # returns the most recent tag that is reachable from a commit
256
+ #
257
+ # options:
258
+ # :all
259
+ # :tags
260
+ # :contains
261
+ # :debug
262
+ # :exact_match
263
+ # :dirty
264
+ # :abbrev
265
+ # :candidates
266
+ # :long
267
+ # :always
268
+ # :match
269
+ #
270
+ def describe(committish=nil, opts={})
271
+ self.lib.describe(committish, opts)
272
+ end
273
+
274
+ # reverts the working directory to the provided commitish.
275
+ # Accepts a range, such as comittish..HEAD
276
+ #
277
+ # options:
278
+ # :no_edit
279
+ #
280
+ def revert(commitish = nil, opts = {})
281
+ self.lib.revert(commitish, opts)
282
+ end
283
+
284
+ # commits all pending changes in the index file to the git repository
285
+ #
286
+ # options:
287
+ # :all
288
+ # :allow_empty
289
+ # :amend
290
+ # :author
291
+ #
292
+ def commit(message, opts = {})
293
+ self.lib.commit(message, opts)
294
+ end
295
+
296
+ # commits all pending changes in the index file to the git repository,
297
+ # but automatically adds all modified files without having to explicitly
298
+ # calling @git.add() on them.
299
+ def commit_all(message, opts = {})
300
+ opts = {:add_all => true}.merge(opts)
301
+ self.lib.commit(message, opts)
302
+ end
303
+
304
+ # checks out a branch as the new git working directory
305
+ def checkout(branch = 'master', opts = {})
306
+ self.lib.checkout(branch, opts)
307
+ end
308
+
309
+ # checks out an old version of a file
310
+ def checkout_file(version, file)
311
+ self.lib.checkout_file(version,file)
312
+ end
313
+
314
+ # fetches changes from a remote branch - this does not modify the working directory,
315
+ # it just gets the changes from the remote if there are any
316
+ def fetch(remote = 'origin', opts={})
317
+ self.lib.fetch(remote, opts)
318
+ end
319
+
320
+ # pushes changes to a remote repository - easiest if this is a cloned repository,
321
+ # otherwise you may have to run something like this first to setup the push parameters:
322
+ #
323
+ # @git.config('remote.remote-name.push', 'refs/heads/master:refs/heads/master')
324
+ #
325
+ def push(remote = 'origin', branch = 'master', opts = {})
326
+ # Small hack to keep backwards compatibility with the 'push(remote, branch, tags)' method signature.
327
+ opts = {:tags => opts} if [true, false].include?(opts)
328
+
329
+ self.lib.push(remote, branch, opts)
330
+ end
331
+
332
+ # merges one or more branches into the current working branch
333
+ #
334
+ # you can specify more than one branch to merge by passing an array of branches
335
+ def merge(branch, message = 'merge')
336
+ self.lib.merge(branch, message)
337
+ end
338
+
339
+ # iterates over the files which are unmerged
340
+ def each_conflict(&block) # :yields: file, your_version, their_version
341
+ self.lib.conflicts(&block)
342
+ end
343
+
344
+ # pulls the given branch from the given remote into the current branch
345
+ #
346
+ # @git.pull # pulls from origin/master
347
+ # @git.pull('upstream') # pulls from upstream/master
348
+ # @git.pull('upstream', 'develope') # pulls from upstream/develop
349
+ #
350
+ def pull(remote='origin', branch='master')
351
+ self.lib.pull(remote, branch)
352
+ end
353
+
354
+ # returns an array of Git:Remote objects
355
+ def remotes
356
+ self.lib.remotes.map { |r| Git::Remote.new(self, r) }
357
+ end
358
+
359
+ # adds a new remote to this repository
360
+ # url can be a git url or a Git::Base object if it's a local reference
361
+ #
362
+ # @git.add_remote('scotts_git', 'git://repo.or.cz/rubygit.git')
363
+ # @git.fetch('scotts_git')
364
+ # @git.merge('scotts_git/master')
365
+ #
366
+ # Options:
367
+ # :fetch => true
368
+ # :track => <branch_name>
369
+ def add_remote(name, url, opts = {})
370
+ url = url.repo.path if url.is_a?(Git::Base)
371
+ self.lib.remote_add(name, url, opts)
372
+ Git::Remote.new(self, name)
373
+ end
374
+
375
+ # sets the url for a remote
376
+ # url can be a git url or a Git::Base object if it's a local reference
377
+ #
378
+ # @git.set_remote_url('scotts_git', 'git://repo.or.cz/rubygit.git')
379
+ #
380
+ def set_remote_url(name, url)
381
+ url = url.repo.path if url.is_a?(Git::Base)
382
+ self.lib.remote_set_url(name, url)
383
+ Git::Remote.new(self, name)
384
+ end
385
+
386
+ # removes a remote from this repository
387
+ #
388
+ # @git.remove_remote('scott_git')
389
+ def remove_remote(name)
390
+ self.lib.remote_remove(name)
391
+ end
392
+
393
+ # returns an array of all Git::Tag objects for this repository
394
+ def tags
395
+ self.lib.tags.map { |r| tag(r) }
396
+ end
397
+
398
+ # Creates a new git tag (Git::Tag)
399
+ # Usage:
400
+ # repo.add_tag('tag_name', object_reference)
401
+ # repo.add_tag('tag_name', object_reference, {:options => 'here'})
402
+ # repo.add_tag('tag_name', {:options => 'here'})
403
+ #
404
+ # Options:
405
+ # :a | :annotate -> true
406
+ # :d -> true
407
+ # :f -> true
408
+ # :m | :message -> String
409
+ # :s -> true
410
+ #
411
+ def add_tag(name, *opts)
412
+ self.lib.tag(name, *opts)
413
+ self.tag(name)
414
+ end
415
+
416
+ # deletes a tag
417
+ def delete_tag(name)
418
+ self.lib.tag(name, {:d => true})
419
+ end
420
+
421
+ # creates an archive file of the given tree-ish
422
+ def archive(treeish, file = nil, opts = {})
423
+ self.object(treeish).archive(file, opts)
424
+ end
425
+
426
+ # repacks the repository
427
+ def repack
428
+ self.lib.repack
429
+ end
430
+
431
+ def gc
432
+ self.lib.gc
433
+ end
434
+
435
+ def apply(file)
436
+ if File.exist?(file)
437
+ self.lib.apply(file)
438
+ end
439
+ end
440
+
441
+ def apply_mail(file)
442
+ self.lib.apply_mail(file) if File.exist?(file)
443
+ end
444
+
445
+ # Shows objects
446
+ #
447
+ # @param [String|NilClass] objectish the target object reference (nil == HEAD)
448
+ # @param [String|NilClass] path the path of the file to be shown
449
+ # @return [String] the object information
450
+ def show(objectish=nil, path=nil)
451
+ self.lib.show(objectish, path)
452
+ end
453
+
454
+ ## LOWER LEVEL INDEX OPERATIONS ##
455
+
456
+ def with_index(new_index) # :yields: new_index
457
+ old_index = @index
458
+ set_index(new_index, false)
459
+ return_value = yield @index
460
+ set_index(old_index)
461
+ return_value
462
+ end
463
+
464
+ def with_temp_index &blk
465
+ # Workaround for JRUBY, since they handle the TempFile path different.
466
+ # MUST be improved to be safer and OS independent.
467
+ if RUBY_PLATFORM == 'java'
468
+ temp_path = "/tmp/temp-index-#{(0...15).map{ ('a'..'z').to_a[rand(26)] }.join}"
469
+ else
470
+ tempfile = Tempfile.new('temp-index')
471
+ temp_path = tempfile.path
472
+ tempfile.close
473
+ tempfile.unlink
474
+ end
475
+
476
+ with_index(temp_path, &blk)
477
+ end
478
+
479
+ def checkout_index(opts = {})
480
+ self.lib.checkout_index(opts)
481
+ end
482
+
483
+ def read_tree(treeish, opts = {})
484
+ self.lib.read_tree(treeish, opts)
485
+ end
486
+
487
+ def write_tree
488
+ self.lib.write_tree
489
+ end
490
+
491
+ def write_and_commit_tree(opts = {})
492
+ tree = write_tree
493
+ commit_tree(tree, opts)
494
+ end
495
+
496
+ def update_ref(branch, commit)
497
+ branch(branch).update_ref(commit)
498
+ end
499
+
500
+
501
+ def ls_files(location=nil)
502
+ self.lib.ls_files(location)
503
+ end
504
+
505
+ def with_working(work_dir) # :yields: the Git::WorkingDirectory
506
+ return_value = false
507
+ old_working = @working_directory
508
+ set_working(work_dir)
509
+ Dir.chdir work_dir do
510
+ return_value = yield @working_directory
511
+ end
512
+ set_working(old_working)
513
+ return_value
514
+ end
515
+
516
+ def with_temp_working &blk
517
+ tempfile = Tempfile.new("temp-workdir")
518
+ temp_dir = tempfile.path
519
+ tempfile.close
520
+ tempfile.unlink
521
+ Dir.mkdir(temp_dir, 0700)
522
+ with_working(temp_dir, &blk)
523
+ end
524
+
525
+
526
+ # runs git rev-parse to convert the objectish to a full sha
527
+ #
528
+ # @git.revparse("HEAD^^")
529
+ # @git.revparse('v2.4^{tree}')
530
+ # @git.revparse('v2.4:/doc/index.html')
531
+ #
532
+ def revparse(objectish)
533
+ self.lib.revparse(objectish)
534
+ end
535
+
536
+ def ls_tree(objectish)
537
+ self.lib.ls_tree(objectish)
538
+ end
539
+
540
+ def cat_file(objectish)
541
+ self.lib.object_contents(objectish)
542
+ end
543
+
544
+ # returns the name of the branch the working directory is currently on
545
+ def current_branch
546
+ self.lib.branch_current
547
+ end
548
+
549
+ end
550
+
551
+ end