pvn 0.0.1

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 (99) hide show
  1. data/README.markdown +38 -0
  2. data/bin/pvn +12 -0
  3. data/bin/pvndiff +10 -0
  4. data/lib/pvn/app.rb +144 -0
  5. data/lib/pvn/base/textlines.rb +35 -0
  6. data/lib/pvn/base/util.rb +14 -0
  7. data/lib/pvn/cmddoc.rb +77 -0
  8. data/lib/pvn/config.rb +65 -0
  9. data/lib/pvn/describe.rb +40 -0
  10. data/lib/pvn/diff/diffcmd.rb +49 -0
  11. data/lib/pvn/diff/differ.rb +67 -0
  12. data/lib/pvn/diff/diffopts.rb +58 -0
  13. data/lib/pvn/doc.rb +34 -0
  14. data/lib/pvn/io/element.rb +113 -0
  15. data/lib/pvn/io/fselement.rb +22 -0
  16. data/lib/pvn/log/formatter/color_formatter.rb +29 -0
  17. data/lib/pvn/log/formatter/entries_formatter.rb +33 -0
  18. data/lib/pvn/log/formatter/entry_formatter.rb +57 -0
  19. data/lib/pvn/log/formatter/log_formatter.rb +59 -0
  20. data/lib/pvn/log/formatter/message_formatter.rb +19 -0
  21. data/lib/pvn/log/formatter/path_formatter.rb +41 -0
  22. data/lib/pvn/log/formatter/summary_formatter.rb +49 -0
  23. data/lib/pvn/log/logcmd.rb +136 -0
  24. data/lib/pvn/log/logentry.rb +116 -0
  25. data/lib/pvn/log/logfactory.rb +101 -0
  26. data/lib/pvn/log/logoptions.rb +43 -0
  27. data/lib/pvn/pct/linecount.rb +33 -0
  28. data/lib/pvn/pct/pctcmd.rb +204 -0
  29. data/lib/pvn/pct/statcmd.rb +13 -0
  30. data/lib/pvn/revision/entry.rb +94 -0
  31. data/lib/pvn/revision.rb +119 -0
  32. data/lib/pvn/subcommands/base/clargs.rb +53 -0
  33. data/lib/pvn/subcommands/base/command.rb +60 -0
  34. data/lib/pvn/subcommands/base/doc.rb +94 -0
  35. data/lib/pvn/subcommands/base/options.rb +22 -0
  36. data/lib/pvn/subcommands/log/command.rb +70 -0
  37. data/lib/pvn/subcommands/log/options.rb +43 -0
  38. data/lib/pvn/subcommands/pct/clargs.rb +33 -0
  39. data/lib/pvn/subcommands/pct/command.rb +57 -0
  40. data/lib/pvn/subcommands/revision/multiple_revisions_option.rb +43 -0
  41. data/lib/pvn/subcommands/revision/revision_option.rb +74 -0
  42. data/lib/pvn/subcommands/revision/revision_regexp_option.rb +39 -0
  43. data/lib/pvn/svn/command/svncmd.rb +39 -0
  44. data/lib/pvn/svn/environment.rb +23 -0
  45. data/lib/pvn/svn/svnelement.rb +89 -0
  46. data/lib/pvn/svn/svninfo.rb +42 -0
  47. data/lib/pvn/svn/svnroot.rb +29 -0
  48. data/lib/pvn/upp/uppcmd.rb +112 -0
  49. data/lib/pvn/wherecmd.rb +55 -0
  50. data/lib/pvn.rb +8 -0
  51. data/lib/svnx/command.rb +60 -0
  52. data/lib/svnx/entry.rb +34 -0
  53. data/lib/svnx/info/command.rb +21 -0
  54. data/lib/svnx/info/entries.rb +27 -0
  55. data/lib/svnx/info/entry.rb +34 -0
  56. data/lib/svnx/log/command.rb +93 -0
  57. data/lib/svnx/log/entries.rb +57 -0
  58. data/lib/svnx/log/entry.rb +54 -0
  59. data/lib/svnx/status/command.rb +21 -0
  60. data/lib/svnx/status/entries.rb +22 -0
  61. data/lib/svnx/status/entry.rb +33 -0
  62. data/lib/synoption/base_option.rb +149 -0
  63. data/lib/synoption/boolean_option.rb +21 -0
  64. data/lib/synoption/doc.rb +81 -0
  65. data/lib/synoption/fixnum_option.rb +13 -0
  66. data/lib/synoption/match.rb +47 -0
  67. data/lib/synoption/option.rb +10 -0
  68. data/lib/synoption/optionable.rb +66 -0
  69. data/lib/synoption/set.rb +114 -0
  70. data/lib/system/cachecmd.rb +65 -0
  71. data/lib/system/cmdexec.rb +13 -0
  72. data/lib/system/cmdline.rb +70 -0
  73. data/lib/system/command/arg.rb +12 -0
  74. data/lib/system/command/cachefile.rb +39 -0
  75. data/lib/system/command/caching.rb +36 -0
  76. data/lib/system/command/line.rb +47 -0
  77. data/lib/system/command.rb +72 -0
  78. data/test/integration/svnx/log/test.rb +43 -0
  79. data/test/unit/pvn/app_test.rb +22 -0
  80. data/test/unit/pvn/io/element/log/log_test.rb +35 -0
  81. data/test/unit/pvn/log/formatter/entry_formatter_test.rb +90 -0
  82. data/test/unit/pvn/revision/entry_test.rb +123 -0
  83. data/test/unit/pvn/subcommands/log/command_test.rb +22 -0
  84. data/test/unit/pvn/subcommands/log/options_test.rb +85 -0
  85. data/test/unit/pvn/subcommands/revision/multiple_revisions_option_test.rb +67 -0
  86. data/test/unit/pvn/subcommands/revision/revision_option_test.rb +57 -0
  87. data/test/unit/pvn/subcommands/revision/revision_regexp_option_test.rb +69 -0
  88. data/test/unit/svnx/info/entry_test.rb +52 -0
  89. data/test/unit/svnx/log/cmargs_test.rb +22 -0
  90. data/test/unit/svnx/log/entries_test.rb +73 -0
  91. data/test/unit/svnx/log/entry_test.rb +21 -0
  92. data/test/unit/svnx/status/entry_test.rb +39 -0
  93. data/test/unit/synoption/base_option_test.rb +182 -0
  94. data/test/unit/synoption/match_test.rb +61 -0
  95. data/test/unit/synoption/option_test.rb +15 -0
  96. data/test/unit/synoption/set_test.rb +37 -0
  97. data/test/unit/system/command/caching_test.rb +97 -0
  98. data/test/unit/system/command/line_test.rb +33 -0
  99. metadata +199 -0
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ require 'rubygems'
5
+ require 'riel'
6
+ require 'pvn/log/logentry'
7
+ require 'pvn/base/textlines'
8
+
9
+ module PVN; module Log; end; end
10
+
11
+ module PVN;;Log
12
+ SVN_LOG_SUMMARY_LINE_RE = Regexp.new('^r(\d+) \| (\S+) \| (\S+) (\S+) (\S+) \((.*)\) \| (\d+) lines?$')
13
+ SVN_LOG_SEPARATOR_LINE_RE = Regexp.new('^-{72}$')
14
+ SVN_LOG_VERBOSE_START_RE = Regexp.new('^Changed paths:$')
15
+ SVN_LOG_FILE_NAME_RE = Regexp.new('^ (\w) (.*)$')
16
+
17
+ class TextFactory
18
+ include Loggable
19
+
20
+ def initialize lines
21
+ @textlines = TextLines.new lines
22
+ @entries = nil
23
+ end
24
+
25
+ def entries
26
+ @entries ||= read_entries
27
+ end
28
+
29
+ def read_entries
30
+ entries = Array.new
31
+ while entry = create_next_entry
32
+ entries << entry
33
+ end
34
+ entries
35
+ end
36
+
37
+ def match_line re, offset = 0
38
+ @textlines.match_line re, offset
39
+ end
40
+
41
+ def advance_line nlines = 1
42
+ @textlines.advance_line nlines
43
+ end
44
+
45
+ def match_log_start_line
46
+ match_line(SVN_LOG_SEPARATOR_LINE_RE) && match_line(SVN_LOG_SUMMARY_LINE_RE, 1)
47
+ end
48
+
49
+ def has_line?
50
+ @textlines.has_line?
51
+ end
52
+
53
+ def current_line
54
+ @textlines.line
55
+ end
56
+
57
+ def read_comment
58
+ comment = Array.new
59
+ while has_line? && !match_line(SVN_LOG_SEPARATOR_LINE_RE)
60
+ comment << current_line.chomp
61
+ advance_line
62
+ end
63
+ comment
64
+ end
65
+
66
+ def read_files
67
+ advance_line
68
+ files = Array.new
69
+ while (ln = current_line) && !ln.strip.empty?
70
+ md = match_line(SVN_LOG_FILE_NAME_RE)
71
+ changetype = md[1]
72
+ fname = md[2]
73
+ files << fname
74
+ advance_line
75
+ end
76
+ files
77
+ end
78
+
79
+ def create_entry fielddata
80
+ advance_line 2
81
+ fields = Hash[Entry::FIELDS.zip fielddata[1 .. -1]]
82
+ if match_line SVN_LOG_VERBOSE_START_RE
83
+ fields[:files] = read_files
84
+ end
85
+ advance_line
86
+ fields[:comment] = read_comment
87
+ return Entry.new fields
88
+ end
89
+
90
+ def create_next_entry
91
+ while has_line?
92
+ if fielddata = match_log_start_line
93
+ return create_entry fielddata
94
+ else
95
+ advance_line
96
+ end
97
+ end
98
+ nil
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ require 'synoption/set'
5
+ require 'synoption/option'
6
+ require 'synoption/boolean_option'
7
+ require 'pvn/subcommands/revision/revision_regexp_option'
8
+
9
+ module PVN
10
+ DEFAULT_LIMIT = 5
11
+
12
+ class LimitOption < Option
13
+ def initialize lmtargs = Hash.new
14
+ super :limit, '-l', "the number of log entries", DEFAULT_LIMIT, :negate => [ %r{^--no-?limit} ]
15
+ end
16
+ end
17
+
18
+ class VerboseOption < BooleanOption
19
+ def initialize optargs = Hash.new
20
+ super :verbose, '-v', [ "include the files in the change" ], false
21
+ end
22
+ end
23
+
24
+ class FormatOption < BooleanOption
25
+ def initialize optargs = Hash.new
26
+ super :format, '-f', "use the custom (colorized) format", true, :negate => [ '-F', %r{^--no-?format} ], :as_cmdline_option => nil
27
+ end
28
+ end
29
+
30
+ class LogOptionSet < OptionSet
31
+ attr_accessor :revision
32
+ attr_reader :format
33
+
34
+ def initialize
35
+ super
36
+
37
+ add LimitOption.new
38
+ @revision = add RevisionRegexpOption.new(:unsets => :limit)
39
+ @verbose = add VerboseOption.new
40
+ @format = add FormatOption.new
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ module PVN
5
+ class LineCount
6
+ attr_accessor :to
7
+ attr_accessor :from
8
+ attr_accessor :name
9
+
10
+ def initialize args = Hash.new
11
+ @name = args[:name] || ""
12
+ @to = args[:to] || 0
13
+ @from = args[:from] || 0
14
+ end
15
+
16
+ def +(other)
17
+ # the new wc has the name of the lhs of the add:
18
+ self.class.new :to => @to + other.to, :from => @from + other.from, :name => @name
19
+ end
20
+
21
+ def diff
22
+ @to - @from
23
+ end
24
+
25
+ def diffpct
26
+ @from.zero? ? "0" : 100.0 * diff / @from
27
+ end
28
+
29
+ def write
30
+ printf "%8d %8d %8d %8.1f%% %s\n", @from, @to, diff, diffpct, @name
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ require 'system/command'
5
+ require 'pvn/pct/linecount'
6
+ require 'pvn/io/fselement'
7
+ require 'pvn/svn/svnelement'
8
+ require 'pvn/svn/svnroot'
9
+ require 'pvn/io/element'
10
+
11
+ module PVN
12
+ class PctOptionSet < OptionSet
13
+ attr_accessor :revision
14
+
15
+ def initialize
16
+ super
17
+ @revision = add RevisionRegexpOption.new
18
+ end
19
+ end
20
+
21
+ class PctCommand < Command
22
+ DEFAULT_LIMIT = 5
23
+ COMMAND = "pct"
24
+
25
+ self.doc do |doc|
26
+ doc.subcommands = [ COMMAND ]
27
+ doc.description = "Print the comparison of size of locally-changed files."
28
+ doc.usage = "[OPTIONS] FILE..."
29
+ doc.summary = [ "Prints the changes in the size (length) of files that have ",
30
+ "been changed locally, to show the extent to which they have",
31
+ "increased or decreased. The columns are:",
32
+ " - length in svn repository",
33
+ " - length in local version",
34
+ " - difference",
35
+ " - percentage change",
36
+ " - file name",
37
+ "When more than one file is specified, the total is displayed",
38
+ "as the last line." ]
39
+ doc.examples << [ "pvn pct main.c", "Prints the number of lines and percentage by which main.c has changed." ]
40
+ end
41
+
42
+ def initialize args
43
+ # not calling super, since all of our stuff goes on in this ctor.
44
+
45
+ info "args: #{args}"
46
+
47
+ @options = PctOptionSet.new
48
+
49
+ @options.process self, args, args[:command_args]
50
+ info "args: #{args}"
51
+
52
+ files = get_files @options.arguments
53
+ info "files: #{files.inspect}"
54
+
55
+ rev = @options.revision.value
56
+ info "rev: #{rev}"
57
+
58
+ @output = Array.new
59
+
60
+ totallc = LineCount.new :name => "total"
61
+
62
+ files.sort.each do |file|
63
+ info "file: #{file}"
64
+
65
+ fromlc, tolc = if rev
66
+ [ file.svn.line_count(rev - 1), file.svn.line_count(rev) ]
67
+ else
68
+ [ file.svn.line_count(rev), file.local.line_count ]
69
+ end
70
+
71
+ info "fromlc: #{fromlc}"
72
+ info "tolc: #{tolc}"
73
+
74
+ filelc = LineCount.new :from => fromlc, :to => tolc, :name => file.local.name
75
+ filelc.write
76
+
77
+ totallc += filelc
78
+ info "totallc: #{totallc.inspect}"
79
+ end
80
+
81
+ if files.size > 1
82
+ totallc.write
83
+ end
84
+ end
85
+
86
+ def to_command subcmd, *args
87
+ cmd = "svn #{subcmd}"
88
+ info "cmd: #{cmd}"
89
+ info "args: #{args}"
90
+ args = args.flatten
91
+
92
+ revcl = @options.revision.to_command_line
93
+ if revcl
94
+ cmd << " " << revcl.join(" ")
95
+ end
96
+ cmd << " " << Util::quote_list(args)
97
+ info "cmd: #{cmd}".on_blue
98
+ cmd
99
+ end
100
+
101
+ def get_changed_files filespec
102
+ rev = @options.revision.value
103
+ info "rev: #{rev}"
104
+
105
+ if rev
106
+ get_changed_files_revision filespec
107
+ else
108
+ get_changed_files_wc filespec
109
+ end
110
+ end
111
+
112
+ def svn_fullname_to_local_file svnname
113
+ svnelement = SVNElement.new :name => '.'
114
+ info "svnelement.info: #{svnelement.info}"
115
+
116
+ fullurl = svnelement.info[:repository_root] + svnname
117
+ info "fullurl : #{fullurl}".yellow
118
+
119
+ svnroot = SVNRootElement.new
120
+ info "svnroot : #{svnroot}".yellow
121
+
122
+ info "svnroot.info : #{svnroot.info}".yellow
123
+
124
+ here = SVNElement.new :name => '.'
125
+
126
+ hereinfo = here.info
127
+ info "hereinfo[:url] : #{hereinfo[:url]}".yellow
128
+ reporoot = hereinfo[:repository_root]
129
+
130
+ info "reporoot : #{reporoot}".yellow
131
+
132
+ info "svnroot[:url] : #{svnroot.info[:url]}".yellow
133
+
134
+ rootpath = svnroot.name
135
+ rooturl = svnroot.info[:url]
136
+
137
+ info "rootpath : #{rootpath}".yellow
138
+ info "rooturl : #{rooturl}".yellow
139
+
140
+ if fullurl.index(rooturl) == 0
141
+ localname = fullurl[rooturl.length .. -1]
142
+ info "localname : #{localname}".yellow
143
+ localname
144
+ else
145
+ info "fullurl : #{fullurl}".on_red
146
+ nil
147
+ end
148
+
149
+ end
150
+
151
+ def get_changed_files_revision filespec
152
+ lc = LogCommand.new :revision => @options.revision.value, :verbose => true
153
+ info "lc: #{lc}"
154
+ entry = lc.entries[0]
155
+ return Array.new if entry.nil?
156
+
157
+ # entry.write true
158
+
159
+ files = entry.files.collect do |svnfile|
160
+ localfile = svn_fullname_to_local_file svnfile
161
+ Element.new :file => localfile
162
+ end
163
+ info "files: #{files}".yellow
164
+ files
165
+ end
166
+
167
+ def get_changed_files_wc filespec
168
+ files = Array.new
169
+ cmd = to_command "st", filespec
170
+ IO.popen cmd do |io|
171
+ io.each do |line|
172
+ info "line: #{line}".yellow
173
+ next if line.index %r{^\?}
174
+ pn = Element.new :file => line.chomp[8 .. -1]
175
+ files << pn if pn.local.file?
176
+ end
177
+ end
178
+ info "files: #{files}".red
179
+ files
180
+ end
181
+
182
+ def get_files fileargs
183
+ info "fileargs: #{fileargs}".yellow
184
+
185
+ files = Array.new
186
+ if fileargs.empty?
187
+ files = get_changed_files fileargs
188
+ else
189
+ fileargs.collect do |fn|
190
+ elmt = Element.new :file => fn
191
+ if elmt.local.directory?
192
+ files.concat get_changed_files(fn)
193
+ else
194
+ files << elmt
195
+ end
196
+ end
197
+ end
198
+
199
+ info "files: #{files}".yellow
200
+
201
+ files
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ module PVN
5
+ class StatOptionSet < OptionSet
6
+ attr_accessor :revision
7
+
8
+ def initialize
9
+ super
10
+ @revision = add RevisionRegexpOption.new
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ require 'svnx/log/entries'
5
+
6
+ module PVN::Revision
7
+ DATE_REGEXP = Regexp.new '^\{(.*?)\}'
8
+ SVN_REVISION_WORDS = %w{ HEAD BASE COMMITTED PREV }
9
+ RELATIVE_REVISION_RE = Regexp.new '^([\+\-])(\d+)$'
10
+
11
+ # Returns the Nth revision from the given logging output.
12
+
13
+ # -n means to count from the end of the list.
14
+ # +n means to count from the beginning of the list.
15
+ # n means the literal revision number.
16
+ class Entry
17
+ include Loggable
18
+
19
+ attr_reader :value
20
+ attr_reader :log_entry
21
+
22
+ class << self
23
+ alias_method :orig_new, :new
24
+
25
+ def new args = Hash.new
26
+ value = args[:value]
27
+
28
+ # these are lines from "svn log -v <file>"
29
+ xmllines = args[:xmllines]
30
+ if xmllines.kind_of? Array
31
+ xmllines = xmllines.join ''
32
+ end
33
+
34
+ case value
35
+ when Fixnum
36
+ if value < 0
37
+ RelativeEntry.orig_new value, xmllines
38
+ else
39
+ FixnumEntry.orig_new value
40
+ end
41
+ when String
42
+ if SVN_REVISION_WORDS.include? value
43
+ StringEntry.orig_new value
44
+ elsif md = RELATIVE_REVISION_RE.match(value)
45
+ RelativeEntry.orig_new md[0].to_i, xmllines
46
+ elsif DATE_REGEXP.match value
47
+ StringEntry.orig_new value
48
+ else
49
+ FixnumEntry.orig_new value.to_i
50
+ end
51
+ when Date
52
+ # $$$ this (and Time) will probably have to be converted to svn's format
53
+ raise "date not yet handled"
54
+ when Time
55
+ raise "time not yet handled"
56
+ end
57
+ end
58
+
59
+ def matches_relative? str
60
+ RELATIVE_REVISION_RE.match str
61
+ end
62
+ end
63
+
64
+ def initialize value
65
+ @value = value
66
+ end
67
+ end
68
+
69
+ class FixnumEntry < Entry
70
+ end
71
+
72
+ class StringEntry < Entry
73
+ end
74
+
75
+ class RelativeEntry < FixnumEntry
76
+ def initialize value, xmllines
77
+ raise "cannot determine relative revision without xmllines" unless xmllines
78
+
79
+ logentries = SVNx::Log::Entries.new :xmllines => xmllines
80
+
81
+ nentries = logentries.size
82
+
83
+ # logentries are in descending order, so the most recent one is index 0
84
+
85
+ if value.abs > nentries
86
+ raise "ERROR: no entry for revision: #{value.abs}; number of entries: #{nentries}"
87
+ else
88
+ idx = value < 0 ? -1 + value.abs : nentries - value
89
+ @log_entry = logentries[idx]
90
+ super @log_entry.revision.to_i
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ require 'rubygems'
5
+ require 'riel'
6
+ require 'pvn/log/logcmd'
7
+
8
+ module PVN
9
+ # Returns the Nth revision from the given logging output.
10
+
11
+ # -n means to count from the end of the list.
12
+ # +n means to count from the beginning of the list.
13
+ # n means the literal revision number.
14
+ class Revision
15
+ include Loggable
16
+
17
+ DATE_REGEXP = Regexp.new('^\{(.*?)\}')
18
+ SVN_REVISION_WORDS = %w{ HEAD BASE COMMITTED PREV }
19
+
20
+ def self.revision_from_args optset, optargs
21
+ revarg = optargs.shift
22
+ RIEL::Log.debug "revarg: #{revarg}"
23
+ RIEL::Log.debug "optargs: #{optargs}"
24
+
25
+ rev = Revision.new(:fname => optset.arguments[0], :value => revarg, :use_cache => false).revision
26
+ RIEL::Log.debug "rev: #{rev}"
27
+
28
+ if rev.nil?
29
+ raise ArgumentError.new "invalid revision: #{revarg} on #{optargs[-1]}"
30
+ end
31
+ rev
32
+ end
33
+
34
+ attr_reader :revision
35
+
36
+ def initialize args = Hash.new
37
+ debug "args : #{args}".yellow
38
+ value = args[:value]
39
+ num = value.kind_of?(String) ? value.strip : value
40
+ debug "num : #{num}".yellow
41
+ @fname = args[:fname]
42
+ debug "fname : #{@fname}".yellow
43
+
44
+ @revision = nil
45
+
46
+ from, to = num.kind_of?(String) ? num.split(':') : num
47
+ info "from: #{from}; to: #{to}".on_blue
48
+ info "from: #{from.class}; to: #{to.class}".on_blue
49
+
50
+ @revision = convert_to_revision from
51
+ if to
52
+ @revision += ':' + convert_to_revision(to)
53
+ end
54
+
55
+ info "@revision: #{@revision}; #{@revision.class}"
56
+ end
57
+
58
+ def to_s
59
+ @revision
60
+ end
61
+
62
+ def convert_to_revision arg
63
+ if SVN_REVISION_WORDS.include? arg
64
+ info "word: #{arg}"
65
+ arg
66
+ elsif neg = Integer.negative?(arg)
67
+ info "neg: #{arg}"
68
+ get_negative_revision neg
69
+ elsif arg.kind_of? Fixnum
70
+ info "fixnum: #{arg}"
71
+ arg.to_s
72
+ elsif md = DATE_REGEXP.match(arg)
73
+ info "md: #{md.inspect}".on_red
74
+ md[0]
75
+ elsif md = %r{^\+(\d+)}.match(arg)
76
+ arg = md[1].to_i
77
+ debug "arg: #{arg}"
78
+ get_revision @fname, nil, arg
79
+ else
80
+ arg.to_s
81
+ end
82
+ end
83
+
84
+ def get_negative_revision neg
85
+ debug "neg : #{neg}"
86
+
87
+ # if these two are the same number (revision(-3) == revision(-2)) then
88
+ # we're at the end/beginning of the list.
89
+ currrev = get_negative_revision_unchecked neg
90
+ if neg == -1
91
+ debug "currrev: #{currrev}"
92
+ @revision = currrev
93
+ else
94
+ debug "neg : #{neg}"
95
+ prevrev = get_negative_revision_unchecked neg + 1
96
+
97
+ debug "prevrev: #{prevrev}"
98
+ debug "currrev: #{currrev}"
99
+
100
+ # $$$ does -r 123 -l 2 == -r 123 -l 3?
101
+ @revision = prevrev == currrev ? nil : currrev
102
+ # @revision = prevrev == currrev ? currrev : currrev
103
+ end
104
+ end
105
+
106
+ def get_negative_revision_unchecked neg
107
+ # count the limit backward, and get the "first" (last) match
108
+ limit = -1 * neg
109
+ get_revision @fname, limit, 1
110
+ end
111
+
112
+ def get_revision fname, limit, num
113
+ logcmd = LogCommand.new :limit => limit, :command_args => [ fname ]
114
+ entries = logcmd.entries
115
+ entry = entries[-1 * num]
116
+ entry && entry.revision.to_i
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/ruby -w
2
+ # -*- ruby -*-
3
+
4
+ module PVN; module App; end; end
5
+
6
+ module PVN::App::Base
7
+ class CmdLineArgs
8
+ include Loggable
9
+
10
+ class << self
11
+ def has_option name
12
+ define_method name do
13
+ info "name: #{name}"
14
+ self.instance_eval do
15
+ meth = name
16
+ info "meth: #{meth}"
17
+ val = @optset.send name
18
+ info "val: #{val}"
19
+ val
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ attr_reader :unprocessed
26
+
27
+ def initialize optset, args
28
+ @optset = optset
29
+ process args
30
+ @unprocessed = args
31
+ end
32
+
33
+ def process args
34
+ options_processed = Array.new
35
+
36
+ while !args.empty?
37
+ processed = false
38
+ @optset.options.each do |opt|
39
+ if opt.process args
40
+ processed = true
41
+ options_processed << opt
42
+ end
43
+ end
44
+
45
+ break unless processed
46
+ end
47
+
48
+ options_processed.each do |opt|
49
+ opt.post_process @optset, args
50
+ end
51
+ end
52
+ end
53
+ end