# * Branch structure!
#
"""
-Usage: rcs-fast-import [-v] [-u] [-l] [-d] [-n] [-V]
+Usage: rcs-fast-import [-v] [-p] [-u] [-l] [-d] [-n] [-V]
Break the content of a git-fast-import stream on stdin into individual RCS
master files in RCS subdirectories of the corresponding directory structure.
With the -v option, emit a twirling-baton prompt during stream read, and
more progress messages during RCS writes.
+Normally, committer and author data are written into each comment as
+RFC-822 mail headers, followed by a blank line. The -p option suppresses
+this, producing a plain RCS comment.
+
With the -l option, check out and lock all resulting files at their latest
revisions; with the -u option, check out but do not lock.
R file operations rename corresponding RCS master files; C file operations
copy them.
-If D, R, or C operations are present, the conversion will not round-trip
-properly (that is, rcs-fast-export | rcs-fast-import won't be an exact
-copy) with differences near deleted, renamed, and copied files.
+If D, R, or C operations are present, the conversion will not
+round-trip properly (that is, the file structure after rcs-fast-export
+| rcs-fast-import won't be an exact copy) with differences near
+deleted, renamed, and copied files.
Warning: All tag metadata other than the name will be lost. The date
imputed to commits is from the author metadata if that exists, otherwise
-from the committer metadata. Committer and author information is lost.
+from the committer metadata.
"""
import sys, os, getopt, re, cStringIO, subprocess, shutil
-import time, calendar, copy, shlex, email.utils
+import time, calendar, copy, shlex, email.message, email.utils
version="0.1"
self.date = Date(person.strip().split("|")[2])
except (ValueError, IndexError):
raise FatalException("malformed attribution %s" % person)
+ def email_out(self, msg, hdr):
+ "Update an RC822 message object with a representation of this."
+ msg[hdr] = self.name + " <" + self.email + ">"
+ msg[hdr + "-Date"] = self.date.rfc822()
class Blob:
"Represent a detached blob of data referenced by a mark."
self.committish = committish
self.tagger = tagger
self.comment = content
+ def email_out(self, eventnum):
+ "Enable do_mailbox_out() to report these."
+ msg = email.message.Message()
+ msg.set_unixfrom(None)
+ msg["Event-Number"] = str(eventnum+1)
+ msg["Tag-Name"] = self.name
+ if self.tagger:
+ self.tagger.email_out(msg, "Tagger")
+ msg.set_payload(self.comment)
+ return str(msg)
class Reset:
"Represents a branch creation."
self.fileops = [] # blob and file operation list
self.properties = {} # commit properties (extension)
self.taglist = [] # Tags that point at this commit
+ def email_out(self):
+ "Enable do_mailbox_out() to report these."
+ msg = email.message.Message()
+ msg.set_unixfrom(None)
+ if self.authors:
+ self.authors[0].email_out(msg, "Author")
+ for (i, coauthor) in enumerate(self.authors[1:]):
+ coauthor.email_out(msg, "Author" + `2+i`)
+ self.committer.email_out(msg, "Committer")
+ empty_properties = []
+ propkeys = self.properties.keys()
+ propkeys.sort()
+ for name in propkeys:
+ value = self.properties[name]
+ if value in (True, False):
+ if value:
+ empty_properties.append(name)
+ else:
+ hdr = "-".join(map(lambda s: s.capitalize(), name.split("-")))
+ msg["Property-" + hdr] = value
+ if empty_properties:
+ msg["Empty-Properties"] = ",".join(empty_properties)
+ msg.set_payload(self.comment)
+ return str(msg)
class Passthrough:
"Represents a passthrough line."
def announce(msg):
print "rcs-fast-import:", msg
-class popen_or_die:
- "Read or write from a subordinate process."
- def __init__(self, cmd, legend="", mode="r"):
- assert mode in ("r", "w")
- self.cmd = cmd
- self.legend = legend
- self.mode = mode
- if self.legend:
- self.legend = " " + self.legend
- def __enter__(self):
- if verbose >= DEBUG_COMMANDS:
- if self.mode == "r":
- announce("reading from '%s'%s" % (self.cmd, self.legend))
- else:
- announce("writing to '%s'%s" % (self.cmd, self.legend))
- if dryrun:
- self.fp = file("/dev/null", self.mode)
- return self.fp
- try:
- self.fp = os.popen(self.cmd, self.mode)
- return self.fp
- except OSError, e:
- raise FatalException("execution of %s%s failed: %s" \
- % (self.cmd, self.legend, e))
- def __exit__(self, type, value, traceback):
- if self.fp.close() is not None:
- raise FatalException("%s%s returned error." \
- % (self.cmd, self.legend))
- return False
-
def do_or_die(cmd, legend=""):
"Either execute a command or raise a fatal exception."
if legend:
legend = " " + legend
if verbose >= DEBUG_COMMANDS:
announce("executing '%s'%s" % (cmd, legend))
+ if not verbose:
+ cmd += ">/dev/null 2>&1"
if dryrun:
return
try:
for name in files:
res.append(os.path.join(root, name))
return res
- def commit(self, event, committables):
+ def commit(self, event, committables, roundtrip):
"Perform RCS file checkins and modifications,"
for (path, mode, copyfile) in committables:
if mode == "160000":
except OSError:
raise FatalException("refusing to step on %s." % path)
responsible = event.authors[0] or event.committer
- command = "ci -q -l"
+ if roundtrip:
+ comment = event.email_out()
+ else:
+ comment = event.comment
+ command = "ci -l"
# If we were going to try to match committer or author
# email addresses to local names, this would be the place
# to do it. Not a whole lot of point in trying, though.
#command += " -w%s" % responsible.email
command += " -d'%s'" % responsible.date.rfc822()
- command += ' -m"%s"' % event.comment.replace('"', '\\"')
+ command += ' -m"%s"' % comment.replace('"', '\\"')
command += " " + path + " " + RCS.master(path)
do_or_die(command, "checkin")
if verbose >= DEBUG_SHUFFLE:
do_or_die("co -u %s" % (" ".join(RCS.rcsfiles()),))
if __name__ == '__main__':
- (options, arguments) = getopt.getopt(sys.argv[1:], "dlnuvV?")
+ (options, arguments) = getopt.getopt(sys.argv[1:], "dlnpuvV?")
do_unlocked_checkouts = False
checkout_mode = None
+ roundtrip = True
for (switch, val) in options:
if (switch == '-?'):
print __doc__
raise SystemExit, 0
elif switch == '-d':
do_deletes = True
+ elif switch == '-p':
+ roundtrip = False
elif switch in ('-l', '-u'):
checkout_mode = switch
elif (switch == '-n'):
vcs.copy(ops.source, op.target)
else:
raise FatalException("unknown op type %s" % op.op)
- vcs.commit(event, committables)
+ vcs.commit(event, committables, roundtrip)
vcs.checkout(checkout_mode)
raise SystemExit, 0
except FatalException, e: