5 # Please note, that this utility must be kept in sync with
6 # CVSROOT/log_accum.pl. If someone has a different output from their
7 # mail client when saving e-mails as text files, feel free to hack it
10 # If someone would like to hack in the ability to generate diffs based
11 # off of this script, by all means, be my guest.
16 $backout_script = "backout-#{Time.now.strftime("%Y-%m-%d-%H-%M")}.sh"
22 $cvs_path = '/usr/bin/cvs'
25 $echo_path = '/usr/bin/echo'
27 $force_script_edit = false
31 $shell_path = '/bin/sh'
34 def debug(level, *msgs)
37 $output.puts "DEBUG(#{level}): #{msgs.shift}"
39 $output.puts msgs.shift
43 $output.puts "\t #{msg}"
49 def usage(msg, info = nil)
50 out = (msg.nil? ? $stdout : $stderr)
51 out.puts "#{File.basename($0)} usage:" << (msg.nil? ? '' : " #{msg}")
52 out.puts "#{info}" unless info.nil?
54 out.puts " -s, --backout-script=<file> Specifies the filename of the script"
55 out.puts " -D, --basedir=<dir> Specifies the base directory [/usr]"
56 out.puts " -a, --commit-author=<uid> Forces a commit author"
57 out.puts " -d, --commit-date=<date> Forces a commit date"
58 out.puts " -m, --commit-file=<path> Specifies a commit message file"
59 out.puts " -M, --commit-message=<msg> Specifies a commit message"
60 out.puts " -c, --cvs-path=<path> Specifies the CVS binary to be used [cvs]"
61 out.puts " -C, --cvsrc-ignore=<bool> If true, will ignore options in ~/.cvsrc"
62 out.puts " -e, --echo-path=<path> Specifies the path to echo"
63 out.puts " -f, --force-remove=<bool> If true, removes new files [false]"
64 out.puts " -F, --force-edit=<bool> If true, add -C to the shell arguments in"
65 out.puts " the backout script if the shell is sh,"
66 out.puts " which forces an edit of the script"
67 out.puts " -O, --output=<stdio> Specifies what fd to direct the output to"
68 out.puts " -A, --shell-args=<string> Specifies the shell arguments to be used"
69 out.puts " -S, --shell-path=<path> Specifies the shell to be used [/bin/sh]"
70 out.puts " -W, --warnings=<bool> Turns on or off warnings [true]"
71 exit(msg.nil? ? 0 : 1)
76 ['--backout-script','-s', GetoptLong::REQUIRED_ARGUMENT],
77 ['--basedir','-D', GetoptLong::REQUIRED_ARGUMENT],
78 ['--commit-author','-a', GetoptLong::REQUIRED_ARGUMENT],
79 ['--commit-date','-d', GetoptLong::REQUIRED_ARGUMENT],
80 ['--commit-file','-m', GetoptLong::REQUIRED_ARGUMENT],
81 ['--commit-message','-M', GetoptLong::REQUIRED_ARGUMENT],
82 ['--cvs-path','-c',GetoptLong::REQUIRED_ARGUMENT],
83 ['--cvsrc-ignore','-C',GetoptLong::REQUIRED_ARGUMENT],
84 ['--echo-path','-e',GetoptLong::REQUIRED_ARGUMENT],
85 ['--force-edit','-F',GetoptLong::REQUIRED_ARGUMENT],
86 ['--force-remove','-f',GetoptLong::REQUIRED_ARGUMENT],
87 ['--output', '-O', GetoptLong::REQUIRED_ARGUMENT],
88 ['--quiet-script','-q',GetoptLong::REQUIRED_ARGUMENT],
89 ['--shell-args','-A',GetoptLong::REQUIRED_ARGUMENT],
90 ['--shell-path','-S',GetoptLong::REQUIRED_ARGUMENT],
91 ['--warnings','-w', GetoptLong::REQUIRED_ARGUMENT],
94 opt_parser = GetoptLong.new(*OPTION_LIST)
95 opt_parser.quiet = true
98 opt_parser.each do |opt,arg|
100 when '--backout-script'
101 debug(3, "backout script was #{$backout_script.inspect} : is #{arg.inspect}")
102 $backout_script = arg
104 debug(3, "base directory was #{$basedir.inspect} : is #{arg.inspect}")
106 when '--commit-author'
107 debug(3, "commit author #{arg.inspect} added to list")
108 $commit_authors.push(arg.dup)
110 debug(3, "commit date #{arg.inspect} added to list")
111 $commit_date.push(arg.dup)
113 debug(3, "commit file was #{$commit_file.inspect} : is #{arg.inspect}")
115 when '--commit-message'
116 debug(3, "commit message was #{$commit_message.inspect} : is #{arg.inspect}")
117 $commit_message = arg
119 debug(3, "cvs path was #{$cvs_path.inspect} : is #{arg.inspect}")
121 when '--cvsrc-ignore'
122 if arg =~ /true|yes/i
124 elsif arg =~ /false|no/i
125 $cvsrc_ignore = false
127 usage("#{opt}: unknown bool format \"#{arg}\"", "Valid options are \"true\", \"false\", \"yes\", or \"no\"")
129 debug(3, "ignoring of ~/.cvsrc is set to #{$cvsrc_ignore.inspect}")
131 debug(3, "echo path was #{$echo_path.inspect} : is #{arg.inspect}")
134 if arg =~ /true|yes/i
135 $force_script_edit = true
136 elsif arg =~ /false|no/i
137 $force_script_edit = false
139 usage("#{opt}: unknown bool format \"#{arg}\"", "Valid options are \"true\", \"false\", \"yes\", or \"no\"")
141 debug(3, "force edit of backout script is set to #{$force_script_edit.inspect}")
142 when '--force-remove'
143 if arg =~ /true|yes/i
145 elsif arg =~ /false|no/i
146 $force_remove = false
148 usage("#{opt}: unknown bool format \"#{arg}\"", "Valid options are \"true\", \"false\", \"yes\", or \"no\"")
150 debug(3, "force removal of files is set to #{$force_remove.inspect}")
158 usage("#{opt}: unknown output format","Valid outputs are \"stdout\" and \"stderr\"")
160 debug(3, "output set to #{arg}")
161 when '--quiet-script'
162 if arg =~ /true|yes/i
164 elsif arg =~ /false|no/i
165 $quiet_script = false
167 usage("#{opt}: unknown bool format \"#{arg}\"", "Valid options are \"true\", \"false\", \"yes\", or \"no\"")
169 debug(3, "quiet script is set to #{$quiet_script.inspect}")
171 debug(3, "shell args were #{$shell_args.inspect} : is #{arg.inspect}")
174 debug(3, "shell path was #{$shell_path.inspect} : is #{arg.inspect}")
177 if arg =~ /true|yes/i
178 $echo_warnings = true
179 elsif arg =~ /false|no/i
180 $echo_warnings = false
182 usage("#{opt}: unknown bool format \"#{arg}\"", "Valid options are \"true\", \"false\", \"yes\", or \"no\"")
184 debug(3, "warnings are set to #{$echo_warnings.inspect}")
187 rescue GetoptLong::InvalidOption
188 usage("invalid argument")
189 rescue GetoptLong::MissingArgument
190 usage("missing argument")
191 rescue GetoptLong::NeedlessArgument => msg
192 usage("passed an extra argument: #{msg}")
195 debug(3, "Verbosity set to: #{$debug}")
198 $cvsbin << " -f" if $cvsrc_ignore
201 usage("require a commit message to parse")
204 $output.puts("Backout directory:\t#{$basedir}")
205 $output.puts("Backout script:\t\t#{$backout_script}")
208 # Backout script - to be run by hand
209 File.open($backout_script, "w+") do |f|
214 f.puts("#!#{$shell_path}#{($force_script_edit && $shell_path == '/bin/sh') ? ' -C' : ''} #{$shell_args}")
216 f.puts("# Generated at: #{Time.now()}")
217 f.puts("# Generated by: #{ENV['USER']}\@#{ENV['HOST']}")
219 f.puts("BASEDIR=#{$basedir}")
220 f.puts('if [ $BASEDIR != $PWD ]; then')
221 f.puts(' echo "Please change to $BASEDIR before running this shell script"')
226 author_regexp = Regexp.new(/^([^\ ]+)\s+([\d]{4})\/([\d]{2})\/([\d]{2}) ([\d]{2}):([\d]{2}):([\d]{2}) ([A-Z]{3})$/)
227 file_regexp = Regexp.new(/^ ([\d\.]+)\s+\+([\d]+) \-([\d]+)\s+(.*?)$/)
228 newdead_regexp = Regexp.new(/^(.*?) \((new|dead)\)$/)
229 rev_regexp = Regexp.new(/^ Revision Changes Path$/)
231 for email_file in ARGV
232 File.open(email_file) do |e|
233 $output.print("Scanning through #{email_file}...")
237 if found_files == false
238 amd = author_regexp.match(line)
240 $commit_authors.push(amd[1].dup)
241 $commit_dates.push(Time.local(*amd[2..7]).dup)
242 elsif rev_regexp.match(line)
245 else # if found_files
246 md = file_regexp.match(line)
250 ndmd = newdead_regexp.match(filename)
254 removals.push(filename)
255 f.puts("#{$force_remove ? '' : '# '}#{$echo_path} -n \"Removing #{filename}...\"") if !$quiet_script
256 f.puts("#{$force_remove ? '' : '# '}#{$cvsbin} rm -f #{filename}")
257 f.puts("#{$force_remove ? '' : '# '}#{$echo_path} \"done.\"") if !$quiet_script
263 f.puts("#{$echo_path} -n \"Updating #{filename} to #{md[1]}...\"") if !$quiet_script
264 f.puts("#{$cvsbin} up -p -r #{md[1]} #{filename} > #{filename}")
265 f.puts("#{$echo_path} \"done.\"") if !$quiet_script
270 $output.puts("done.")
272 end # for email_file in ARGV...
274 if removals.length > 0 && $force_remove == false
275 f.puts("#{$echo_warnings ? '' : '# '}#{$echo_path} \"You may want to remove the following file#{removals.length > 1 ? 's' : ''}:\"")
276 for filename in removals
277 f.puts("#{$echo_warnings ? '' : '# '}#{$echo_path} \"\t#{filename}\"")
280 f.puts("#{$echo_warnings ? '' : '# '}#{$echo_path} \"There is code in #{$backout_script} to remove #{removals.length > 1 ? 'these files' : 'this file'} for you,\"")
281 f.puts("#{$echo_warnings ? '' : '# '}#{$echo_path} \"just uncomment them or pass the option --force-remove=true to #{$0}.\"")
285 f.puts("# # # Uncomment the following line to commit the backout.")
286 f.puts("# # #{$echo_path} -n \"Committing backout...\"") if !$quiet_script
287 if !$commit_message.nil?
288 if $commit_message.empty? or $commit_message =~ /^default|no|yes|true|false/i
289 $commit_message = "Backout of commit by #{$commit_authors.join(', ')} done on #{$commit_dates.join(', ')} because\n[___FILL_IN_THE_BLANK___]\n"
293 f.puts("# # # EDIT COMMIT MESSAGE HERE")
294 f.puts("CVSCOMMITMSG=<<DONTUSECVSMSG")
295 f.puts($commit_message)
296 f.puts('DONTUSECVSMSG')
298 elsif !$commit_file.nil?
299 f.puts("if [ ! -r #{$commit_file} ]; then")
300 f.puts(" #{$echo_path} \"The commit message file #{$commit_file} is not readable,\"")
301 f.puts(" #{$echo_path} \"please fix this and re-run the script.\"")
307 f.print("# # #{$cvsbin} ci")
308 if !$commit_message.nil?
309 f.print(" -m \"$CVSCOMMITMSG\"")
310 elsif !$commit_file.nil?
311 f.print(" -F \"#{$commit_file}\"")
313 f.puts(" #{files.join(' ')}")
316 if $commit_message.nil? and $commit_file.nil?
317 f.print("# # #{$echo_path} \"Commit complete. Backout should be complete. Please check to verify.\"")
319 f.puts("# # #{$echo_path} \"done.\"")
325 $output.puts("Change to #{$basedir} and run this script. Please look through this script and")
326 $output.puts("make changes as necessary. There are commented out commands available")
327 $output.puts("in the script.")
329 if !$commit_message.nil?
330 $output.puts("If you scroll to the bottom of #{$backout_script} you should be able to")
331 $output.puts("find a HERE document with your commit message, if you would like to make")
332 $output.puts("any further changes to your message.")
335 if !$commit_file.nil?
337 stat = File.stat($commit_file)
339 $output.puts("The output file specified, \"#{$commit_file}\" DOES NOT EXIST!!! Please be sure to")
340 $output.puts("create/edit the file \"#{$commit_file}\" before you run this script")
344 $output.puts("Example script usage:")
345 $output.puts("\tmv #{$backout_script} #{$basedir}")
346 $output.puts("\tcd #{$basedir}")
347 $output.puts("\tless #{$backout_script}")
348 $output.puts("\t#{$shell_path} #{$backout_script}")
349 $output.puts("\trm -f #{$backout_script}")