vcs 0.4.1 → 0.5.2.4
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.
- data/NEWS +29 -1
- data/SPEC.gemspec +5 -5
- data/SPEC.yml +9 -7
- data/contrib/emacs-support/vcs.el +11 -0
- data/lib/vcs/add.rb +1 -1
- data/lib/vcs/app.rb +4 -3
- data/lib/vcs/back.rb +1 -1
- data/lib/vcs/changelog.rb +18 -2
- data/lib/vcs/common_commit.rb +94 -49
- data/lib/vcs/conflict.rb +12 -8
- data/lib/vcs/cvs.rb +22 -2
- data/lib/vcs/delete.rb +1 -1
- data/lib/vcs/diff.rb +23 -7
- data/lib/vcs/edit.rb +6 -1
- data/lib/vcs/environment.rb +2 -2
- data/lib/vcs/form.rb +87 -47
- data/lib/vcs/ignore.rb +1 -1
- data/lib/vcs/junk.rb +1 -1
- data/lib/vcs/list.rb +5 -5
- data/lib/vcs/mail.rb +30 -17
- data/lib/vcs/message.rb +10 -9
- data/lib/vcs/opt_parse.rb +3 -2
- data/lib/vcs/script.rb +1 -1
- data/lib/vcs/status.rb +40 -16
- data/lib/vcs/svn.rb +8 -0
- data/lib/vcs/url.rb +1 -1
- data/lib/vcs/vcs.rb +130 -58
- data/lib/vcs/version.rb +10 -0
- metadata +69 -68
- data/SPEC.dyn.yml +0 -10
data/lib/vcs/delete.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Copyright:: Copyright (c) 2005 LRDE. All rights reserved.
|
2
2
|
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
3
3
|
# License:: Gnu General Public License.
|
4
|
-
# Revision:: $Id: delete.rb
|
4
|
+
# Revision:: $Id: /lrde/tools/trunk/vcs/lib/vcs/delete.rb 9106 2005-10-03T12:37:03.631187Z pouill_n $
|
5
5
|
|
6
6
|
class Vcs
|
7
7
|
|
data/lib/vcs/diff.rb
CHANGED
@@ -16,6 +16,17 @@ class Vcs
|
|
16
16
|
end
|
17
17
|
add_conf_checker :check_gnu_diff
|
18
18
|
|
19
|
+
def diffw_base ( files_orig=[], options={} )
|
20
|
+
files = []
|
21
|
+
status(files_orig, just_standard_options(options)) do |se|
|
22
|
+
next if se.file_st.chr =~ /[?X\\,+D]/
|
23
|
+
next if se.file.to_s == 'ChangeLog'
|
24
|
+
next if se.file.directory?
|
25
|
+
files << se.file unless files.include? se.file
|
26
|
+
end
|
27
|
+
files
|
28
|
+
end
|
29
|
+
|
19
30
|
end # class Vcs
|
20
31
|
|
21
32
|
|
@@ -23,16 +34,21 @@ class Svn < Vcs
|
|
23
34
|
|
24
35
|
# A diff only for your eyes
|
25
36
|
def diffw! ( files_orig=[], options={} )
|
26
|
-
files =
|
27
|
-
status(files_orig) do |se|
|
28
|
-
next if se.file_st.chr =~ /[?X\\,+D]/
|
29
|
-
next if se.file.to_s == 'ChangeLog'
|
30
|
-
next if se.file.directory?
|
31
|
-
files << se.file unless files.include? se.file
|
32
|
-
end
|
37
|
+
files = diffw_base(files_orig, options)
|
33
38
|
return if files.empty? and not files_orig.empty?
|
34
39
|
diff_! files,
|
35
40
|
options.merge(:diff_cmd => 'diff', :extensions => '-NPbuw')
|
36
41
|
end
|
37
42
|
|
38
43
|
end # class Svn
|
44
|
+
|
45
|
+
class Cvs < Vcs
|
46
|
+
|
47
|
+
# A diff only for your eyes
|
48
|
+
def diffw! ( files_orig=[], options={} )
|
49
|
+
files = diffw_base(files_orig, options)
|
50
|
+
return if files.empty? and not files_orig.empty?
|
51
|
+
diff_! files, options.merge(:N => true, :u => true)
|
52
|
+
end
|
53
|
+
|
54
|
+
end # class Cvs
|
data/lib/vcs/edit.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
2
2
|
# Copyright:: Copyright (c) 2004, 2005 LRDE. All rights reserved.
|
3
3
|
# License:: GNU General Public License (GPL).
|
4
|
-
# Revision:: $Id: edit.rb
|
4
|
+
# Revision:: $Id: /w/lrde/tools/trunk/vcs/lib/vcs/edit.rb 21851 2006-02-18T14:20:56.019344Z pouillar $
|
5
5
|
|
6
6
|
require 'vcs/vcs'
|
7
7
|
|
@@ -12,4 +12,9 @@ class Vcs
|
|
12
12
|
cmd.run(@runner)
|
13
13
|
end
|
14
14
|
|
15
|
+
def paginate! ( files=[], options={} )
|
16
|
+
cmd = Vcs.pager + files > [STDOUT, STDERR]
|
17
|
+
cmd.run(@runner)
|
18
|
+
end
|
19
|
+
|
15
20
|
end # class Vcs
|
data/lib/vcs/environment.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# Copyright:: Copyright (c) 2005 LRDE. All rights reserved.
|
2
2
|
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
3
3
|
# License:: Gnu General Public License.
|
4
|
-
# Revision:: $Id: environment.rb
|
4
|
+
# Revision:: $Id: /w/lrde/tools/trunk/vcs/lib/vcs/environment.rb 21852 2006-02-18T14:21:45.776145Z pouillar $
|
5
5
|
|
6
6
|
class Vcs
|
7
7
|
|
8
8
|
class << self
|
9
9
|
|
10
10
|
def fullname
|
11
|
-
env('FULLNAME') { Etc.getpwnam(user).gecos }
|
11
|
+
env('FULLNAME') { Etc.getpwnam(user).gecos.gsub(/,+$/, '') }
|
12
12
|
end
|
13
13
|
|
14
14
|
def editor
|
data/lib/vcs/form.rb
CHANGED
@@ -1,39 +1,50 @@
|
|
1
1
|
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
2
2
|
# Copyright:: Copyright (c) 2005 LRDE. All rights reserved.
|
3
3
|
# License:: GNU General Public License (GPL).
|
4
|
-
# Revision:: $Id: form.rb
|
4
|
+
# Revision:: $Id: /w/lrde/tools/trunk/vcs/lib/vcs/form.rb 22059 2006-02-20T21:45:33.186346Z pouillar $
|
5
5
|
|
6
6
|
class Vcs
|
7
7
|
|
8
8
|
def edit_form! ( *args )
|
9
9
|
mk_form(*args)
|
10
|
-
edit! Form
|
11
|
-
|
12
|
-
|
13
|
-
else
|
14
|
-
mk_iform(*args)
|
15
|
-
return YAML.load(IForm.read)['commited']
|
16
|
-
end
|
10
|
+
edit! Form
|
11
|
+
mk_iform(*args)
|
12
|
+
return YAML.load(IForm.read)['Revision'] || :committing
|
17
13
|
end
|
18
14
|
|
15
|
+
@@last_line ||= '--This line, and those below, will be ignored--'
|
19
16
|
|
20
17
|
def mk_iform! ( *args )
|
21
|
-
with_cache! IForm, 'instanciated form
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
18
|
+
with_cache! IForm, 'instanciated form' do
|
19
|
+
form = mk_form(*args).read
|
20
|
+
|
21
|
+
# Remove the first info line if present
|
22
|
+
form.gsub!(/\A--.*\n/, '')
|
23
|
+
|
24
|
+
# Get the message between the `message line' and the `last line'
|
25
|
+
message = form[/^#{@@message_line}$(.*)^#{@@last_line}$/m, 1]
|
26
|
+
|
27
|
+
# Remove every thing after the `message line'
|
28
|
+
form.gsub!(/^#{@@message_line}$.*\Z/m, '')
|
29
|
+
|
30
|
+
# Handle ChangeLog entries to make it Yaml compliant
|
31
|
+
form.gsub!(/^(ChangeLog:)$/, '\1 |2')
|
32
|
+
form.gsub!(/^\t/, " \t")
|
33
|
+
form.gsub!(/^ {8}/, " \t")
|
34
|
+
|
35
|
+
header = YAML.load(form)
|
36
|
+
|
37
|
+
input = header['Log'] || header['ChangeLog']
|
38
|
+
|
39
|
+
if header['Title'].nil? or header['Title'].blank?
|
31
40
|
raise Failure, "No title found. Reopen `#{Form}' and add it"
|
32
41
|
end
|
33
|
-
header['
|
34
|
-
|
42
|
+
header['Title'] += '.' unless header['Title'] =~ /[.?!]$/
|
43
|
+
rev = '<%= rev %>' # make the revision substitution afterward
|
44
|
+
b = getBinding(header.merge(:rev => rev, :revision => rev))
|
35
45
|
input = ERB.new(input, nil, '<-%->', '$erbout_').result(b)
|
36
46
|
LogEntry.open('w') { |f| f.print input }
|
47
|
+
header['Message'] = message
|
37
48
|
header.each_value do |v|
|
38
49
|
next unless v.is_a? String
|
39
50
|
v.replace(ERB.new(v, nil, '<-%->', '$erbout_').result(b))
|
@@ -44,51 +55,80 @@ class Vcs
|
|
44
55
|
IForm = ',iform'.to_path unless defined? Vcs::IForm
|
45
56
|
|
46
57
|
|
47
|
-
@@subject_format ||= '<%=
|
58
|
+
@@subject_format ||= '<%= Revision %>: <%= Title %>'
|
59
|
+
@@message_line ||= '--text follow this line--'
|
48
60
|
|
49
61
|
def mk_form! ( files=[], options={} )
|
50
62
|
with_cache! Form, 'complete form (title, subject, ChangeLog entry, diff)' do
|
51
63
|
|
52
64
|
puts "
|
53
|
-
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
58
|
-
|<%= title %>
|
59
|
-
|
|
65
|
+
|--You must fill this file correctly to continue--#{' '*13}-*- vcs -*-
|
66
|
+
|Title: \"\"
|
67
|
+
|Subject: #{@@subject_format.dump}
|
68
|
+
|From: \"#{Vcs.full_email}\"
|
69
|
+
|To: [#{options[:to].join(', ')}]
|
60
70
|
|".head_cut!
|
61
71
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
72
|
+
options = just_standard_options options
|
73
|
+
|
74
|
+
case Vcs.user_conf.log_mode.to_sym
|
75
|
+
when :change_log
|
76
|
+
puts "
|
77
|
+
|ChangeLog:
|
78
|
+
|".head_cut!
|
79
|
+
mk_log_entry(files, options).each_line(&method(:log_to_changelog))
|
80
|
+
when :log
|
81
|
+
puts "
|
82
|
+
|Log: |2
|
83
|
+
|".head_cut!
|
84
|
+
mk_log_entry(files, options).each_line { |line| puts " #{line}" }
|
85
|
+
when :yaml_log
|
86
|
+
puts "
|
87
|
+
|YamlLog:
|
88
|
+
|".head_cut!
|
89
|
+
mk_yaml_log_entry!(files, options)
|
90
|
+
end
|
91
|
+
puts "
|
92
|
+
|#{@@message_line}
|
77
93
|
|
|
94
|
+
|#{@@last_line}
|
78
95
|
|".head_cut!
|
79
96
|
|
80
|
-
|
97
|
+
if Vcs.user_conf.new_user
|
98
|
+
puts "|
|
99
|
+
|Instructions:
|
100
|
+
|- Fill the changelog/log entry.
|
101
|
+
|- The first line must be removed when this file is filled.
|
102
|
+
|- After you must specify a title, for the news/mail subject.
|
103
|
+
| The tag <%= Title %> will be automatically replaced by your title,
|
104
|
+
| <%= Subject %> by the subject line, <%= Rev %> by the revision...
|
105
|
+
|- Everywhere in the document you can get/compute some values with
|
106
|
+
| these tags <%= aRubyExpression %> even some vcs specific call.
|
107
|
+
| For example <%= status.read %> will include the 'svn status' output.
|
108
|
+
|- The Title will be automatically added on the top of your ChangeLog
|
109
|
+
| entry. The line with the date and your name will be added too.
|
110
|
+
|
|
111
|
+
|".head_cut!
|
112
|
+
case Vcs.user_conf.log_mode.to_sym
|
113
|
+
when :log
|
114
|
+
puts "- Tabulations and stars ('*') will be added in the ChangeLog before each line."
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
mk_message!(files, options)
|
119
|
+
Message.unlink
|
81
120
|
end
|
82
121
|
end
|
83
122
|
alias_command :mkf, :mk_form
|
84
123
|
Form = ',form'.to_path unless defined? Vcs::Form
|
85
124
|
|
86
125
|
def getBinding ( header )
|
87
|
-
code = []
|
126
|
+
code = ['module GetBinding']
|
88
127
|
header.each do |k, v|
|
89
|
-
code << "#{k} = #{v.inspect}"
|
128
|
+
code << "#{k.to_s.capitalize} = #{v.inspect}"
|
129
|
+
code << "#{k.to_s.downcase} = #{v.inspect}"
|
90
130
|
end
|
91
|
-
code << 'binding'
|
131
|
+
code << 'binding' << 'end'
|
92
132
|
eval(code.join('; '))
|
93
133
|
end
|
94
134
|
protected :getBinding
|
data/lib/vcs/ignore.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
2
2
|
# Copyright:: Copyright (c) 2005 LRDE. All rights reserved.
|
3
3
|
# License:: Gnu General Public License.
|
4
|
-
# Revision:: $Id: ignore.rb
|
4
|
+
# Revision:: $Id: /lrde/tools/trunk/vcs/lib/vcs/ignore.rb 9101 2005-10-02T20:22:46.082320Z pouill_n $
|
5
5
|
|
6
6
|
|
7
7
|
class Svn
|
data/lib/vcs/junk.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
|
2
2
|
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
3
3
|
# License:: Gnu General Public License.
|
4
|
-
# Revision:: $Id: junk.rb
|
4
|
+
# Revision:: $Id: /lrde/tools/trunk/vcs/lib/vcs/junk.rb 9098 2005-09-30T16:43:57.025414Z pouill_n $
|
5
5
|
|
6
6
|
|
7
7
|
class Vcs
|
data/lib/vcs/list.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
2
2
|
# Copyright:: Copyright (c) 2005 LRDE. All rights reserved.
|
3
3
|
# License:: Gnu General Public License.
|
4
|
-
# Revision:: $Id: list.rb
|
4
|
+
# Revision:: $Id: /w/lrde/tools/trunk/vcs/lib/vcs/list.rb 21853 2006-02-18T14:22:27.363424Z pouillar $
|
5
5
|
|
6
6
|
require 'vcs/svn'
|
7
7
|
|
@@ -35,16 +35,16 @@ class Svn
|
|
35
35
|
files << '.' if files.empty?
|
36
36
|
files.each do |file|
|
37
37
|
file.to_path.find do |path|
|
38
|
-
Find.prune if path.to_s =~ /(\/|^)\.svn$/
|
39
38
|
if path.directory?
|
40
|
-
if
|
39
|
+
if path.dirname.to_s == '.svn'
|
40
|
+
elsif (path/'.svn').exist?
|
41
41
|
inside_versioned << path
|
42
42
|
else
|
43
43
|
outside_versioned << path
|
44
|
-
Find.prune
|
45
44
|
end
|
45
|
+
Find.prune
|
46
46
|
else
|
47
|
-
|
47
|
+
inside_versioned << path
|
48
48
|
end
|
49
49
|
end
|
50
50
|
end
|
data/lib/vcs/mail.rb
CHANGED
@@ -10,8 +10,11 @@ class Vcs
|
|
10
10
|
|
11
11
|
MAIL = Sendmail::MAIL_FILE unless defined? MAIL
|
12
12
|
@@mailer ||= Sendmail.new
|
13
|
-
@@default_options ||= {
|
14
|
-
|
13
|
+
@@default_options ||= {
|
14
|
+
:mime => true,
|
15
|
+
:header => { 'X-Mailer' => "Vcs.mail (#{Vcs::Version})" }
|
16
|
+
}
|
17
|
+
cattr_accessor :mail_options
|
15
18
|
|
16
19
|
#
|
17
20
|
# Mail.
|
@@ -19,27 +22,37 @@ class Vcs
|
|
19
22
|
# FIXME handle options properly.
|
20
23
|
# Delegate the option parsing to Sendmail.
|
21
24
|
def mail! ( files=[], options={} )
|
22
|
-
|
23
25
|
# Backward compatiblity
|
24
|
-
files, options = [], files if files.is_a? Hash
|
25
|
-
|
26
|
-
|
26
|
+
files, options = [], files if files.is_a? Hash or files.is_a? OpenStruct
|
27
|
+
options = @@default_options.merge(options)
|
28
|
+
options[:signed] = Vcs.user_conf.sign
|
29
|
+
options = @@mailer.parse_mail_options(options)
|
30
|
+
if editing?
|
31
|
+
Vcs.mail_options = options
|
32
|
+
return
|
33
|
+
end
|
34
|
+
header = YAML.load(IForm.read)
|
35
|
+
%w[ Log ChangeLog YamlLog Message Title ].each { |x| header.delete x }
|
36
|
+
options.to = header['To']
|
37
|
+
from = header['From']
|
38
|
+
options.from = from[/<(.*)>/, 1] || from
|
39
|
+
options.header.merge! header
|
40
|
+
options.header.symbolize_keys!
|
41
|
+
return if header['To'].nil?
|
42
|
+
rev = header['Revision']
|
27
43
|
unless MAIL.exist?
|
28
|
-
options
|
29
|
-
options[:signed] = Vcs.user_conf.sign
|
30
|
-
print_body(MAIL, @@mailer.parse_mail_options(options), files)
|
44
|
+
print_body(MAIL, options, files)
|
31
45
|
end
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
if defined? @@mail and @@mail.exist?
|
38
|
-
logger.info { "#{MAIL}: Contains the generated mail" }
|
39
|
-
logger.info { " AND information to send it (smtpserver, port...)" }
|
46
|
+
if sending?
|
47
|
+
mail = MAIL.read.gsub(/<%= rev %>/, rev.to_s)
|
48
|
+
MAIL.open('w') { |f| f.puts mail }
|
49
|
+
@@mailer.sendmail
|
50
|
+
puts 'Mail: Sent.'
|
40
51
|
end
|
41
52
|
end
|
42
53
|
|
54
|
+
at_exit { MAIL.unlink if defined? MAIL and MAIL.exist? }
|
55
|
+
|
43
56
|
def mail_conf_checker
|
44
57
|
if Vcs.user_conf.sign
|
45
58
|
unless `gpg --version` =~ /^gpg \(GnuPG\)/
|
data/lib/vcs/message.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
# License:: GNU General Public License (GPL).
|
4
4
|
# Revision:: $Id: header 98 2004-09-29 12:07:43Z ertai $
|
5
5
|
|
6
|
+
|
7
|
+
|
6
8
|
class Vcs
|
7
9
|
|
8
10
|
def print_body ( file, options, files=[] )
|
@@ -11,7 +13,6 @@ class Vcs
|
|
11
13
|
f.puts '---'
|
12
14
|
f.puts options.to_yaml.gsub(/---\s*/, '')
|
13
15
|
f.puts '---'
|
14
|
-
f.puts
|
15
16
|
with(f).mk_message!(files)
|
16
17
|
end
|
17
18
|
end
|
@@ -21,23 +22,24 @@ class Vcs
|
|
21
22
|
def mk_message! ( files=[], options={} )
|
22
23
|
with_cache! Message, 'generated message (ChangeLog, diffstat, diff)' do
|
23
24
|
url!
|
25
|
+
if msg = options['Message']
|
26
|
+
puts msg
|
27
|
+
end
|
28
|
+
options = just_standard_options options
|
24
29
|
if defined? COLLABOA
|
25
|
-
puts
|
26
30
|
puts 'You can also view this changeset here:'
|
27
31
|
puts
|
28
|
-
|
29
|
-
next_rev += 1 unless commited?
|
30
|
-
puts "http://#{COLLABOA}/repository/changesets/#{next_rev}"
|
32
|
+
puts "http://#{COLLABOA}/repository/changesets/<%= rev %>"
|
31
33
|
end
|
32
34
|
puts
|
33
35
|
flush
|
34
|
-
mk_message_entry!(files)
|
36
|
+
mk_message_entry!(files, options)
|
35
37
|
puts
|
36
38
|
flush
|
37
|
-
diffstat!(files)
|
39
|
+
diffstat!(files, options)
|
38
40
|
puts
|
39
41
|
flush
|
40
|
-
diffw(files).each_line do |line|
|
42
|
+
diffw(files, options).each_line do |line|
|
41
43
|
print line if line !~ /^=+$/
|
42
44
|
end
|
43
45
|
end
|
@@ -45,5 +47,4 @@ class Vcs
|
|
45
47
|
alias_command :msg, :mk_message
|
46
48
|
Message = ',message'.to_path unless defined? Message
|
47
49
|
|
48
|
-
|
49
50
|
end # class Vcs
|
data/lib/vcs/opt_parse.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
|
2
2
|
# Copyright:: Copyright (c) 2005 LRDE. All rights reserved.
|
3
3
|
# License:: GNU General Public License (GPL).
|
4
|
-
# Revision:: $Id: opt_parse.rb
|
4
|
+
# Revision:: $Id: /w/lrde/tools/trunk/vcs/lib/vcs/opt_parse.rb 22373 2006-02-28T14:38:25.955275Z pouillar $
|
5
5
|
|
6
6
|
|
7
7
|
class Vcs
|
@@ -44,6 +44,7 @@ class Vcs
|
|
44
44
|
o.on('--mk-alias', 'Put the result of this command in your .zshrc or .bashrc') do
|
45
45
|
VcsApp.all_vcs.each do |n|
|
46
46
|
n = n.to_s.downcase
|
47
|
+
next if n == 'prcs'
|
47
48
|
if VcsApp.path.executable?
|
48
49
|
puts "alias #{n}=#{VcsApp.path}-#{n} ;"
|
49
50
|
else
|
@@ -71,7 +72,7 @@ class Vcs
|
|
71
72
|
end
|
72
73
|
|
73
74
|
o.on_tail('--version', 'Show version') do
|
74
|
-
STDOUT.puts "Vcs version: #{Vcs.
|
75
|
+
STDOUT.puts "Vcs version: #{Vcs::Version.major_minor} (#{Vcs::Version.tag.pretty_string})"
|
75
76
|
exit
|
76
77
|
end
|
77
78
|
|