da3eb35841c5bdb807dab4c929daa4320a39956f
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
29master = "refs/heads/p4"
30branch = "refs/heads/p4-import"
31prefix = sys.argv[1]
32changeRange = ""
33try:
34 atIdx = prefix.index("@")
35 changeRange = prefix[atIdx:]
36 prefix = prefix[0:atIdx]
37except ValueError:
38 changeRange = ""
39
40if prefix.endswith("..."):
41 prefix = prefix[:-3]
42
43if not prefix.endswith("/"):
44 prefix += "/"
45
46def p4CmdList(cmd):
47 pipe = os.popen("p4 -G %s" % cmd, "rb")
48 result = []
49 try:
50 while True:
51 entry = marshal.load(pipe)
52 result.append(entry)
53 except EOFError:
54 pass
55 pipe.close()
56 return result
57
58def p4Cmd(cmd):
59 list = p4CmdList(cmd)
60 result = {}
61 for entry in list:
62 result.update(entry)
63 return result;
64
65def getUserMap():
66 users = {}
67
68 for output in p4CmdList("users"):
69 if not output.has_key("User"):
70 continue
71 users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
72 return users
73
74users = getUserMap()
75topMerge = ""
76
77incremental = 0
78# try incremental import
79if len(changeRange) == 0:
80 try:
81 sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % master)
82 output = sout.read()
83 tagIdx = output.index(" tags/p4/")
84 caretIdx = output.index("^")
85 revision = int(output[tagIdx + 9 : caretIdx]) + 1
86 changeRange = "@%s,#head" % revision
87 topMerge = os.popen("git-rev-parse %s" % master).read()[:-1]
88 incremental = 1
89 except:
90 pass
91
92if incremental == 0:
93 branch = master
94
95output = os.popen("p4 changes %s...%s" % (prefix, changeRange)).readlines()
96
97changes = []
98for line in output:
99 changeNum = line.split(" ")[1]
100 changes.append(changeNum)
101
102changes.reverse()
103
104if len(changes) == 0:
105 print "no changes to import!"
106 sys.exit(1)
107
108sys.stderr.write("\n")
109
110tz = - time.timezone / 36
111
112gitOutput, gitStream, gitError = popen2.popen3("git-fast-import")
113
114cnt = 1
115for change in changes:
116 description = p4Cmd("describe %s" % change)
117
118 sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
119 sys.stdout.flush()
120 cnt = cnt + 1
121
122 epoch = description["time"]
123 author = description["user"]
124
125 gitStream.write("commit %s\n" % branch)
126 committer = ""
127 if author in users:
128 committer = "%s %s %s" % (users[author], epoch, tz)
129 else:
130 committer = "%s <a@b> %s %s" % (author, epoch, tz)
131
132 gitStream.write("committer %s\n" % committer)
133
134 gitStream.write("data <<EOT\n")
135 gitStream.write(description["desc"])
136 gitStream.write("\n[ imported from %s; change %s ]\n" % (prefix, change))
137 gitStream.write("EOT\n\n")
138
139 if len(topMerge) > 0:
140 gitStream.write("merge %s\n" % topMerge)
141 topMerge = ""
142
143 fnum = 0
144 while description.has_key("depotFile%s" % fnum):
145 path = description["depotFile%s" % fnum]
146 if not path.startswith(prefix):
147 print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, prefix, change)
148 fnum = fnum + 1
149 continue
150
151 rev = description["rev%s" % fnum]
152 depotPath = path + "#" + rev
153 relPath = path[len(prefix):]
154 action = description["action%s" % fnum]
155
156 if action == "delete":
157 gitStream.write("D %s\n" % relPath)
158 else:
159 mode = 644
160 if description["type%s" % fnum].startswith("x"):
161 mode = 755
162
163 data = os.popen("p4 print -q \"%s\"" % depotPath, "rb").read()
164
165 gitStream.write("M %s inline %s\n" % (mode, relPath))
166 gitStream.write("data %s\n" % len(data))
167 gitStream.write(data)
168 gitStream.write("\n")
169
170 fnum = fnum + 1
171
172 gitStream.write("\n")
173
174 gitStream.write("tag p4/%s\n" % change)
175 gitStream.write("from %s\n" % branch);
176 gitStream.write("tagger %s\n" % committer);
177 gitStream.write("data 0\n\n")
178
179
180gitStream.close()
181gitOutput.close()
182gitError.close()
183
184if incremental == 1:
185 os.popen("git rebase p4-import p4")
186 os.popen("git branch -d p4-import")
187
188print ""