##// END OF EJS Templates
convert: handle deleted files when converting from Perforce (issue4743)
Eugene Baranov -
r25745:501c51d6 stable
parent child Browse files
Show More
@@ -1,207 +1,208
1 # Perforce source for convert extension.
1 # Perforce source for convert extension.
2 #
2 #
3 # Copyright 2009, Frank Kingswood <frank@kingswood-consulting.co.uk>
3 # Copyright 2009, Frank Kingswood <frank@kingswood-consulting.co.uk>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from mercurial import util
8 from mercurial import util
9 from mercurial.i18n import _
9 from mercurial.i18n import _
10
10
11 from common import commit, converter_source, checktool, NoRepo
11 from common import commit, converter_source, checktool, NoRepo
12 import marshal
12 import marshal
13 import re
13 import re
14
14
15 def loaditer(f):
15 def loaditer(f):
16 "Yield the dictionary objects generated by p4"
16 "Yield the dictionary objects generated by p4"
17 try:
17 try:
18 while True:
18 while True:
19 d = marshal.load(f)
19 d = marshal.load(f)
20 if not d:
20 if not d:
21 break
21 break
22 yield d
22 yield d
23 except EOFError:
23 except EOFError:
24 pass
24 pass
25
25
26 class p4_source(converter_source):
26 class p4_source(converter_source):
27 def __init__(self, ui, path, rev=None):
27 def __init__(self, ui, path, rev=None):
28 super(p4_source, self).__init__(ui, path, rev=rev)
28 super(p4_source, self).__init__(ui, path, rev=rev)
29
29
30 if "/" in path and not path.startswith('//'):
30 if "/" in path and not path.startswith('//'):
31 raise NoRepo(_('%s does not look like a P4 repository') % path)
31 raise NoRepo(_('%s does not look like a P4 repository') % path)
32
32
33 checktool('p4', abort=False)
33 checktool('p4', abort=False)
34
34
35 self.p4changes = {}
35 self.p4changes = {}
36 self.heads = {}
36 self.heads = {}
37 self.changeset = {}
37 self.changeset = {}
38 self.files = {}
38 self.files = {}
39 self.tags = {}
39 self.tags = {}
40 self.lastbranch = {}
40 self.lastbranch = {}
41 self.parent = {}
41 self.parent = {}
42 self.encoding = "latin_1"
42 self.encoding = "latin_1"
43 self.depotname = {} # mapping from local name to depot name
43 self.depotname = {} # mapping from local name to depot name
44 self.re_type = re.compile(
44 self.re_type = re.compile(
45 "([a-z]+)?(text|binary|symlink|apple|resource|unicode|utf\d+)"
45 "([a-z]+)?(text|binary|symlink|apple|resource|unicode|utf\d+)"
46 "(\+\w+)?$")
46 "(\+\w+)?$")
47 self.re_keywords = re.compile(
47 self.re_keywords = re.compile(
48 r"\$(Id|Header|Date|DateTime|Change|File|Revision|Author)"
48 r"\$(Id|Header|Date|DateTime|Change|File|Revision|Author)"
49 r":[^$\n]*\$")
49 r":[^$\n]*\$")
50 self.re_keywords_old = re.compile("\$(Id|Header):[^$\n]*\$")
50 self.re_keywords_old = re.compile("\$(Id|Header):[^$\n]*\$")
51
51
52 self._parse(ui, path)
52 self._parse(ui, path)
53
53
54 def _parse_view(self, path):
54 def _parse_view(self, path):
55 "Read changes affecting the path"
55 "Read changes affecting the path"
56 cmd = 'p4 -G changes -s submitted %s' % util.shellquote(path)
56 cmd = 'p4 -G changes -s submitted %s' % util.shellquote(path)
57 stdout = util.popen(cmd, mode='rb')
57 stdout = util.popen(cmd, mode='rb')
58 for d in loaditer(stdout):
58 for d in loaditer(stdout):
59 c = d.get("change", None)
59 c = d.get("change", None)
60 if c:
60 if c:
61 self.p4changes[c] = True
61 self.p4changes[c] = True
62
62
63 def _parse(self, ui, path):
63 def _parse(self, ui, path):
64 "Prepare list of P4 filenames and revisions to import"
64 "Prepare list of P4 filenames and revisions to import"
65 ui.status(_('reading p4 views\n'))
65 ui.status(_('reading p4 views\n'))
66
66
67 # read client spec or view
67 # read client spec or view
68 if "/" in path:
68 if "/" in path:
69 self._parse_view(path)
69 self._parse_view(path)
70 if path.startswith("//") and path.endswith("/..."):
70 if path.startswith("//") and path.endswith("/..."):
71 views = {path[:-3]:""}
71 views = {path[:-3]:""}
72 else:
72 else:
73 views = {"//": ""}
73 views = {"//": ""}
74 else:
74 else:
75 cmd = 'p4 -G client -o %s' % util.shellquote(path)
75 cmd = 'p4 -G client -o %s' % util.shellquote(path)
76 clientspec = marshal.load(util.popen(cmd, mode='rb'))
76 clientspec = marshal.load(util.popen(cmd, mode='rb'))
77
77
78 views = {}
78 views = {}
79 for client in clientspec:
79 for client in clientspec:
80 if client.startswith("View"):
80 if client.startswith("View"):
81 sview, cview = clientspec[client].split()
81 sview, cview = clientspec[client].split()
82 self._parse_view(sview)
82 self._parse_view(sview)
83 if sview.endswith("...") and cview.endswith("..."):
83 if sview.endswith("...") and cview.endswith("..."):
84 sview = sview[:-3]
84 sview = sview[:-3]
85 cview = cview[:-3]
85 cview = cview[:-3]
86 cview = cview[2:]
86 cview = cview[2:]
87 cview = cview[cview.find("/") + 1:]
87 cview = cview[cview.find("/") + 1:]
88 views[sview] = cview
88 views[sview] = cview
89
89
90 # list of changes that affect our source files
90 # list of changes that affect our source files
91 self.p4changes = self.p4changes.keys()
91 self.p4changes = self.p4changes.keys()
92 self.p4changes.sort(key=int)
92 self.p4changes.sort(key=int)
93
93
94 # list with depot pathnames, longest first
94 # list with depot pathnames, longest first
95 vieworder = views.keys()
95 vieworder = views.keys()
96 vieworder.sort(key=len, reverse=True)
96 vieworder.sort(key=len, reverse=True)
97
97
98 # handle revision limiting
98 # handle revision limiting
99 startrev = self.ui.config('convert', 'p4.startrev', default=0)
99 startrev = self.ui.config('convert', 'p4.startrev', default=0)
100 self.p4changes = [x for x in self.p4changes
100 self.p4changes = [x for x in self.p4changes
101 if ((not startrev or int(x) >= int(startrev)) and
101 if ((not startrev or int(x) >= int(startrev)) and
102 (not self.rev or int(x) <= int(self.rev)))]
102 (not self.rev or int(x) <= int(self.rev)))]
103
103
104 # now read the full changelists to get the list of file revisions
104 # now read the full changelists to get the list of file revisions
105 ui.status(_('collecting p4 changelists\n'))
105 ui.status(_('collecting p4 changelists\n'))
106 lastid = None
106 lastid = None
107 for change in self.p4changes:
107 for change in self.p4changes:
108 cmd = "p4 -G describe -s %s" % change
108 cmd = "p4 -G describe -s %s" % change
109 stdout = util.popen(cmd, mode='rb')
109 stdout = util.popen(cmd, mode='rb')
110 d = marshal.load(stdout)
110 d = marshal.load(stdout)
111 desc = self.recode(d.get("desc", ""))
111 desc = self.recode(d.get("desc", ""))
112 shortdesc = desc.split("\n", 1)[0]
112 shortdesc = desc.split("\n", 1)[0]
113 t = '%s %s' % (d["change"], repr(shortdesc)[1:-1])
113 t = '%s %s' % (d["change"], repr(shortdesc)[1:-1])
114 ui.status(util.ellipsis(t, 80) + '\n')
114 ui.status(util.ellipsis(t, 80) + '\n')
115
115
116 if lastid:
116 if lastid:
117 parents = [lastid]
117 parents = [lastid]
118 else:
118 else:
119 parents = []
119 parents = []
120
120
121 date = (int(d["time"]), 0) # timezone not set
121 date = (int(d["time"]), 0) # timezone not set
122 c = commit(author=self.recode(d["user"]),
122 c = commit(author=self.recode(d["user"]),
123 date=util.datestr(date, '%Y-%m-%d %H:%M:%S %1%2'),
123 date=util.datestr(date, '%Y-%m-%d %H:%M:%S %1%2'),
124 parents=parents, desc=desc, branch='',
124 parents=parents, desc=desc, branch='',
125 extra={"p4": change})
125 extra={"p4": change})
126
126
127 files = []
127 files = []
128 i = 0
128 i = 0
129 while ("depotFile%d" % i) in d and ("rev%d" % i) in d:
129 while ("depotFile%d" % i) in d and ("rev%d" % i) in d:
130 oldname = d["depotFile%d" % i]
130 oldname = d["depotFile%d" % i]
131 filename = None
131 filename = None
132 for v in vieworder:
132 for v in vieworder:
133 if oldname.startswith(v):
133 if oldname.startswith(v):
134 filename = views[v] + oldname[len(v):]
134 filename = views[v] + oldname[len(v):]
135 break
135 break
136 if filename:
136 if filename:
137 files.append((filename, d["rev%d" % i]))
137 files.append((filename, d["rev%d" % i]))
138 self.depotname[filename] = oldname
138 self.depotname[filename] = oldname
139 i += 1
139 i += 1
140 self.changeset[change] = c
140 self.changeset[change] = c
141 self.files[change] = files
141 self.files[change] = files
142 lastid = change
142 lastid = change
143
143
144 if lastid:
144 if lastid:
145 self.heads = [lastid]
145 self.heads = [lastid]
146
146
147 def getheads(self):
147 def getheads(self):
148 return self.heads
148 return self.heads
149
149
150 def getfile(self, name, rev):
150 def getfile(self, name, rev):
151 cmd = 'p4 -G print %s' \
151 cmd = 'p4 -G print %s' \
152 % util.shellquote("%s#%s" % (self.depotname[name], rev))
152 % util.shellquote("%s#%s" % (self.depotname[name], rev))
153 stdout = util.popen(cmd, mode='rb')
153 stdout = util.popen(cmd, mode='rb')
154
154
155 mode = None
155 mode = None
156 contents = ""
156 contents = ""
157 keywords = None
157 keywords = None
158
158
159 for d in loaditer(stdout):
159 for d in loaditer(stdout):
160 code = d["code"]
160 code = d["code"]
161 data = d.get("data")
161 data = d.get("data")
162
162
163 if code == "error":
163 if code == "error":
164 raise IOError(d["generic"], data)
164 raise IOError(d["generic"], data)
165
165
166 elif code == "stat":
166 elif code == "stat":
167 if d.get("action") == "purge":
167 action = d.get("action")
168 if action in ["purge", "delete", "move/delete"]:
168 return None, None
169 return None, None
169 p4type = self.re_type.match(d["type"])
170 p4type = self.re_type.match(d["type"])
170 if p4type:
171 if p4type:
171 mode = ""
172 mode = ""
172 flags = (p4type.group(1) or "") + (p4type.group(3) or "")
173 flags = (p4type.group(1) or "") + (p4type.group(3) or "")
173 if "x" in flags:
174 if "x" in flags:
174 mode = "x"
175 mode = "x"
175 if p4type.group(2) == "symlink":
176 if p4type.group(2) == "symlink":
176 mode = "l"
177 mode = "l"
177 if "ko" in flags:
178 if "ko" in flags:
178 keywords = self.re_keywords_old
179 keywords = self.re_keywords_old
179 elif "k" in flags:
180 elif "k" in flags:
180 keywords = self.re_keywords
181 keywords = self.re_keywords
181
182
182 elif code == "text" or code == "binary":
183 elif code == "text" or code == "binary":
183 contents += data
184 contents += data
184
185
185 if mode is None:
186 if mode is None:
186 return None, None
187 return None, None
187
188
188 if keywords:
189 if keywords:
189 contents = keywords.sub("$\\1$", contents)
190 contents = keywords.sub("$\\1$", contents)
190 if mode == "l" and contents.endswith("\n"):
191 if mode == "l" and contents.endswith("\n"):
191 contents = contents[:-1]
192 contents = contents[:-1]
192
193
193 return contents, mode
194 return contents, mode
194
195
195 def getchanges(self, rev, full):
196 def getchanges(self, rev, full):
196 if full:
197 if full:
197 raise util.Abort(_("convert from p4 do not support --full"))
198 raise util.Abort(_("convert from p4 do not support --full"))
198 return self.files[rev], {}, set()
199 return self.files[rev], {}, set()
199
200
200 def getcommit(self, rev):
201 def getcommit(self, rev):
201 return self.changeset[rev]
202 return self.changeset[rev]
202
203
203 def gettags(self):
204 def gettags(self):
204 return self.tags
205 return self.tags
205
206
206 def getchangedfiles(self, rev, i):
207 def getchangedfiles(self, rev, i):
207 return sorted([x[0] for x in self.files[rev]])
208 return sorted([x[0] for x in self.files[rev]])
General Comments 0
You need to be logged in to leave comments. Login now