##// END OF EJS Templates
convert: remove pycompat.iteritems()...
Gregory Szorc -
r49769:417a1691 default
parent child Browse files
Show More
@@ -1,331 +1,330 b''
1 # cvs.py: CVS conversion code inspired by hg-cvs-import and git-cvsimport
1 # cvs.py: CVS conversion code inspired by hg-cvs-import and git-cvsimport
2 #
2 #
3 # Copyright 2005-2009 Olivia Mackall <olivia@selenic.com> and others
3 # Copyright 2005-2009 Olivia Mackall <olivia@selenic.com> and others
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 import errno
8 import errno
9 import os
9 import os
10 import re
10 import re
11 import socket
11 import socket
12
12
13 from mercurial.i18n import _
13 from mercurial.i18n import _
14 from mercurial.pycompat import (
14 from mercurial.pycompat import (
15 getattr,
15 getattr,
16 open,
16 open,
17 )
17 )
18 from mercurial import (
18 from mercurial import (
19 encoding,
19 encoding,
20 error,
20 error,
21 pycompat,
22 util,
21 util,
23 )
22 )
24 from mercurial.utils import (
23 from mercurial.utils import (
25 dateutil,
24 dateutil,
26 procutil,
25 procutil,
27 )
26 )
28
27
29 from . import (
28 from . import (
30 common,
29 common,
31 cvsps,
30 cvsps,
32 )
31 )
33
32
34 stringio = util.stringio
33 stringio = util.stringio
35 checktool = common.checktool
34 checktool = common.checktool
36 commit = common.commit
35 commit = common.commit
37 converter_source = common.converter_source
36 converter_source = common.converter_source
38 makedatetimestamp = common.makedatetimestamp
37 makedatetimestamp = common.makedatetimestamp
39 NoRepo = common.NoRepo
38 NoRepo = common.NoRepo
40
39
41
40
42 class convert_cvs(converter_source):
41 class convert_cvs(converter_source):
43 def __init__(self, ui, repotype, path, revs=None):
42 def __init__(self, ui, repotype, path, revs=None):
44 super(convert_cvs, self).__init__(ui, repotype, path, revs=revs)
43 super(convert_cvs, self).__init__(ui, repotype, path, revs=revs)
45
44
46 cvs = os.path.join(path, b"CVS")
45 cvs = os.path.join(path, b"CVS")
47 if not os.path.exists(cvs):
46 if not os.path.exists(cvs):
48 raise NoRepo(_(b"%s does not look like a CVS checkout") % path)
47 raise NoRepo(_(b"%s does not look like a CVS checkout") % path)
49
48
50 checktool(b'cvs')
49 checktool(b'cvs')
51
50
52 self.changeset = None
51 self.changeset = None
53 self.files = {}
52 self.files = {}
54 self.tags = {}
53 self.tags = {}
55 self.lastbranch = {}
54 self.lastbranch = {}
56 self.socket = None
55 self.socket = None
57 self.cvsroot = open(os.path.join(cvs, b"Root"), b'rb').read()[:-1]
56 self.cvsroot = open(os.path.join(cvs, b"Root"), b'rb').read()[:-1]
58 self.cvsrepo = open(os.path.join(cvs, b"Repository"), b'rb').read()[:-1]
57 self.cvsrepo = open(os.path.join(cvs, b"Repository"), b'rb').read()[:-1]
59 self.encoding = encoding.encoding
58 self.encoding = encoding.encoding
60
59
61 self._connect()
60 self._connect()
62
61
63 def _parse(self):
62 def _parse(self):
64 if self.changeset is not None:
63 if self.changeset is not None:
65 return
64 return
66 self.changeset = {}
65 self.changeset = {}
67
66
68 maxrev = 0
67 maxrev = 0
69 if self.revs:
68 if self.revs:
70 if len(self.revs) > 1:
69 if len(self.revs) > 1:
71 raise error.Abort(
70 raise error.Abort(
72 _(
71 _(
73 b'cvs source does not support specifying '
72 b'cvs source does not support specifying '
74 b'multiple revs'
73 b'multiple revs'
75 )
74 )
76 )
75 )
77 # TODO: handle tags
76 # TODO: handle tags
78 try:
77 try:
79 # patchset number?
78 # patchset number?
80 maxrev = int(self.revs[0])
79 maxrev = int(self.revs[0])
81 except ValueError:
80 except ValueError:
82 raise error.Abort(
81 raise error.Abort(
83 _(b'revision %s is not a patchset number') % self.revs[0]
82 _(b'revision %s is not a patchset number') % self.revs[0]
84 )
83 )
85
84
86 d = encoding.getcwd()
85 d = encoding.getcwd()
87 try:
86 try:
88 os.chdir(self.path)
87 os.chdir(self.path)
89
88
90 cache = b'update'
89 cache = b'update'
91 if not self.ui.configbool(b'convert', b'cvsps.cache'):
90 if not self.ui.configbool(b'convert', b'cvsps.cache'):
92 cache = None
91 cache = None
93 db = cvsps.createlog(self.ui, cache=cache)
92 db = cvsps.createlog(self.ui, cache=cache)
94 db = cvsps.createchangeset(
93 db = cvsps.createchangeset(
95 self.ui,
94 self.ui,
96 db,
95 db,
97 fuzz=int(self.ui.config(b'convert', b'cvsps.fuzz')),
96 fuzz=int(self.ui.config(b'convert', b'cvsps.fuzz')),
98 mergeto=self.ui.config(b'convert', b'cvsps.mergeto'),
97 mergeto=self.ui.config(b'convert', b'cvsps.mergeto'),
99 mergefrom=self.ui.config(b'convert', b'cvsps.mergefrom'),
98 mergefrom=self.ui.config(b'convert', b'cvsps.mergefrom'),
100 )
99 )
101
100
102 for cs in db:
101 for cs in db:
103 if maxrev and cs.id > maxrev:
102 if maxrev and cs.id > maxrev:
104 break
103 break
105 id = b"%d" % cs.id
104 id = b"%d" % cs.id
106 cs.author = self.recode(cs.author)
105 cs.author = self.recode(cs.author)
107 self.lastbranch[cs.branch] = id
106 self.lastbranch[cs.branch] = id
108 cs.comment = self.recode(cs.comment)
107 cs.comment = self.recode(cs.comment)
109 if self.ui.configbool(b'convert', b'localtimezone'):
108 if self.ui.configbool(b'convert', b'localtimezone'):
110 cs.date = makedatetimestamp(cs.date[0])
109 cs.date = makedatetimestamp(cs.date[0])
111 date = dateutil.datestr(cs.date, b'%Y-%m-%d %H:%M:%S %1%2')
110 date = dateutil.datestr(cs.date, b'%Y-%m-%d %H:%M:%S %1%2')
112 self.tags.update(dict.fromkeys(cs.tags, id))
111 self.tags.update(dict.fromkeys(cs.tags, id))
113
112
114 files = {}
113 files = {}
115 for f in cs.entries:
114 for f in cs.entries:
116 files[f.file] = b"%s%s" % (
115 files[f.file] = b"%s%s" % (
117 b'.'.join([(b"%d" % x) for x in f.revision]),
116 b'.'.join([(b"%d" % x) for x in f.revision]),
118 [b'', b'(DEAD)'][f.dead],
117 [b'', b'(DEAD)'][f.dead],
119 )
118 )
120
119
121 # add current commit to set
120 # add current commit to set
122 c = commit(
121 c = commit(
123 author=cs.author,
122 author=cs.author,
124 date=date,
123 date=date,
125 parents=[(b"%d" % p.id) for p in cs.parents],
124 parents=[(b"%d" % p.id) for p in cs.parents],
126 desc=cs.comment,
125 desc=cs.comment,
127 branch=cs.branch or b'',
126 branch=cs.branch or b'',
128 )
127 )
129 self.changeset[id] = c
128 self.changeset[id] = c
130 self.files[id] = files
129 self.files[id] = files
131
130
132 self.heads = self.lastbranch.values()
131 self.heads = self.lastbranch.values()
133 finally:
132 finally:
134 os.chdir(d)
133 os.chdir(d)
135
134
136 def _connect(self):
135 def _connect(self):
137 root = self.cvsroot
136 root = self.cvsroot
138 conntype = None
137 conntype = None
139 user, host = None, None
138 user, host = None, None
140 cmd = [b'cvs', b'server']
139 cmd = [b'cvs', b'server']
141
140
142 self.ui.status(_(b"connecting to %s\n") % root)
141 self.ui.status(_(b"connecting to %s\n") % root)
143
142
144 if root.startswith(b":pserver:"):
143 if root.startswith(b":pserver:"):
145 root = root[9:]
144 root = root[9:]
146 m = re.match(r'(?:(.*?)(?::(.*?))?@)?([^:/]*)(?::(\d*))?(.*)', root)
145 m = re.match(r'(?:(.*?)(?::(.*?))?@)?([^:/]*)(?::(\d*))?(.*)', root)
147 if m:
146 if m:
148 conntype = b"pserver"
147 conntype = b"pserver"
149 user, passw, serv, port, root = m.groups()
148 user, passw, serv, port, root = m.groups()
150 if not user:
149 if not user:
151 user = b"anonymous"
150 user = b"anonymous"
152 if not port:
151 if not port:
153 port = 2401
152 port = 2401
154 else:
153 else:
155 port = int(port)
154 port = int(port)
156 format0 = b":pserver:%s@%s:%s" % (user, serv, root)
155 format0 = b":pserver:%s@%s:%s" % (user, serv, root)
157 format1 = b":pserver:%s@%s:%d%s" % (user, serv, port, root)
156 format1 = b":pserver:%s@%s:%d%s" % (user, serv, port, root)
158
157
159 if not passw:
158 if not passw:
160 passw = b"A"
159 passw = b"A"
161 cvspass = os.path.expanduser(b"~/.cvspass")
160 cvspass = os.path.expanduser(b"~/.cvspass")
162 try:
161 try:
163 pf = open(cvspass, b'rb')
162 pf = open(cvspass, b'rb')
164 for line in pf.read().splitlines():
163 for line in pf.read().splitlines():
165 part1, part2 = line.split(b' ', 1)
164 part1, part2 = line.split(b' ', 1)
166 # /1 :pserver:user@example.com:2401/cvsroot/foo
165 # /1 :pserver:user@example.com:2401/cvsroot/foo
167 # Ah<Z
166 # Ah<Z
168 if part1 == b'/1':
167 if part1 == b'/1':
169 part1, part2 = part2.split(b' ', 1)
168 part1, part2 = part2.split(b' ', 1)
170 format = format1
169 format = format1
171 # :pserver:user@example.com:/cvsroot/foo Ah<Z
170 # :pserver:user@example.com:/cvsroot/foo Ah<Z
172 else:
171 else:
173 format = format0
172 format = format0
174 if part1 == format:
173 if part1 == format:
175 passw = part2
174 passw = part2
176 break
175 break
177 pf.close()
176 pf.close()
178 except IOError as inst:
177 except IOError as inst:
179 if inst.errno != errno.ENOENT:
178 if inst.errno != errno.ENOENT:
180 if not getattr(inst, 'filename', None):
179 if not getattr(inst, 'filename', None):
181 inst.filename = cvspass
180 inst.filename = cvspass
182 raise
181 raise
183
182
184 sck = socket.socket()
183 sck = socket.socket()
185 sck.connect((serv, port))
184 sck.connect((serv, port))
186 sck.send(
185 sck.send(
187 b"\n".join(
186 b"\n".join(
188 [
187 [
189 b"BEGIN AUTH REQUEST",
188 b"BEGIN AUTH REQUEST",
190 root,
189 root,
191 user,
190 user,
192 passw,
191 passw,
193 b"END AUTH REQUEST",
192 b"END AUTH REQUEST",
194 b"",
193 b"",
195 ]
194 ]
196 )
195 )
197 )
196 )
198 if sck.recv(128) != b"I LOVE YOU\n":
197 if sck.recv(128) != b"I LOVE YOU\n":
199 raise error.Abort(_(b"CVS pserver authentication failed"))
198 raise error.Abort(_(b"CVS pserver authentication failed"))
200
199
201 self.writep = self.readp = sck.makefile(b'r+')
200 self.writep = self.readp = sck.makefile(b'r+')
202
201
203 if not conntype and root.startswith(b":local:"):
202 if not conntype and root.startswith(b":local:"):
204 conntype = b"local"
203 conntype = b"local"
205 root = root[7:]
204 root = root[7:]
206
205
207 if not conntype:
206 if not conntype:
208 # :ext:user@host/home/user/path/to/cvsroot
207 # :ext:user@host/home/user/path/to/cvsroot
209 if root.startswith(b":ext:"):
208 if root.startswith(b":ext:"):
210 root = root[5:]
209 root = root[5:]
211 m = re.match(br'(?:([^@:/]+)@)?([^:/]+):?(.*)', root)
210 m = re.match(br'(?:([^@:/]+)@)?([^:/]+):?(.*)', root)
212 # Do not take Windows path "c:\foo\bar" for a connection strings
211 # Do not take Windows path "c:\foo\bar" for a connection strings
213 if os.path.isdir(root) or not m:
212 if os.path.isdir(root) or not m:
214 conntype = b"local"
213 conntype = b"local"
215 else:
214 else:
216 conntype = b"rsh"
215 conntype = b"rsh"
217 user, host, root = m.group(1), m.group(2), m.group(3)
216 user, host, root = m.group(1), m.group(2), m.group(3)
218
217
219 if conntype != b"pserver":
218 if conntype != b"pserver":
220 if conntype == b"rsh":
219 if conntype == b"rsh":
221 rsh = encoding.environ.get(b"CVS_RSH") or b"ssh"
220 rsh = encoding.environ.get(b"CVS_RSH") or b"ssh"
222 if user:
221 if user:
223 cmd = [rsh, b'-l', user, host] + cmd
222 cmd = [rsh, b'-l', user, host] + cmd
224 else:
223 else:
225 cmd = [rsh, host] + cmd
224 cmd = [rsh, host] + cmd
226
225
227 # popen2 does not support argument lists under Windows
226 # popen2 does not support argument lists under Windows
228 cmd = b' '.join(procutil.shellquote(arg) for arg in cmd)
227 cmd = b' '.join(procutil.shellquote(arg) for arg in cmd)
229 self.writep, self.readp = procutil.popen2(cmd)
228 self.writep, self.readp = procutil.popen2(cmd)
230
229
231 self.realroot = root
230 self.realroot = root
232
231
233 self.writep.write(b"Root %s\n" % root)
232 self.writep.write(b"Root %s\n" % root)
234 self.writep.write(
233 self.writep.write(
235 b"Valid-responses ok error Valid-requests Mode"
234 b"Valid-responses ok error Valid-requests Mode"
236 b" M Mbinary E Checked-in Created Updated"
235 b" M Mbinary E Checked-in Created Updated"
237 b" Merged Removed\n"
236 b" Merged Removed\n"
238 )
237 )
239 self.writep.write(b"valid-requests\n")
238 self.writep.write(b"valid-requests\n")
240 self.writep.flush()
239 self.writep.flush()
241 r = self.readp.readline()
240 r = self.readp.readline()
242 if not r.startswith(b"Valid-requests"):
241 if not r.startswith(b"Valid-requests"):
243 raise error.Abort(
242 raise error.Abort(
244 _(
243 _(
245 b'unexpected response from CVS server '
244 b'unexpected response from CVS server '
246 b'(expected "Valid-requests", but got %r)'
245 b'(expected "Valid-requests", but got %r)'
247 )
246 )
248 % r
247 % r
249 )
248 )
250 if b"UseUnchanged" in r:
249 if b"UseUnchanged" in r:
251 self.writep.write(b"UseUnchanged\n")
250 self.writep.write(b"UseUnchanged\n")
252 self.writep.flush()
251 self.writep.flush()
253 self.readp.readline()
252 self.readp.readline()
254
253
255 def getheads(self):
254 def getheads(self):
256 self._parse()
255 self._parse()
257 return self.heads
256 return self.heads
258
257
259 def getfile(self, name, rev):
258 def getfile(self, name, rev):
260 def chunkedread(fp, count):
259 def chunkedread(fp, count):
261 # file-objects returned by socket.makefile() do not handle
260 # file-objects returned by socket.makefile() do not handle
262 # large read() requests very well.
261 # large read() requests very well.
263 chunksize = 65536
262 chunksize = 65536
264 output = stringio()
263 output = stringio()
265 while count > 0:
264 while count > 0:
266 data = fp.read(min(count, chunksize))
265 data = fp.read(min(count, chunksize))
267 if not data:
266 if not data:
268 raise error.Abort(
267 raise error.Abort(
269 _(b"%d bytes missing from remote file") % count
268 _(b"%d bytes missing from remote file") % count
270 )
269 )
271 count -= len(data)
270 count -= len(data)
272 output.write(data)
271 output.write(data)
273 return output.getvalue()
272 return output.getvalue()
274
273
275 self._parse()
274 self._parse()
276 if rev.endswith(b"(DEAD)"):
275 if rev.endswith(b"(DEAD)"):
277 return None, None
276 return None, None
278
277
279 args = (b"-N -P -kk -r %s --" % rev).split()
278 args = (b"-N -P -kk -r %s --" % rev).split()
280 args.append(self.cvsrepo + b'/' + name)
279 args.append(self.cvsrepo + b'/' + name)
281 for x in args:
280 for x in args:
282 self.writep.write(b"Argument %s\n" % x)
281 self.writep.write(b"Argument %s\n" % x)
283 self.writep.write(b"Directory .\n%s\nco\n" % self.realroot)
282 self.writep.write(b"Directory .\n%s\nco\n" % self.realroot)
284 self.writep.flush()
283 self.writep.flush()
285
284
286 data = b""
285 data = b""
287 mode = None
286 mode = None
288 while True:
287 while True:
289 line = self.readp.readline()
288 line = self.readp.readline()
290 if line.startswith(b"Created ") or line.startswith(b"Updated "):
289 if line.startswith(b"Created ") or line.startswith(b"Updated "):
291 self.readp.readline() # path
290 self.readp.readline() # path
292 self.readp.readline() # entries
291 self.readp.readline() # entries
293 mode = self.readp.readline()[:-1]
292 mode = self.readp.readline()[:-1]
294 count = int(self.readp.readline()[:-1])
293 count = int(self.readp.readline()[:-1])
295 data = chunkedread(self.readp, count)
294 data = chunkedread(self.readp, count)
296 elif line.startswith(b" "):
295 elif line.startswith(b" "):
297 data += line[1:]
296 data += line[1:]
298 elif line.startswith(b"M "):
297 elif line.startswith(b"M "):
299 pass
298 pass
300 elif line.startswith(b"Mbinary "):
299 elif line.startswith(b"Mbinary "):
301 count = int(self.readp.readline()[:-1])
300 count = int(self.readp.readline()[:-1])
302 data = chunkedread(self.readp, count)
301 data = chunkedread(self.readp, count)
303 else:
302 else:
304 if line == b"ok\n":
303 if line == b"ok\n":
305 if mode is None:
304 if mode is None:
306 raise error.Abort(_(b'malformed response from CVS'))
305 raise error.Abort(_(b'malformed response from CVS'))
307 return (data, b"x" in mode and b"x" or b"")
306 return (data, b"x" in mode and b"x" or b"")
308 elif line.startswith(b"E "):
307 elif line.startswith(b"E "):
309 self.ui.warn(_(b"cvs server: %s\n") % line[2:])
308 self.ui.warn(_(b"cvs server: %s\n") % line[2:])
310 elif line.startswith(b"Remove"):
309 elif line.startswith(b"Remove"):
311 self.readp.readline()
310 self.readp.readline()
312 else:
311 else:
313 raise error.Abort(_(b"unknown CVS response: %s") % line)
312 raise error.Abort(_(b"unknown CVS response: %s") % line)
314
313
315 def getchanges(self, rev, full):
314 def getchanges(self, rev, full):
316 if full:
315 if full:
317 raise error.Abort(_(b"convert from cvs does not support --full"))
316 raise error.Abort(_(b"convert from cvs does not support --full"))
318 self._parse()
317 self._parse()
319 return sorted(pycompat.iteritems(self.files[rev])), {}, set()
318 return sorted(self.files[rev].items()), {}, set()
320
319
321 def getcommit(self, rev):
320 def getcommit(self, rev):
322 self._parse()
321 self._parse()
323 return self.changeset[rev]
322 return self.changeset[rev]
324
323
325 def gettags(self):
324 def gettags(self):
326 self._parse()
325 self._parse()
327 return self.tags
326 return self.tags
328
327
329 def getchangedfiles(self, rev, i):
328 def getchangedfiles(self, rev, i):
330 self._parse()
329 self._parse()
331 return sorted(self.files[rev])
330 return sorted(self.files[rev])
@@ -1,732 +1,731 b''
1 # hg.py - hg backend for convert extension
1 # hg.py - hg backend for convert extension
2 #
2 #
3 # Copyright 2005-2009 Olivia Mackall <olivia@selenic.com> and others
3 # Copyright 2005-2009 Olivia Mackall <olivia@selenic.com> and others
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 # Notes for hg->hg conversion:
8 # Notes for hg->hg conversion:
9 #
9 #
10 # * Old versions of Mercurial didn't trim the whitespace from the ends
10 # * Old versions of Mercurial didn't trim the whitespace from the ends
11 # of commit messages, but new versions do. Changesets created by
11 # of commit messages, but new versions do. Changesets created by
12 # those older versions, then converted, may thus have different
12 # those older versions, then converted, may thus have different
13 # hashes for changesets that are otherwise identical.
13 # hashes for changesets that are otherwise identical.
14 #
14 #
15 # * Using "--config convert.hg.saverev=true" will make the source
15 # * Using "--config convert.hg.saverev=true" will make the source
16 # identifier to be stored in the converted revision. This will cause
16 # identifier to be stored in the converted revision. This will cause
17 # the converted revision to have a different identity than the
17 # the converted revision to have a different identity than the
18 # source.
18 # source.
19
19
20 import os
20 import os
21 import re
21 import re
22 import time
22 import time
23
23
24 from mercurial.i18n import _
24 from mercurial.i18n import _
25 from mercurial.pycompat import open
25 from mercurial.pycompat import open
26 from mercurial.node import (
26 from mercurial.node import (
27 bin,
27 bin,
28 hex,
28 hex,
29 sha1nodeconstants,
29 sha1nodeconstants,
30 )
30 )
31 from mercurial import (
31 from mercurial import (
32 bookmarks,
32 bookmarks,
33 context,
33 context,
34 error,
34 error,
35 exchange,
35 exchange,
36 hg,
36 hg,
37 lock as lockmod,
37 lock as lockmod,
38 logcmdutil,
38 logcmdutil,
39 merge as mergemod,
39 merge as mergemod,
40 mergestate,
40 mergestate,
41 phases,
41 phases,
42 pycompat,
43 util,
42 util,
44 )
43 )
45 from mercurial.utils import dateutil
44 from mercurial.utils import dateutil
46
45
47 stringio = util.stringio
46 stringio = util.stringio
48
47
49 from . import common
48 from . import common
50
49
51 mapfile = common.mapfile
50 mapfile = common.mapfile
52 NoRepo = common.NoRepo
51 NoRepo = common.NoRepo
53
52
54 sha1re = re.compile(br'\b[0-9a-f]{12,40}\b')
53 sha1re = re.compile(br'\b[0-9a-f]{12,40}\b')
55
54
56
55
57 class mercurial_sink(common.converter_sink):
56 class mercurial_sink(common.converter_sink):
58 def __init__(self, ui, repotype, path):
57 def __init__(self, ui, repotype, path):
59 common.converter_sink.__init__(self, ui, repotype, path)
58 common.converter_sink.__init__(self, ui, repotype, path)
60 self.branchnames = ui.configbool(b'convert', b'hg.usebranchnames')
59 self.branchnames = ui.configbool(b'convert', b'hg.usebranchnames')
61 self.clonebranches = ui.configbool(b'convert', b'hg.clonebranches')
60 self.clonebranches = ui.configbool(b'convert', b'hg.clonebranches')
62 self.tagsbranch = ui.config(b'convert', b'hg.tagsbranch')
61 self.tagsbranch = ui.config(b'convert', b'hg.tagsbranch')
63 self.lastbranch = None
62 self.lastbranch = None
64 if os.path.isdir(path) and len(os.listdir(path)) > 0:
63 if os.path.isdir(path) and len(os.listdir(path)) > 0:
65 try:
64 try:
66 self.repo = hg.repository(self.ui, path)
65 self.repo = hg.repository(self.ui, path)
67 if not self.repo.local():
66 if not self.repo.local():
68 raise NoRepo(
67 raise NoRepo(
69 _(b'%s is not a local Mercurial repository') % path
68 _(b'%s is not a local Mercurial repository') % path
70 )
69 )
71 except error.RepoError as err:
70 except error.RepoError as err:
72 ui.traceback()
71 ui.traceback()
73 raise NoRepo(err.args[0])
72 raise NoRepo(err.args[0])
74 else:
73 else:
75 try:
74 try:
76 ui.status(_(b'initializing destination %s repository\n') % path)
75 ui.status(_(b'initializing destination %s repository\n') % path)
77 self.repo = hg.repository(self.ui, path, create=True)
76 self.repo = hg.repository(self.ui, path, create=True)
78 if not self.repo.local():
77 if not self.repo.local():
79 raise NoRepo(
78 raise NoRepo(
80 _(b'%s is not a local Mercurial repository') % path
79 _(b'%s is not a local Mercurial repository') % path
81 )
80 )
82 self.created.append(path)
81 self.created.append(path)
83 except error.RepoError:
82 except error.RepoError:
84 ui.traceback()
83 ui.traceback()
85 raise NoRepo(
84 raise NoRepo(
86 _(b"could not create hg repository %s as sink") % path
85 _(b"could not create hg repository %s as sink") % path
87 )
86 )
88 self.lock = None
87 self.lock = None
89 self.wlock = None
88 self.wlock = None
90 self.filemapmode = False
89 self.filemapmode = False
91 self.subrevmaps = {}
90 self.subrevmaps = {}
92
91
93 def before(self):
92 def before(self):
94 self.ui.debug(b'run hg sink pre-conversion action\n')
93 self.ui.debug(b'run hg sink pre-conversion action\n')
95 self.wlock = self.repo.wlock()
94 self.wlock = self.repo.wlock()
96 self.lock = self.repo.lock()
95 self.lock = self.repo.lock()
97
96
98 def after(self):
97 def after(self):
99 self.ui.debug(b'run hg sink post-conversion action\n')
98 self.ui.debug(b'run hg sink post-conversion action\n')
100 if self.lock:
99 if self.lock:
101 self.lock.release()
100 self.lock.release()
102 if self.wlock:
101 if self.wlock:
103 self.wlock.release()
102 self.wlock.release()
104
103
105 def revmapfile(self):
104 def revmapfile(self):
106 return self.repo.vfs.join(b"shamap")
105 return self.repo.vfs.join(b"shamap")
107
106
108 def authorfile(self):
107 def authorfile(self):
109 return self.repo.vfs.join(b"authormap")
108 return self.repo.vfs.join(b"authormap")
110
109
111 def setbranch(self, branch, pbranches):
110 def setbranch(self, branch, pbranches):
112 if not self.clonebranches:
111 if not self.clonebranches:
113 return
112 return
114
113
115 setbranch = branch != self.lastbranch
114 setbranch = branch != self.lastbranch
116 self.lastbranch = branch
115 self.lastbranch = branch
117 if not branch:
116 if not branch:
118 branch = b'default'
117 branch = b'default'
119 pbranches = [(b[0], b[1] and b[1] or b'default') for b in pbranches]
118 pbranches = [(b[0], b[1] and b[1] or b'default') for b in pbranches]
120
119
121 branchpath = os.path.join(self.path, branch)
120 branchpath = os.path.join(self.path, branch)
122 if setbranch:
121 if setbranch:
123 self.after()
122 self.after()
124 try:
123 try:
125 self.repo = hg.repository(self.ui, branchpath)
124 self.repo = hg.repository(self.ui, branchpath)
126 except Exception:
125 except Exception:
127 self.repo = hg.repository(self.ui, branchpath, create=True)
126 self.repo = hg.repository(self.ui, branchpath, create=True)
128 self.before()
127 self.before()
129
128
130 # pbranches may bring revisions from other branches (merge parents)
129 # pbranches may bring revisions from other branches (merge parents)
131 # Make sure we have them, or pull them.
130 # Make sure we have them, or pull them.
132 missings = {}
131 missings = {}
133 for b in pbranches:
132 for b in pbranches:
134 try:
133 try:
135 self.repo.lookup(b[0])
134 self.repo.lookup(b[0])
136 except Exception:
135 except Exception:
137 missings.setdefault(b[1], []).append(b[0])
136 missings.setdefault(b[1], []).append(b[0])
138
137
139 if missings:
138 if missings:
140 self.after()
139 self.after()
141 for pbranch, heads in sorted(missings.items()):
140 for pbranch, heads in sorted(missings.items()):
142 pbranchpath = os.path.join(self.path, pbranch)
141 pbranchpath = os.path.join(self.path, pbranch)
143 prepo = hg.peer(self.ui, {}, pbranchpath)
142 prepo = hg.peer(self.ui, {}, pbranchpath)
144 self.ui.note(
143 self.ui.note(
145 _(b'pulling from %s into %s\n') % (pbranch, branch)
144 _(b'pulling from %s into %s\n') % (pbranch, branch)
146 )
145 )
147 exchange.pull(
146 exchange.pull(
148 self.repo, prepo, heads=[prepo.lookup(h) for h in heads]
147 self.repo, prepo, heads=[prepo.lookup(h) for h in heads]
149 )
148 )
150 self.before()
149 self.before()
151
150
152 def _rewritetags(self, source, revmap, data):
151 def _rewritetags(self, source, revmap, data):
153 fp = stringio()
152 fp = stringio()
154 for line in data.splitlines():
153 for line in data.splitlines():
155 s = line.split(b' ', 1)
154 s = line.split(b' ', 1)
156 if len(s) != 2:
155 if len(s) != 2:
157 self.ui.warn(_(b'invalid tag entry: "%s"\n') % line)
156 self.ui.warn(_(b'invalid tag entry: "%s"\n') % line)
158 fp.write(b'%s\n' % line) # Bogus, but keep for hash stability
157 fp.write(b'%s\n' % line) # Bogus, but keep for hash stability
159 continue
158 continue
160 revid = revmap.get(source.lookuprev(s[0]))
159 revid = revmap.get(source.lookuprev(s[0]))
161 if not revid:
160 if not revid:
162 if s[0] == sha1nodeconstants.nullhex:
161 if s[0] == sha1nodeconstants.nullhex:
163 revid = s[0]
162 revid = s[0]
164 else:
163 else:
165 # missing, but keep for hash stability
164 # missing, but keep for hash stability
166 self.ui.warn(_(b'missing tag entry: "%s"\n') % line)
165 self.ui.warn(_(b'missing tag entry: "%s"\n') % line)
167 fp.write(b'%s\n' % line)
166 fp.write(b'%s\n' % line)
168 continue
167 continue
169 fp.write(b'%s %s\n' % (revid, s[1]))
168 fp.write(b'%s %s\n' % (revid, s[1]))
170 return fp.getvalue()
169 return fp.getvalue()
171
170
172 def _rewritesubstate(self, source, data):
171 def _rewritesubstate(self, source, data):
173 fp = stringio()
172 fp = stringio()
174 for line in data.splitlines():
173 for line in data.splitlines():
175 s = line.split(b' ', 1)
174 s = line.split(b' ', 1)
176 if len(s) != 2:
175 if len(s) != 2:
177 continue
176 continue
178
177
179 revid = s[0]
178 revid = s[0]
180 subpath = s[1]
179 subpath = s[1]
181 if revid != sha1nodeconstants.nullhex:
180 if revid != sha1nodeconstants.nullhex:
182 revmap = self.subrevmaps.get(subpath)
181 revmap = self.subrevmaps.get(subpath)
183 if revmap is None:
182 if revmap is None:
184 revmap = mapfile(
183 revmap = mapfile(
185 self.ui, self.repo.wjoin(subpath, b'.hg/shamap')
184 self.ui, self.repo.wjoin(subpath, b'.hg/shamap')
186 )
185 )
187 self.subrevmaps[subpath] = revmap
186 self.subrevmaps[subpath] = revmap
188
187
189 # It is reasonable that one or more of the subrepos don't
188 # It is reasonable that one or more of the subrepos don't
190 # need to be converted, in which case they can be cloned
189 # need to be converted, in which case they can be cloned
191 # into place instead of converted. Therefore, only warn
190 # into place instead of converted. Therefore, only warn
192 # once.
191 # once.
193 msg = _(b'no ".hgsubstate" updates will be made for "%s"\n')
192 msg = _(b'no ".hgsubstate" updates will be made for "%s"\n')
194 if len(revmap) == 0:
193 if len(revmap) == 0:
195 sub = self.repo.wvfs.reljoin(subpath, b'.hg')
194 sub = self.repo.wvfs.reljoin(subpath, b'.hg')
196
195
197 if self.repo.wvfs.exists(sub):
196 if self.repo.wvfs.exists(sub):
198 self.ui.warn(msg % subpath)
197 self.ui.warn(msg % subpath)
199
198
200 newid = revmap.get(revid)
199 newid = revmap.get(revid)
201 if not newid:
200 if not newid:
202 if len(revmap) > 0:
201 if len(revmap) > 0:
203 self.ui.warn(
202 self.ui.warn(
204 _(b"%s is missing from %s/.hg/shamap\n")
203 _(b"%s is missing from %s/.hg/shamap\n")
205 % (revid, subpath)
204 % (revid, subpath)
206 )
205 )
207 else:
206 else:
208 revid = newid
207 revid = newid
209
208
210 fp.write(b'%s %s\n' % (revid, subpath))
209 fp.write(b'%s %s\n' % (revid, subpath))
211
210
212 return fp.getvalue()
211 return fp.getvalue()
213
212
214 def _calculatemergedfiles(self, source, p1ctx, p2ctx):
213 def _calculatemergedfiles(self, source, p1ctx, p2ctx):
215 """Calculates the files from p2 that we need to pull in when merging p1
214 """Calculates the files from p2 that we need to pull in when merging p1
216 and p2, given that the merge is coming from the given source.
215 and p2, given that the merge is coming from the given source.
217
216
218 This prevents us from losing files that only exist in the target p2 and
217 This prevents us from losing files that only exist in the target p2 and
219 that don't come from the source repo (like if you're merging multiple
218 that don't come from the source repo (like if you're merging multiple
220 repositories together).
219 repositories together).
221 """
220 """
222 anc = [p1ctx.ancestor(p2ctx)]
221 anc = [p1ctx.ancestor(p2ctx)]
223 # Calculate what files are coming from p2
222 # Calculate what files are coming from p2
224 # TODO: mresult.commitinfo might be able to get that info
223 # TODO: mresult.commitinfo might be able to get that info
225 mresult = mergemod.calculateupdates(
224 mresult = mergemod.calculateupdates(
226 self.repo,
225 self.repo,
227 p1ctx,
226 p1ctx,
228 p2ctx,
227 p2ctx,
229 anc,
228 anc,
230 branchmerge=True,
229 branchmerge=True,
231 force=True,
230 force=True,
232 acceptremote=False,
231 acceptremote=False,
233 followcopies=False,
232 followcopies=False,
234 )
233 )
235
234
236 for file, (action, info, msg) in mresult.filemap():
235 for file, (action, info, msg) in mresult.filemap():
237 if source.targetfilebelongstosource(file):
236 if source.targetfilebelongstosource(file):
238 # If the file belongs to the source repo, ignore the p2
237 # If the file belongs to the source repo, ignore the p2
239 # since it will be covered by the existing fileset.
238 # since it will be covered by the existing fileset.
240 continue
239 continue
241
240
242 # If the file requires actual merging, abort. We don't have enough
241 # If the file requires actual merging, abort. We don't have enough
243 # context to resolve merges correctly.
242 # context to resolve merges correctly.
244 if action in mergestate.CONVERT_MERGE_ACTIONS:
243 if action in mergestate.CONVERT_MERGE_ACTIONS:
245 raise error.Abort(
244 raise error.Abort(
246 _(
245 _(
247 b"unable to convert merge commit "
246 b"unable to convert merge commit "
248 b"since target parents do not merge cleanly (file "
247 b"since target parents do not merge cleanly (file "
249 b"%s, parents %s and %s)"
248 b"%s, parents %s and %s)"
250 )
249 )
251 % (file, p1ctx, p2ctx)
250 % (file, p1ctx, p2ctx)
252 )
251 )
253 elif action == mergestate.ACTION_KEEP:
252 elif action == mergestate.ACTION_KEEP:
254 # 'keep' means nothing changed from p1
253 # 'keep' means nothing changed from p1
255 continue
254 continue
256 else:
255 else:
257 # Any other change means we want to take the p2 version
256 # Any other change means we want to take the p2 version
258 yield file
257 yield file
259
258
260 def putcommit(
259 def putcommit(
261 self, files, copies, parents, commit, source, revmap, full, cleanp2
260 self, files, copies, parents, commit, source, revmap, full, cleanp2
262 ):
261 ):
263 files = dict(files)
262 files = dict(files)
264
263
265 def getfilectx(repo, memctx, f):
264 def getfilectx(repo, memctx, f):
266 if p2ctx and f in p2files and f not in copies:
265 if p2ctx and f in p2files and f not in copies:
267 self.ui.debug(b'reusing %s from p2\n' % f)
266 self.ui.debug(b'reusing %s from p2\n' % f)
268 try:
267 try:
269 return p2ctx[f]
268 return p2ctx[f]
270 except error.ManifestLookupError:
269 except error.ManifestLookupError:
271 # If the file doesn't exist in p2, then we're syncing a
270 # If the file doesn't exist in p2, then we're syncing a
272 # delete, so just return None.
271 # delete, so just return None.
273 return None
272 return None
274 try:
273 try:
275 v = files[f]
274 v = files[f]
276 except KeyError:
275 except KeyError:
277 return None
276 return None
278 data, mode = source.getfile(f, v)
277 data, mode = source.getfile(f, v)
279 if data is None:
278 if data is None:
280 return None
279 return None
281 if f == b'.hgtags':
280 if f == b'.hgtags':
282 data = self._rewritetags(source, revmap, data)
281 data = self._rewritetags(source, revmap, data)
283 if f == b'.hgsubstate':
282 if f == b'.hgsubstate':
284 data = self._rewritesubstate(source, data)
283 data = self._rewritesubstate(source, data)
285 return context.memfilectx(
284 return context.memfilectx(
286 self.repo,
285 self.repo,
287 memctx,
286 memctx,
288 f,
287 f,
289 data,
288 data,
290 b'l' in mode,
289 b'l' in mode,
291 b'x' in mode,
290 b'x' in mode,
292 copies.get(f),
291 copies.get(f),
293 )
292 )
294
293
295 pl = []
294 pl = []
296 for p in parents:
295 for p in parents:
297 if p not in pl:
296 if p not in pl:
298 pl.append(p)
297 pl.append(p)
299 parents = pl
298 parents = pl
300 nparents = len(parents)
299 nparents = len(parents)
301 if self.filemapmode and nparents == 1:
300 if self.filemapmode and nparents == 1:
302 m1node = self.repo.changelog.read(bin(parents[0]))[0]
301 m1node = self.repo.changelog.read(bin(parents[0]))[0]
303 parent = parents[0]
302 parent = parents[0]
304
303
305 if len(parents) < 2:
304 if len(parents) < 2:
306 parents.append(self.repo.nullid)
305 parents.append(self.repo.nullid)
307 if len(parents) < 2:
306 if len(parents) < 2:
308 parents.append(self.repo.nullid)
307 parents.append(self.repo.nullid)
309 p2 = parents.pop(0)
308 p2 = parents.pop(0)
310
309
311 text = commit.desc
310 text = commit.desc
312
311
313 sha1s = re.findall(sha1re, text)
312 sha1s = re.findall(sha1re, text)
314 for sha1 in sha1s:
313 for sha1 in sha1s:
315 oldrev = source.lookuprev(sha1)
314 oldrev = source.lookuprev(sha1)
316 newrev = revmap.get(oldrev)
315 newrev = revmap.get(oldrev)
317 if newrev is not None:
316 if newrev is not None:
318 text = text.replace(sha1, newrev[: len(sha1)])
317 text = text.replace(sha1, newrev[: len(sha1)])
319
318
320 extra = commit.extra.copy()
319 extra = commit.extra.copy()
321
320
322 sourcename = self.repo.ui.config(b'convert', b'hg.sourcename')
321 sourcename = self.repo.ui.config(b'convert', b'hg.sourcename')
323 if sourcename:
322 if sourcename:
324 extra[b'convert_source'] = sourcename
323 extra[b'convert_source'] = sourcename
325
324
326 for label in (
325 for label in (
327 b'source',
326 b'source',
328 b'transplant_source',
327 b'transplant_source',
329 b'rebase_source',
328 b'rebase_source',
330 b'intermediate-source',
329 b'intermediate-source',
331 ):
330 ):
332 node = extra.get(label)
331 node = extra.get(label)
333
332
334 if node is None:
333 if node is None:
335 continue
334 continue
336
335
337 # Only transplant stores its reference in binary
336 # Only transplant stores its reference in binary
338 if label == b'transplant_source':
337 if label == b'transplant_source':
339 node = hex(node)
338 node = hex(node)
340
339
341 newrev = revmap.get(node)
340 newrev = revmap.get(node)
342 if newrev is not None:
341 if newrev is not None:
343 if label == b'transplant_source':
342 if label == b'transplant_source':
344 newrev = bin(newrev)
343 newrev = bin(newrev)
345
344
346 extra[label] = newrev
345 extra[label] = newrev
347
346
348 if self.branchnames and commit.branch:
347 if self.branchnames and commit.branch:
349 extra[b'branch'] = commit.branch
348 extra[b'branch'] = commit.branch
350 if commit.rev and commit.saverev:
349 if commit.rev and commit.saverev:
351 extra[b'convert_revision'] = commit.rev
350 extra[b'convert_revision'] = commit.rev
352
351
353 while parents:
352 while parents:
354 p1 = p2
353 p1 = p2
355 p2 = parents.pop(0)
354 p2 = parents.pop(0)
356 p1ctx = self.repo[p1]
355 p1ctx = self.repo[p1]
357 p2ctx = None
356 p2ctx = None
358 if p2 != self.repo.nullid:
357 if p2 != self.repo.nullid:
359 p2ctx = self.repo[p2]
358 p2ctx = self.repo[p2]
360 fileset = set(files)
359 fileset = set(files)
361 if full:
360 if full:
362 fileset.update(self.repo[p1])
361 fileset.update(self.repo[p1])
363 fileset.update(self.repo[p2])
362 fileset.update(self.repo[p2])
364
363
365 if p2ctx:
364 if p2ctx:
366 p2files = set(cleanp2)
365 p2files = set(cleanp2)
367 for file in self._calculatemergedfiles(source, p1ctx, p2ctx):
366 for file in self._calculatemergedfiles(source, p1ctx, p2ctx):
368 p2files.add(file)
367 p2files.add(file)
369 fileset.add(file)
368 fileset.add(file)
370
369
371 ctx = context.memctx(
370 ctx = context.memctx(
372 self.repo,
371 self.repo,
373 (p1, p2),
372 (p1, p2),
374 text,
373 text,
375 fileset,
374 fileset,
376 getfilectx,
375 getfilectx,
377 commit.author,
376 commit.author,
378 commit.date,
377 commit.date,
379 extra,
378 extra,
380 )
379 )
381
380
382 # We won't know if the conversion changes the node until after the
381 # We won't know if the conversion changes the node until after the
383 # commit, so copy the source's phase for now.
382 # commit, so copy the source's phase for now.
384 self.repo.ui.setconfig(
383 self.repo.ui.setconfig(
385 b'phases',
384 b'phases',
386 b'new-commit',
385 b'new-commit',
387 phases.phasenames[commit.phase],
386 phases.phasenames[commit.phase],
388 b'convert',
387 b'convert',
389 )
388 )
390
389
391 with self.repo.transaction(b"convert") as tr:
390 with self.repo.transaction(b"convert") as tr:
392 if self.repo.ui.config(b'convert', b'hg.preserve-hash'):
391 if self.repo.ui.config(b'convert', b'hg.preserve-hash'):
393 origctx = commit.ctx
392 origctx = commit.ctx
394 else:
393 else:
395 origctx = None
394 origctx = None
396 node = hex(self.repo.commitctx(ctx, origctx=origctx))
395 node = hex(self.repo.commitctx(ctx, origctx=origctx))
397
396
398 # If the node value has changed, but the phase is lower than
397 # If the node value has changed, but the phase is lower than
399 # draft, set it back to draft since it hasn't been exposed
398 # draft, set it back to draft since it hasn't been exposed
400 # anywhere.
399 # anywhere.
401 if commit.rev != node:
400 if commit.rev != node:
402 ctx = self.repo[node]
401 ctx = self.repo[node]
403 if ctx.phase() < phases.draft:
402 if ctx.phase() < phases.draft:
404 phases.registernew(
403 phases.registernew(
405 self.repo, tr, phases.draft, [ctx.rev()]
404 self.repo, tr, phases.draft, [ctx.rev()]
406 )
405 )
407
406
408 text = b"(octopus merge fixup)\n"
407 text = b"(octopus merge fixup)\n"
409 p2 = node
408 p2 = node
410
409
411 if self.filemapmode and nparents == 1:
410 if self.filemapmode and nparents == 1:
412 man = self.repo.manifestlog.getstorage(b'')
411 man = self.repo.manifestlog.getstorage(b'')
413 mnode = self.repo.changelog.read(bin(p2))[0]
412 mnode = self.repo.changelog.read(bin(p2))[0]
414 closed = b'close' in commit.extra
413 closed = b'close' in commit.extra
415 if not closed and not man.cmp(m1node, man.revision(mnode)):
414 if not closed and not man.cmp(m1node, man.revision(mnode)):
416 self.ui.status(_(b"filtering out empty revision\n"))
415 self.ui.status(_(b"filtering out empty revision\n"))
417 self.repo.rollback(force=True)
416 self.repo.rollback(force=True)
418 return parent
417 return parent
419 return p2
418 return p2
420
419
421 def puttags(self, tags):
420 def puttags(self, tags):
422 tagparent = self.repo.branchtip(self.tagsbranch, ignoremissing=True)
421 tagparent = self.repo.branchtip(self.tagsbranch, ignoremissing=True)
423 tagparent = tagparent or self.repo.nullid
422 tagparent = tagparent or self.repo.nullid
424
423
425 oldlines = set()
424 oldlines = set()
426 for branch, heads in pycompat.iteritems(self.repo.branchmap()):
425 for branch, heads in self.repo.branchmap().items():
427 for h in heads:
426 for h in heads:
428 if b'.hgtags' in self.repo[h]:
427 if b'.hgtags' in self.repo[h]:
429 oldlines.update(
428 oldlines.update(
430 set(self.repo[h][b'.hgtags'].data().splitlines(True))
429 set(self.repo[h][b'.hgtags'].data().splitlines(True))
431 )
430 )
432 oldlines = sorted(list(oldlines))
431 oldlines = sorted(list(oldlines))
433
432
434 newlines = sorted([(b"%s %s\n" % (tags[tag], tag)) for tag in tags])
433 newlines = sorted([(b"%s %s\n" % (tags[tag], tag)) for tag in tags])
435 if newlines == oldlines:
434 if newlines == oldlines:
436 return None, None
435 return None, None
437
436
438 # if the old and new tags match, then there is nothing to update
437 # if the old and new tags match, then there is nothing to update
439 oldtags = set()
438 oldtags = set()
440 newtags = set()
439 newtags = set()
441 for line in oldlines:
440 for line in oldlines:
442 s = line.strip().split(b' ', 1)
441 s = line.strip().split(b' ', 1)
443 if len(s) != 2:
442 if len(s) != 2:
444 continue
443 continue
445 oldtags.add(s[1])
444 oldtags.add(s[1])
446 for line in newlines:
445 for line in newlines:
447 s = line.strip().split(b' ', 1)
446 s = line.strip().split(b' ', 1)
448 if len(s) != 2:
447 if len(s) != 2:
449 continue
448 continue
450 if s[1] not in oldtags:
449 if s[1] not in oldtags:
451 newtags.add(s[1].strip())
450 newtags.add(s[1].strip())
452
451
453 if not newtags:
452 if not newtags:
454 return None, None
453 return None, None
455
454
456 data = b"".join(newlines)
455 data = b"".join(newlines)
457
456
458 def getfilectx(repo, memctx, f):
457 def getfilectx(repo, memctx, f):
459 return context.memfilectx(repo, memctx, f, data, False, False, None)
458 return context.memfilectx(repo, memctx, f, data, False, False, None)
460
459
461 self.ui.status(_(b"updating tags\n"))
460 self.ui.status(_(b"updating tags\n"))
462 date = b"%d 0" % int(time.mktime(time.gmtime()))
461 date = b"%d 0" % int(time.mktime(time.gmtime()))
463 extra = {b'branch': self.tagsbranch}
462 extra = {b'branch': self.tagsbranch}
464 ctx = context.memctx(
463 ctx = context.memctx(
465 self.repo,
464 self.repo,
466 (tagparent, None),
465 (tagparent, None),
467 b"update tags",
466 b"update tags",
468 [b".hgtags"],
467 [b".hgtags"],
469 getfilectx,
468 getfilectx,
470 b"convert-repo",
469 b"convert-repo",
471 date,
470 date,
472 extra,
471 extra,
473 )
472 )
474 node = self.repo.commitctx(ctx)
473 node = self.repo.commitctx(ctx)
475 return hex(node), hex(tagparent)
474 return hex(node), hex(tagparent)
476
475
477 def setfilemapmode(self, active):
476 def setfilemapmode(self, active):
478 self.filemapmode = active
477 self.filemapmode = active
479
478
480 def putbookmarks(self, updatedbookmark):
479 def putbookmarks(self, updatedbookmark):
481 if not len(updatedbookmark):
480 if not len(updatedbookmark):
482 return
481 return
483 wlock = lock = tr = None
482 wlock = lock = tr = None
484 try:
483 try:
485 wlock = self.repo.wlock()
484 wlock = self.repo.wlock()
486 lock = self.repo.lock()
485 lock = self.repo.lock()
487 tr = self.repo.transaction(b'bookmark')
486 tr = self.repo.transaction(b'bookmark')
488 self.ui.status(_(b"updating bookmarks\n"))
487 self.ui.status(_(b"updating bookmarks\n"))
489 destmarks = self.repo._bookmarks
488 destmarks = self.repo._bookmarks
490 changes = [
489 changes = [
491 (bookmark, bin(updatedbookmark[bookmark]))
490 (bookmark, bin(updatedbookmark[bookmark]))
492 for bookmark in updatedbookmark
491 for bookmark in updatedbookmark
493 ]
492 ]
494 destmarks.applychanges(self.repo, tr, changes)
493 destmarks.applychanges(self.repo, tr, changes)
495 tr.close()
494 tr.close()
496 finally:
495 finally:
497 lockmod.release(lock, wlock, tr)
496 lockmod.release(lock, wlock, tr)
498
497
499 def hascommitfrommap(self, rev):
498 def hascommitfrommap(self, rev):
500 # the exact semantics of clonebranches is unclear so we can't say no
499 # the exact semantics of clonebranches is unclear so we can't say no
501 return rev in self.repo or self.clonebranches
500 return rev in self.repo or self.clonebranches
502
501
503 def hascommitforsplicemap(self, rev):
502 def hascommitforsplicemap(self, rev):
504 if rev not in self.repo and self.clonebranches:
503 if rev not in self.repo and self.clonebranches:
505 raise error.Abort(
504 raise error.Abort(
506 _(
505 _(
507 b'revision %s not found in destination '
506 b'revision %s not found in destination '
508 b'repository (lookups with clonebranches=true '
507 b'repository (lookups with clonebranches=true '
509 b'are not implemented)'
508 b'are not implemented)'
510 )
509 )
511 % rev
510 % rev
512 )
511 )
513 return rev in self.repo
512 return rev in self.repo
514
513
515
514
516 class mercurial_source(common.converter_source):
515 class mercurial_source(common.converter_source):
517 def __init__(self, ui, repotype, path, revs=None):
516 def __init__(self, ui, repotype, path, revs=None):
518 common.converter_source.__init__(self, ui, repotype, path, revs)
517 common.converter_source.__init__(self, ui, repotype, path, revs)
519 self.ignoreerrors = ui.configbool(b'convert', b'hg.ignoreerrors')
518 self.ignoreerrors = ui.configbool(b'convert', b'hg.ignoreerrors')
520 self.ignored = set()
519 self.ignored = set()
521 self.saverev = ui.configbool(b'convert', b'hg.saverev')
520 self.saverev = ui.configbool(b'convert', b'hg.saverev')
522 try:
521 try:
523 self.repo = hg.repository(self.ui, path)
522 self.repo = hg.repository(self.ui, path)
524 # try to provoke an exception if this isn't really a hg
523 # try to provoke an exception if this isn't really a hg
525 # repo, but some other bogus compatible-looking url
524 # repo, but some other bogus compatible-looking url
526 if not self.repo.local():
525 if not self.repo.local():
527 raise error.RepoError
526 raise error.RepoError
528 except error.RepoError:
527 except error.RepoError:
529 ui.traceback()
528 ui.traceback()
530 raise NoRepo(_(b"%s is not a local Mercurial repository") % path)
529 raise NoRepo(_(b"%s is not a local Mercurial repository") % path)
531 self.lastrev = None
530 self.lastrev = None
532 self.lastctx = None
531 self.lastctx = None
533 self._changescache = None, None
532 self._changescache = None, None
534 self.convertfp = None
533 self.convertfp = None
535 # Restrict converted revisions to startrev descendants
534 # Restrict converted revisions to startrev descendants
536 startnode = ui.config(b'convert', b'hg.startrev')
535 startnode = ui.config(b'convert', b'hg.startrev')
537 hgrevs = ui.config(b'convert', b'hg.revs')
536 hgrevs = ui.config(b'convert', b'hg.revs')
538 if hgrevs is None:
537 if hgrevs is None:
539 if startnode is not None:
538 if startnode is not None:
540 try:
539 try:
541 startnode = self.repo.lookup(startnode)
540 startnode = self.repo.lookup(startnode)
542 except error.RepoError:
541 except error.RepoError:
543 raise error.Abort(
542 raise error.Abort(
544 _(b'%s is not a valid start revision') % startnode
543 _(b'%s is not a valid start revision') % startnode
545 )
544 )
546 startrev = self.repo.changelog.rev(startnode)
545 startrev = self.repo.changelog.rev(startnode)
547 children = {startnode: 1}
546 children = {startnode: 1}
548 for r in self.repo.changelog.descendants([startrev]):
547 for r in self.repo.changelog.descendants([startrev]):
549 children[self.repo.changelog.node(r)] = 1
548 children[self.repo.changelog.node(r)] = 1
550 self.keep = children.__contains__
549 self.keep = children.__contains__
551 else:
550 else:
552 self.keep = util.always
551 self.keep = util.always
553 if revs:
552 if revs:
554 self._heads = [self.repo.lookup(r) for r in revs]
553 self._heads = [self.repo.lookup(r) for r in revs]
555 else:
554 else:
556 self._heads = self.repo.heads()
555 self._heads = self.repo.heads()
557 else:
556 else:
558 if revs or startnode is not None:
557 if revs or startnode is not None:
559 raise error.Abort(
558 raise error.Abort(
560 _(
559 _(
561 b'hg.revs cannot be combined with '
560 b'hg.revs cannot be combined with '
562 b'hg.startrev or --rev'
561 b'hg.startrev or --rev'
563 )
562 )
564 )
563 )
565 nodes = set()
564 nodes = set()
566 parents = set()
565 parents = set()
567 for r in logcmdutil.revrange(self.repo, [hgrevs]):
566 for r in logcmdutil.revrange(self.repo, [hgrevs]):
568 ctx = self.repo[r]
567 ctx = self.repo[r]
569 nodes.add(ctx.node())
568 nodes.add(ctx.node())
570 parents.update(p.node() for p in ctx.parents())
569 parents.update(p.node() for p in ctx.parents())
571 self.keep = nodes.__contains__
570 self.keep = nodes.__contains__
572 self._heads = nodes - parents
571 self._heads = nodes - parents
573
572
574 def _changectx(self, rev):
573 def _changectx(self, rev):
575 if self.lastrev != rev:
574 if self.lastrev != rev:
576 self.lastctx = self.repo[rev]
575 self.lastctx = self.repo[rev]
577 self.lastrev = rev
576 self.lastrev = rev
578 return self.lastctx
577 return self.lastctx
579
578
580 def _parents(self, ctx):
579 def _parents(self, ctx):
581 return [p for p in ctx.parents() if p and self.keep(p.node())]
580 return [p for p in ctx.parents() if p and self.keep(p.node())]
582
581
583 def getheads(self):
582 def getheads(self):
584 return [hex(h) for h in self._heads if self.keep(h)]
583 return [hex(h) for h in self._heads if self.keep(h)]
585
584
586 def getfile(self, name, rev):
585 def getfile(self, name, rev):
587 try:
586 try:
588 fctx = self._changectx(rev)[name]
587 fctx = self._changectx(rev)[name]
589 return fctx.data(), fctx.flags()
588 return fctx.data(), fctx.flags()
590 except error.LookupError:
589 except error.LookupError:
591 return None, None
590 return None, None
592
591
593 def _changedfiles(self, ctx1, ctx2):
592 def _changedfiles(self, ctx1, ctx2):
594 ma, r = [], []
593 ma, r = [], []
595 maappend = ma.append
594 maappend = ma.append
596 rappend = r.append
595 rappend = r.append
597 d = ctx1.manifest().diff(ctx2.manifest())
596 d = ctx1.manifest().diff(ctx2.manifest())
598 for f, ((node1, flag1), (node2, flag2)) in d.items():
597 for f, ((node1, flag1), (node2, flag2)) in d.items():
599 if node2 is None:
598 if node2 is None:
600 rappend(f)
599 rappend(f)
601 else:
600 else:
602 maappend(f)
601 maappend(f)
603 return ma, r
602 return ma, r
604
603
605 def getchanges(self, rev, full):
604 def getchanges(self, rev, full):
606 ctx = self._changectx(rev)
605 ctx = self._changectx(rev)
607 parents = self._parents(ctx)
606 parents = self._parents(ctx)
608 if full or not parents:
607 if full or not parents:
609 files = copyfiles = ctx.manifest()
608 files = copyfiles = ctx.manifest()
610 if parents:
609 if parents:
611 if self._changescache[0] == rev:
610 if self._changescache[0] == rev:
612 ma, r = self._changescache[1]
611 ma, r = self._changescache[1]
613 else:
612 else:
614 ma, r = self._changedfiles(parents[0], ctx)
613 ma, r = self._changedfiles(parents[0], ctx)
615 if not full:
614 if not full:
616 files = ma + r
615 files = ma + r
617 copyfiles = ma
616 copyfiles = ma
618 # _getcopies() is also run for roots and before filtering so missing
617 # _getcopies() is also run for roots and before filtering so missing
619 # revlogs are detected early
618 # revlogs are detected early
620 copies = self._getcopies(ctx, parents, copyfiles)
619 copies = self._getcopies(ctx, parents, copyfiles)
621 cleanp2 = set()
620 cleanp2 = set()
622 if len(parents) == 2:
621 if len(parents) == 2:
623 d = parents[1].manifest().diff(ctx.manifest(), clean=True)
622 d = parents[1].manifest().diff(ctx.manifest(), clean=True)
624 for f, value in d.items():
623 for f, value in d.items():
625 if value is None:
624 if value is None:
626 cleanp2.add(f)
625 cleanp2.add(f)
627 changes = [(f, rev) for f in files if f not in self.ignored]
626 changes = [(f, rev) for f in files if f not in self.ignored]
628 changes.sort()
627 changes.sort()
629 return changes, copies, cleanp2
628 return changes, copies, cleanp2
630
629
631 def _getcopies(self, ctx, parents, files):
630 def _getcopies(self, ctx, parents, files):
632 copies = {}
631 copies = {}
633 for name in files:
632 for name in files:
634 if name in self.ignored:
633 if name in self.ignored:
635 continue
634 continue
636 try:
635 try:
637 copysource = ctx.filectx(name).copysource()
636 copysource = ctx.filectx(name).copysource()
638 if copysource in self.ignored:
637 if copysource in self.ignored:
639 continue
638 continue
640 # Ignore copy sources not in parent revisions
639 # Ignore copy sources not in parent revisions
641 if not any(copysource in p for p in parents):
640 if not any(copysource in p for p in parents):
642 continue
641 continue
643 copies[name] = copysource
642 copies[name] = copysource
644 except TypeError:
643 except TypeError:
645 pass
644 pass
646 except error.LookupError as e:
645 except error.LookupError as e:
647 if not self.ignoreerrors:
646 if not self.ignoreerrors:
648 raise
647 raise
649 self.ignored.add(name)
648 self.ignored.add(name)
650 self.ui.warn(_(b'ignoring: %s\n') % e)
649 self.ui.warn(_(b'ignoring: %s\n') % e)
651 return copies
650 return copies
652
651
653 def getcommit(self, rev):
652 def getcommit(self, rev):
654 ctx = self._changectx(rev)
653 ctx = self._changectx(rev)
655 _parents = self._parents(ctx)
654 _parents = self._parents(ctx)
656 parents = [p.hex() for p in _parents]
655 parents = [p.hex() for p in _parents]
657 optparents = [p.hex() for p in ctx.parents() if p and p not in _parents]
656 optparents = [p.hex() for p in ctx.parents() if p and p not in _parents]
658 crev = rev
657 crev = rev
659
658
660 return common.commit(
659 return common.commit(
661 author=ctx.user(),
660 author=ctx.user(),
662 date=dateutil.datestr(ctx.date(), b'%Y-%m-%d %H:%M:%S %1%2'),
661 date=dateutil.datestr(ctx.date(), b'%Y-%m-%d %H:%M:%S %1%2'),
663 desc=ctx.description(),
662 desc=ctx.description(),
664 rev=crev,
663 rev=crev,
665 parents=parents,
664 parents=parents,
666 optparents=optparents,
665 optparents=optparents,
667 branch=ctx.branch(),
666 branch=ctx.branch(),
668 extra=ctx.extra(),
667 extra=ctx.extra(),
669 sortkey=ctx.rev(),
668 sortkey=ctx.rev(),
670 saverev=self.saverev,
669 saverev=self.saverev,
671 phase=ctx.phase(),
670 phase=ctx.phase(),
672 ctx=ctx,
671 ctx=ctx,
673 )
672 )
674
673
675 def numcommits(self):
674 def numcommits(self):
676 return len(self.repo)
675 return len(self.repo)
677
676
678 def gettags(self):
677 def gettags(self):
679 # This will get written to .hgtags, filter non global tags out.
678 # This will get written to .hgtags, filter non global tags out.
680 tags = [
679 tags = [
681 t
680 t
682 for t in self.repo.tagslist()
681 for t in self.repo.tagslist()
683 if self.repo.tagtype(t[0]) == b'global'
682 if self.repo.tagtype(t[0]) == b'global'
684 ]
683 ]
685 return {name: hex(node) for name, node in tags if self.keep(node)}
684 return {name: hex(node) for name, node in tags if self.keep(node)}
686
685
687 def getchangedfiles(self, rev, i):
686 def getchangedfiles(self, rev, i):
688 ctx = self._changectx(rev)
687 ctx = self._changectx(rev)
689 parents = self._parents(ctx)
688 parents = self._parents(ctx)
690 if not parents and i is None:
689 if not parents and i is None:
691 i = 0
690 i = 0
692 ma, r = ctx.manifest().keys(), []
691 ma, r = ctx.manifest().keys(), []
693 else:
692 else:
694 i = i or 0
693 i = i or 0
695 ma, r = self._changedfiles(parents[i], ctx)
694 ma, r = self._changedfiles(parents[i], ctx)
696 ma, r = [[f for f in l if f not in self.ignored] for l in (ma, r)]
695 ma, r = [[f for f in l if f not in self.ignored] for l in (ma, r)]
697
696
698 if i == 0:
697 if i == 0:
699 self._changescache = (rev, (ma, r))
698 self._changescache = (rev, (ma, r))
700
699
701 return ma + r
700 return ma + r
702
701
703 def converted(self, rev, destrev):
702 def converted(self, rev, destrev):
704 if self.convertfp is None:
703 if self.convertfp is None:
705 self.convertfp = open(self.repo.vfs.join(b'shamap'), b'ab')
704 self.convertfp = open(self.repo.vfs.join(b'shamap'), b'ab')
706 self.convertfp.write(util.tonativeeol(b'%s %s\n' % (destrev, rev)))
705 self.convertfp.write(util.tonativeeol(b'%s %s\n' % (destrev, rev)))
707 self.convertfp.flush()
706 self.convertfp.flush()
708
707
709 def before(self):
708 def before(self):
710 self.ui.debug(b'run hg source pre-conversion action\n')
709 self.ui.debug(b'run hg source pre-conversion action\n')
711
710
712 def after(self):
711 def after(self):
713 self.ui.debug(b'run hg source post-conversion action\n')
712 self.ui.debug(b'run hg source post-conversion action\n')
714
713
715 def hasnativeorder(self):
714 def hasnativeorder(self):
716 return True
715 return True
717
716
718 def hasnativeclose(self):
717 def hasnativeclose(self):
719 return True
718 return True
720
719
721 def lookuprev(self, rev):
720 def lookuprev(self, rev):
722 try:
721 try:
723 return hex(self.repo.lookup(rev))
722 return hex(self.repo.lookup(rev))
724 except (error.RepoError, error.LookupError):
723 except (error.RepoError, error.LookupError):
725 return None
724 return None
726
725
727 def getbookmarks(self):
726 def getbookmarks(self):
728 return bookmarks.listbookmarks(self.repo)
727 return bookmarks.listbookmarks(self.repo)
729
728
730 def checkrevformat(self, revstr, mapname=b'splicemap'):
729 def checkrevformat(self, revstr, mapname=b'splicemap'):
731 """Mercurial, revision string is a 40 byte hex"""
730 """Mercurial, revision string is a 40 byte hex"""
732 self.checkhexformat(revstr, mapname)
731 self.checkhexformat(revstr, mapname)
General Comments 0
You need to be logged in to leave comments. Login now