##// END OF EJS Templates
Merge with crew-stable
Patrick Mezard -
r5959:0162c6cc merge default
parent child Browse files
Show More
@@ -0,0 +1,54 b''
1 #!/bin/sh
2
3 echo "[extensions]" >> $HGRCPATH
4 echo "hgext.convert = " >> $HGRCPATH
5 echo "[convert]" >> $HGRCPATH
6 echo "hg.tagsbranch=0" >> $HGRCPATH
7
8 hg init source
9 cd source
10 echo a > a
11 hg ci -qAm adda
12 # Add a merge with one parent in the same branch
13 echo a >> a
14 hg ci -qAm changea
15 hg up -qC 0
16 hg branch branch0
17 echo b > b
18 hg ci -qAm addb
19 hg up -qC
20 hg merge
21 hg ci -qm mergeab
22 hg tag -ql mergeab
23 cd ..
24
25 # Miss perl... sometimes
26 cat > filter.py <<EOF
27 import sys, re
28
29 r = re.compile(r'^(?:\d+|pulling from)')
30 sys.stdout.writelines([l for l in sys.stdin if r.search(l)])
31 EOF
32
33 echo % convert
34 hg convert -v --config convert.hg.clonebranches=1 source dest |
35 python filter.py
36
37 # Add a merge with both parents and child in different branches
38 cd source
39 hg branch branch1
40 echo a > file1
41 hg ci -qAm c1
42 hg up -qC mergeab
43 hg branch branch2
44 echo a > file2
45 hg ci -qAm c2
46 hg merge branch1
47 hg branch branch3
48 hg ci -qAm c3
49 cd ..
50
51 echo % incremental conversion
52 hg convert -v --config convert.hg.clonebranches=1 source dest |
53 python filter.py
54
@@ -0,0 +1,29 b''
1 marked working directory as branch branch0
2 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3 (branch merge, don't forget to commit)
4 % convert
5 3 adda
6 2 addb
7 pulling from default into branch0
8 1 changesets found
9 1 changea
10 0 mergeab
11 pulling from default into branch0
12 1 changesets found
13 marked working directory as branch branch1
14 marked working directory as branch branch2
15 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
16 (branch merge, don't forget to commit)
17 marked working directory as branch branch3
18 % incremental conversion
19 2 c1
20 pulling from branch0 into branch1
21 2 changesets found
22 1 c2
23 pulling from branch0 into branch2
24 2 changesets found
25 0 c3
26 pulling from branch2 into branch3
27 3 changesets found
28 pulling from branch1 into branch3
29 1 changesets found
@@ -1,342 +1,341 b''
1 # common code for the convert extension
1 # common code for the convert extension
2 import base64, errno
2 import base64, errno
3 import os
3 import os
4 import cPickle as pickle
4 import cPickle as pickle
5 from mercurial import util
5 from mercurial import util
6 from mercurial.i18n import _
6 from mercurial.i18n import _
7
7
8 def encodeargs(args):
8 def encodeargs(args):
9 def encodearg(s):
9 def encodearg(s):
10 lines = base64.encodestring(s)
10 lines = base64.encodestring(s)
11 lines = [l.splitlines()[0] for l in lines]
11 lines = [l.splitlines()[0] for l in lines]
12 return ''.join(lines)
12 return ''.join(lines)
13
13
14 s = pickle.dumps(args)
14 s = pickle.dumps(args)
15 return encodearg(s)
15 return encodearg(s)
16
16
17 def decodeargs(s):
17 def decodeargs(s):
18 s = base64.decodestring(s)
18 s = base64.decodestring(s)
19 return pickle.loads(s)
19 return pickle.loads(s)
20
20
21 def checktool(exe, name=None):
21 def checktool(exe, name=None):
22 name = name or exe
22 name = name or exe
23 if not util.find_exe(exe):
23 if not util.find_exe(exe):
24 raise util.Abort('cannot find required "%s" tool' % name)
24 raise util.Abort('cannot find required "%s" tool' % name)
25
25
26 class NoRepo(Exception): pass
26 class NoRepo(Exception): pass
27
27
28 SKIPREV = 'SKIP'
28 SKIPREV = 'SKIP'
29
29
30 class commit(object):
30 class commit(object):
31 def __init__(self, author, date, desc, parents, branch=None, rev=None,
31 def __init__(self, author, date, desc, parents, branch=None, rev=None,
32 extra={}):
32 extra={}):
33 self.author = author
33 self.author = author
34 self.date = date
34 self.date = date
35 self.desc = desc
35 self.desc = desc
36 self.parents = parents
36 self.parents = parents
37 self.branch = branch
37 self.branch = branch
38 self.rev = rev
38 self.rev = rev
39 self.extra = extra
39 self.extra = extra
40
40
41 class converter_source(object):
41 class converter_source(object):
42 """Conversion source interface"""
42 """Conversion source interface"""
43
43
44 def __init__(self, ui, path=None, rev=None):
44 def __init__(self, ui, path=None, rev=None):
45 """Initialize conversion source (or raise NoRepo("message")
45 """Initialize conversion source (or raise NoRepo("message")
46 exception if path is not a valid repository)"""
46 exception if path is not a valid repository)"""
47 self.ui = ui
47 self.ui = ui
48 self.path = path
48 self.path = path
49 self.rev = rev
49 self.rev = rev
50
50
51 self.encoding = 'utf-8'
51 self.encoding = 'utf-8'
52
52
53 def before(self):
53 def before(self):
54 pass
54 pass
55
55
56 def after(self):
56 def after(self):
57 pass
57 pass
58
58
59 def setrevmap(self, revmap):
59 def setrevmap(self, revmap):
60 """set the map of already-converted revisions"""
60 """set the map of already-converted revisions"""
61 pass
61 pass
62
62
63 def getheads(self):
63 def getheads(self):
64 """Return a list of this repository's heads"""
64 """Return a list of this repository's heads"""
65 raise NotImplementedError()
65 raise NotImplementedError()
66
66
67 def getfile(self, name, rev):
67 def getfile(self, name, rev):
68 """Return file contents as a string"""
68 """Return file contents as a string"""
69 raise NotImplementedError()
69 raise NotImplementedError()
70
70
71 def getmode(self, name, rev):
71 def getmode(self, name, rev):
72 """Return file mode, eg. '', 'x', or 'l'"""
72 """Return file mode, eg. '', 'x', or 'l'"""
73 raise NotImplementedError()
73 raise NotImplementedError()
74
74
75 def getchanges(self, version):
75 def getchanges(self, version):
76 """Returns a tuple of (files, copies)
76 """Returns a tuple of (files, copies)
77 Files is a sorted list of (filename, id) tuples for all files changed
77 Files is a sorted list of (filename, id) tuples for all files changed
78 in version, where id is the source revision id of the file.
78 in version, where id is the source revision id of the file.
79
79
80 copies is a dictionary of dest: source
80 copies is a dictionary of dest: source
81 """
81 """
82 raise NotImplementedError()
82 raise NotImplementedError()
83
83
84 def getcommit(self, version):
84 def getcommit(self, version):
85 """Return the commit object for version"""
85 """Return the commit object for version"""
86 raise NotImplementedError()
86 raise NotImplementedError()
87
87
88 def gettags(self):
88 def gettags(self):
89 """Return the tags as a dictionary of name: revision"""
89 """Return the tags as a dictionary of name: revision"""
90 raise NotImplementedError()
90 raise NotImplementedError()
91
91
92 def recode(self, s, encoding=None):
92 def recode(self, s, encoding=None):
93 if not encoding:
93 if not encoding:
94 encoding = self.encoding or 'utf-8'
94 encoding = self.encoding or 'utf-8'
95
95
96 if isinstance(s, unicode):
96 if isinstance(s, unicode):
97 return s.encode("utf-8")
97 return s.encode("utf-8")
98 try:
98 try:
99 return s.decode(encoding).encode("utf-8")
99 return s.decode(encoding).encode("utf-8")
100 except:
100 except:
101 try:
101 try:
102 return s.decode("latin-1").encode("utf-8")
102 return s.decode("latin-1").encode("utf-8")
103 except:
103 except:
104 return s.decode(encoding, "replace").encode("utf-8")
104 return s.decode(encoding, "replace").encode("utf-8")
105
105
106 def getchangedfiles(self, rev, i):
106 def getchangedfiles(self, rev, i):
107 """Return the files changed by rev compared to parent[i].
107 """Return the files changed by rev compared to parent[i].
108
108
109 i is an index selecting one of the parents of rev. The return
109 i is an index selecting one of the parents of rev. The return
110 value should be the list of files that are different in rev and
110 value should be the list of files that are different in rev and
111 this parent.
111 this parent.
112
112
113 If rev has no parents, i is None.
113 If rev has no parents, i is None.
114
114
115 This function is only needed to support --filemap
115 This function is only needed to support --filemap
116 """
116 """
117 raise NotImplementedError()
117 raise NotImplementedError()
118
118
119 def converted(self, rev, sinkrev):
119 def converted(self, rev, sinkrev):
120 '''Notify the source that a revision has been converted.'''
120 '''Notify the source that a revision has been converted.'''
121 pass
121 pass
122
122
123
123
124 class converter_sink(object):
124 class converter_sink(object):
125 """Conversion sink (target) interface"""
125 """Conversion sink (target) interface"""
126
126
127 def __init__(self, ui, path):
127 def __init__(self, ui, path):
128 """Initialize conversion sink (or raise NoRepo("message")
128 """Initialize conversion sink (or raise NoRepo("message")
129 exception if path is not a valid repository)
129 exception if path is not a valid repository)
130
130
131 created is a list of paths to remove if a fatal error occurs
131 created is a list of paths to remove if a fatal error occurs
132 later"""
132 later"""
133 self.ui = ui
133 self.ui = ui
134 self.path = path
134 self.path = path
135 self.created = []
135 self.created = []
136
136
137 def getheads(self):
137 def getheads(self):
138 """Return a list of this repository's heads"""
138 """Return a list of this repository's heads"""
139 raise NotImplementedError()
139 raise NotImplementedError()
140
140
141 def revmapfile(self):
141 def revmapfile(self):
142 """Path to a file that will contain lines
142 """Path to a file that will contain lines
143 source_rev_id sink_rev_id
143 source_rev_id sink_rev_id
144 mapping equivalent revision identifiers for each system."""
144 mapping equivalent revision identifiers for each system."""
145 raise NotImplementedError()
145 raise NotImplementedError()
146
146
147 def authorfile(self):
147 def authorfile(self):
148 """Path to a file that will contain lines
148 """Path to a file that will contain lines
149 srcauthor=dstauthor
149 srcauthor=dstauthor
150 mapping equivalent authors identifiers for each system."""
150 mapping equivalent authors identifiers for each system."""
151 return None
151 return None
152
152
153 def putfile(self, f, e, data):
153 def putfile(self, f, e, data):
154 """Put file for next putcommit().
154 """Put file for next putcommit().
155 f: path to file
155 f: path to file
156 e: '', 'x', or 'l' (regular file, executable, or symlink)
156 e: '', 'x', or 'l' (regular file, executable, or symlink)
157 data: file contents"""
157 data: file contents"""
158 raise NotImplementedError()
158 raise NotImplementedError()
159
159
160 def delfile(self, f):
160 def delfile(self, f):
161 """Delete file for next putcommit().
161 """Delete file for next putcommit().
162 f: path to file"""
162 f: path to file"""
163 raise NotImplementedError()
163 raise NotImplementedError()
164
164
165 def putcommit(self, files, parents, commit):
165 def putcommit(self, files, parents, commit):
166 """Create a revision with all changed files listed in 'files'
166 """Create a revision with all changed files listed in 'files'
167 and having listed parents. 'commit' is a commit object containing
167 and having listed parents. 'commit' is a commit object containing
168 at a minimum the author, date, and message for this changeset.
168 at a minimum the author, date, and message for this changeset.
169 Called after putfile() and delfile() calls. Note that the sink
169 Called after putfile() and delfile() calls. Note that the sink
170 repository is not told to update itself to a particular revision
170 repository is not told to update itself to a particular revision
171 (or even what that revision would be) before it receives the
171 (or even what that revision would be) before it receives the
172 file data."""
172 file data."""
173 raise NotImplementedError()
173 raise NotImplementedError()
174
174
175 def puttags(self, tags):
175 def puttags(self, tags):
176 """Put tags into sink.
176 """Put tags into sink.
177 tags: {tagname: sink_rev_id, ...}"""
177 tags: {tagname: sink_rev_id, ...}"""
178 raise NotImplementedError()
178 raise NotImplementedError()
179
179
180 def setbranch(self, branch, pbranch, parents):
180 def setbranch(self, branch, pbranches):
181 """Set the current branch name. Called before the first putfile
181 """Set the current branch name. Called before the first putfile
182 on the branch.
182 on the branch.
183 branch: branch name for subsequent commits
183 branch: branch name for subsequent commits
184 pbranch: branch name of parent commit
184 pbranches: (converted parent revision, parent branch) tuples"""
185 parents: destination revisions of parent"""
186 pass
185 pass
187
186
188 def setfilemapmode(self, active):
187 def setfilemapmode(self, active):
189 """Tell the destination that we're using a filemap
188 """Tell the destination that we're using a filemap
190
189
191 Some converter_sources (svn in particular) can claim that a file
190 Some converter_sources (svn in particular) can claim that a file
192 was changed in a revision, even if there was no change. This method
191 was changed in a revision, even if there was no change. This method
193 tells the destination that we're using a filemap and that it should
192 tells the destination that we're using a filemap and that it should
194 filter empty revisions.
193 filter empty revisions.
195 """
194 """
196 pass
195 pass
197
196
198 def before(self):
197 def before(self):
199 pass
198 pass
200
199
201 def after(self):
200 def after(self):
202 pass
201 pass
203
202
204
203
205 class commandline(object):
204 class commandline(object):
206 def __init__(self, ui, command):
205 def __init__(self, ui, command):
207 self.ui = ui
206 self.ui = ui
208 self.command = command
207 self.command = command
209
208
210 def prerun(self):
209 def prerun(self):
211 pass
210 pass
212
211
213 def postrun(self):
212 def postrun(self):
214 pass
213 pass
215
214
216 def _cmdline(self, cmd, *args, **kwargs):
215 def _cmdline(self, cmd, *args, **kwargs):
217 cmdline = [self.command, cmd] + list(args)
216 cmdline = [self.command, cmd] + list(args)
218 for k, v in kwargs.iteritems():
217 for k, v in kwargs.iteritems():
219 if len(k) == 1:
218 if len(k) == 1:
220 cmdline.append('-' + k)
219 cmdline.append('-' + k)
221 else:
220 else:
222 cmdline.append('--' + k.replace('_', '-'))
221 cmdline.append('--' + k.replace('_', '-'))
223 try:
222 try:
224 if len(k) == 1:
223 if len(k) == 1:
225 cmdline.append('' + v)
224 cmdline.append('' + v)
226 else:
225 else:
227 cmdline[-1] += '=' + v
226 cmdline[-1] += '=' + v
228 except TypeError:
227 except TypeError:
229 pass
228 pass
230 cmdline = [util.shellquote(arg) for arg in cmdline]
229 cmdline = [util.shellquote(arg) for arg in cmdline]
231 cmdline += ['<', util.nulldev]
230 cmdline += ['<', util.nulldev]
232 cmdline = ' '.join(cmdline)
231 cmdline = ' '.join(cmdline)
233 self.ui.debug(cmdline, '\n')
232 self.ui.debug(cmdline, '\n')
234 return cmdline
233 return cmdline
235
234
236 def _run(self, cmd, *args, **kwargs):
235 def _run(self, cmd, *args, **kwargs):
237 cmdline = self._cmdline(cmd, *args, **kwargs)
236 cmdline = self._cmdline(cmd, *args, **kwargs)
238 self.prerun()
237 self.prerun()
239 try:
238 try:
240 return util.popen(cmdline)
239 return util.popen(cmdline)
241 finally:
240 finally:
242 self.postrun()
241 self.postrun()
243
242
244 def run(self, cmd, *args, **kwargs):
243 def run(self, cmd, *args, **kwargs):
245 fp = self._run(cmd, *args, **kwargs)
244 fp = self._run(cmd, *args, **kwargs)
246 output = fp.read()
245 output = fp.read()
247 self.ui.debug(output)
246 self.ui.debug(output)
248 return output, fp.close()
247 return output, fp.close()
249
248
250 def checkexit(self, status, output=''):
249 def checkexit(self, status, output=''):
251 if status:
250 if status:
252 if output:
251 if output:
253 self.ui.warn(_('%s error:\n') % self.command)
252 self.ui.warn(_('%s error:\n') % self.command)
254 self.ui.warn(output)
253 self.ui.warn(output)
255 msg = util.explain_exit(status)[0]
254 msg = util.explain_exit(status)[0]
256 raise util.Abort(_('%s %s') % (self.command, msg))
255 raise util.Abort(_('%s %s') % (self.command, msg))
257
256
258 def run0(self, cmd, *args, **kwargs):
257 def run0(self, cmd, *args, **kwargs):
259 output, status = self.run(cmd, *args, **kwargs)
258 output, status = self.run(cmd, *args, **kwargs)
260 self.checkexit(status, output)
259 self.checkexit(status, output)
261 return output
260 return output
262
261
263 def getargmax(self):
262 def getargmax(self):
264 if '_argmax' in self.__dict__:
263 if '_argmax' in self.__dict__:
265 return self._argmax
264 return self._argmax
266
265
267 # POSIX requires at least 4096 bytes for ARG_MAX
266 # POSIX requires at least 4096 bytes for ARG_MAX
268 self._argmax = 4096
267 self._argmax = 4096
269 try:
268 try:
270 self._argmax = os.sysconf("SC_ARG_MAX")
269 self._argmax = os.sysconf("SC_ARG_MAX")
271 except:
270 except:
272 pass
271 pass
273
272
274 # Windows shells impose their own limits on command line length,
273 # Windows shells impose their own limits on command line length,
275 # down to 2047 bytes for cmd.exe under Windows NT/2k and 2500 bytes
274 # down to 2047 bytes for cmd.exe under Windows NT/2k and 2500 bytes
276 # for older 4nt.exe. See http://support.microsoft.com/kb/830473 for
275 # for older 4nt.exe. See http://support.microsoft.com/kb/830473 for
277 # details about cmd.exe limitations.
276 # details about cmd.exe limitations.
278
277
279 # Since ARG_MAX is for command line _and_ environment, lower our limit
278 # Since ARG_MAX is for command line _and_ environment, lower our limit
280 # (and make happy Windows shells while doing this).
279 # (and make happy Windows shells while doing this).
281
280
282 self._argmax = self._argmax/2 - 1
281 self._argmax = self._argmax/2 - 1
283 return self._argmax
282 return self._argmax
284
283
285 def limit_arglist(self, arglist, cmd, *args, **kwargs):
284 def limit_arglist(self, arglist, cmd, *args, **kwargs):
286 limit = self.getargmax() - len(self._cmdline(cmd, *args, **kwargs))
285 limit = self.getargmax() - len(self._cmdline(cmd, *args, **kwargs))
287 bytes = 0
286 bytes = 0
288 fl = []
287 fl = []
289 for fn in arglist:
288 for fn in arglist:
290 b = len(fn) + 3
289 b = len(fn) + 3
291 if bytes + b < limit or len(fl) == 0:
290 if bytes + b < limit or len(fl) == 0:
292 fl.append(fn)
291 fl.append(fn)
293 bytes += b
292 bytes += b
294 else:
293 else:
295 yield fl
294 yield fl
296 fl = [fn]
295 fl = [fn]
297 bytes = b
296 bytes = b
298 if fl:
297 if fl:
299 yield fl
298 yield fl
300
299
301 def xargs(self, arglist, cmd, *args, **kwargs):
300 def xargs(self, arglist, cmd, *args, **kwargs):
302 for l in self.limit_arglist(arglist, cmd, *args, **kwargs):
301 for l in self.limit_arglist(arglist, cmd, *args, **kwargs):
303 self.run0(cmd, *(list(args) + l), **kwargs)
302 self.run0(cmd, *(list(args) + l), **kwargs)
304
303
305 class mapfile(dict):
304 class mapfile(dict):
306 def __init__(self, ui, path):
305 def __init__(self, ui, path):
307 super(mapfile, self).__init__()
306 super(mapfile, self).__init__()
308 self.ui = ui
307 self.ui = ui
309 self.path = path
308 self.path = path
310 self.fp = None
309 self.fp = None
311 self.order = []
310 self.order = []
312 self._read()
311 self._read()
313
312
314 def _read(self):
313 def _read(self):
315 try:
314 try:
316 fp = open(self.path, 'r')
315 fp = open(self.path, 'r')
317 except IOError, err:
316 except IOError, err:
318 if err.errno != errno.ENOENT:
317 if err.errno != errno.ENOENT:
319 raise
318 raise
320 return
319 return
321 for line in fp:
320 for line in fp:
322 key, value = line[:-1].split(' ', 1)
321 key, value = line[:-1].split(' ', 1)
323 if key not in self:
322 if key not in self:
324 self.order.append(key)
323 self.order.append(key)
325 super(mapfile, self).__setitem__(key, value)
324 super(mapfile, self).__setitem__(key, value)
326 fp.close()
325 fp.close()
327
326
328 def __setitem__(self, key, value):
327 def __setitem__(self, key, value):
329 if self.fp is None:
328 if self.fp is None:
330 try:
329 try:
331 self.fp = open(self.path, 'a')
330 self.fp = open(self.path, 'a')
332 except IOError, err:
331 except IOError, err:
333 raise util.Abort(_('could not open map file %r: %s') %
332 raise util.Abort(_('could not open map file %r: %s') %
334 (self.path, err.strerror))
333 (self.path, err.strerror))
335 self.fp.write('%s %s\n' % (key, value))
334 self.fp.write('%s %s\n' % (key, value))
336 self.fp.flush()
335 self.fp.flush()
337 super(mapfile, self).__setitem__(key, value)
336 super(mapfile, self).__setitem__(key, value)
338
337
339 def close(self):
338 def close(self):
340 if self.fp:
339 if self.fp:
341 self.fp.close()
340 self.fp.close()
342 self.fp = None
341 self.fp = None
@@ -1,321 +1,321 b''
1 # convcmd - convert extension commands definition
1 # convcmd - convert extension commands definition
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from common import NoRepo, SKIPREV, converter_source, converter_sink, mapfile
8 from common import NoRepo, SKIPREV, converter_source, converter_sink, mapfile
9 from cvs import convert_cvs
9 from cvs import convert_cvs
10 from darcs import darcs_source
10 from darcs import darcs_source
11 from git import convert_git
11 from git import convert_git
12 from hg import mercurial_source, mercurial_sink
12 from hg import mercurial_source, mercurial_sink
13 from subversion import debugsvnlog, svn_source, svn_sink
13 from subversion import debugsvnlog, svn_source, svn_sink
14 import filemap
14 import filemap
15
15
16 import os, shutil
16 import os, shutil
17 from mercurial import hg, util
17 from mercurial import hg, util
18 from mercurial.i18n import _
18 from mercurial.i18n import _
19
19
20 source_converters = [
20 source_converters = [
21 ('cvs', convert_cvs),
21 ('cvs', convert_cvs),
22 ('git', convert_git),
22 ('git', convert_git),
23 ('svn', svn_source),
23 ('svn', svn_source),
24 ('hg', mercurial_source),
24 ('hg', mercurial_source),
25 ('darcs', darcs_source),
25 ('darcs', darcs_source),
26 ]
26 ]
27
27
28 sink_converters = [
28 sink_converters = [
29 ('hg', mercurial_sink),
29 ('hg', mercurial_sink),
30 ('svn', svn_sink),
30 ('svn', svn_sink),
31 ]
31 ]
32
32
33 def convertsource(ui, path, type, rev):
33 def convertsource(ui, path, type, rev):
34 exceptions = []
34 exceptions = []
35 for name, source in source_converters:
35 for name, source in source_converters:
36 try:
36 try:
37 if not type or name == type:
37 if not type or name == type:
38 return source(ui, path, rev)
38 return source(ui, path, rev)
39 except NoRepo, inst:
39 except NoRepo, inst:
40 exceptions.append(inst)
40 exceptions.append(inst)
41 if not ui.quiet:
41 if not ui.quiet:
42 for inst in exceptions:
42 for inst in exceptions:
43 ui.write(_("%s\n") % inst)
43 ui.write(_("%s\n") % inst)
44 raise util.Abort('%s: unknown repository type' % path)
44 raise util.Abort('%s: unknown repository type' % path)
45
45
46 def convertsink(ui, path, type):
46 def convertsink(ui, path, type):
47 for name, sink in sink_converters:
47 for name, sink in sink_converters:
48 try:
48 try:
49 if not type or name == type:
49 if not type or name == type:
50 return sink(ui, path)
50 return sink(ui, path)
51 except NoRepo, inst:
51 except NoRepo, inst:
52 ui.note(_("convert: %s\n") % inst)
52 ui.note(_("convert: %s\n") % inst)
53 raise util.Abort('%s: unknown repository type' % path)
53 raise util.Abort('%s: unknown repository type' % path)
54
54
55 class converter(object):
55 class converter(object):
56 def __init__(self, ui, source, dest, revmapfile, opts):
56 def __init__(self, ui, source, dest, revmapfile, opts):
57
57
58 self.source = source
58 self.source = source
59 self.dest = dest
59 self.dest = dest
60 self.ui = ui
60 self.ui = ui
61 self.opts = opts
61 self.opts = opts
62 self.commitcache = {}
62 self.commitcache = {}
63 self.authors = {}
63 self.authors = {}
64 self.authorfile = None
64 self.authorfile = None
65
65
66 self.map = mapfile(ui, revmapfile)
66 self.map = mapfile(ui, revmapfile)
67
67
68 # Read first the dst author map if any
68 # Read first the dst author map if any
69 authorfile = self.dest.authorfile()
69 authorfile = self.dest.authorfile()
70 if authorfile and os.path.exists(authorfile):
70 if authorfile and os.path.exists(authorfile):
71 self.readauthormap(authorfile)
71 self.readauthormap(authorfile)
72 # Extend/Override with new author map if necessary
72 # Extend/Override with new author map if necessary
73 if opts.get('authors'):
73 if opts.get('authors'):
74 self.readauthormap(opts.get('authors'))
74 self.readauthormap(opts.get('authors'))
75 self.authorfile = self.dest.authorfile()
75 self.authorfile = self.dest.authorfile()
76
76
77 def walktree(self, heads):
77 def walktree(self, heads):
78 '''Return a mapping that identifies the uncommitted parents of every
78 '''Return a mapping that identifies the uncommitted parents of every
79 uncommitted changeset.'''
79 uncommitted changeset.'''
80 visit = heads
80 visit = heads
81 known = {}
81 known = {}
82 parents = {}
82 parents = {}
83 while visit:
83 while visit:
84 n = visit.pop(0)
84 n = visit.pop(0)
85 if n in known or n in self.map: continue
85 if n in known or n in self.map: continue
86 known[n] = 1
86 known[n] = 1
87 commit = self.cachecommit(n)
87 commit = self.cachecommit(n)
88 parents[n] = []
88 parents[n] = []
89 for p in commit.parents:
89 for p in commit.parents:
90 parents[n].append(p)
90 parents[n].append(p)
91 visit.append(p)
91 visit.append(p)
92
92
93 return parents
93 return parents
94
94
95 def toposort(self, parents):
95 def toposort(self, parents):
96 '''Return an ordering such that every uncommitted changeset is
96 '''Return an ordering such that every uncommitted changeset is
97 preceeded by all its uncommitted ancestors.'''
97 preceeded by all its uncommitted ancestors.'''
98 visit = parents.keys()
98 visit = parents.keys()
99 seen = {}
99 seen = {}
100 children = {}
100 children = {}
101
101
102 while visit:
102 while visit:
103 n = visit.pop(0)
103 n = visit.pop(0)
104 if n in seen: continue
104 if n in seen: continue
105 seen[n] = 1
105 seen[n] = 1
106 # Ensure that nodes without parents are present in the 'children'
106 # Ensure that nodes without parents are present in the 'children'
107 # mapping.
107 # mapping.
108 children.setdefault(n, [])
108 children.setdefault(n, [])
109 for p in parents[n]:
109 for p in parents[n]:
110 if not p in self.map:
110 if not p in self.map:
111 visit.append(p)
111 visit.append(p)
112 children.setdefault(p, []).append(n)
112 children.setdefault(p, []).append(n)
113
113
114 s = []
114 s = []
115 removed = {}
115 removed = {}
116 visit = children.keys()
116 visit = children.keys()
117 while visit:
117 while visit:
118 n = visit.pop(0)
118 n = visit.pop(0)
119 if n in removed: continue
119 if n in removed: continue
120 dep = 0
120 dep = 0
121 if n in parents:
121 if n in parents:
122 for p in parents[n]:
122 for p in parents[n]:
123 if p in self.map: continue
123 if p in self.map: continue
124 if p not in removed:
124 if p not in removed:
125 # we're still dependent
125 # we're still dependent
126 visit.append(n)
126 visit.append(n)
127 dep = 1
127 dep = 1
128 break
128 break
129
129
130 if not dep:
130 if not dep:
131 # all n's parents are in the list
131 # all n's parents are in the list
132 removed[n] = 1
132 removed[n] = 1
133 if n not in self.map:
133 if n not in self.map:
134 s.append(n)
134 s.append(n)
135 if n in children:
135 if n in children:
136 for c in children[n]:
136 for c in children[n]:
137 visit.insert(0, c)
137 visit.insert(0, c)
138
138
139 if self.opts.get('datesort'):
139 if self.opts.get('datesort'):
140 depth = {}
140 depth = {}
141 for n in s:
141 for n in s:
142 depth[n] = 0
142 depth[n] = 0
143 pl = [p for p in self.commitcache[n].parents
143 pl = [p for p in self.commitcache[n].parents
144 if p not in self.map]
144 if p not in self.map]
145 if pl:
145 if pl:
146 depth[n] = max([depth[p] for p in pl]) + 1
146 depth[n] = max([depth[p] for p in pl]) + 1
147
147
148 s = [(depth[n], util.parsedate(self.commitcache[n].date), n)
148 s = [(depth[n], util.parsedate(self.commitcache[n].date), n)
149 for n in s]
149 for n in s]
150 s.sort()
150 s.sort()
151 s = [e[2] for e in s]
151 s = [e[2] for e in s]
152
152
153 return s
153 return s
154
154
155 def writeauthormap(self):
155 def writeauthormap(self):
156 authorfile = self.authorfile
156 authorfile = self.authorfile
157 if authorfile:
157 if authorfile:
158 self.ui.status('Writing author map file %s\n' % authorfile)
158 self.ui.status('Writing author map file %s\n' % authorfile)
159 ofile = open(authorfile, 'w+')
159 ofile = open(authorfile, 'w+')
160 for author in self.authors:
160 for author in self.authors:
161 ofile.write("%s=%s\n" % (author, self.authors[author]))
161 ofile.write("%s=%s\n" % (author, self.authors[author]))
162 ofile.close()
162 ofile.close()
163
163
164 def readauthormap(self, authorfile):
164 def readauthormap(self, authorfile):
165 afile = open(authorfile, 'r')
165 afile = open(authorfile, 'r')
166 for line in afile:
166 for line in afile:
167 try:
167 try:
168 srcauthor = line.split('=')[0].strip()
168 srcauthor = line.split('=')[0].strip()
169 dstauthor = line.split('=')[1].strip()
169 dstauthor = line.split('=')[1].strip()
170 if srcauthor in self.authors and dstauthor != self.authors[srcauthor]:
170 if srcauthor in self.authors and dstauthor != self.authors[srcauthor]:
171 self.ui.status(
171 self.ui.status(
172 'Overriding mapping for author %s, was %s, will be %s\n'
172 'Overriding mapping for author %s, was %s, will be %s\n'
173 % (srcauthor, self.authors[srcauthor], dstauthor))
173 % (srcauthor, self.authors[srcauthor], dstauthor))
174 else:
174 else:
175 self.ui.debug('Mapping author %s to %s\n'
175 self.ui.debug('Mapping author %s to %s\n'
176 % (srcauthor, dstauthor))
176 % (srcauthor, dstauthor))
177 self.authors[srcauthor] = dstauthor
177 self.authors[srcauthor] = dstauthor
178 except IndexError:
178 except IndexError:
179 self.ui.warn(
179 self.ui.warn(
180 'Ignoring bad line in author file map %s: %s\n'
180 'Ignoring bad line in author file map %s: %s\n'
181 % (authorfile, line))
181 % (authorfile, line))
182 afile.close()
182 afile.close()
183
183
184 def cachecommit(self, rev):
184 def cachecommit(self, rev):
185 commit = self.source.getcommit(rev)
185 commit = self.source.getcommit(rev)
186 commit.author = self.authors.get(commit.author, commit.author)
186 commit.author = self.authors.get(commit.author, commit.author)
187 self.commitcache[rev] = commit
187 self.commitcache[rev] = commit
188 return commit
188 return commit
189
189
190 def copy(self, rev):
190 def copy(self, rev):
191 commit = self.commitcache[rev]
191 commit = self.commitcache[rev]
192 do_copies = hasattr(self.dest, 'copyfile')
192 do_copies = hasattr(self.dest, 'copyfile')
193 filenames = []
193 filenames = []
194
194
195 changes = self.source.getchanges(rev)
195 changes = self.source.getchanges(rev)
196 if isinstance(changes, basestring):
196 if isinstance(changes, basestring):
197 if changes == SKIPREV:
197 if changes == SKIPREV:
198 dest = SKIPREV
198 dest = SKIPREV
199 else:
199 else:
200 dest = self.map[changes]
200 dest = self.map[changes]
201 self.map[rev] = dest
201 self.map[rev] = dest
202 return
202 return
203 files, copies = changes
203 files, copies = changes
204 parents = [self.map[r] for r in commit.parents]
204 pbranches = []
205 if commit.parents:
205 if commit.parents:
206 prev = commit.parents[0]
206 for prev in commit.parents:
207 if prev not in self.commitcache:
207 if prev not in self.commitcache:
208 self.cachecommit(prev)
208 self.cachecommit(prev)
209 pbranch = self.commitcache[prev].branch
209 pbranches.append((self.map[prev],
210 else:
210 self.commitcache[prev].branch))
211 pbranch = None
211 self.dest.setbranch(commit.branch, pbranches)
212 self.dest.setbranch(commit.branch, pbranch, parents)
213 for f, v in files:
212 for f, v in files:
214 filenames.append(f)
213 filenames.append(f)
215 try:
214 try:
216 data = self.source.getfile(f, v)
215 data = self.source.getfile(f, v)
217 except IOError, inst:
216 except IOError, inst:
218 self.dest.delfile(f)
217 self.dest.delfile(f)
219 else:
218 else:
220 e = self.source.getmode(f, v)
219 e = self.source.getmode(f, v)
221 self.dest.putfile(f, e, data)
220 self.dest.putfile(f, e, data)
222 if do_copies:
221 if do_copies:
223 if f in copies:
222 if f in copies:
224 copyf = copies[f]
223 copyf = copies[f]
225 # Merely marks that a copy happened.
224 # Merely marks that a copy happened.
226 self.dest.copyfile(copyf, f)
225 self.dest.copyfile(copyf, f)
227
226
227 parents = [b[0] for b in pbranches]
228 newnode = self.dest.putcommit(filenames, parents, commit)
228 newnode = self.dest.putcommit(filenames, parents, commit)
229 self.source.converted(rev, newnode)
229 self.source.converted(rev, newnode)
230 self.map[rev] = newnode
230 self.map[rev] = newnode
231
231
232 def convert(self):
232 def convert(self):
233
233
234 def recode(s):
234 def recode(s):
235 return s.decode('utf-8').encode(orig_encoding, 'replace')
235 return s.decode('utf-8').encode(orig_encoding, 'replace')
236
236
237 try:
237 try:
238 self.source.before()
238 self.source.before()
239 self.dest.before()
239 self.dest.before()
240 self.source.setrevmap(self.map)
240 self.source.setrevmap(self.map)
241 self.ui.status("scanning source...\n")
241 self.ui.status("scanning source...\n")
242 heads = self.source.getheads()
242 heads = self.source.getheads()
243 parents = self.walktree(heads)
243 parents = self.walktree(heads)
244 self.ui.status("sorting...\n")
244 self.ui.status("sorting...\n")
245 t = self.toposort(parents)
245 t = self.toposort(parents)
246 num = len(t)
246 num = len(t)
247 c = None
247 c = None
248
248
249 self.ui.status("converting...\n")
249 self.ui.status("converting...\n")
250 for c in t:
250 for c in t:
251 num -= 1
251 num -= 1
252 desc = self.commitcache[c].desc
252 desc = self.commitcache[c].desc
253 if "\n" in desc:
253 if "\n" in desc:
254 desc = desc.splitlines()[0]
254 desc = desc.splitlines()[0]
255 # convert log message to local encoding without using
255 # convert log message to local encoding without using
256 # tolocal() because util._encoding conver() use it as
256 # tolocal() because util._encoding conver() use it as
257 # 'utf-8'
257 # 'utf-8'
258 self.ui.status("%d %s\n" % (num, recode(desc)))
258 self.ui.status("%d %s\n" % (num, recode(desc)))
259 self.ui.note(_("source: %s\n" % recode(c)))
259 self.ui.note(_("source: %s\n" % recode(c)))
260 self.copy(c)
260 self.copy(c)
261
261
262 tags = self.source.gettags()
262 tags = self.source.gettags()
263 ctags = {}
263 ctags = {}
264 for k in tags:
264 for k in tags:
265 v = tags[k]
265 v = tags[k]
266 if self.map.get(v, SKIPREV) != SKIPREV:
266 if self.map.get(v, SKIPREV) != SKIPREV:
267 ctags[k] = self.map[v]
267 ctags[k] = self.map[v]
268
268
269 if c and ctags:
269 if c and ctags:
270 nrev = self.dest.puttags(ctags)
270 nrev = self.dest.puttags(ctags)
271 # write another hash correspondence to override the previous
271 # write another hash correspondence to override the previous
272 # one so we don't end up with extra tag heads
272 # one so we don't end up with extra tag heads
273 if nrev:
273 if nrev:
274 self.map[c] = nrev
274 self.map[c] = nrev
275
275
276 self.writeauthormap()
276 self.writeauthormap()
277 finally:
277 finally:
278 self.cleanup()
278 self.cleanup()
279
279
280 def cleanup(self):
280 def cleanup(self):
281 try:
281 try:
282 self.dest.after()
282 self.dest.after()
283 finally:
283 finally:
284 self.source.after()
284 self.source.after()
285 self.map.close()
285 self.map.close()
286
286
287 orig_encoding = 'ascii'
287 orig_encoding = 'ascii'
288
288
289 def convert(ui, src, dest=None, revmapfile=None, **opts):
289 def convert(ui, src, dest=None, revmapfile=None, **opts):
290 global orig_encoding
290 global orig_encoding
291 orig_encoding = util._encoding
291 orig_encoding = util._encoding
292 util._encoding = 'UTF-8'
292 util._encoding = 'UTF-8'
293
293
294 if not dest:
294 if not dest:
295 dest = hg.defaultdest(src) + "-hg"
295 dest = hg.defaultdest(src) + "-hg"
296 ui.status("assuming destination %s\n" % dest)
296 ui.status("assuming destination %s\n" % dest)
297
297
298 destc = convertsink(ui, dest, opts.get('dest_type'))
298 destc = convertsink(ui, dest, opts.get('dest_type'))
299
299
300 try:
300 try:
301 srcc = convertsource(ui, src, opts.get('source_type'),
301 srcc = convertsource(ui, src, opts.get('source_type'),
302 opts.get('rev'))
302 opts.get('rev'))
303 except Exception:
303 except Exception:
304 for path in destc.created:
304 for path in destc.created:
305 shutil.rmtree(path, True)
305 shutil.rmtree(path, True)
306 raise
306 raise
307
307
308 fmap = opts.get('filemap')
308 fmap = opts.get('filemap')
309 if fmap:
309 if fmap:
310 srcc = filemap.filemap_source(ui, srcc, fmap)
310 srcc = filemap.filemap_source(ui, srcc, fmap)
311 destc.setfilemapmode(True)
311 destc.setfilemapmode(True)
312
312
313 if not revmapfile:
313 if not revmapfile:
314 try:
314 try:
315 revmapfile = destc.revmapfile()
315 revmapfile = destc.revmapfile()
316 except:
316 except:
317 revmapfile = os.path.join(destc, "map")
317 revmapfile = os.path.join(destc, "map")
318
318
319 c = converter(ui, srcc, destc, revmapfile, opts)
319 c = converter(ui, srcc, destc, revmapfile, opts)
320 c.convert()
320 c.convert()
321
321
@@ -1,288 +1,301 b''
1 # hg backend for convert extension
1 # hg backend for convert extension
2
2
3 # Notes for hg->hg conversion:
3 # Notes for hg->hg conversion:
4 #
4 #
5 # * Old versions of Mercurial didn't trim the whitespace from the ends
5 # * Old versions of Mercurial didn't trim the whitespace from the ends
6 # of commit messages, but new versions do. Changesets created by
6 # of commit messages, but new versions do. Changesets created by
7 # those older versions, then converted, may thus have different
7 # those older versions, then converted, may thus have different
8 # hashes for changesets that are otherwise identical.
8 # hashes for changesets that are otherwise identical.
9 #
9 #
10 # * By default, the source revision is stored in the converted
10 # * By default, the source revision is stored in the converted
11 # revision. This will cause the converted revision to have a
11 # revision. This will cause the converted revision to have a
12 # different identity than the source. To avoid this, use the
12 # different identity than the source. To avoid this, use the
13 # following option: "--config convert.hg.saverev=false"
13 # following option: "--config convert.hg.saverev=false"
14
14
15
15
16 import os, time
16 import os, time
17 from mercurial.i18n import _
17 from mercurial.i18n import _
18 from mercurial.node import *
18 from mercurial.node import *
19 from mercurial import hg, lock, revlog, util
19 from mercurial import hg, lock, revlog, util
20
20
21 from common import NoRepo, commit, converter_source, converter_sink
21 from common import NoRepo, commit, converter_source, converter_sink
22
22
23 class mercurial_sink(converter_sink):
23 class mercurial_sink(converter_sink):
24 def __init__(self, ui, path):
24 def __init__(self, ui, path):
25 converter_sink.__init__(self, ui, path)
25 converter_sink.__init__(self, ui, path)
26 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
26 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
27 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
27 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
28 self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
28 self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
29 self.lastbranch = None
29 self.lastbranch = None
30 if os.path.isdir(path) and len(os.listdir(path)) > 0:
30 if os.path.isdir(path) and len(os.listdir(path)) > 0:
31 try:
31 try:
32 self.repo = hg.repository(self.ui, path)
32 self.repo = hg.repository(self.ui, path)
33 if not self.repo.local():
33 if not self.repo.local():
34 raise NoRepo(_('%s is not a local Mercurial repo') % path)
34 raise NoRepo(_('%s is not a local Mercurial repo') % path)
35 except hg.RepoError, err:
35 except hg.RepoError, err:
36 ui.print_exc()
36 ui.print_exc()
37 raise NoRepo(err.args[0])
37 raise NoRepo(err.args[0])
38 else:
38 else:
39 try:
39 try:
40 ui.status(_('initializing destination %s repository\n') % path)
40 ui.status(_('initializing destination %s repository\n') % path)
41 self.repo = hg.repository(self.ui, path, create=True)
41 self.repo = hg.repository(self.ui, path, create=True)
42 if not self.repo.local():
42 if not self.repo.local():
43 raise NoRepo(_('%s is not a local Mercurial repo') % path)
43 raise NoRepo(_('%s is not a local Mercurial repo') % path)
44 self.created.append(path)
44 self.created.append(path)
45 except hg.RepoError, err:
45 except hg.RepoError, err:
46 ui.print_exc()
46 ui.print_exc()
47 raise NoRepo("could not create hg repo %s as sink" % path)
47 raise NoRepo("could not create hg repo %s as sink" % path)
48 self.lock = None
48 self.lock = None
49 self.wlock = None
49 self.wlock = None
50 self.filemapmode = False
50 self.filemapmode = False
51
51
52 def before(self):
52 def before(self):
53 self.ui.debug(_('run hg sink pre-conversion action\n'))
53 self.ui.debug(_('run hg sink pre-conversion action\n'))
54 self.wlock = self.repo.wlock()
54 self.wlock = self.repo.wlock()
55 self.lock = self.repo.lock()
55 self.lock = self.repo.lock()
56 self.repo.dirstate.clear()
56 self.repo.dirstate.clear()
57
57
58 def after(self):
58 def after(self):
59 self.ui.debug(_('run hg sink post-conversion action\n'))
59 self.ui.debug(_('run hg sink post-conversion action\n'))
60 self.repo.dirstate.invalidate()
60 self.repo.dirstate.invalidate()
61 self.lock = None
61 self.lock = None
62 self.wlock = None
62 self.wlock = None
63
63
64 def revmapfile(self):
64 def revmapfile(self):
65 return os.path.join(self.path, ".hg", "shamap")
65 return os.path.join(self.path, ".hg", "shamap")
66
66
67 def authorfile(self):
67 def authorfile(self):
68 return os.path.join(self.path, ".hg", "authormap")
68 return os.path.join(self.path, ".hg", "authormap")
69
69
70 def getheads(self):
70 def getheads(self):
71 h = self.repo.changelog.heads()
71 h = self.repo.changelog.heads()
72 return [ hex(x) for x in h ]
72 return [ hex(x) for x in h ]
73
73
74 def putfile(self, f, e, data):
74 def putfile(self, f, e, data):
75 self.repo.wwrite(f, data, e)
75 self.repo.wwrite(f, data, e)
76 if f not in self.repo.dirstate:
76 if f not in self.repo.dirstate:
77 self.repo.dirstate.normallookup(f)
77 self.repo.dirstate.normallookup(f)
78
78
79 def copyfile(self, source, dest):
79 def copyfile(self, source, dest):
80 self.repo.copy(source, dest)
80 self.repo.copy(source, dest)
81
81
82 def delfile(self, f):
82 def delfile(self, f):
83 try:
83 try:
84 util.unlink(self.repo.wjoin(f))
84 util.unlink(self.repo.wjoin(f))
85 #self.repo.remove([f])
85 #self.repo.remove([f])
86 except OSError:
86 except OSError:
87 pass
87 pass
88
88
89 def setbranch(self, branch, pbranch, parents):
89 def setbranch(self, branch, pbranches):
90 if (not self.clonebranches) or (branch == self.lastbranch):
90 if not self.clonebranches:
91 return
91 return
92
92
93 setbranch = (branch != self.lastbranch)
93 self.lastbranch = branch
94 self.lastbranch = branch
94 self.after()
95 if not branch:
95 if not branch:
96 branch = 'default'
96 branch = 'default'
97 if not pbranch:
97 pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches]
98 pbranch = 'default'
98 pbranch = pbranches and pbranches[0][1] or 'default'
99
99
100 branchpath = os.path.join(self.path, branch)
100 branchpath = os.path.join(self.path, branch)
101 if setbranch:
102 self.after()
101 try:
103 try:
102 self.repo = hg.repository(self.ui, branchpath)
104 self.repo = hg.repository(self.ui, branchpath)
103 except:
105 except:
104 if not parents:
105 self.repo = hg.repository(self.ui, branchpath, create=True)
106 self.repo = hg.repository(self.ui, branchpath, create=True)
106 else:
107 self.before()
107 self.ui.note(_('cloning branch %s to %s\n') % (pbranch, branch))
108
108 hg.clone(self.ui, os.path.join(self.path, pbranch),
109 # pbranches may bring revisions from other branches (merge parents)
109 branchpath, rev=parents, update=False,
110 # Make sure we have them, or pull them.
110 stream=True)
111 missings = {}
111 self.repo = hg.repository(self.ui, branchpath)
112 for b in pbranches:
113 try:
114 self.repo.lookup(b[0])
115 except:
116 missings.setdefault(b[1], []).append(b[0])
117
118 if missings:
119 self.after()
120 for pbranch, heads in missings.iteritems():
121 pbranchpath = os.path.join(self.path, pbranch)
122 prepo = hg.repository(self.ui, pbranchpath)
123 self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
124 self.repo.pull(prepo, [prepo.lookup(h) for h in heads])
112 self.before()
125 self.before()
113
126
114 def putcommit(self, files, parents, commit):
127 def putcommit(self, files, parents, commit):
115 seen = {}
128 seen = {}
116 pl = []
129 pl = []
117 for p in parents:
130 for p in parents:
118 if p not in seen:
131 if p not in seen:
119 pl.append(p)
132 pl.append(p)
120 seen[p] = 1
133 seen[p] = 1
121 parents = pl
134 parents = pl
122 nparents = len(parents)
135 nparents = len(parents)
123 if self.filemapmode and nparents == 1:
136 if self.filemapmode and nparents == 1:
124 m1node = self.repo.changelog.read(bin(parents[0]))[0]
137 m1node = self.repo.changelog.read(bin(parents[0]))[0]
125 parent = parents[0]
138 parent = parents[0]
126
139
127 if len(parents) < 2: parents.append("0" * 40)
140 if len(parents) < 2: parents.append("0" * 40)
128 if len(parents) < 2: parents.append("0" * 40)
141 if len(parents) < 2: parents.append("0" * 40)
129 p2 = parents.pop(0)
142 p2 = parents.pop(0)
130
143
131 text = commit.desc
144 text = commit.desc
132 extra = commit.extra.copy()
145 extra = commit.extra.copy()
133 if self.branchnames and commit.branch:
146 if self.branchnames and commit.branch:
134 extra['branch'] = commit.branch
147 extra['branch'] = commit.branch
135 if commit.rev:
148 if commit.rev:
136 extra['convert_revision'] = commit.rev
149 extra['convert_revision'] = commit.rev
137
150
138 while parents:
151 while parents:
139 p1 = p2
152 p1 = p2
140 p2 = parents.pop(0)
153 p2 = parents.pop(0)
141 a = self.repo.rawcommit(files, text, commit.author, commit.date,
154 a = self.repo.rawcommit(files, text, commit.author, commit.date,
142 bin(p1), bin(p2), extra=extra)
155 bin(p1), bin(p2), extra=extra)
143 self.repo.dirstate.clear()
156 self.repo.dirstate.clear()
144 text = "(octopus merge fixup)\n"
157 text = "(octopus merge fixup)\n"
145 p2 = hg.hex(self.repo.changelog.tip())
158 p2 = hg.hex(self.repo.changelog.tip())
146
159
147 if self.filemapmode and nparents == 1:
160 if self.filemapmode and nparents == 1:
148 man = self.repo.manifest
161 man = self.repo.manifest
149 mnode = self.repo.changelog.read(bin(p2))[0]
162 mnode = self.repo.changelog.read(bin(p2))[0]
150 if not man.cmp(m1node, man.revision(mnode)):
163 if not man.cmp(m1node, man.revision(mnode)):
151 self.repo.rollback()
164 self.repo.rollback()
152 self.repo.dirstate.clear()
165 self.repo.dirstate.clear()
153 return parent
166 return parent
154 return p2
167 return p2
155
168
156 def puttags(self, tags):
169 def puttags(self, tags):
157 try:
170 try:
158 old = self.repo.wfile(".hgtags").read()
171 old = self.repo.wfile(".hgtags").read()
159 oldlines = old.splitlines(1)
172 oldlines = old.splitlines(1)
160 oldlines.sort()
173 oldlines.sort()
161 except:
174 except:
162 oldlines = []
175 oldlines = []
163
176
164 k = tags.keys()
177 k = tags.keys()
165 k.sort()
178 k.sort()
166 newlines = []
179 newlines = []
167 for tag in k:
180 for tag in k:
168 newlines.append("%s %s\n" % (tags[tag], tag))
181 newlines.append("%s %s\n" % (tags[tag], tag))
169
182
170 newlines.sort()
183 newlines.sort()
171
184
172 if newlines != oldlines:
185 if newlines != oldlines:
173 self.ui.status("updating tags\n")
186 self.ui.status("updating tags\n")
174 f = self.repo.wfile(".hgtags", "w")
187 f = self.repo.wfile(".hgtags", "w")
175 f.write("".join(newlines))
188 f.write("".join(newlines))
176 f.close()
189 f.close()
177 if not oldlines: self.repo.add([".hgtags"])
190 if not oldlines: self.repo.add([".hgtags"])
178 date = "%s 0" % int(time.mktime(time.gmtime()))
191 date = "%s 0" % int(time.mktime(time.gmtime()))
179 extra = {}
192 extra = {}
180 if self.tagsbranch != 'default':
193 if self.tagsbranch != 'default':
181 extra['branch'] = self.tagsbranch
194 extra['branch'] = self.tagsbranch
182 try:
195 try:
183 tagparent = self.repo.changectx(self.tagsbranch).node()
196 tagparent = self.repo.changectx(self.tagsbranch).node()
184 except hg.RepoError, inst:
197 except hg.RepoError, inst:
185 tagparent = nullid
198 tagparent = nullid
186 self.repo.rawcommit([".hgtags"], "update tags", "convert-repo",
199 self.repo.rawcommit([".hgtags"], "update tags", "convert-repo",
187 date, tagparent, nullid, extra=extra)
200 date, tagparent, nullid, extra=extra)
188 return hex(self.repo.changelog.tip())
201 return hex(self.repo.changelog.tip())
189
202
190 def setfilemapmode(self, active):
203 def setfilemapmode(self, active):
191 self.filemapmode = active
204 self.filemapmode = active
192
205
193 class mercurial_source(converter_source):
206 class mercurial_source(converter_source):
194 def __init__(self, ui, path, rev=None):
207 def __init__(self, ui, path, rev=None):
195 converter_source.__init__(self, ui, path, rev)
208 converter_source.__init__(self, ui, path, rev)
196 self.saverev = ui.configbool('convert', 'hg.saverev', True)
209 self.saverev = ui.configbool('convert', 'hg.saverev', True)
197 try:
210 try:
198 self.repo = hg.repository(self.ui, path)
211 self.repo = hg.repository(self.ui, path)
199 # try to provoke an exception if this isn't really a hg
212 # try to provoke an exception if this isn't really a hg
200 # repo, but some other bogus compatible-looking url
213 # repo, but some other bogus compatible-looking url
201 if not self.repo.local():
214 if not self.repo.local():
202 raise hg.RepoError()
215 raise hg.RepoError()
203 except hg.RepoError:
216 except hg.RepoError:
204 ui.print_exc()
217 ui.print_exc()
205 raise NoRepo("%s is not a local Mercurial repo" % path)
218 raise NoRepo("%s is not a local Mercurial repo" % path)
206 self.lastrev = None
219 self.lastrev = None
207 self.lastctx = None
220 self.lastctx = None
208 self._changescache = None
221 self._changescache = None
209 self.convertfp = None
222 self.convertfp = None
210
223
211 def changectx(self, rev):
224 def changectx(self, rev):
212 if self.lastrev != rev:
225 if self.lastrev != rev:
213 self.lastctx = self.repo.changectx(rev)
226 self.lastctx = self.repo.changectx(rev)
214 self.lastrev = rev
227 self.lastrev = rev
215 return self.lastctx
228 return self.lastctx
216
229
217 def getheads(self):
230 def getheads(self):
218 if self.rev:
231 if self.rev:
219 return [hex(self.repo.changectx(self.rev).node())]
232 return [hex(self.repo.changectx(self.rev).node())]
220 else:
233 else:
221 return [hex(node) for node in self.repo.heads()]
234 return [hex(node) for node in self.repo.heads()]
222
235
223 def getfile(self, name, rev):
236 def getfile(self, name, rev):
224 try:
237 try:
225 return self.changectx(rev).filectx(name).data()
238 return self.changectx(rev).filectx(name).data()
226 except revlog.LookupError, err:
239 except revlog.LookupError, err:
227 raise IOError(err)
240 raise IOError(err)
228
241
229 def getmode(self, name, rev):
242 def getmode(self, name, rev):
230 m = self.changectx(rev).manifest()
243 m = self.changectx(rev).manifest()
231 return (m.execf(name) and 'x' or '') + (m.linkf(name) and 'l' or '')
244 return (m.execf(name) and 'x' or '') + (m.linkf(name) and 'l' or '')
232
245
233 def getchanges(self, rev):
246 def getchanges(self, rev):
234 ctx = self.changectx(rev)
247 ctx = self.changectx(rev)
235 if self._changescache and self._changescache[0] == rev:
248 if self._changescache and self._changescache[0] == rev:
236 m, a, r = self._changescache[1]
249 m, a, r = self._changescache[1]
237 else:
250 else:
238 m, a, r = self.repo.status(ctx.parents()[0].node(), ctx.node())[:3]
251 m, a, r = self.repo.status(ctx.parents()[0].node(), ctx.node())[:3]
239 changes = [(name, rev) for name in m + a + r]
252 changes = [(name, rev) for name in m + a + r]
240 changes.sort()
253 changes.sort()
241 return (changes, self.getcopies(ctx, m + a))
254 return (changes, self.getcopies(ctx, m + a))
242
255
243 def getcopies(self, ctx, files):
256 def getcopies(self, ctx, files):
244 copies = {}
257 copies = {}
245 for name in files:
258 for name in files:
246 try:
259 try:
247 copies[name] = ctx.filectx(name).renamed()[0]
260 copies[name] = ctx.filectx(name).renamed()[0]
248 except TypeError:
261 except TypeError:
249 pass
262 pass
250 return copies
263 return copies
251
264
252 def getcommit(self, rev):
265 def getcommit(self, rev):
253 ctx = self.changectx(rev)
266 ctx = self.changectx(rev)
254 parents = [hex(p.node()) for p in ctx.parents() if p.node() != nullid]
267 parents = [hex(p.node()) for p in ctx.parents() if p.node() != nullid]
255 if self.saverev:
268 if self.saverev:
256 crev = rev
269 crev = rev
257 else:
270 else:
258 crev = None
271 crev = None
259 return commit(author=ctx.user(), date=util.datestr(ctx.date()),
272 return commit(author=ctx.user(), date=util.datestr(ctx.date()),
260 desc=ctx.description(), rev=crev, parents=parents,
273 desc=ctx.description(), rev=crev, parents=parents,
261 branch=ctx.branch(), extra=ctx.extra())
274 branch=ctx.branch(), extra=ctx.extra())
262
275
263 def gettags(self):
276 def gettags(self):
264 tags = [t for t in self.repo.tagslist() if t[0] != 'tip']
277 tags = [t for t in self.repo.tagslist() if t[0] != 'tip']
265 return dict([(name, hex(node)) for name, node in tags])
278 return dict([(name, hex(node)) for name, node in tags])
266
279
267 def getchangedfiles(self, rev, i):
280 def getchangedfiles(self, rev, i):
268 ctx = self.changectx(rev)
281 ctx = self.changectx(rev)
269 i = i or 0
282 i = i or 0
270 changes = self.repo.status(ctx.parents()[i].node(), ctx.node())[:3]
283 changes = self.repo.status(ctx.parents()[i].node(), ctx.node())[:3]
271
284
272 if i == 0:
285 if i == 0:
273 self._changescache = (rev, changes)
286 self._changescache = (rev, changes)
274
287
275 return changes[0] + changes[1] + changes[2]
288 return changes[0] + changes[1] + changes[2]
276
289
277 def converted(self, rev, destrev):
290 def converted(self, rev, destrev):
278 if self.convertfp is None:
291 if self.convertfp is None:
279 self.convertfp = open(os.path.join(self.path, '.hg', 'shamap'),
292 self.convertfp = open(os.path.join(self.path, '.hg', 'shamap'),
280 'a')
293 'a')
281 self.convertfp.write('%s %s\n' % (destrev, rev))
294 self.convertfp.write('%s %s\n' % (destrev, rev))
282 self.convertfp.flush()
295 self.convertfp.flush()
283
296
284 def before(self):
297 def before(self):
285 self.ui.debug(_('run hg source pre-conversion action\n'))
298 self.ui.debug(_('run hg source pre-conversion action\n'))
286
299
287 def after(self):
300 def after(self):
288 self.ui.debug(_('run hg source post-conversion action\n'))
301 self.ui.debug(_('run hg source post-conversion action\n'))
General Comments 0
You need to be logged in to leave comments. Login now