* Optimize memory usage by discarding unneeded text
"""
-import sys
+import sys, os, stat
import pprint # Functional equivalent of Ruby pp?
def usage():
-
print >>sys.stderr, """\
rcs-fast-export [options] file [file ...]
self.msg = msg
class RCS:
+ RCS.marks = {}
+
@staticmethod
def clean(arg):
- # strip an optional final ;
+ "Strip an optional final ;"
if arg.endswith(";"):
arg = arg[:-1]
return arg
@staticmethod
def sanitize(arg):
- # strip the first and last @, and de-double @@s
+ "Strip the first and last @, and de-double @@s"
if type(arg) == type([]):
if not arg[0].startswith("@@"):
raise FatalException('malformed first line')
@staticmethod
def at_clean(arg):
- # clean and sanitize
+ "Clean and sanitize."
return RCS.sanitize(RCS.clean(arg))
- def RCS.mark(key)
- @@marks ||= {}
- if @@marks.key? key
- @@marks[key]
- else
- @@marks[key] = @@marks.length + 1
- end
- end
-
- def RCS.blob(file, rev)
- RCS.mark([file, rev])
- end
-
- def RCS.commit(commit)
- RCS.mark(commit)
- end
+ @staticmethod
+ def mark(key):
+ "Return a sequence mark for an object, which may be a blob or a commit."
+ if not key in RCS.marks:
+ RCS.marks[key] = len(RCS.marks)+1
+ return RCS.marks[key]
- class File
- attr_accessor :head, :comment, :desc, :revision, :fname, :mode
- def initialize(fname, executable)
- @fname = fname.dup
- @head = nil
- @comment = nil
- @desc = []
- @revision = Hash.new { |h, r| h[r] = Revision.new(self, r) }
- @mode = executable ? '755' : '644'
- end
+ @staticmethod
+ def blob(file, rev):
+ return RCS.mark([file, rev])
- def has_revision?(rev)
- @revision.has_key?(rev) and not @revision[rev].author.nil?
- end
+ @staticmethod
+ def commit(commit):
+ return RCS.mark(commit)
+
+ class File:
+ def __init__(master):
+ self.fname = master
+ self.head = None
+ self.comment = None
+ self.desc = []
+ self.revision = {} #Hash.new { |h, r| h[r] = Revision.new(self, r) }
+ executable = stat.S_IXUSER(os.stat(master)[stat.ST_MODE])
+ self.mode = '755' if executable else '644'
+
+ def has_revision(rev):
+ return rev in revision and self.revision[rev].author is not None
+
+ def export_commits(opts):
+ counter = 0
+ exported = []
+ while self.revision:
+ counter += 1
+
+ # a string sort is a very good candidate for
+ # export order, getting a miss only for
+ # multi-digit revision components
+ keys = self.revision.keys()
+ keys.sort()
+
+ print >>sys.stderr,"commit export loop %d" % counter
+ if exported:
+ print >>sys.stderr,"\t#%d commits exported so far: %s" % ", ".join(exported)
+ print >>sys.stderr,"\t#%d to export: %s" % (len(keys), ",".join(keys))
+ for key in keys:
+ rev = self.revision[key]
+ # the parent commit is rev.next if we're on the
+ # master branch (rev.branch is None) or
+ # rev.diff_base otherwise
+ if rev.branch is None:
+ parent = rev.next
+ else:
+ parent = rev.diff_base
+ # A commit can only be exported if it has no
+ # parent, or if the parent has been exported
+ # already. Skip this commit otherwise
+ if parent and parent not in exported:
+ continue
+
+ branch = rev.branch or 'master'
+ author = opts.authors[rev.author] or (rev.author + " <empty>")
+ date = `rev.date.tv_sec` + " +0000"
+ log = ""
+ if opts.log_filename:
+ log += self.fname + ": "
+ log += rev.log.to_s
+
+ print "commit refs/heads/" + branch
+ print "mark :" + RCS.commit(key)
+ print "committer %s %s" % (author, date)
+ print "data %d" % len(log)
+ if log:
+ print log
+ if rev.branch_point:
+ print "from :" + RCS.commit(parent)
+ print "M %s :%s %s" % (RCS.blob(self.fname, key), fname)
+ # TODO FIXME this *should* be safe, in
+ # that it should not unduly move
+ # branches back in time, but I'm not
+ # 100% sure ...
+ for sym in rev.branches:
+ print "reset refs/heads/" + sym
+ print "from :" + RCS.commit(key)
+ for sym in rev.symbols:
+ print "reset refs/tags/" + sym
+ print "from :" + RCS.commit(key)
+ if opts.tag_each_rev:
+ print "reset refs/tags/" + key
+ print "from :" + RCS.commit(key)
+ exported.append(key)
+ for k in exported: del self.revision[k]
+
+ class Revision:
+ def __init__(fileobj, rev):
+ self.file = fileobj
+ self.rev = rev
+ self.author = None
+ self.date = None
+ self.state = None
+ self.next = None
+ self.branches = []
+ self.branch = None
+ self.branch_point = None
+ self.diff_base = None
+ self.log = []
+ self.text = []
+ self.symbols = set([])
+ def __hash__(self):
+ return hash(file, rev)
+ #def date=(str)
+ # @date = Time.rcs(str)
+ #end
+ def blob(self):
+ str = "".join(self.text)
+ return "blob\nmark :#%s\ndata %d\n%s\n" \
+ (RCS.blob(self.file.fname, self.rev), len(str), str)
- def export_commits(opts={})
- counter = 0
- exported = []
- until @revision.empty?
- counter += 1
-
- # a string sort is a very good candidate for
- # export order, getting a miss only for
- # multi-digit revision components
- keys = @revision.keys.sort
-
- STDERR.puts "commit export loop ##{counter}"
- STDERR.puts "\t#{exported.length} commits exported so far: #{exported.join(', ')}" unless exported.empty?
- STDERR.puts "\t#{keys.size} to export: #{keys.join(', ')}"
-
- keys.each do |key|
- rev = @revision[key]
- # the parent commit is rev.next if we're on the
- # master branch (rev.branch is nil) or
- # rev.diff_base otherwise
- from = rev.branch.nil? ? rev.next : rev.diff_base
- # A commit can only be exported if it has no
- # parent, or if the parent has been exported
- # already. Skip this commit otherwise
- if from and not exported.include? from
- next
- end
-
- branch = rev.branch || 'master'
- author = opts[:authors][rev.author] || "#{rev.author} <empty>"
- date = "#{rev.date.tv_sec} +0000"
- log = String.new
- if opts[:log_filename]
- log << @fname << ": "
- end
- log << rev.log.to_s
-
- puts "commit refs/heads/#{branch}"
- puts "mark :#{RCS.commit key}"
- puts "committer #{author} #{date}"
- puts "data #{log.length}"
- puts log unless log.empty?
- puts "from :#{RCS.commit from}" if rev.branch_point
- puts "M #{@mode} :#{RCS.blob @fname, key} #{@fname}"
-
- # TODO FIXME this *should* be safe, in
- # that it should not unduly move
- # branches back in time, but I'm not
- # 100% sure ...
- rev.branches.each do |sym|
- puts "reset refs/heads/#{sym}"
- puts "from :#{RCS.commit key}"
- end
- rev.symbols.each do |sym|
- puts "reset refs/tags/#{sym}"
- puts "from :#{RCS.commit key}"
- end
- if opts[:tag_each_rev]
- puts "reset refs/tags/#{key}"
- puts "from :#{RCS.commit key}"
- end
-
- exported.push key
- end
- exported.each { |k| @revision.delete(k) }
+ @staticmethod
+ def parse(rcsfile):
+ rcs = RCS.File(rcsfile)
+ status = ["basic"]
+ rev = None
+ lines = []
+ difflines = []
+ linebuffer = []
+ with file(rcs.fname) as fp:
+ while True:
+ if linebuffer:
+ line = linebuffer.pop()
+ else:
+ line = fp.readline()
+ if not line:
+ break
+ if status[-1] == "basic":
+ m = re.match(r"^([a-z]+)\s+([^;]*);?", line)
+ if not m:
+ raise FatalException("failed to find command")
+ command = m.group(1)
+ args = m.group(2)
+ if not command:
+ if debug:
+ print >>sys.stderr,"Skipping empty command"
+ continue
+ if command == 'head':
+ rcs.head = RCS.clean(args)
+ continue
+ if command == 'symbols':
+ status.append('symbols')
+ if args:
+ linebuffer.append(args)
+ continue
+ if command == 'comment':
+ rcs.comment = RCS.at_clean(args)
+ continue
+ m = re.match("^[0-9.]+$", command)
+ if m:
+ rev = command
+ if rcs.has_revision(rev):
+ status.appand("revision_data")
+ else:
+ status.append("new_revision")
+ continue
+ if command == 'desc':
+ status.append("desc")
+ lines = []
+ status.append("read_lines")
+ continue
+ if command in ('branch', 'access', 'locks', 'expand'):
+ if debug:
+ print >>sys.stderr,"Skipping unhandled command %s" % command
+ status.append("skipping_lines")
+ if args:
+ linebuffer.apppend(args)
+ continue
+ raise FatalException("Unknown command %s" % command)
+ elif status[-1] == "skipping_lines":
+ if line.strip.endswith(";"):
+ status.pop()
+ continue
+ elif status[-1] == "symbols":
+ # we can have multiple symbols per line
+ for pair in line.strip().split(";"):
+ if not pair:
+ status.pop()
+ else:
+ (sym, rev) = pair.strip().split(':')
+ if rev:
+ rcs.revision[rev].symbols.append(sym)
+ else:
+ status.pop()
+ when :desc
+ rcs.desc.replace lines.dup
+ status.pop
+ when :read_lines
+ # we sanitize lines as we read them
+
+ actual_line = line.dup
+
+ # the first line must begin with a @, which we strip
+ if lines.empty?
+ ats = line.match(/^@+/)
+ raise 'malformed line' unless ats
+ actual_line.replace line.sub(/^@/,'')
+ end
+
+ # if the line ends with an ODD number of @, it's the
+ # last line -- we work on actual_line so that content
+ # such as @\n or @ work correctly (they would be
+ # encoded respectively as ['@@@\n','@\n'] and
+ # ['@@@@\n']
+ ats = actual_line.chomp.match(/@+$/)
+ if nomore = (ats && Regexp.last_match(0).length.odd?)
+ actual_line.replace actual_line.chomp.sub(/@$/,'')
+ end
+ lines << actual_line.gsub('@@','@')
+ if nomore
+ status.pop
+ redo
+ end
+ when :new_revision
+ case line.chomp
+ when /^date\s+(\S+);\s+author\s+(\S+);\s+state\s+(\S+);$/
+ rcs.revision[rev].date = $1
+ rcs.revision[rev].author = $2
+ rcs.revision[rev].state = $3
+ when 'branches'
+ status.push :branches
+ when /branches\s*;/
+ next
+ when /^next\s+(\S+)?;$/
+ nxt = rcs.revision[rev].next = $1
+ next unless nxt
+ raise "multiple diff_bases for #{nxt}" unless rcs.revision[nxt].diff_base.None?
+ rcs.revision[nxt].diff_base = rev
+ rcs.revision[nxt].branch = rcs.revision[rev].branch
+ else
+ status.pop
+ end
+ when :branches
+ candidate = line.split(';',2)
+ branch = candidate.first.strip
+ rcs.revision[rev].branches.push branch
+ raise "multiple diff_bases for #{branch}" unless rcs.revision[branch].diff_base.None?
+ rcs.revision[branch].diff_base = rev
+ # we drop the last number from the branch name
+ rcs.revision[branch].branch = branch.sub(/\.\d+$/,'.x')
+ rcs.revision[branch].branch_point = rev
+ status.pop if candidate.length > 1
+ when :revision_data
+ case line.chomp
+ when 'log'
+ status.push :log
+ lines.clear
+ status.push :read_lines
+ when 'text'
+ if rev == rcs.head
+ status.push :head
+ else
+ status.push :diff
+ end
+ lines.clear
+ status.push :read_lines
+ else
+ status.pop
+ end
+ when :log
+ rcs.revision[rev].log.replace lines.dup
+ status.pop
+ when :head
+ rcs.revision[rev].text.replace lines.dup
+ print rcs.revision[rev].blob
+ status.pop
+ when :diff
+ difflines.replace lines.dup
+ difflines.pop if difflines.last.empty?
+ base = rcs.revision[rev].diff_base
+ unless rcs.revision[base].text
+ pp rcs
+ print rev, base
+ raise 'no diff base!'
+ end
+ # deep copy
+ buffer = []
+ rcs.revision[base].text.each { |l| buffer << [l.dup] }
+
+ adding = false
+ index = None
+ count = None
+
+ while l = difflines.shift
+ if adding
+ raise 'negative index during insertion' if index < 0
+ raise 'negative count during insertion' if count < 0
+ adding << l
+ count -= 1
+ # collected all the lines, put the before
+ unless count > 0
+ unless buffer[index]
+ buffer[index] = []
+ end
+ buffer[index].unshift(*adding)
+ adding = false
+ end
+ next
+ end
+
+ l.chomp!
+ raise 'malformed diff' unless l =~ /^([ad])(\d+) (\d+)$/
+ diff_cmd = $1.intern
+ index = $2.to_i
+ count = $3.to_i
+ case diff_cmd
+ when :d
+ # for deletion, index 1 is the first index, so the Ruby
+ # index is one less than the diff one
+ index -= 1
+ # we replace them with empty string so that 'a' commands
+ # referring to the same line work properly
+ while count > 0
+ buffer[index].clear
+ index += 1
+ count -= 1
+ end
+ when :a
+ # addition will prepend the appropriate lines
+ # to the given index, and in this case Ruby
+ # and diff indices are the same
+ adding = []
+ end
+ end
+
+ # turn the buffer into an array of lines, deleting the empty ones
+ buffer.delete_if { |l| l.empty? }
+ buffer.flatten!
+
+ rcs.revision[rev].text = buffer
+ print rcs.revision[rev].blob
+ status.pop
+ else
+ raise "Unknown status #{status.last}"
+ end
+ end
+ end
+
+ # clean up the symbols/branches: look for revisions that have
+ # one or more symbols but no dates, and make them into
+ # branches, pointing to the highest commit with that key
+ branches = []
+ keys = rcs.revision.keys
+ rcs.revision.each do |key, rev|
+ if rev.date.None? and not rev.symbols.empty?
+ top = keys.select { |k| k.match(/^#{key}\./) }.sort.last
+ tr = rcs.revision[top]
+ raise "unhandled complex branch structure met: #{rev.inspect} refers #{tr.inspect}" if tr.date.None?
+ tr.branches |= rev.symbols
+ branches << key
end
end
- end
-
- class Revision
- attr_accessor :rev, :author, :state, :next
- attr_accessor :branches, :log, :text, :symbols
- attr_accessor :branch, :diff_base, :branch_point
- attr_reader :date
- def initialize(file, rev)
- @file = file
- @rev = rev
- @author = nil
- @date = nil
- @state = nil
- @next = nil
- @branches = []
- @branch = nil
- @branch_point = nil
- @diff_base = nil
- @log = []
- @text = []
- @symbols = Set.new
- end
-
- def date=(str)
- @date = Time.rcs(str)
- end
-
- def blob
- str = @text.join('')
- ret = "blob\nmark :#{RCS.blob @file.fname, @rev}\ndata #{str.length}\n#{str}\n"
- ret
- end
- end
-
- def RCS.parse(fname, rcsfile)
- rcs = RCS::File.new(fname, ::File.executable?(rcsfile))
-
- ::File.open(rcsfile, 'r') do |file|
- status = [:basic]
- rev = nil
- lines = []
- difflines = []
- file.each_line do |line|
- case status.last
- when :basic
- command, args = line.split($;,2)
- next if command.empty?
-
- if command.chomp!(';')
- STDERR.puts "Skipping empty command #{command.inspect}" if $DEBUG
- next
- end
-
- case command
- when 'head'
- rcs.head = RCS.clean(args.chomp)
- when 'symbols'
- status.push :symbols
- next if args.empty?
- line = args; redo
- when 'comment'
- rcs.comment = RCS.at_clean(args.chomp)
- when /^[0-9.]+$/
- rev = command.dup
- if rcs.has_revision?(rev)
- status.push :revision_data
- else
- status.push :new_revision
- end
- when 'desc'
- status.push :desc
- lines.clear
- status.push :read_lines
- when 'branch', 'access', 'locks', 'expand'
- STDERR.puts "Skipping unhandled command #{command.inspect}" if $DEBUG
- status.push :skipping_lines
- next if args.empty?
- line = args; redo
- else
- raise "Unknown command #{command.inspect}"
- end
- when :skipping_lines
- status.pop if line.strip.chomp!(';')
- when :symbols
- # we can have multiple symbols per line
- pairs = line.strip.split($;)
- pairs.each do |pair|
- sym, rev = pair.strip.split(':',2);
- if rev
- status.pop if rev.chomp!(';')
- rcs.revision[rev].symbols << sym
- else
- status.pop
- end
- end
- when :desc
- rcs.desc.replace lines.dup
- status.pop
- when :read_lines
- # we sanitize lines as we read them
-
- actual_line = line.dup
-
- # the first line must begin with a @, which we strip
- if lines.empty?
- ats = line.match(/^@+/)
- raise 'malformed line' unless ats
- actual_line.replace line.sub(/^@/,'')
- end
-
- # if the line ends with an ODD number of @, it's the
- # last line -- we work on actual_line so that content
- # such as @\n or @ work correctly (they would be
- # encoded respectively as ['@@@\n','@\n'] and
- # ['@@@@\n']
- ats = actual_line.chomp.match(/@+$/)
- if nomore = (ats && Regexp.last_match(0).length.odd?)
- actual_line.replace actual_line.chomp.sub(/@$/,'')
- end
- lines << actual_line.gsub('@@','@')
- if nomore
- status.pop
- redo
- end
- when :new_revision
- case line.chomp
- when /^date\s+(\S+);\s+author\s+(\S+);\s+state\s+(\S+);$/
- rcs.revision[rev].date = $1
- rcs.revision[rev].author = $2
- rcs.revision[rev].state = $3
- when 'branches'
- status.push :branches
- when /branches\s*;/
- next
- when /^next\s+(\S+)?;$/
- nxt = rcs.revision[rev].next = $1
- next unless nxt
- raise "multiple diff_bases for #{nxt}" unless rcs.revision[nxt].diff_base.nil?
- rcs.revision[nxt].diff_base = rev
- rcs.revision[nxt].branch = rcs.revision[rev].branch
- else
- status.pop
- end
- when :branches
- candidate = line.split(';',2)
- branch = candidate.first.strip
- rcs.revision[rev].branches.push branch
- raise "multiple diff_bases for #{branch}" unless rcs.revision[branch].diff_base.nil?
- rcs.revision[branch].diff_base = rev
- # we drop the last number from the branch name
- rcs.revision[branch].branch = branch.sub(/\.\d+$/,'.x')
- rcs.revision[branch].branch_point = rev
- status.pop if candidate.length > 1
- when :revision_data
- case line.chomp
- when 'log'
- status.push :log
- lines.clear
- status.push :read_lines
- when 'text'
- if rev == rcs.head
- status.push :head
- else
- status.push :diff
- end
- lines.clear
- status.push :read_lines
- else
- status.pop
- end
- when :log
- rcs.revision[rev].log.replace lines.dup
- status.pop
- when :head
- rcs.revision[rev].text.replace lines.dup
- puts rcs.revision[rev].blob
- status.pop
- when :diff
- difflines.replace lines.dup
- difflines.pop if difflines.last.empty?
- base = rcs.revision[rev].diff_base
- unless rcs.revision[base].text
- pp rcs
- puts rev, base
- raise 'no diff base!'
- end
- # deep copy
- buffer = []
- rcs.revision[base].text.each { |l| buffer << [l.dup] }
-
- adding = false
- index = nil
- count = nil
-
- while l = difflines.shift
- if adding
- raise 'negative index during insertion' if index < 0
- raise 'negative count during insertion' if count < 0
- adding << l
- count -= 1
- # collected all the lines, put the before
- unless count > 0
- unless buffer[index]
- buffer[index] = []
- end
- buffer[index].unshift(*adding)
- adding = false
- end
- next
- end
-
- l.chomp!
- raise 'malformed diff' unless l =~ /^([ad])(\d+) (\d+)$/
- diff_cmd = $1.intern
- index = $2.to_i
- count = $3.to_i
- case diff_cmd
- when :d
- # for deletion, index 1 is the first index, so the Ruby
- # index is one less than the diff one
- index -= 1
- # we replace them with empty string so that 'a' commands
- # referring to the same line work properly
- while count > 0
- buffer[index].clear
- index += 1
- count -= 1
- end
- when :a
- # addition will prepend the appropriate lines
- # to the given index, and in this case Ruby
- # and diff indices are the same
- adding = []
- end
- end
-
- # turn the buffer into an array of lines, deleting the empty ones
- buffer.delete_if { |l| l.empty? }
- buffer.flatten!
-
- rcs.revision[rev].text = buffer
- puts rcs.revision[rev].blob
- status.pop
- else
- raise "Unknown status #{status.last}"
- end
- end
- end
-
- # clean up the symbols/branches: look for revisions that have
- # one or more symbols but no dates, and make them into
- # branches, pointing to the highest commit with that key
- branches = []
- keys = rcs.revision.keys
- rcs.revision.each do |key, rev|
- if rev.date.nil? and not rev.symbols.empty?
- top = keys.select { |k| k.match(/^#{key}\./) }.sort.last
- tr = rcs.revision[top]
- raise "unhandled complex branch structure met: #{rev.inspect} refers #{tr.inspect}" if tr.date.nil?
- tr.branches |= rev.symbols
- branches << key
- end
- end
- branches.each { |k| rcs.revision.delete k }
+ branches.each { |k| rcs.revision.delete k }
- return rcs
- end
+ return rcs
+ end
class Tree
def initialize(commit)
xdate = "#{numdate} +0000"
key = numdate.to_s
- puts "commit refs/heads/#{xbranch}"
- puts "mark :#{RCS.commit key}"
- puts "committer #{xauthor} #{xdate}"
- puts "data #{xlog.length}"
- puts xlog unless xlog.empty?
+ print "commit refs/heads/#{xbranch}"
+ print "mark :#{RCS.commit key}"
+ print "committer #{xauthor} #{xdate}"
+ print "data #{xlog.length}"
+ print xlog unless xlog.empty?
# TODO branching support for multi-file export
- # puts "from :#{RCS.commit from}" if self.branch_point
- puts self.tree.to_s
+ # print "from :#{RCS.commit from}" if self.branch_point
+ print self.tree.to_s
# TODO branching support for multi-file export
# rev.branches.each do |sym|
- # puts "reset refs/heads/#{sym}"
- # puts "from :#{RCS.commit key}"
+ # print "reset refs/heads/#{sym}"
+ # print "from :#{RCS.commit key}"
# end
self.symbols.each do |sym|
- puts "reset refs/tags/#{sym}"
- puts "from :#{RCS.commit key}"
+ print "reset refs/tags/#{sym}"
+ print "from :#{RCS.commit key}"
end
end
when '--authors-file'
authors = load_authors_file(arg)
redef = parse_options[:authors].keys & authors.keys
- STDERR.puts "Authors file #{arg} redefines #{redef.join(', ')}" unless redef.empty?
+ print >>sys.stderr,"Authors file #{arg} redefines #{redef.join(', ')}" unless redef.empty?
parse_options[:authors].merge!(authors)
when '--rcs-suffixes'
# TODO
parse_options[:tag_each_rev] = true
when '--no-tag-each-rev'
# this is the default, which is fine since the missing key
- # (default) returns nil which is false in Ruby
+ # (default) returns None which is false in Ruby
parse_options[:tag_each_rev] = false
when '--log-filename'
parse_options[:log_filename] = true
when '--no-log-filename'
# this is the default, which is fine since the missing key
- # (default) returns nil which is false in Ruby
+ # (default) returns None which is false in Ruby
parse_options[:log_filename] = false
when ''
file_list << arg
begin
rcs << RCS.parse(filename, rcsfile)
rescue Exception => e
- STDERR.puts "Failed to parse #{filename} @ #{rcsfile}:#{$.}"
+ print >>sys.stderr,"Failed to parse #{filename} @ #{rcsfile}:#{$.}"
raise e
end
end
else
- STDERR.puts "Cannot handle #{arg} of #{ftype} type"
+ print >>sys.stderr,"Cannot handle #{arg} of #{ftype} type"
status |= 1
end
end
if rcs.length == 1
rcs.first.export_commits(parse_options)
else
- STDERR.puts "Preparing commits"
+ print >>sys.stderr,"Preparing commits"
commits = []
end
end
- STDERR.puts "Sorting by date"
+ print >>sys.stderr,"Sorting by date"
commits.sort!
if $DEBUG
- STDERR.puts "RAW commits (#{commits.length}):"
+ print >>sys.stderr,"RAW commits (#{commits.length}):"
commits.each do |c|
PP.pp c.to_a, $stderr
end
else
- STDERR.puts "#{commits.length} single-file commits"
+ print >>sys.stderr,"#{commits.length} single-file commits"
end
- STDERR.puts "Coalescing [1] by date with fuzz #{parse_options[:commit_fuzz]}"
+ print >>sys.stderr,"Coalescing [1] by date with fuzz #{parse_options[:commit_fuzz]}"
commits.reverse_each do |c|
commits.reverse_each do |k|
next if k.date > c.date
unless c.symbols.subset?(k.symbols) or k.symbols.subset?(c.symbols)
if parse_options[:symbol_check]
- STDERR.puts "Not coalescing #{c.log.inspect}\n\tfor (#{c.tree.filenames.join(', ')})\n\tand (#{k.tree.filenames.join(', ')})"
- STDERR.puts "\tbecause their symbols disagree:\n\t#{c.symbols.to_a.inspect} and #{k.symbols.to_a.inspect} disagree on #{(c.symbols ^ k.symbols).to_a.inspect}"
- STDERR.puts "\tretry with the --no-symbol-check option if you want to merge these commits anyway"
+ print >>sys.stderr,"Not coalescing #{c.log.inspect}\n\tfor (#{c.tree.filenames.join(', ')})\n\tand (#{k.tree.filenames.join(', ')})"
+ print >>sys.stderr,"\tbecause their symbols disagree:\n\t#{c.symbols.to_a.inspect} and #{k.symbols.to_a.inspect} disagree on #{(c.symbols ^ k.symbols).to_a.inspect}"
+ print >>sys.stderr,"\tretry with the --no-symbol-check option if you want to merge these commits anyway"
next
elsif $DEBUG
- STDERR.puts "Coalescing #{c.log.inspect}\n\tfor (#{c.tree.filenames.join(', ')})\n\tand (#{k.tree.filenames.join(', ')})"
- STDERR.puts "\twith disagreeing symbols:\n\t#{c.symbols.to_a.inspect} and #{k.symbols.to_a.inspect} disagree on #{(c.symbols ^ k.symbols).to_a.inspect}"
+ print >>sys.stderr,"Coalescing #{c.log.inspect}\n\tfor (#{c.tree.filenames.join(', ')})\n\tand (#{k.tree.filenames.join(', ')})"
+ print >>sys.stderr,"\twith disagreeing symbols:\n\t#{c.symbols.to_a.inspect} and #{k.symbols.to_a.inspect} disagree on #{(c.symbols ^ k.symbols).to_a.inspect}"
end
end
begin
c.merge! k
rescue RuntimeError => err
fuzz = c.date - k.date
- STDERR.puts "Fuzzy commit coalescing failed: #{err}"
- STDERR.puts "\tretry with commit fuzz < #{fuzz} if you don't want to see this message"
+ print >>sys.stderr,"Fuzzy commit coalescing failed: #{err}"
+ print >>sys.stderr,"\tretry with commit fuzz < #{fuzz} if you don't want to see this message"
break
end
commits.delete k
end
if $DEBUG
- STDERR.puts "[1] commits (#{commits.length}):"
+ print >>sys.stderr,"[1] commits (#{commits.length}):"
commits.each do |c|
PP.pp c.to_a, $stderr
end
else
- STDERR.puts "#{commits.length} coalesced commits"
+ print >>sys.stderr,"#{commits.length} coalesced commits"
end
commits.each { |c| c.export(parse_options) }
end
exit status
-