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