Mercurial > hg
comparison hgext/convert/p4.py @ 7823:11efa41037e2 1.2
convert: Perforce source for conversion to Mercurial
author | Frank Kingswood <frank@kingswood-consulting.co.uk> |
---|---|
date | Tue, 03 Mar 2009 21:32:23 +0000 |
parents | |
children | bc027d72c289 |
comparison
equal
deleted
inserted
replaced
7822:1079e666e938 | 7823:11efa41037e2 |
---|---|
1 # | |
2 # Perforce source for convert extension. | |
3 # | |
4 # Copyright 2009, Frank Kingswood <frank@kingswood-consulting.co.uk> | |
5 # | |
6 # This software may be used and distributed according to the terms | |
7 # of the GNU General Public License, incorporated herein by reference. | |
8 # | |
9 | |
10 from mercurial import util | |
11 from mercurial.i18n import _ | |
12 | |
13 from common import commit, converter_source, checktool | |
14 import marshal | |
15 | |
16 def loaditer(f): | |
17 "Yield the dictionary objects generated by p4" | |
18 try: | |
19 while True: | |
20 d = marshal.load(f) | |
21 if not d: | |
22 break | |
23 yield d | |
24 except EOFError: | |
25 pass | |
26 | |
27 class p4_source(converter_source): | |
28 def __init__(self, ui, path, rev=None): | |
29 super(p4_source, self).__init__(ui, path, rev=rev) | |
30 | |
31 checktool('p4') | |
32 | |
33 self.p4changes = {} | |
34 self.heads = {} | |
35 self.changeset = {} | |
36 self.files = {} | |
37 self.tags = {} | |
38 self.lastbranch = {} | |
39 self.parent = {} | |
40 self.encoding = "latin_1" | |
41 self.depotname = {} # mapping from local name to depot name | |
42 self.modecache = {} | |
43 | |
44 self._parse(ui, path) | |
45 | |
46 def _parse_view(self, path): | |
47 "Read changes affecting the path" | |
48 cmd = "p4 -G changes -s submitted '%s'" % path | |
49 stdout = util.popen(cmd) | |
50 for d in loaditer(stdout): | |
51 c = d.get("change", None) | |
52 if c: | |
53 self.p4changes[c] = True | |
54 | |
55 def _parse(self, ui, path): | |
56 "Prepare list of P4 filenames and revisions to import" | |
57 ui.status(_('reading p4 views\n')) | |
58 | |
59 # read client spec or view | |
60 if "/" in path: | |
61 self._parse_view(path) | |
62 if path.startswith("//") and path.endswith("/..."): | |
63 views = {path[:-3]:""} | |
64 else: | |
65 views = {"//": ""} | |
66 else: | |
67 cmd = "p4 -G client -o '%s'" % path | |
68 clientspec = marshal.load(util.popen(cmd)) | |
69 | |
70 views = {} | |
71 for client in clientspec: | |
72 if client.startswith("View"): | |
73 sview, cview = clientspec[client].split() | |
74 self._parse_view(sview) | |
75 if sview.endswith("...") and cview.endswith("..."): | |
76 sview = sview[:-3] | |
77 cview = cview[:-3] | |
78 cview = cview[2:] | |
79 cview = cview[cview.find("/") + 1:] | |
80 views[sview] = cview | |
81 | |
82 # list of changes that affect our source files | |
83 self.p4changes = self.p4changes.keys() | |
84 self.p4changes.sort(key=int) | |
85 | |
86 # list with depot pathnames, longest first | |
87 vieworder = views.keys() | |
88 vieworder.sort(key=lambda x: -len(x)) | |
89 | |
90 # handle revision limiting | |
91 startrev = self.ui.config('convert', 'p4.startrev', default=0) | |
92 self.p4changes = [x for x in self.p4changes | |
93 if ((not startrev or int(x) >= int(startrev)) and | |
94 (not self.rev or int(x) <= int(self.rev)))] | |
95 | |
96 # now read the full changelists to get the list of file revisions | |
97 ui.status(_('collecting p4 changelists\n')) | |
98 lastid = None | |
99 for change in self.p4changes: | |
100 cmd = "p4 -G describe %s" % change | |
101 stdout = util.popen(cmd) | |
102 d = marshal.load(stdout) | |
103 | |
104 desc = self.recode(d["desc"]) | |
105 shortdesc = desc.split("\n", 1)[0] | |
106 t = '%s %s' % (d["change"], repr(shortdesc)[1:-1]) | |
107 ui.status(util.ellipsis(t, 80) + '\n') | |
108 | |
109 if lastid: | |
110 parents = [lastid] | |
111 else: | |
112 parents = [] | |
113 | |
114 date = (int(d["time"]), 0) # timezone not set | |
115 c = commit(author=self.recode(d["user"]), date=util.datestr(date), | |
116 parents=parents, desc=desc, branch='', extra={"p4": change}) | |
117 | |
118 files = [] | |
119 i = 0 | |
120 while ("depotFile%d" % i) in d and ("rev%d" % i) in d: | |
121 oldname = d["depotFile%d" % i] | |
122 filename = None | |
123 for v in vieworder: | |
124 if oldname.startswith(v): | |
125 filename = views[v] + oldname[len(v):] | |
126 break | |
127 if filename: | |
128 files.append((filename, d["rev%d" % i])) | |
129 self.depotname[filename] = oldname | |
130 i += 1 | |
131 self.changeset[change] = c | |
132 self.files[change] = files | |
133 lastid = change | |
134 | |
135 if lastid: | |
136 self.heads = [lastid] | |
137 | |
138 def getheads(self): | |
139 return self.heads | |
140 | |
141 def getfile(self, name, rev): | |
142 cmd = "p4 -G print '%s#%s'" % (self.depotname[name], rev) | |
143 stdout = util.popen(cmd) | |
144 | |
145 mode = None | |
146 data = "" | |
147 | |
148 for d in loaditer(stdout): | |
149 if d["code"] == "stat": | |
150 if "+x" in d["type"]: | |
151 mode = "x" | |
152 else: | |
153 mode = "" | |
154 elif d["code"] == "text": | |
155 data += d["data"] | |
156 | |
157 if mode is None: | |
158 raise IOError() | |
159 | |
160 self.modecache[(name, rev)] = mode | |
161 return data | |
162 | |
163 def getmode(self, name, rev): | |
164 return self.modecache[(name, rev)] | |
165 | |
166 def getchanges(self, rev): | |
167 return self.files[rev], {} | |
168 | |
169 def getcommit(self, rev): | |
170 return self.changeset[rev] | |
171 | |
172 def gettags(self): | |
173 return self.tags | |
174 | |
175 def getchangedfiles(self, rev, i): | |
176 return util.sort([x[0] for x in self.files[rev]]) |