Code

Catch io exceptions from git fast-import again and print the error message.
[git.git] / contrib / fast-import / p4-fast-export.py
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 p4 submit (hah!)
11 #
12 import os, string, sys, time
13 import marshal, popen2
15 branch = "refs/heads/p4"
16 prefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read()
17 if len(prefix) != 0:
18     prefix = prefix[:-1]
20 if len(sys.argv) == 1 and len(prefix) != 0:
21     print "[using previously specified depot path %s]" % prefix
22 elif len(sys.argv) != 2:
23     print "usage: %s //depot/path[@revRange]" % sys.argv[0]
24     print "\n    example:"
25     print "    %s //depot/my/project/ -- to import the current head"
26     print "    %s //depot/my/project/@all -- to import everything"
27     print "    %s //depot/my/project/@1,6 -- to import only from revision 1 to 6"
28     print ""
29     print "    (a ... is not needed in the path p4 specification, it's added implicitly)"
30     print ""
31     sys.exit(1)
32 else:
33     if len(prefix) != 0 and prefix != sys.argv[1]:
34         print "previous import used depot path %s and now %s was specified. this doesn't work!" % (prefix, sys.argv[1])
35         sys.exit(1)
36     prefix = sys.argv[1]
38 changeRange = ""
39 revision = ""
40 users = {}
41 initialParent = ""
43 if prefix.find("@") != -1:
44     atIdx = prefix.index("@")
45     changeRange = prefix[atIdx:]
46     if changeRange == "@all":
47         changeRange = ""
48     elif changeRange.find(",") == -1:
49         revision = changeRange
50         changeRange = ""
51     prefix = prefix[0:atIdx]
52 elif prefix.find("#") != -1:
53     hashIdx = prefix.index("#")
54     revision = prefix[hashIdx:]
55     prefix = prefix[0:hashIdx]
56 elif len(previousDepotPath) == 0:
57     revision = "#head"
59 if prefix.endswith("..."):
60     prefix = prefix[:-3]
62 if not prefix.endswith("/"):
63     prefix += "/"
65 def p4CmdList(cmd):
66     pipe = os.popen("p4 -G %s" % cmd, "rb")
67     result = []
68     try:
69         while True:
70             entry = marshal.load(pipe)
71             result.append(entry)
72     except EOFError:
73         pass
74     pipe.close()
75     return result
77 def p4Cmd(cmd):
78     list = p4CmdList(cmd)
79     result = {}
80     for entry in list:
81         result.update(entry)
82     return result;
84 def commit(details):
85     global initialParent
86     global users
88     epoch = details["time"]
89     author = details["user"]
91     gitStream.write("commit %s\n" % branch)
92     committer = ""
93     if author in users:
94         committer = "%s %s %s" % (users[author], epoch, tz)
95     else:
96         committer = "%s <a@b> %s %s" % (author, epoch, tz)
98     gitStream.write("committer %s\n" % committer)
100     gitStream.write("data <<EOT\n")
101     gitStream.write(details["desc"])
102     gitStream.write("\n[ imported from %s; change %s ]\n" % (prefix, details["change"]))
103     gitStream.write("EOT\n\n")
105     if len(initialParent) > 0:
106         gitStream.write("from %s\n" % initialParent)
107         initialParent = ""
109     fnum = 0
110     while details.has_key("depotFile%s" % fnum):
111         path = details["depotFile%s" % fnum]
112         if not path.startswith(prefix):
113             print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, prefix, change)
114             fnum = fnum + 1
115             continue
117         rev = details["rev%s" % fnum]
118         depotPath = path + "#" + rev
119         relPath = path[len(prefix):]
120         action = details["action%s" % fnum]
122         if action == "delete":
123             gitStream.write("D %s\n" % relPath)
124         else:
125             mode = 644
126             if details["type%s" % fnum].startswith("x"):
127                 mode = 755
129             data = os.popen("p4 print -q \"%s\"" % depotPath, "rb").read()
131             gitStream.write("M %s inline %s\n" % (mode, relPath))
132             gitStream.write("data %s\n" % len(data))
133             gitStream.write(data)
134             gitStream.write("\n")
136         fnum = fnum + 1
138     gitStream.write("\n")
140     gitStream.write("tag p4/%s\n" % details["change"])
141     gitStream.write("from %s\n" % branch);
142     gitStream.write("tagger %s\n" % committer);
143     gitStream.write("data 0\n\n")
146 def getUserMap():
147     users = {}
149     for output in p4CmdList("users"):
150         if not output.has_key("User"):
151             continue
152         users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
153     return users
155 users = getUserMap()
157 if len(changeRange) == 0:
158     try:
159         sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch)
160         output = sout.read()
161         tagIdx = output.index(" tags/p4/")
162         caretIdx = output.index("^")
163         rev = int(output[tagIdx + 9 : caretIdx]) + 1
164         changeRange = "@%s,#head" % rev
165         initialParent = os.popen("git-rev-parse %s" % branch).read()[:-1]
166     except:
167         pass
169 sys.stderr.write("\n")
171 tz = - time.timezone / 36
172 tzsign = ("%s" % tz)[0]
173 if tzsign != '+' and tzsign != '-':
174     tz = "+" + ("%s" % tz)
176 if len(revision) > 0:
177     print "Doing initial import of %s from revision %s" % (prefix, revision)
179     details = { "user" : "git perforce import user", "time" : int(time.time()) }
180     details["desc"] = "Initial import of %s from the state at revision %s" % (prefix, revision)
181     details["change"] = revision
182     newestRevision = 0
184     fileCnt = 0
185     for info in p4CmdList("files %s...%s" % (prefix, revision)):
186         change = int(info["change"])
187         if change > newestRevision:
188             newestRevision = change
190         if info["action"] == "delete":
191             continue
193         for prop in [ "depotFile", "rev", "action", "type" ]:
194             details["%s%s" % (prop, fileCnt)] = info[prop]
196         fileCnt = fileCnt + 1
198     details["change"] = newestRevision
200     gitOutput, gitStream, gitError = popen2.popen3("git-fast-import")
201     try:
202         commit(details)
203     except:
204         print gitError.read()
206     gitStream.close()
207     gitOutput.close()
208     gitError.close()
209 else:
210     output = os.popen("p4 changes %s...%s" % (prefix, changeRange)).readlines()
212     changes = []
213     for line in output:
214         changeNum = line.split(" ")[1]
215         changes.append(changeNum)
217     changes.reverse()
219     if len(changes) == 0:
220         print "no changes to import!"
221         sys.exit(1)
223     gitOutput, gitStream, gitError = popen2.popen3("git-fast-import")
225     cnt = 1
226     for change in changes:
227         description = p4Cmd("describe %s" % change)
229         sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
230         sys.stdout.flush()
231         cnt = cnt + 1
233         try:
234             commit(description)
235         except:
236             print gitError.read()
237             sys.exit(1)
239     gitStream.close()
240     gitOutput.close()
241     gitError.close()
243 print ""
245 os.popen("git-repo-config p4.depotpath %s" % prefix).read()
247 sys.exit(0)