##// END OF EJS Templates
convert: fix util.popen regression in darcs converter
Patrick Mezard -
r5529:5499dbb4 default
parent child Browse files
Show More
@@ -1,292 +1,292 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 cPickle as pickle
3 import cPickle as pickle
4 from mercurial import util
4 from mercurial import util
5 from mercurial.i18n import _
5 from mercurial.i18n import _
6
6
7 def encodeargs(args):
7 def encodeargs(args):
8 def encodearg(s):
8 def encodearg(s):
9 lines = base64.encodestring(s)
9 lines = base64.encodestring(s)
10 lines = [l.splitlines()[0] for l in lines]
10 lines = [l.splitlines()[0] for l in lines]
11 return ''.join(lines)
11 return ''.join(lines)
12
12
13 s = pickle.dumps(args)
13 s = pickle.dumps(args)
14 return encodearg(s)
14 return encodearg(s)
15
15
16 def decodeargs(s):
16 def decodeargs(s):
17 s = base64.decodestring(s)
17 s = base64.decodestring(s)
18 return pickle.loads(s)
18 return pickle.loads(s)
19
19
20 def checktool(exe, name=None):
20 def checktool(exe, name=None):
21 name = name or exe
21 name = name or exe
22 if not util.find_exe(exe):
22 if not util.find_exe(exe):
23 raise util.Abort('cannot find required "%s" tool' % name)
23 raise util.Abort('cannot find required "%s" tool' % name)
24
24
25 class NoRepo(Exception): pass
25 class NoRepo(Exception): pass
26
26
27 SKIPREV = 'SKIP'
27 SKIPREV = 'SKIP'
28
28
29 class commit(object):
29 class commit(object):
30 def __init__(self, author, date, desc, parents, branch=None, rev=None,
30 def __init__(self, author, date, desc, parents, branch=None, rev=None,
31 extra={}):
31 extra={}):
32 self.author = author
32 self.author = author
33 self.date = date
33 self.date = date
34 self.desc = desc
34 self.desc = desc
35 self.parents = parents
35 self.parents = parents
36 self.branch = branch
36 self.branch = branch
37 self.rev = rev
37 self.rev = rev
38 self.extra = extra
38 self.extra = extra
39
39
40 class converter_source(object):
40 class converter_source(object):
41 """Conversion source interface"""
41 """Conversion source interface"""
42
42
43 def __init__(self, ui, path, rev=None):
43 def __init__(self, ui, path, rev=None):
44 """Initialize conversion source (or raise NoRepo("message")
44 """Initialize conversion source (or raise NoRepo("message")
45 exception if path is not a valid repository)"""
45 exception if path is not a valid repository)"""
46 self.ui = ui
46 self.ui = ui
47 self.path = path
47 self.path = path
48 self.rev = rev
48 self.rev = rev
49
49
50 self.encoding = 'utf-8'
50 self.encoding = 'utf-8'
51
51
52 def before(self):
52 def before(self):
53 pass
53 pass
54
54
55 def after(self):
55 def after(self):
56 pass
56 pass
57
57
58 def setrevmap(self, revmap):
58 def setrevmap(self, revmap):
59 """set the map of already-converted revisions"""
59 """set the map of already-converted revisions"""
60 pass
60 pass
61
61
62 def getheads(self):
62 def getheads(self):
63 """Return a list of this repository's heads"""
63 """Return a list of this repository's heads"""
64 raise NotImplementedError()
64 raise NotImplementedError()
65
65
66 def getfile(self, name, rev):
66 def getfile(self, name, rev):
67 """Return file contents as a string"""
67 """Return file contents as a string"""
68 raise NotImplementedError()
68 raise NotImplementedError()
69
69
70 def getmode(self, name, rev):
70 def getmode(self, name, rev):
71 """Return file mode, eg. '', 'x', or 'l'"""
71 """Return file mode, eg. '', 'x', or 'l'"""
72 raise NotImplementedError()
72 raise NotImplementedError()
73
73
74 def getchanges(self, version):
74 def getchanges(self, version):
75 """Returns a tuple of (files, copies)
75 """Returns a tuple of (files, copies)
76 Files is a sorted list of (filename, id) tuples for all files changed
76 Files is a sorted list of (filename, id) tuples for all files changed
77 in version, where id is the source revision id of the file.
77 in version, where id is the source revision id of the file.
78
78
79 copies is a dictionary of dest: source
79 copies is a dictionary of dest: source
80 """
80 """
81 raise NotImplementedError()
81 raise NotImplementedError()
82
82
83 def getcommit(self, version):
83 def getcommit(self, version):
84 """Return the commit object for version"""
84 """Return the commit object for version"""
85 raise NotImplementedError()
85 raise NotImplementedError()
86
86
87 def gettags(self):
87 def gettags(self):
88 """Return the tags as a dictionary of name: revision"""
88 """Return the tags as a dictionary of name: revision"""
89 raise NotImplementedError()
89 raise NotImplementedError()
90
90
91 def recode(self, s, encoding=None):
91 def recode(self, s, encoding=None):
92 if not encoding:
92 if not encoding:
93 encoding = self.encoding or 'utf-8'
93 encoding = self.encoding or 'utf-8'
94
94
95 if isinstance(s, unicode):
95 if isinstance(s, unicode):
96 return s.encode("utf-8")
96 return s.encode("utf-8")
97 try:
97 try:
98 return s.decode(encoding).encode("utf-8")
98 return s.decode(encoding).encode("utf-8")
99 except:
99 except:
100 try:
100 try:
101 return s.decode("latin-1").encode("utf-8")
101 return s.decode("latin-1").encode("utf-8")
102 except:
102 except:
103 return s.decode(encoding, "replace").encode("utf-8")
103 return s.decode(encoding, "replace").encode("utf-8")
104
104
105 def getchangedfiles(self, rev, i):
105 def getchangedfiles(self, rev, i):
106 """Return the files changed by rev compared to parent[i].
106 """Return the files changed by rev compared to parent[i].
107
107
108 i is an index selecting one of the parents of rev. The return
108 i is an index selecting one of the parents of rev. The return
109 value should be the list of files that are different in rev and
109 value should be the list of files that are different in rev and
110 this parent.
110 this parent.
111
111
112 If rev has no parents, i is None.
112 If rev has no parents, i is None.
113
113
114 This function is only needed to support --filemap
114 This function is only needed to support --filemap
115 """
115 """
116 raise NotImplementedError()
116 raise NotImplementedError()
117
117
118 class converter_sink(object):
118 class converter_sink(object):
119 """Conversion sink (target) interface"""
119 """Conversion sink (target) interface"""
120
120
121 def __init__(self, ui, path):
121 def __init__(self, ui, path):
122 """Initialize conversion sink (or raise NoRepo("message")
122 """Initialize conversion sink (or raise NoRepo("message")
123 exception if path is not a valid repository)
123 exception if path is not a valid repository)
124
124
125 created is a list of paths to remove if a fatal error occurs
125 created is a list of paths to remove if a fatal error occurs
126 later"""
126 later"""
127 self.ui = ui
127 self.ui = ui
128 self.path = path
128 self.path = path
129 self.created = []
129 self.created = []
130
130
131 def getheads(self):
131 def getheads(self):
132 """Return a list of this repository's heads"""
132 """Return a list of this repository's heads"""
133 raise NotImplementedError()
133 raise NotImplementedError()
134
134
135 def revmapfile(self):
135 def revmapfile(self):
136 """Path to a file that will contain lines
136 """Path to a file that will contain lines
137 source_rev_id sink_rev_id
137 source_rev_id sink_rev_id
138 mapping equivalent revision identifiers for each system."""
138 mapping equivalent revision identifiers for each system."""
139 raise NotImplementedError()
139 raise NotImplementedError()
140
140
141 def authorfile(self):
141 def authorfile(self):
142 """Path to a file that will contain lines
142 """Path to a file that will contain lines
143 srcauthor=dstauthor
143 srcauthor=dstauthor
144 mapping equivalent authors identifiers for each system."""
144 mapping equivalent authors identifiers for each system."""
145 return None
145 return None
146
146
147 def putfile(self, f, e, data):
147 def putfile(self, f, e, data):
148 """Put file for next putcommit().
148 """Put file for next putcommit().
149 f: path to file
149 f: path to file
150 e: '', 'x', or 'l' (regular file, executable, or symlink)
150 e: '', 'x', or 'l' (regular file, executable, or symlink)
151 data: file contents"""
151 data: file contents"""
152 raise NotImplementedError()
152 raise NotImplementedError()
153
153
154 def delfile(self, f):
154 def delfile(self, f):
155 """Delete file for next putcommit().
155 """Delete file for next putcommit().
156 f: path to file"""
156 f: path to file"""
157 raise NotImplementedError()
157 raise NotImplementedError()
158
158
159 def putcommit(self, files, parents, commit):
159 def putcommit(self, files, parents, commit):
160 """Create a revision with all changed files listed in 'files'
160 """Create a revision with all changed files listed in 'files'
161 and having listed parents. 'commit' is a commit object containing
161 and having listed parents. 'commit' is a commit object containing
162 at a minimum the author, date, and message for this changeset.
162 at a minimum the author, date, and message for this changeset.
163 Called after putfile() and delfile() calls. Note that the sink
163 Called after putfile() and delfile() calls. Note that the sink
164 repository is not told to update itself to a particular revision
164 repository is not told to update itself to a particular revision
165 (or even what that revision would be) before it receives the
165 (or even what that revision would be) before it receives the
166 file data."""
166 file data."""
167 raise NotImplementedError()
167 raise NotImplementedError()
168
168
169 def puttags(self, tags):
169 def puttags(self, tags):
170 """Put tags into sink.
170 """Put tags into sink.
171 tags: {tagname: sink_rev_id, ...}"""
171 tags: {tagname: sink_rev_id, ...}"""
172 raise NotImplementedError()
172 raise NotImplementedError()
173
173
174 def setbranch(self, branch, pbranch, parents):
174 def setbranch(self, branch, pbranch, parents):
175 """Set the current branch name. Called before the first putfile
175 """Set the current branch name. Called before the first putfile
176 on the branch.
176 on the branch.
177 branch: branch name for subsequent commits
177 branch: branch name for subsequent commits
178 pbranch: branch name of parent commit
178 pbranch: branch name of parent commit
179 parents: destination revisions of parent"""
179 parents: destination revisions of parent"""
180 pass
180 pass
181
181
182 def setfilemapmode(self, active):
182 def setfilemapmode(self, active):
183 """Tell the destination that we're using a filemap
183 """Tell the destination that we're using a filemap
184
184
185 Some converter_sources (svn in particular) can claim that a file
185 Some converter_sources (svn in particular) can claim that a file
186 was changed in a revision, even if there was no change. This method
186 was changed in a revision, even if there was no change. This method
187 tells the destination that we're using a filemap and that it should
187 tells the destination that we're using a filemap and that it should
188 filter empty revisions.
188 filter empty revisions.
189 """
189 """
190 pass
190 pass
191
191
192 def before(self):
192 def before(self):
193 pass
193 pass
194
194
195 def after(self):
195 def after(self):
196 pass
196 pass
197
197
198
198
199 class commandline(object):
199 class commandline(object):
200 def __init__(self, ui, command):
200 def __init__(self, ui, command):
201 self.ui = ui
201 self.ui = ui
202 self.command = command
202 self.command = command
203
203
204 def prerun(self):
204 def prerun(self):
205 pass
205 pass
206
206
207 def postrun(self):
207 def postrun(self):
208 pass
208 pass
209
209
210 def _run(self, cmd, *args, **kwargs):
210 def _run(self, cmd, *args, **kwargs):
211 cmdline = [self.command, cmd] + list(args)
211 cmdline = [self.command, cmd] + list(args)
212 for k, v in kwargs.iteritems():
212 for k, v in kwargs.iteritems():
213 if len(k) == 1:
213 if len(k) == 1:
214 cmdline.append('-' + k)
214 cmdline.append('-' + k)
215 else:
215 else:
216 cmdline.append('--' + k.replace('_', '-'))
216 cmdline.append('--' + k.replace('_', '-'))
217 try:
217 try:
218 if len(k) == 1:
218 if len(k) == 1:
219 cmdline.append('' + v)
219 cmdline.append('' + v)
220 else:
220 else:
221 cmdline[-1] += '=' + v
221 cmdline[-1] += '=' + v
222 except TypeError:
222 except TypeError:
223 pass
223 pass
224 cmdline = [util.shellquote(arg) for arg in cmdline]
224 cmdline = [util.shellquote(arg) for arg in cmdline]
225 cmdline += ['<', util.nulldev]
225 cmdline += ['<', util.nulldev]
226 cmdline = util.quotecommand(' '.join(cmdline))
226 cmdline = ' '.join(cmdline)
227 self.ui.debug(cmdline, '\n')
227 self.ui.debug(cmdline, '\n')
228
228
229 self.prerun()
229 self.prerun()
230 try:
230 try:
231 return util.popen(cmdline)
231 return util.popen(cmdline)
232 finally:
232 finally:
233 self.postrun()
233 self.postrun()
234
234
235 def run(self, cmd, *args, **kwargs):
235 def run(self, cmd, *args, **kwargs):
236 fp = self._run(cmd, *args, **kwargs)
236 fp = self._run(cmd, *args, **kwargs)
237 output = fp.read()
237 output = fp.read()
238 self.ui.debug(output)
238 self.ui.debug(output)
239 return output, fp.close()
239 return output, fp.close()
240
240
241 def checkexit(self, status, output=''):
241 def checkexit(self, status, output=''):
242 if status:
242 if status:
243 if output:
243 if output:
244 self.ui.warn(_('%s error:\n') % self.command)
244 self.ui.warn(_('%s error:\n') % self.command)
245 self.ui.warn(output)
245 self.ui.warn(output)
246 msg = util.explain_exit(status)[0]
246 msg = util.explain_exit(status)[0]
247 raise util.Abort(_('%s %s') % (self.command, msg))
247 raise util.Abort(_('%s %s') % (self.command, msg))
248
248
249 def run0(self, cmd, *args, **kwargs):
249 def run0(self, cmd, *args, **kwargs):
250 output, status = self.run(cmd, *args, **kwargs)
250 output, status = self.run(cmd, *args, **kwargs)
251 self.checkexit(status, output)
251 self.checkexit(status, output)
252 return output
252 return output
253
253
254
254
255 class mapfile(dict):
255 class mapfile(dict):
256 def __init__(self, ui, path):
256 def __init__(self, ui, path):
257 super(mapfile, self).__init__()
257 super(mapfile, self).__init__()
258 self.ui = ui
258 self.ui = ui
259 self.path = path
259 self.path = path
260 self.fp = None
260 self.fp = None
261 self.order = []
261 self.order = []
262 self._read()
262 self._read()
263
263
264 def _read(self):
264 def _read(self):
265 try:
265 try:
266 fp = open(self.path, 'r')
266 fp = open(self.path, 'r')
267 except IOError, err:
267 except IOError, err:
268 if err.errno != errno.ENOENT:
268 if err.errno != errno.ENOENT:
269 raise
269 raise
270 return
270 return
271 for line in fp:
271 for line in fp:
272 key, value = line[:-1].split(' ', 1)
272 key, value = line[:-1].split(' ', 1)
273 if key not in self:
273 if key not in self:
274 self.order.append(key)
274 self.order.append(key)
275 super(mapfile, self).__setitem__(key, value)
275 super(mapfile, self).__setitem__(key, value)
276 fp.close()
276 fp.close()
277
277
278 def __setitem__(self, key, value):
278 def __setitem__(self, key, value):
279 if self.fp is None:
279 if self.fp is None:
280 try:
280 try:
281 self.fp = open(self.path, 'a')
281 self.fp = open(self.path, 'a')
282 except IOError, err:
282 except IOError, err:
283 raise util.Abort(_('could not open map file %r: %s') %
283 raise util.Abort(_('could not open map file %r: %s') %
284 (self.path, err.strerror))
284 (self.path, err.strerror))
285 self.fp.write('%s %s\n' % (key, value))
285 self.fp.write('%s %s\n' % (key, value))
286 self.fp.flush()
286 self.fp.flush()
287 super(mapfile, self).__setitem__(key, value)
287 super(mapfile, self).__setitem__(key, value)
288
288
289 def close(self):
289 def close(self):
290 if self.fp:
290 if self.fp:
291 self.fp.close()
291 self.fp.close()
292 self.fp = None
292 self.fp = None
General Comments 0
You need to be logged in to leave comments. Login now