##// END OF EJS Templates
convert: common use absolute_import
timeless -
r28410:48b04018 default
parent child Browse files
Show More
@@ -1,478 +1,489
1 # common.py - common code for the convert extension
1 # common.py - common code for the convert extension
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@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 from __future__ import absolute_import
7
8
8 import base64, errno, subprocess, os, datetime, re
9 import base64
9 import cPickle as pickle
10 import cPickle as pickle
10 from mercurial import phases, util, error
11 import datetime
12 import errno
13 import os
14 import re
15 import subprocess
16
17 from mercurial import (
18 error,
19 phases,
20 util,
21 )
11 from mercurial.i18n import _
22 from mercurial.i18n import _
12
23
13 propertycache = util.propertycache
24 propertycache = util.propertycache
14
25
15 def encodeargs(args):
26 def encodeargs(args):
16 def encodearg(s):
27 def encodearg(s):
17 lines = base64.encodestring(s)
28 lines = base64.encodestring(s)
18 lines = [l.splitlines()[0] for l in lines]
29 lines = [l.splitlines()[0] for l in lines]
19 return ''.join(lines)
30 return ''.join(lines)
20
31
21 s = pickle.dumps(args)
32 s = pickle.dumps(args)
22 return encodearg(s)
33 return encodearg(s)
23
34
24 def decodeargs(s):
35 def decodeargs(s):
25 s = base64.decodestring(s)
36 s = base64.decodestring(s)
26 return pickle.loads(s)
37 return pickle.loads(s)
27
38
28 class MissingTool(Exception):
39 class MissingTool(Exception):
29 pass
40 pass
30
41
31 def checktool(exe, name=None, abort=True):
42 def checktool(exe, name=None, abort=True):
32 name = name or exe
43 name = name or exe
33 if not util.findexe(exe):
44 if not util.findexe(exe):
34 if abort:
45 if abort:
35 exc = error.Abort
46 exc = error.Abort
36 else:
47 else:
37 exc = MissingTool
48 exc = MissingTool
38 raise exc(_('cannot find required "%s" tool') % name)
49 raise exc(_('cannot find required "%s" tool') % name)
39
50
40 class NoRepo(Exception):
51 class NoRepo(Exception):
41 pass
52 pass
42
53
43 SKIPREV = 'SKIP'
54 SKIPREV = 'SKIP'
44
55
45 class commit(object):
56 class commit(object):
46 def __init__(self, author, date, desc, parents, branch=None, rev=None,
57 def __init__(self, author, date, desc, parents, branch=None, rev=None,
47 extra={}, sortkey=None, saverev=True, phase=phases.draft):
58 extra={}, sortkey=None, saverev=True, phase=phases.draft):
48 self.author = author or 'unknown'
59 self.author = author or 'unknown'
49 self.date = date or '0 0'
60 self.date = date or '0 0'
50 self.desc = desc
61 self.desc = desc
51 self.parents = parents
62 self.parents = parents
52 self.branch = branch
63 self.branch = branch
53 self.rev = rev
64 self.rev = rev
54 self.extra = extra
65 self.extra = extra
55 self.sortkey = sortkey
66 self.sortkey = sortkey
56 self.saverev = saverev
67 self.saverev = saverev
57 self.phase = phase
68 self.phase = phase
58
69
59 class converter_source(object):
70 class converter_source(object):
60 """Conversion source interface"""
71 """Conversion source interface"""
61
72
62 def __init__(self, ui, path=None, revs=None):
73 def __init__(self, ui, path=None, revs=None):
63 """Initialize conversion source (or raise NoRepo("message")
74 """Initialize conversion source (or raise NoRepo("message")
64 exception if path is not a valid repository)"""
75 exception if path is not a valid repository)"""
65 self.ui = ui
76 self.ui = ui
66 self.path = path
77 self.path = path
67 self.revs = revs
78 self.revs = revs
68
79
69 self.encoding = 'utf-8'
80 self.encoding = 'utf-8'
70
81
71 def checkhexformat(self, revstr, mapname='splicemap'):
82 def checkhexformat(self, revstr, mapname='splicemap'):
72 """ fails if revstr is not a 40 byte hex. mercurial and git both uses
83 """ fails if revstr is not a 40 byte hex. mercurial and git both uses
73 such format for their revision numbering
84 such format for their revision numbering
74 """
85 """
75 if not re.match(r'[0-9a-fA-F]{40,40}$', revstr):
86 if not re.match(r'[0-9a-fA-F]{40,40}$', revstr):
76 raise error.Abort(_('%s entry %s is not a valid revision'
87 raise error.Abort(_('%s entry %s is not a valid revision'
77 ' identifier') % (mapname, revstr))
88 ' identifier') % (mapname, revstr))
78
89
79 def before(self):
90 def before(self):
80 pass
91 pass
81
92
82 def after(self):
93 def after(self):
83 pass
94 pass
84
95
85 def targetfilebelongstosource(self, targetfilename):
96 def targetfilebelongstosource(self, targetfilename):
86 """Returns true if the given targetfile belongs to the source repo. This
97 """Returns true if the given targetfile belongs to the source repo. This
87 is useful when only a subdirectory of the target belongs to the source
98 is useful when only a subdirectory of the target belongs to the source
88 repo."""
99 repo."""
89 # For normal full repo converts, this is always True.
100 # For normal full repo converts, this is always True.
90 return True
101 return True
91
102
92 def setrevmap(self, revmap):
103 def setrevmap(self, revmap):
93 """set the map of already-converted revisions"""
104 """set the map of already-converted revisions"""
94 pass
105 pass
95
106
96 def getheads(self):
107 def getheads(self):
97 """Return a list of this repository's heads"""
108 """Return a list of this repository's heads"""
98 raise NotImplementedError
109 raise NotImplementedError
99
110
100 def getfile(self, name, rev):
111 def getfile(self, name, rev):
101 """Return a pair (data, mode) where data is the file content
112 """Return a pair (data, mode) where data is the file content
102 as a string and mode one of '', 'x' or 'l'. rev is the
113 as a string and mode one of '', 'x' or 'l'. rev is the
103 identifier returned by a previous call to getchanges().
114 identifier returned by a previous call to getchanges().
104 Data is None if file is missing/deleted in rev.
115 Data is None if file is missing/deleted in rev.
105 """
116 """
106 raise NotImplementedError
117 raise NotImplementedError
107
118
108 def getchanges(self, version, full):
119 def getchanges(self, version, full):
109 """Returns a tuple of (files, copies, cleanp2).
120 """Returns a tuple of (files, copies, cleanp2).
110
121
111 files is a sorted list of (filename, id) tuples for all files
122 files is a sorted list of (filename, id) tuples for all files
112 changed between version and its first parent returned by
123 changed between version and its first parent returned by
113 getcommit(). If full, all files in that revision is returned.
124 getcommit(). If full, all files in that revision is returned.
114 id is the source revision id of the file.
125 id is the source revision id of the file.
115
126
116 copies is a dictionary of dest: source
127 copies is a dictionary of dest: source
117
128
118 cleanp2 is the set of files filenames that are clean against p2.
129 cleanp2 is the set of files filenames that are clean against p2.
119 (Files that are clean against p1 are already not in files (unless
130 (Files that are clean against p1 are already not in files (unless
120 full). This makes it possible to handle p2 clean files similarly.)
131 full). This makes it possible to handle p2 clean files similarly.)
121 """
132 """
122 raise NotImplementedError
133 raise NotImplementedError
123
134
124 def getcommit(self, version):
135 def getcommit(self, version):
125 """Return the commit object for version"""
136 """Return the commit object for version"""
126 raise NotImplementedError
137 raise NotImplementedError
127
138
128 def numcommits(self):
139 def numcommits(self):
129 """Return the number of commits in this source.
140 """Return the number of commits in this source.
130
141
131 If unknown, return None.
142 If unknown, return None.
132 """
143 """
133 return None
144 return None
134
145
135 def gettags(self):
146 def gettags(self):
136 """Return the tags as a dictionary of name: revision
147 """Return the tags as a dictionary of name: revision
137
148
138 Tag names must be UTF-8 strings.
149 Tag names must be UTF-8 strings.
139 """
150 """
140 raise NotImplementedError
151 raise NotImplementedError
141
152
142 def recode(self, s, encoding=None):
153 def recode(self, s, encoding=None):
143 if not encoding:
154 if not encoding:
144 encoding = self.encoding or 'utf-8'
155 encoding = self.encoding or 'utf-8'
145
156
146 if isinstance(s, unicode):
157 if isinstance(s, unicode):
147 return s.encode("utf-8")
158 return s.encode("utf-8")
148 try:
159 try:
149 return s.decode(encoding).encode("utf-8")
160 return s.decode(encoding).encode("utf-8")
150 except UnicodeError:
161 except UnicodeError:
151 try:
162 try:
152 return s.decode("latin-1").encode("utf-8")
163 return s.decode("latin-1").encode("utf-8")
153 except UnicodeError:
164 except UnicodeError:
154 return s.decode(encoding, "replace").encode("utf-8")
165 return s.decode(encoding, "replace").encode("utf-8")
155
166
156 def getchangedfiles(self, rev, i):
167 def getchangedfiles(self, rev, i):
157 """Return the files changed by rev compared to parent[i].
168 """Return the files changed by rev compared to parent[i].
158
169
159 i is an index selecting one of the parents of rev. The return
170 i is an index selecting one of the parents of rev. The return
160 value should be the list of files that are different in rev and
171 value should be the list of files that are different in rev and
161 this parent.
172 this parent.
162
173
163 If rev has no parents, i is None.
174 If rev has no parents, i is None.
164
175
165 This function is only needed to support --filemap
176 This function is only needed to support --filemap
166 """
177 """
167 raise NotImplementedError
178 raise NotImplementedError
168
179
169 def converted(self, rev, sinkrev):
180 def converted(self, rev, sinkrev):
170 '''Notify the source that a revision has been converted.'''
181 '''Notify the source that a revision has been converted.'''
171 pass
182 pass
172
183
173 def hasnativeorder(self):
184 def hasnativeorder(self):
174 """Return true if this source has a meaningful, native revision
185 """Return true if this source has a meaningful, native revision
175 order. For instance, Mercurial revisions are store sequentially
186 order. For instance, Mercurial revisions are store sequentially
176 while there is no such global ordering with Darcs.
187 while there is no such global ordering with Darcs.
177 """
188 """
178 return False
189 return False
179
190
180 def hasnativeclose(self):
191 def hasnativeclose(self):
181 """Return true if this source has ability to close branch.
192 """Return true if this source has ability to close branch.
182 """
193 """
183 return False
194 return False
184
195
185 def lookuprev(self, rev):
196 def lookuprev(self, rev):
186 """If rev is a meaningful revision reference in source, return
197 """If rev is a meaningful revision reference in source, return
187 the referenced identifier in the same format used by getcommit().
198 the referenced identifier in the same format used by getcommit().
188 return None otherwise.
199 return None otherwise.
189 """
200 """
190 return None
201 return None
191
202
192 def getbookmarks(self):
203 def getbookmarks(self):
193 """Return the bookmarks as a dictionary of name: revision
204 """Return the bookmarks as a dictionary of name: revision
194
205
195 Bookmark names are to be UTF-8 strings.
206 Bookmark names are to be UTF-8 strings.
196 """
207 """
197 return {}
208 return {}
198
209
199 def checkrevformat(self, revstr, mapname='splicemap'):
210 def checkrevformat(self, revstr, mapname='splicemap'):
200 """revstr is a string that describes a revision in the given
211 """revstr is a string that describes a revision in the given
201 source control system. Return true if revstr has correct
212 source control system. Return true if revstr has correct
202 format.
213 format.
203 """
214 """
204 return True
215 return True
205
216
206 class converter_sink(object):
217 class converter_sink(object):
207 """Conversion sink (target) interface"""
218 """Conversion sink (target) interface"""
208
219
209 def __init__(self, ui, path):
220 def __init__(self, ui, path):
210 """Initialize conversion sink (or raise NoRepo("message")
221 """Initialize conversion sink (or raise NoRepo("message")
211 exception if path is not a valid repository)
222 exception if path is not a valid repository)
212
223
213 created is a list of paths to remove if a fatal error occurs
224 created is a list of paths to remove if a fatal error occurs
214 later"""
225 later"""
215 self.ui = ui
226 self.ui = ui
216 self.path = path
227 self.path = path
217 self.created = []
228 self.created = []
218
229
219 def revmapfile(self):
230 def revmapfile(self):
220 """Path to a file that will contain lines
231 """Path to a file that will contain lines
221 source_rev_id sink_rev_id
232 source_rev_id sink_rev_id
222 mapping equivalent revision identifiers for each system."""
233 mapping equivalent revision identifiers for each system."""
223 raise NotImplementedError
234 raise NotImplementedError
224
235
225 def authorfile(self):
236 def authorfile(self):
226 """Path to a file that will contain lines
237 """Path to a file that will contain lines
227 srcauthor=dstauthor
238 srcauthor=dstauthor
228 mapping equivalent authors identifiers for each system."""
239 mapping equivalent authors identifiers for each system."""
229 return None
240 return None
230
241
231 def putcommit(self, files, copies, parents, commit, source, revmap, full,
242 def putcommit(self, files, copies, parents, commit, source, revmap, full,
232 cleanp2):
243 cleanp2):
233 """Create a revision with all changed files listed in 'files'
244 """Create a revision with all changed files listed in 'files'
234 and having listed parents. 'commit' is a commit object
245 and having listed parents. 'commit' is a commit object
235 containing at a minimum the author, date, and message for this
246 containing at a minimum the author, date, and message for this
236 changeset. 'files' is a list of (path, version) tuples,
247 changeset. 'files' is a list of (path, version) tuples,
237 'copies' is a dictionary mapping destinations to sources,
248 'copies' is a dictionary mapping destinations to sources,
238 'source' is the source repository, and 'revmap' is a mapfile
249 'source' is the source repository, and 'revmap' is a mapfile
239 of source revisions to converted revisions. Only getfile() and
250 of source revisions to converted revisions. Only getfile() and
240 lookuprev() should be called on 'source'. 'full' means that 'files'
251 lookuprev() should be called on 'source'. 'full' means that 'files'
241 is complete and all other files should be removed.
252 is complete and all other files should be removed.
242 'cleanp2' is a set of the filenames that are unchanged from p2
253 'cleanp2' is a set of the filenames that are unchanged from p2
243 (only in the common merge case where there two parents).
254 (only in the common merge case where there two parents).
244
255
245 Note that the sink repository is not told to update itself to
256 Note that the sink repository is not told to update itself to
246 a particular revision (or even what that revision would be)
257 a particular revision (or even what that revision would be)
247 before it receives the file data.
258 before it receives the file data.
248 """
259 """
249 raise NotImplementedError
260 raise NotImplementedError
250
261
251 def puttags(self, tags):
262 def puttags(self, tags):
252 """Put tags into sink.
263 """Put tags into sink.
253
264
254 tags: {tagname: sink_rev_id, ...} where tagname is an UTF-8 string.
265 tags: {tagname: sink_rev_id, ...} where tagname is an UTF-8 string.
255 Return a pair (tag_revision, tag_parent_revision), or (None, None)
266 Return a pair (tag_revision, tag_parent_revision), or (None, None)
256 if nothing was changed.
267 if nothing was changed.
257 """
268 """
258 raise NotImplementedError
269 raise NotImplementedError
259
270
260 def setbranch(self, branch, pbranches):
271 def setbranch(self, branch, pbranches):
261 """Set the current branch name. Called before the first putcommit
272 """Set the current branch name. Called before the first putcommit
262 on the branch.
273 on the branch.
263 branch: branch name for subsequent commits
274 branch: branch name for subsequent commits
264 pbranches: (converted parent revision, parent branch) tuples"""
275 pbranches: (converted parent revision, parent branch) tuples"""
265 pass
276 pass
266
277
267 def setfilemapmode(self, active):
278 def setfilemapmode(self, active):
268 """Tell the destination that we're using a filemap
279 """Tell the destination that we're using a filemap
269
280
270 Some converter_sources (svn in particular) can claim that a file
281 Some converter_sources (svn in particular) can claim that a file
271 was changed in a revision, even if there was no change. This method
282 was changed in a revision, even if there was no change. This method
272 tells the destination that we're using a filemap and that it should
283 tells the destination that we're using a filemap and that it should
273 filter empty revisions.
284 filter empty revisions.
274 """
285 """
275 pass
286 pass
276
287
277 def before(self):
288 def before(self):
278 pass
289 pass
279
290
280 def after(self):
291 def after(self):
281 pass
292 pass
282
293
283 def putbookmarks(self, bookmarks):
294 def putbookmarks(self, bookmarks):
284 """Put bookmarks into sink.
295 """Put bookmarks into sink.
285
296
286 bookmarks: {bookmarkname: sink_rev_id, ...}
297 bookmarks: {bookmarkname: sink_rev_id, ...}
287 where bookmarkname is an UTF-8 string.
298 where bookmarkname is an UTF-8 string.
288 """
299 """
289 pass
300 pass
290
301
291 def hascommitfrommap(self, rev):
302 def hascommitfrommap(self, rev):
292 """Return False if a rev mentioned in a filemap is known to not be
303 """Return False if a rev mentioned in a filemap is known to not be
293 present."""
304 present."""
294 raise NotImplementedError
305 raise NotImplementedError
295
306
296 def hascommitforsplicemap(self, rev):
307 def hascommitforsplicemap(self, rev):
297 """This method is for the special needs for splicemap handling and not
308 """This method is for the special needs for splicemap handling and not
298 for general use. Returns True if the sink contains rev, aborts on some
309 for general use. Returns True if the sink contains rev, aborts on some
299 special cases."""
310 special cases."""
300 raise NotImplementedError
311 raise NotImplementedError
301
312
302 class commandline(object):
313 class commandline(object):
303 def __init__(self, ui, command):
314 def __init__(self, ui, command):
304 self.ui = ui
315 self.ui = ui
305 self.command = command
316 self.command = command
306
317
307 def prerun(self):
318 def prerun(self):
308 pass
319 pass
309
320
310 def postrun(self):
321 def postrun(self):
311 pass
322 pass
312
323
313 def _cmdline(self, cmd, *args, **kwargs):
324 def _cmdline(self, cmd, *args, **kwargs):
314 cmdline = [self.command, cmd] + list(args)
325 cmdline = [self.command, cmd] + list(args)
315 for k, v in kwargs.iteritems():
326 for k, v in kwargs.iteritems():
316 if len(k) == 1:
327 if len(k) == 1:
317 cmdline.append('-' + k)
328 cmdline.append('-' + k)
318 else:
329 else:
319 cmdline.append('--' + k.replace('_', '-'))
330 cmdline.append('--' + k.replace('_', '-'))
320 try:
331 try:
321 if len(k) == 1:
332 if len(k) == 1:
322 cmdline.append('' + v)
333 cmdline.append('' + v)
323 else:
334 else:
324 cmdline[-1] += '=' + v
335 cmdline[-1] += '=' + v
325 except TypeError:
336 except TypeError:
326 pass
337 pass
327 cmdline = [util.shellquote(arg) for arg in cmdline]
338 cmdline = [util.shellquote(arg) for arg in cmdline]
328 if not self.ui.debugflag:
339 if not self.ui.debugflag:
329 cmdline += ['2>', os.devnull]
340 cmdline += ['2>', os.devnull]
330 cmdline = ' '.join(cmdline)
341 cmdline = ' '.join(cmdline)
331 return cmdline
342 return cmdline
332
343
333 def _run(self, cmd, *args, **kwargs):
344 def _run(self, cmd, *args, **kwargs):
334 def popen(cmdline):
345 def popen(cmdline):
335 p = subprocess.Popen(cmdline, shell=True, bufsize=-1,
346 p = subprocess.Popen(cmdline, shell=True, bufsize=-1,
336 close_fds=util.closefds,
347 close_fds=util.closefds,
337 stdout=subprocess.PIPE)
348 stdout=subprocess.PIPE)
338 return p
349 return p
339 return self._dorun(popen, cmd, *args, **kwargs)
350 return self._dorun(popen, cmd, *args, **kwargs)
340
351
341 def _run2(self, cmd, *args, **kwargs):
352 def _run2(self, cmd, *args, **kwargs):
342 return self._dorun(util.popen2, cmd, *args, **kwargs)
353 return self._dorun(util.popen2, cmd, *args, **kwargs)
343
354
344 def _dorun(self, openfunc, cmd, *args, **kwargs):
355 def _dorun(self, openfunc, cmd, *args, **kwargs):
345 cmdline = self._cmdline(cmd, *args, **kwargs)
356 cmdline = self._cmdline(cmd, *args, **kwargs)
346 self.ui.debug('running: %s\n' % (cmdline,))
357 self.ui.debug('running: %s\n' % (cmdline,))
347 self.prerun()
358 self.prerun()
348 try:
359 try:
349 return openfunc(cmdline)
360 return openfunc(cmdline)
350 finally:
361 finally:
351 self.postrun()
362 self.postrun()
352
363
353 def run(self, cmd, *args, **kwargs):
364 def run(self, cmd, *args, **kwargs):
354 p = self._run(cmd, *args, **kwargs)
365 p = self._run(cmd, *args, **kwargs)
355 output = p.communicate()[0]
366 output = p.communicate()[0]
356 self.ui.debug(output)
367 self.ui.debug(output)
357 return output, p.returncode
368 return output, p.returncode
358
369
359 def runlines(self, cmd, *args, **kwargs):
370 def runlines(self, cmd, *args, **kwargs):
360 p = self._run(cmd, *args, **kwargs)
371 p = self._run(cmd, *args, **kwargs)
361 output = p.stdout.readlines()
372 output = p.stdout.readlines()
362 p.wait()
373 p.wait()
363 self.ui.debug(''.join(output))
374 self.ui.debug(''.join(output))
364 return output, p.returncode
375 return output, p.returncode
365
376
366 def checkexit(self, status, output=''):
377 def checkexit(self, status, output=''):
367 if status:
378 if status:
368 if output:
379 if output:
369 self.ui.warn(_('%s error:\n') % self.command)
380 self.ui.warn(_('%s error:\n') % self.command)
370 self.ui.warn(output)
381 self.ui.warn(output)
371 msg = util.explainexit(status)[0]
382 msg = util.explainexit(status)[0]
372 raise error.Abort('%s %s' % (self.command, msg))
383 raise error.Abort('%s %s' % (self.command, msg))
373
384
374 def run0(self, cmd, *args, **kwargs):
385 def run0(self, cmd, *args, **kwargs):
375 output, status = self.run(cmd, *args, **kwargs)
386 output, status = self.run(cmd, *args, **kwargs)
376 self.checkexit(status, output)
387 self.checkexit(status, output)
377 return output
388 return output
378
389
379 def runlines0(self, cmd, *args, **kwargs):
390 def runlines0(self, cmd, *args, **kwargs):
380 output, status = self.runlines(cmd, *args, **kwargs)
391 output, status = self.runlines(cmd, *args, **kwargs)
381 self.checkexit(status, ''.join(output))
392 self.checkexit(status, ''.join(output))
382 return output
393 return output
383
394
384 @propertycache
395 @propertycache
385 def argmax(self):
396 def argmax(self):
386 # POSIX requires at least 4096 bytes for ARG_MAX
397 # POSIX requires at least 4096 bytes for ARG_MAX
387 argmax = 4096
398 argmax = 4096
388 try:
399 try:
389 argmax = os.sysconf("SC_ARG_MAX")
400 argmax = os.sysconf("SC_ARG_MAX")
390 except (AttributeError, ValueError):
401 except (AttributeError, ValueError):
391 pass
402 pass
392
403
393 # Windows shells impose their own limits on command line length,
404 # Windows shells impose their own limits on command line length,
394 # down to 2047 bytes for cmd.exe under Windows NT/2k and 2500 bytes
405 # down to 2047 bytes for cmd.exe under Windows NT/2k and 2500 bytes
395 # for older 4nt.exe. See http://support.microsoft.com/kb/830473 for
406 # for older 4nt.exe. See http://support.microsoft.com/kb/830473 for
396 # details about cmd.exe limitations.
407 # details about cmd.exe limitations.
397
408
398 # Since ARG_MAX is for command line _and_ environment, lower our limit
409 # Since ARG_MAX is for command line _and_ environment, lower our limit
399 # (and make happy Windows shells while doing this).
410 # (and make happy Windows shells while doing this).
400 return argmax // 2 - 1
411 return argmax // 2 - 1
401
412
402 def _limit_arglist(self, arglist, cmd, *args, **kwargs):
413 def _limit_arglist(self, arglist, cmd, *args, **kwargs):
403 cmdlen = len(self._cmdline(cmd, *args, **kwargs))
414 cmdlen = len(self._cmdline(cmd, *args, **kwargs))
404 limit = self.argmax - cmdlen
415 limit = self.argmax - cmdlen
405 bytes = 0
416 bytes = 0
406 fl = []
417 fl = []
407 for fn in arglist:
418 for fn in arglist:
408 b = len(fn) + 3
419 b = len(fn) + 3
409 if bytes + b < limit or len(fl) == 0:
420 if bytes + b < limit or len(fl) == 0:
410 fl.append(fn)
421 fl.append(fn)
411 bytes += b
422 bytes += b
412 else:
423 else:
413 yield fl
424 yield fl
414 fl = [fn]
425 fl = [fn]
415 bytes = b
426 bytes = b
416 if fl:
427 if fl:
417 yield fl
428 yield fl
418
429
419 def xargs(self, arglist, cmd, *args, **kwargs):
430 def xargs(self, arglist, cmd, *args, **kwargs):
420 for l in self._limit_arglist(arglist, cmd, *args, **kwargs):
431 for l in self._limit_arglist(arglist, cmd, *args, **kwargs):
421 self.run0(cmd, *(list(args) + l), **kwargs)
432 self.run0(cmd, *(list(args) + l), **kwargs)
422
433
423 class mapfile(dict):
434 class mapfile(dict):
424 def __init__(self, ui, path):
435 def __init__(self, ui, path):
425 super(mapfile, self).__init__()
436 super(mapfile, self).__init__()
426 self.ui = ui
437 self.ui = ui
427 self.path = path
438 self.path = path
428 self.fp = None
439 self.fp = None
429 self.order = []
440 self.order = []
430 self._read()
441 self._read()
431
442
432 def _read(self):
443 def _read(self):
433 if not self.path:
444 if not self.path:
434 return
445 return
435 try:
446 try:
436 fp = open(self.path, 'r')
447 fp = open(self.path, 'r')
437 except IOError as err:
448 except IOError as err:
438 if err.errno != errno.ENOENT:
449 if err.errno != errno.ENOENT:
439 raise
450 raise
440 return
451 return
441 for i, line in enumerate(fp):
452 for i, line in enumerate(fp):
442 line = line.splitlines()[0].rstrip()
453 line = line.splitlines()[0].rstrip()
443 if not line:
454 if not line:
444 # Ignore blank lines
455 # Ignore blank lines
445 continue
456 continue
446 try:
457 try:
447 key, value = line.rsplit(' ', 1)
458 key, value = line.rsplit(' ', 1)
448 except ValueError:
459 except ValueError:
449 raise error.Abort(
460 raise error.Abort(
450 _('syntax error in %s(%d): key/value pair expected')
461 _('syntax error in %s(%d): key/value pair expected')
451 % (self.path, i + 1))
462 % (self.path, i + 1))
452 if key not in self:
463 if key not in self:
453 self.order.append(key)
464 self.order.append(key)
454 super(mapfile, self).__setitem__(key, value)
465 super(mapfile, self).__setitem__(key, value)
455 fp.close()
466 fp.close()
456
467
457 def __setitem__(self, key, value):
468 def __setitem__(self, key, value):
458 if self.fp is None:
469 if self.fp is None:
459 try:
470 try:
460 self.fp = open(self.path, 'a')
471 self.fp = open(self.path, 'a')
461 except IOError as err:
472 except IOError as err:
462 raise error.Abort(_('could not open map file %r: %s') %
473 raise error.Abort(_('could not open map file %r: %s') %
463 (self.path, err.strerror))
474 (self.path, err.strerror))
464 self.fp.write('%s %s\n' % (key, value))
475 self.fp.write('%s %s\n' % (key, value))
465 self.fp.flush()
476 self.fp.flush()
466 super(mapfile, self).__setitem__(key, value)
477 super(mapfile, self).__setitem__(key, value)
467
478
468 def close(self):
479 def close(self):
469 if self.fp:
480 if self.fp:
470 self.fp.close()
481 self.fp.close()
471 self.fp = None
482 self.fp = None
472
483
473 def makedatetimestamp(t):
484 def makedatetimestamp(t):
474 """Like util.makedate() but for time t instead of current time"""
485 """Like util.makedate() but for time t instead of current time"""
475 delta = (datetime.datetime.utcfromtimestamp(t) -
486 delta = (datetime.datetime.utcfromtimestamp(t) -
476 datetime.datetime.fromtimestamp(t))
487 datetime.datetime.fromtimestamp(t))
477 tz = delta.days * 86400 + delta.seconds
488 tz = delta.days * 86400 + delta.seconds
478 return t, tz
489 return t, tz
@@ -1,137 +1,136
1 #require test-repo
1 #require test-repo
2
2
3 $ cd "$TESTDIR"/..
3 $ cd "$TESTDIR"/..
4
4
5 $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs python contrib/check-py3-compat.py
5 $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs python contrib/check-py3-compat.py
6 contrib/check-code.py not using absolute_import
6 contrib/check-code.py not using absolute_import
7 contrib/check-code.py requires print_function
7 contrib/check-code.py requires print_function
8 contrib/debugshell.py not using absolute_import
8 contrib/debugshell.py not using absolute_import
9 contrib/import-checker.py not using absolute_import
9 contrib/import-checker.py not using absolute_import
10 contrib/import-checker.py requires print_function
10 contrib/import-checker.py requires print_function
11 contrib/memory.py not using absolute_import
11 contrib/memory.py not using absolute_import
12 contrib/perf.py not using absolute_import
12 contrib/perf.py not using absolute_import
13 contrib/python-hook-examples.py not using absolute_import
13 contrib/python-hook-examples.py not using absolute_import
14 contrib/revsetbenchmarks.py not using absolute_import
14 contrib/revsetbenchmarks.py not using absolute_import
15 contrib/revsetbenchmarks.py requires print_function
15 contrib/revsetbenchmarks.py requires print_function
16 contrib/showstack.py not using absolute_import
16 contrib/showstack.py not using absolute_import
17 contrib/synthrepo.py not using absolute_import
17 contrib/synthrepo.py not using absolute_import
18 contrib/win32/hgwebdir_wsgi.py not using absolute_import
18 contrib/win32/hgwebdir_wsgi.py not using absolute_import
19 doc/check-seclevel.py not using absolute_import
19 doc/check-seclevel.py not using absolute_import
20 doc/gendoc.py not using absolute_import
20 doc/gendoc.py not using absolute_import
21 doc/hgmanpage.py not using absolute_import
21 doc/hgmanpage.py not using absolute_import
22 hgext/__init__.py not using absolute_import
22 hgext/__init__.py not using absolute_import
23 hgext/color.py not using absolute_import
23 hgext/color.py not using absolute_import
24 hgext/convert/__init__.py not using absolute_import
24 hgext/convert/__init__.py not using absolute_import
25 hgext/convert/bzr.py not using absolute_import
25 hgext/convert/bzr.py not using absolute_import
26 hgext/convert/common.py not using absolute_import
27 hgext/convert/cvs.py not using absolute_import
26 hgext/convert/cvs.py not using absolute_import
28 hgext/convert/transport.py not using absolute_import
27 hgext/convert/transport.py not using absolute_import
29 hgext/eol.py not using absolute_import
28 hgext/eol.py not using absolute_import
30 hgext/extdiff.py not using absolute_import
29 hgext/extdiff.py not using absolute_import
31 hgext/factotum.py not using absolute_import
30 hgext/factotum.py not using absolute_import
32 hgext/fetch.py not using absolute_import
31 hgext/fetch.py not using absolute_import
33 hgext/gpg.py not using absolute_import
32 hgext/gpg.py not using absolute_import
34 hgext/graphlog.py not using absolute_import
33 hgext/graphlog.py not using absolute_import
35 hgext/hgcia.py not using absolute_import
34 hgext/hgcia.py not using absolute_import
36 hgext/hgk.py not using absolute_import
35 hgext/hgk.py not using absolute_import
37 hgext/highlight/__init__.py not using absolute_import
36 hgext/highlight/__init__.py not using absolute_import
38 hgext/highlight/highlight.py not using absolute_import
37 hgext/highlight/highlight.py not using absolute_import
39 hgext/histedit.py not using absolute_import
38 hgext/histedit.py not using absolute_import
40 hgext/largefiles/__init__.py not using absolute_import
39 hgext/largefiles/__init__.py not using absolute_import
41 hgext/largefiles/basestore.py not using absolute_import
40 hgext/largefiles/basestore.py not using absolute_import
42 hgext/largefiles/lfcommands.py not using absolute_import
41 hgext/largefiles/lfcommands.py not using absolute_import
43 hgext/largefiles/lfutil.py not using absolute_import
42 hgext/largefiles/lfutil.py not using absolute_import
44 hgext/largefiles/localstore.py not using absolute_import
43 hgext/largefiles/localstore.py not using absolute_import
45 hgext/largefiles/overrides.py not using absolute_import
44 hgext/largefiles/overrides.py not using absolute_import
46 hgext/largefiles/proto.py not using absolute_import
45 hgext/largefiles/proto.py not using absolute_import
47 hgext/largefiles/remotestore.py not using absolute_import
46 hgext/largefiles/remotestore.py not using absolute_import
48 hgext/largefiles/reposetup.py not using absolute_import
47 hgext/largefiles/reposetup.py not using absolute_import
49 hgext/largefiles/uisetup.py not using absolute_import
48 hgext/largefiles/uisetup.py not using absolute_import
50 hgext/largefiles/wirestore.py not using absolute_import
49 hgext/largefiles/wirestore.py not using absolute_import
51 hgext/mq.py not using absolute_import
50 hgext/mq.py not using absolute_import
52 hgext/notify.py not using absolute_import
51 hgext/notify.py not using absolute_import
53 hgext/patchbomb.py not using absolute_import
52 hgext/patchbomb.py not using absolute_import
54 hgext/rebase.py not using absolute_import
53 hgext/rebase.py not using absolute_import
55 hgext/share.py not using absolute_import
54 hgext/share.py not using absolute_import
56 hgext/transplant.py not using absolute_import
55 hgext/transplant.py not using absolute_import
57 hgext/win32mbcs.py not using absolute_import
56 hgext/win32mbcs.py not using absolute_import
58 hgext/win32text.py not using absolute_import
57 hgext/win32text.py not using absolute_import
59 i18n/check-translation.py not using absolute_import
58 i18n/check-translation.py not using absolute_import
60 i18n/polib.py not using absolute_import
59 i18n/polib.py not using absolute_import
61 setup.py not using absolute_import
60 setup.py not using absolute_import
62 tests/filterpyflakes.py requires print_function
61 tests/filterpyflakes.py requires print_function
63 tests/generate-working-copy-states.py requires print_function
62 tests/generate-working-copy-states.py requires print_function
64 tests/get-with-headers.py requires print_function
63 tests/get-with-headers.py requires print_function
65 tests/heredoctest.py requires print_function
64 tests/heredoctest.py requires print_function
66 tests/hypothesishelpers.py not using absolute_import
65 tests/hypothesishelpers.py not using absolute_import
67 tests/hypothesishelpers.py requires print_function
66 tests/hypothesishelpers.py requires print_function
68 tests/killdaemons.py not using absolute_import
67 tests/killdaemons.py not using absolute_import
69 tests/md5sum.py not using absolute_import
68 tests/md5sum.py not using absolute_import
70 tests/mockblackbox.py not using absolute_import
69 tests/mockblackbox.py not using absolute_import
71 tests/printenv.py not using absolute_import
70 tests/printenv.py not using absolute_import
72 tests/readlink.py not using absolute_import
71 tests/readlink.py not using absolute_import
73 tests/readlink.py requires print_function
72 tests/readlink.py requires print_function
74 tests/revlog-formatv0.py not using absolute_import
73 tests/revlog-formatv0.py not using absolute_import
75 tests/run-tests.py not using absolute_import
74 tests/run-tests.py not using absolute_import
76 tests/seq.py not using absolute_import
75 tests/seq.py not using absolute_import
77 tests/seq.py requires print_function
76 tests/seq.py requires print_function
78 tests/silenttestrunner.py not using absolute_import
77 tests/silenttestrunner.py not using absolute_import
79 tests/silenttestrunner.py requires print_function
78 tests/silenttestrunner.py requires print_function
80 tests/sitecustomize.py not using absolute_import
79 tests/sitecustomize.py not using absolute_import
81 tests/svn-safe-append.py not using absolute_import
80 tests/svn-safe-append.py not using absolute_import
82 tests/svnxml.py not using absolute_import
81 tests/svnxml.py not using absolute_import
83 tests/test-ancestor.py requires print_function
82 tests/test-ancestor.py requires print_function
84 tests/test-atomictempfile.py not using absolute_import
83 tests/test-atomictempfile.py not using absolute_import
85 tests/test-batching.py not using absolute_import
84 tests/test-batching.py not using absolute_import
86 tests/test-batching.py requires print_function
85 tests/test-batching.py requires print_function
87 tests/test-bdiff.py not using absolute_import
86 tests/test-bdiff.py not using absolute_import
88 tests/test-bdiff.py requires print_function
87 tests/test-bdiff.py requires print_function
89 tests/test-context.py not using absolute_import
88 tests/test-context.py not using absolute_import
90 tests/test-context.py requires print_function
89 tests/test-context.py requires print_function
91 tests/test-demandimport.py not using absolute_import
90 tests/test-demandimport.py not using absolute_import
92 tests/test-demandimport.py requires print_function
91 tests/test-demandimport.py requires print_function
93 tests/test-doctest.py not using absolute_import
92 tests/test-doctest.py not using absolute_import
94 tests/test-duplicateoptions.py not using absolute_import
93 tests/test-duplicateoptions.py not using absolute_import
95 tests/test-duplicateoptions.py requires print_function
94 tests/test-duplicateoptions.py requires print_function
96 tests/test-filecache.py not using absolute_import
95 tests/test-filecache.py not using absolute_import
97 tests/test-filecache.py requires print_function
96 tests/test-filecache.py requires print_function
98 tests/test-filelog.py not using absolute_import
97 tests/test-filelog.py not using absolute_import
99 tests/test-filelog.py requires print_function
98 tests/test-filelog.py requires print_function
100 tests/test-hg-parseurl.py not using absolute_import
99 tests/test-hg-parseurl.py not using absolute_import
101 tests/test-hg-parseurl.py requires print_function
100 tests/test-hg-parseurl.py requires print_function
102 tests/test-hgweb-auth.py not using absolute_import
101 tests/test-hgweb-auth.py not using absolute_import
103 tests/test-hgweb-auth.py requires print_function
102 tests/test-hgweb-auth.py requires print_function
104 tests/test-hgwebdir-paths.py not using absolute_import
103 tests/test-hgwebdir-paths.py not using absolute_import
105 tests/test-hybridencode.py not using absolute_import
104 tests/test-hybridencode.py not using absolute_import
106 tests/test-hybridencode.py requires print_function
105 tests/test-hybridencode.py requires print_function
107 tests/test-lrucachedict.py not using absolute_import
106 tests/test-lrucachedict.py not using absolute_import
108 tests/test-lrucachedict.py requires print_function
107 tests/test-lrucachedict.py requires print_function
109 tests/test-manifest.py not using absolute_import
108 tests/test-manifest.py not using absolute_import
110 tests/test-minirst.py not using absolute_import
109 tests/test-minirst.py not using absolute_import
111 tests/test-minirst.py requires print_function
110 tests/test-minirst.py requires print_function
112 tests/test-parseindex2.py not using absolute_import
111 tests/test-parseindex2.py not using absolute_import
113 tests/test-parseindex2.py requires print_function
112 tests/test-parseindex2.py requires print_function
114 tests/test-pathencode.py not using absolute_import
113 tests/test-pathencode.py not using absolute_import
115 tests/test-pathencode.py requires print_function
114 tests/test-pathencode.py requires print_function
116 tests/test-propertycache.py not using absolute_import
115 tests/test-propertycache.py not using absolute_import
117 tests/test-propertycache.py requires print_function
116 tests/test-propertycache.py requires print_function
118 tests/test-revlog-ancestry.py not using absolute_import
117 tests/test-revlog-ancestry.py not using absolute_import
119 tests/test-revlog-ancestry.py requires print_function
118 tests/test-revlog-ancestry.py requires print_function
120 tests/test-run-tests.py not using absolute_import
119 tests/test-run-tests.py not using absolute_import
121 tests/test-simplemerge.py not using absolute_import
120 tests/test-simplemerge.py not using absolute_import
122 tests/test-status-inprocess.py not using absolute_import
121 tests/test-status-inprocess.py not using absolute_import
123 tests/test-status-inprocess.py requires print_function
122 tests/test-status-inprocess.py requires print_function
124 tests/test-symlink-os-yes-fs-no.py not using absolute_import
123 tests/test-symlink-os-yes-fs-no.py not using absolute_import
125 tests/test-trusted.py not using absolute_import
124 tests/test-trusted.py not using absolute_import
126 tests/test-trusted.py requires print_function
125 tests/test-trusted.py requires print_function
127 tests/test-ui-color.py not using absolute_import
126 tests/test-ui-color.py not using absolute_import
128 tests/test-ui-color.py requires print_function
127 tests/test-ui-color.py requires print_function
129 tests/test-ui-config.py not using absolute_import
128 tests/test-ui-config.py not using absolute_import
130 tests/test-ui-config.py requires print_function
129 tests/test-ui-config.py requires print_function
131 tests/test-ui-verbosity.py not using absolute_import
130 tests/test-ui-verbosity.py not using absolute_import
132 tests/test-ui-verbosity.py requires print_function
131 tests/test-ui-verbosity.py requires print_function
133 tests/test-url.py not using absolute_import
132 tests/test-url.py not using absolute_import
134 tests/test-url.py requires print_function
133 tests/test-url.py requires print_function
135 tests/test-walkrepo.py requires print_function
134 tests/test-walkrepo.py requires print_function
136 tests/test-wireproto.py requires print_function
135 tests/test-wireproto.py requires print_function
137 tests/tinyproxy.py requires print_function
136 tests/tinyproxy.py requires print_function
General Comments 0
You need to be logged in to leave comments. Login now