]> thyrsus.com Git - rcs-fast-import.git/commitdiff
11% translated.
authorEric S. Raymond <esr@thyrsus.com>
Fri, 12 Nov 2010 04:24:57 +0000 (23:24 -0500)
committerEric S. Raymond <esr@thyrsus.com>
Fri, 12 Nov 2010 04:24:57 +0000 (23:24 -0500)
rcs-fast-export

index ed873ab053b525e04e9cbd40bca970842e6a1ccd..858bcedc327e192de924e80a4d62f6f8cf551f95 100644 (file)
@@ -1,30 +1,20 @@
-#!/usr/bin/ruby
-
-=begin
+#!/usr/bin/env python
+"""
 TODO
-       * Refactor commit coalescing
-       * Add --strict-symbol-check to only coalesce commits if their symbol lists are equal
-       * Further coalescing options? (e.g. small logfile differences)
-       * Proper branching support in multi-file export
-       * Optimize memory usage by discarding unneeded text
-=end
-
-require 'pp'
-require 'set'
-
-# Integer#odd? was introduced in Ruby 1.8.7, backport it to
-# older versions
-unless 2.respond_to? :odd?
-       class Integer
-               def odd?
-                       self % 2 == 1
-               end
-       end
-end
+* Refactor commit coalescing
+* Add --strict-symbol-check to only coalesce commits if their symbol lists are equal
+* Further coalescing options? (e.g. small logfile differences)
+* Proper branching support in multi-file export
+* Optimize memory usage by discarding unneeded text
+"""
 
-def usage
-       STDERR.puts <<EOM
-#{$0} [options] file [file ...]
+import sys
+import pprint  # Functional equivalent of Ruby pp?
+
+def usage():
+
+    print >>sys.stderr, """\
+rcs-fast-export [options] file [file ...]
 
 Fast-export the RCS history of one or more files. If a directory is specified,
 all RCS-tracked files in the directory and its descendants are exported.
@@ -64,169 +54,164 @@ Config options:
        rcs.commitFuzz          for --rcs-commit-fuzz
        rcs.symbolCheck         for --rcs-symbol-check
        rcs.tagFuzz             for --rcs-tag-fuzz
-
-EOM
-end
-
-def not_found(arg)
-       STDERR.puts "Could not find #{arg}"
-end
-
-# returns a hash that maps usernames to author names & emails
-def load_authors_file(fn)
-       hash = {}
-       begin
-               File.open(File.expand_path(fn)) do |io|
-                       io.each_line do |line|
-                               uname, author = line.split('=', 2)
-                               uname.strip!
-                               author.strip!
-                               STDERR.puts "Username #{uname} redefined to #{author}" if hash.has_key? uname
-                               hash[uname] = author
-                       end
-               end
-       rescue
-               not_found(fn)
-       end
-       return hash
-end
-
-class Time
-       def Time.rcs(string)
-               fields = string.split('.')
-               raise ArgumentError, "wrong number of fields for RCS date #{string}" unless fields.length == 6
-               Time.utc(*fields)
-       end
-end
-
-module RCS
-       # strip an optional final ;
-       def RCS.clean(arg)
-               arg.chomp(';')
-       end
-
-       # strip the first and last @, and de-double @@s
-       def RCS.sanitize(arg)
-               case arg
-               when Array
-                       ret = arg.dup
-                       raise 'malformed first line' unless ret.first[0,1] == '@'
-                       raise 'malformed last line' unless ret.last[-1,1] == '@'
-                       ret.first.sub!(/^@/,'')
-                       ret.last.sub!(/@$/,'')
-                       ret.map { |l| l.gsub('@@','@') }
-               when String
-                       arg.chomp('@').sub(/^@/,'').gsub('@@','@')
-               else
-                       raise
-               end
-       end
-
-       # clean and sanitize
-       def RCS.at_clean(arg)
-               RCS.sanitize RCS.clean(arg)
-       end
-
-       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
-
-       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
-
-               def has_revision?(rev)
-                       @revision.has_key?(rev) and not @revision[rev].author.nil?
-               end
-
-               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) }
-                       end
-               end
-       end
+"""
+
+def not_found(arg):
+    print >>sys.stderr,"Could not find %s" % arg
+
+def load_authors_file(fn):
+    "Returns a dict that maps usernames to author names & emails."
+    authdict = {}
+    try:
+        for line in open(fn):
+            (uname, author) = map(lambda x: x.strip(), line.split("-"))
+            if uname in hashdict:
+                print >>sys.stderr, "Username %(uname)s redefined to %(author)s" % locals()
+            authdict[uname] = author
+        return authdict
+    except OSError:
+        not_found(fn)
+
+class FatalException:
+    "Unrecoverable error."
+    def __init__(self, msg):
+        self.msg = msg
+
+class RCS:
+    @staticmethod
+    def clean(arg):
+        # 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
+        if type(arg) == type([]):
+            if not arg[0].startswith("@@"):
+                raise FatalException('malformed first line')
+            if not (arg[-1][1] == "@" and arg[-1][-1] == "@"):
+                raise FatalException('malformed last line')
+            arg[0] = arg[0][1:]
+            arg[-1] = arg[-1][:-1]
+            arg = map(lambda s: s.replace("@@", "@"), arg)
+            return arg
+        elif type(arg) == type(""):
+            if arg.endswith("@"):
+                arg = arg[:-1]
+            if arg.startswith("@"):
+                arg = arg[1:]
+            arg = arg.replace("@@", "@")
+            return arg
+        else:
+            raise FatalException("unexpected argument type in sanitize")
+
+    @staticmethod
+    def at_clean(arg):
+        # 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
+
+    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
+
+            def has_revision?(rev)
+                    @revision.has_key?(rev) and not @revision[rev].author.nil?
+            end
+
+            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) }
+                    end
+            end
+    end
 
        class Revision
                attr_accessor :rev, :author, :state, :next
@@ -889,3 +874,6 @@ else
 end
 
 exit status
+    
+       
+