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