contrib / fast-import / p4-fast-export.pyon commit Avoid calling fstat for every imported file (slow!) and instead read the file data first into the python process and use the length of the bytes read for the size field of git fast-import. (2385536)
   1#!/usr/bin/python
   2#
   3# p4-fast-export.py
   4#
   5# Author: Simon Hausmann <hausmann@kde.org>
   6# License: MIT <http://www.opensource.org/licenses/mit-license.php>
   7#
   8# TODO:
   9#       - support integrations (at least p4i)
  10#       - support incremental imports
  11#       - create tags
  12#       - instead of reading all files into a variable try to pipe from
  13#       - support p4 submit (hah!)
  14#       - don't hardcode the import to master
  15#
  16import os, string, sys, time
  17import marshal, popen2
  18
  19if len(sys.argv) != 2:
  20    print "usage: %s //depot/path[@revRange]" % sys.argv[0]
  21    print "\n    example:"
  22    print "    %s //depot/my/project/ -- to import everything"
  23    print "    %s //depot/my/project/@1,6 -- to import only from revision 1 to 6"
  24    print ""
  25    print "    (a ... is not needed in the path p4 specification, it's added implicitly)"
  26    print ""
  27    sys.exit(1)
  28
  29prefix = sys.argv[1]
  30changeRange = ""
  31try:
  32    atIdx = prefix.index("@")
  33    changeRange = prefix[atIdx:]
  34    prefix = prefix[0:atIdx]
  35except ValueError:
  36    changeRange = ""
  37
  38if not prefix.endswith("/"):
  39    prefix += "/"
  40
  41def p4CmdList(cmd):
  42    pipe = os.popen("p4 -G %s" % cmd, "rb")
  43    result = []
  44    try:
  45        while True:
  46            entry = marshal.load(pipe)
  47            result.append(entry)
  48    except EOFError:
  49        pass
  50    pipe.close()
  51    return result
  52
  53def p4Cmd(cmd):
  54    list = p4CmdList(cmd)
  55    result = {}
  56    for entry in list:
  57        result.update(entry)
  58    return result;
  59
  60def getUserMap():
  61    users = {}
  62
  63    for output in p4CmdList("users"):
  64        if not output.has_key("User"):
  65            continue
  66        users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
  67    return users
  68
  69users = getUserMap()
  70
  71output = os.popen("p4 changes %s...%s" % (prefix, changeRange)).readlines()
  72
  73changes = []
  74for line in output:
  75    changeNum = line.split(" ")[1]
  76    changes.append(changeNum)
  77
  78changes.reverse()
  79
  80sys.stderr.write("\n")
  81
  82tz = - time.timezone / 36
  83
  84gitOutput, gitStream, gitError = popen2.popen3("git-fast-import")
  85
  86cnt = 1
  87for change in changes:
  88    description = p4Cmd("describe %s" % change)
  89
  90    sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
  91    cnt = cnt + 1
  92
  93    epoch = description["time"]
  94    author = description["user"]
  95
  96    gitStream.write("commit refs/heads/master\n")
  97    if author in users:
  98        gitStream.write("committer %s %s %s\n" % (users[author], epoch, tz))
  99    else:
 100        gitStream.write("committer %s <a@b> %s %s\n" % (author, epoch, tz))
 101    gitStream.write("data <<EOT\n")
 102    gitStream.write(description["desc"])
 103    gitStream.write("EOT\n\n")
 104
 105    fnum = 0
 106    while description.has_key("depotFile%s" % fnum):
 107        path = description["depotFile%s" % fnum]
 108        if not path.startswith(prefix):
 109            print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, prefix, change)
 110            fnum = fnum + 1
 111            continue
 112
 113        rev = description["rev%s" % fnum]
 114        depotPath = path + "#" + rev
 115        relPath = path[len(prefix):]
 116        action = description["action%s" % fnum]
 117
 118        if action == "delete":
 119            gitStream.write("D %s\n" % relPath)
 120        else:
 121            mode = 644
 122            if description["type%s" % fnum].startswith("x"):
 123                mode = 755
 124
 125            data = os.popen("p4 print -q \"%s\"" % depotPath, "rb").read()
 126
 127            gitStream.write("M %s inline %s\n" % (mode, relPath))
 128            gitStream.write("data %s\n" % len(data))
 129            gitStream.write(data)
 130            gitStream.write("\n")
 131
 132        fnum = fnum + 1
 133
 134    gitStream.write("\n")
 135
 136gitStream.close()
 137gitOutput.close()
 138gitError.close()
 139
 140print ""