"""Pulls changelists from Perforce into a Bazaar shared repository. For example, > pull.py //depot/project C:\\repo Requires that the P4PORT and P4USER environment variables are set. Requires that bzrp4 is installed as a Bazaar plugin. """ import os import os.path import P4 import subprocess import sys def p4_fast_export_path(): dir = os.path.dirname(__file__) return os.path.join(dir, 'p4-fast-export.py') def last_change_in_source_depot_path(src): p4 = P4.P4() p4.connect() result = p4.run_changes('-m', '1', p4_wildcard(src)) p4.disconnect() return result[0]['change'] def parse_change_from_marks_line(line): return int(line[1:line.find(' ')]) def last_change_in_marks_file(dest): last_change = 0 try: marks_file = open(marks_file_path(dest), 'r') for line in marks_file: if ':' == line[0]: change = parse_change_from_marks_line(line) if change > last_change: last_change = change except IOError: pass return last_change def range_begin(dest): last_change = last_change_in_marks_file(dest) return str(last_change + 1) def range_end(src): return last_change_in_source_depot_path(src) def p4_wildcard(path): return '%s/...' % path def manual_range(src): return -1 != src.find('@') def computed_range(src): return not manual_range(src) def depot_path_and_range(src, dest): if manual_range(src): return src else: return '%s@%s,%s' % (src, range_begin(dest), range_end(src)) def marks_file_path(dest): return os.path.join(dest, '.bzr', 'repository', 'fastimport-id-map') def export_file_path(dest): return os.path.join(dest, '.bzr', 'repository', 'p4-fastexport') def bzr_fast_import_command(dest): # TODO: Make this portable, not Windows-specific. command = ['bzr.bat', 'fast-import', '-'] return command def pull(src, dest): print depot_path_and_range(src, dest) if computed_range(src) and range_begin(dest) > range_end(src): return export_file = open(export_file_path(dest), 'ab') try: export_file.seek(0, os.SEEK_END) proc1 = subprocess.Popen(['python', p4_fast_export_path(), depot_path_and_range(src, dest)], stdout=export_file) if 0 != proc1.wait(): raise IOError() except Exception, e: raise e finally: export_file.close() import_file = open(export_file_path(dest), 'rb') try: proc2 = subprocess.Popen(bzr_fast_import_command(dest), stdin=import_file, cwd=dest) if 0 != proc2.wait(): raise IOError() except Exception, e: raise e finally: import_file.close() def main(args): pull_src = args[0] pull_dest = args[1] pull(pull_src, pull_dest) if __name__ == '__main__': main(sys.argv[1:])