##// END OF EJS Templates
convert/gnuarch: robustify cat-log retrieval...
Edouard Gomez -
r7580:8c5afb3c default
parent child Browse files
Show More
@@ -1,295 +1,297 b''
1 # GNU Arch support for the convert extension
1 # GNU Arch support for the convert extension
2
2
3 from common import NoRepo, commandline, commit, converter_source
3 from common import NoRepo, commandline, commit, converter_source
4 from mercurial.i18n import _
4 from mercurial.i18n import _
5 from mercurial import util
5 from mercurial import util
6 import os, shutil, tempfile, stat, locale
6 import os, shutil, tempfile, stat, locale
7 from email.Parser import Parser
7 from email.Parser import Parser
8
8
9 class gnuarch_source(converter_source, commandline):
9 class gnuarch_source(converter_source, commandline):
10
10
11 class gnuarch_rev:
11 class gnuarch_rev:
12 def __init__(self, rev):
12 def __init__(self, rev):
13 self.rev = rev
13 self.rev = rev
14 self.summary = ''
14 self.summary = ''
15 self.date = None
15 self.date = None
16 self.author = ''
16 self.author = ''
17 self.add_files = []
17 self.add_files = []
18 self.mod_files = []
18 self.mod_files = []
19 self.del_files = []
19 self.del_files = []
20 self.ren_files = {}
20 self.ren_files = {}
21 self.ren_dirs = {}
21 self.ren_dirs = {}
22
22
23 def __init__(self, ui, path, rev=None):
23 def __init__(self, ui, path, rev=None):
24 super(gnuarch_source, self).__init__(ui, path, rev=rev)
24 super(gnuarch_source, self).__init__(ui, path, rev=rev)
25
25
26 if not os.path.exists(os.path.join(path, '{arch}')):
26 if not os.path.exists(os.path.join(path, '{arch}')):
27 raise NoRepo(_("%s does not look like a GNU Arch repo") % path)
27 raise NoRepo(_("%s does not look like a GNU Arch repo") % path)
28
28
29 # Could use checktool, but we want to check for baz or tla.
29 # Could use checktool, but we want to check for baz or tla.
30 self.execmd = None
30 self.execmd = None
31 if util.find_exe('baz'):
31 if util.find_exe('baz'):
32 self.execmd = 'baz'
32 self.execmd = 'baz'
33 else:
33 else:
34 if util.find_exe('tla'):
34 if util.find_exe('tla'):
35 self.execmd = 'tla'
35 self.execmd = 'tla'
36 else:
36 else:
37 raise util.Abort(_('cannot find a GNU Arch tool'))
37 raise util.Abort(_('cannot find a GNU Arch tool'))
38
38
39 commandline.__init__(self, ui, self.execmd)
39 commandline.__init__(self, ui, self.execmd)
40
40
41 self.path = os.path.realpath(path)
41 self.path = os.path.realpath(path)
42 self.tmppath = None
42 self.tmppath = None
43
43
44 self.treeversion = None
44 self.treeversion = None
45 self.lastrev = None
45 self.lastrev = None
46 self.changes = {}
46 self.changes = {}
47 self.parents = {}
47 self.parents = {}
48 self.tags = {}
48 self.tags = {}
49 self.modecache = {}
49 self.modecache = {}
50 self.catlogparser = Parser()
50 self.catlogparser = Parser()
51 self.locale = locale.getpreferredencoding()
51 self.locale = locale.getpreferredencoding()
52
52
53 def before(self):
53 def before(self):
54 if self.execmd == 'tla':
54 if self.execmd == 'tla':
55 output = self.run0('tree-version', self.path)
55 output = self.run0('tree-version', self.path)
56 else:
56 else:
57 output = self.run0('tree-version', '-d', self.path)
57 output = self.run0('tree-version', '-d', self.path)
58 self.treeversion = output.strip()
58 self.treeversion = output.strip()
59
59
60 self.ui.status(_('analyzing tree version %s...\n') % self.treeversion)
60 self.ui.status(_('analyzing tree version %s...\n') % self.treeversion)
61
61
62 # Get name of temporary directory
62 # Get name of temporary directory
63 version = self.treeversion.split('/')
63 version = self.treeversion.split('/')
64 self.tmppath = os.path.join(tempfile.gettempdir(),
64 self.tmppath = os.path.join(tempfile.gettempdir(),
65 'hg-%s' % version[1])
65 'hg-%s' % version[1])
66
66
67 # Generate parents dictionary
67 # Generate parents dictionary
68 child = []
68 child = []
69 output, status = self.runlines('revisions', self.treeversion)
69 output, status = self.runlines('revisions', self.treeversion)
70 self.checkexit(status, 'archive registered?')
70 self.checkexit(status, 'archive registered?')
71 for l in output:
71 for l in output:
72 rev = l.strip()
72 rev = l.strip()
73 self.changes[rev] = self.gnuarch_rev(rev)
73 self.changes[rev] = self.gnuarch_rev(rev)
74
74
75 # Read author, date and summary
75 # Read author, date and summary
76 catlog = self.run0('cat-log', '-d', self.path, rev)
76 catlog, status = self.run('cat-log', '-d', self.path, rev)
77 if status:
78 catlog = self.run0('cat-archive-log', rev)
77 self._parsecatlog(catlog, rev)
79 self._parsecatlog(catlog, rev)
78
80
79 self.parents[rev] = child
81 self.parents[rev] = child
80 child = [rev]
82 child = [rev]
81 if rev == self.rev:
83 if rev == self.rev:
82 break
84 break
83 self.parents[None] = child
85 self.parents[None] = child
84
86
85 def after(self):
87 def after(self):
86 self.ui.debug(_('cleaning up %s\n') % self.tmppath)
88 self.ui.debug(_('cleaning up %s\n') % self.tmppath)
87 shutil.rmtree(self.tmppath, ignore_errors=True)
89 shutil.rmtree(self.tmppath, ignore_errors=True)
88
90
89 def getheads(self):
91 def getheads(self):
90 return self.parents[None]
92 return self.parents[None]
91
93
92 def getfile(self, name, rev):
94 def getfile(self, name, rev):
93 if rev != self.lastrev:
95 if rev != self.lastrev:
94 raise util.Abort(_('internal calling inconsistency'))
96 raise util.Abort(_('internal calling inconsistency'))
95
97
96 # Raise IOError if necessary (i.e. deleted files).
98 # Raise IOError if necessary (i.e. deleted files).
97 if not os.path.exists(os.path.join(self.tmppath, name)):
99 if not os.path.exists(os.path.join(self.tmppath, name)):
98 raise IOError
100 raise IOError
99
101
100 data, mode = self._getfile(name, rev)
102 data, mode = self._getfile(name, rev)
101 self.modecache[(name, rev)] = mode
103 self.modecache[(name, rev)] = mode
102
104
103 return data
105 return data
104
106
105 def getmode(self, name, rev):
107 def getmode(self, name, rev):
106 return self.modecache[(name, rev)]
108 return self.modecache[(name, rev)]
107
109
108 def getchanges(self, rev):
110 def getchanges(self, rev):
109 self.modecache = {}
111 self.modecache = {}
110 self._update(rev)
112 self._update(rev)
111 changes = []
113 changes = []
112 copies = {}
114 copies = {}
113
115
114 for f in self.changes[rev].add_files:
116 for f in self.changes[rev].add_files:
115 changes.append((f, rev))
117 changes.append((f, rev))
116
118
117 for f in self.changes[rev].mod_files:
119 for f in self.changes[rev].mod_files:
118 changes.append((f, rev))
120 changes.append((f, rev))
119
121
120 for f in self.changes[rev].del_files:
122 for f in self.changes[rev].del_files:
121 changes.append((f, rev))
123 changes.append((f, rev))
122
124
123 for src in self.changes[rev].ren_files:
125 for src in self.changes[rev].ren_files:
124 to = self.changes[rev].ren_files[src]
126 to = self.changes[rev].ren_files[src]
125 changes.append((src, rev))
127 changes.append((src, rev))
126 changes.append((to, rev))
128 changes.append((to, rev))
127 copies[src] = to
129 copies[src] = to
128
130
129 for src in self.changes[rev].ren_dirs:
131 for src in self.changes[rev].ren_dirs:
130 to = self.changes[rev].ren_dirs[src]
132 to = self.changes[rev].ren_dirs[src]
131 chgs, cps = self._rendirchanges(src, to);
133 chgs, cps = self._rendirchanges(src, to);
132 changes += [(f, rev) for f in chgs]
134 changes += [(f, rev) for f in chgs]
133 for c in cps:
135 for c in cps:
134 copies[c] = cps[c]
136 copies[c] = cps[c]
135
137
136 self.lastrev = rev
138 self.lastrev = rev
137 return util.sort(changes), copies
139 return util.sort(changes), copies
138
140
139 def getcommit(self, rev):
141 def getcommit(self, rev):
140 changes = self.changes[rev]
142 changes = self.changes[rev]
141 return commit(author = changes.author, date = changes.date,
143 return commit(author = changes.author, date = changes.date,
142 desc = changes.summary, parents = self.parents[rev])
144 desc = changes.summary, parents = self.parents[rev])
143
145
144 def gettags(self):
146 def gettags(self):
145 return self.tags
147 return self.tags
146
148
147 def _execute(self, cmd, *args, **kwargs):
149 def _execute(self, cmd, *args, **kwargs):
148 cmdline = [self.execmd, cmd]
150 cmdline = [self.execmd, cmd]
149 cmdline += args
151 cmdline += args
150 cmdline = [util.shellquote(arg) for arg in cmdline]
152 cmdline = [util.shellquote(arg) for arg in cmdline]
151 cmdline += ['>', util.nulldev, '2>', util.nulldev]
153 cmdline += ['>', util.nulldev, '2>', util.nulldev]
152 cmdline = util.quotecommand(' '.join(cmdline))
154 cmdline = util.quotecommand(' '.join(cmdline))
153 self.ui.debug(cmdline, '\n')
155 self.ui.debug(cmdline, '\n')
154 return os.system(cmdline)
156 return os.system(cmdline)
155
157
156 def _update(self, rev):
158 def _update(self, rev):
157 if rev == 'base-0':
159 if rev == 'base-0':
158 # Initialise 'base-0' revision
160 # Initialise 'base-0' revision
159 self._obtainrevision(rev)
161 self._obtainrevision(rev)
160 else:
162 else:
161 self.ui.debug(_('applying revision %s...\n') % rev)
163 self.ui.debug(_('applying revision %s...\n') % rev)
162 revision = '%s--%s' % (self.treeversion, rev)
164 revision = '%s--%s' % (self.treeversion, rev)
163 changeset, status = self.runlines('replay', '-d', self.tmppath,
165 changeset, status = self.runlines('replay', '-d', self.tmppath,
164 revision)
166 revision)
165 if status:
167 if status:
166 # Something went wrong while merging (baz or tla
168 # Something went wrong while merging (baz or tla
167 # issue?), get latest revision and try from there
169 # issue?), get latest revision and try from there
168 shutil.rmtree(self.tmppath, ignore_errors=True)
170 shutil.rmtree(self.tmppath, ignore_errors=True)
169 self._obtainrevision(rev)
171 self._obtainrevision(rev)
170 else:
172 else:
171 old_rev = self.parents[rev][0]
173 old_rev = self.parents[rev][0]
172 self.ui.debug(_('computing changeset between %s and %s...\n')
174 self.ui.debug(_('computing changeset between %s and %s...\n')
173 % (old_rev, rev))
175 % (old_rev, rev))
174 self._parsechangeset(changeset, rev)
176 self._parsechangeset(changeset, rev)
175
177
176 def _getfile(self, name, rev):
178 def _getfile(self, name, rev):
177 mode = os.lstat(os.path.join(self.tmppath, name)).st_mode
179 mode = os.lstat(os.path.join(self.tmppath, name)).st_mode
178 if stat.S_ISLNK(mode):
180 if stat.S_ISLNK(mode):
179 data = os.readlink(os.path.join(self.tmppath, name))
181 data = os.readlink(os.path.join(self.tmppath, name))
180 mode = mode and 'l' or ''
182 mode = mode and 'l' or ''
181 else:
183 else:
182 data = open(os.path.join(self.tmppath, name), 'rb').read()
184 data = open(os.path.join(self.tmppath, name), 'rb').read()
183 mode = (mode & 0111) and 'x' or ''
185 mode = (mode & 0111) and 'x' or ''
184 return data, mode
186 return data, mode
185
187
186 def _exclude(self, name):
188 def _exclude(self, name):
187 exclude = [ '{arch}', '.arch-ids', '.arch-inventory' ]
189 exclude = [ '{arch}', '.arch-ids', '.arch-inventory' ]
188 for exc in exclude:
190 for exc in exclude:
189 if name.find(exc) != -1:
191 if name.find(exc) != -1:
190 return True
192 return True
191 return False
193 return False
192
194
193 def _readcontents(self, path):
195 def _readcontents(self, path):
194 files = []
196 files = []
195 contents = os.listdir(path)
197 contents = os.listdir(path)
196 while len(contents) > 0:
198 while len(contents) > 0:
197 c = contents.pop()
199 c = contents.pop()
198 p = os.path.join(path, c)
200 p = os.path.join(path, c)
199 # os.walk could be used, but here we avoid internal GNU
201 # os.walk could be used, but here we avoid internal GNU
200 # Arch files and directories, thus saving a lot time.
202 # Arch files and directories, thus saving a lot time.
201 if not self._exclude(p):
203 if not self._exclude(p):
202 if os.path.isdir(p):
204 if os.path.isdir(p):
203 contents += [os.path.join(c, f) for f in os.listdir(p)]
205 contents += [os.path.join(c, f) for f in os.listdir(p)]
204 else:
206 else:
205 files.append(c)
207 files.append(c)
206 return files
208 return files
207
209
208 def _rendirchanges(self, src, dest):
210 def _rendirchanges(self, src, dest):
209 changes = []
211 changes = []
210 copies = {}
212 copies = {}
211 files = self._readcontents(os.path.join(self.tmppath, dest))
213 files = self._readcontents(os.path.join(self.tmppath, dest))
212 for f in files:
214 for f in files:
213 s = os.path.join(src, f)
215 s = os.path.join(src, f)
214 d = os.path.join(dest, f)
216 d = os.path.join(dest, f)
215 changes.append(s)
217 changes.append(s)
216 changes.append(d)
218 changes.append(d)
217 copies[s] = d
219 copies[s] = d
218 return changes, copies
220 return changes, copies
219
221
220 def _obtainrevision(self, rev):
222 def _obtainrevision(self, rev):
221 self.ui.debug(_('obtaining revision %s...\n') % rev)
223 self.ui.debug(_('obtaining revision %s...\n') % rev)
222 revision = '%s--%s' % (self.treeversion, rev)
224 revision = '%s--%s' % (self.treeversion, rev)
223 output = self._execute('get', revision, self.tmppath)
225 output = self._execute('get', revision, self.tmppath)
224 self.checkexit(output)
226 self.checkexit(output)
225 self.ui.debug(_('analysing revision %s...\n') % rev)
227 self.ui.debug(_('analysing revision %s...\n') % rev)
226 files = self._readcontents(self.tmppath)
228 files = self._readcontents(self.tmppath)
227 self.changes[rev].add_files += files
229 self.changes[rev].add_files += files
228
230
229 def _stripbasepath(self, path):
231 def _stripbasepath(self, path):
230 if path.startswith('./'):
232 if path.startswith('./'):
231 return path[2:]
233 return path[2:]
232 return path
234 return path
233
235
234 def _parsecatlog(self, data, rev):
236 def _parsecatlog(self, data, rev):
235 try:
237 try:
236 catlog = self.catlogparser.parsestr(data)
238 catlog = self.catlogparser.parsestr(data)
237 self.changes[rev].date = util.datestr(
239 self.changes[rev].date = util.datestr(
238 util.strdate(catlog['Standard-date'],
240 util.strdate(catlog['Standard-date'],
239 '%Y-%m-%d %H:%M:%S'))
241 '%Y-%m-%d %H:%M:%S'))
240 self.changes[rev].author = catlog['Creator']
242 self.changes[rev].author = catlog['Creator']
241 self.changes[rev].summary = catlog['Summary']
243 self.changes[rev].summary = catlog['Summary']
242 except Exception, err:
244 except Exception, err:
243 raise util.Abort(_('could not parse cat-log of %s') % rev)
245 raise util.Abort(_('could not parse cat-log of %s') % rev)
244
246
245 def _parsechangeset(self, data, rev):
247 def _parsechangeset(self, data, rev):
246 for l in data:
248 for l in data:
247 l = l.strip()
249 l = l.strip()
248 # Added file (ignore added directory)
250 # Added file (ignore added directory)
249 if l.startswith('A') and not l.startswith('A/'):
251 if l.startswith('A') and not l.startswith('A/'):
250 file = self._stripbasepath(l[1:].strip())
252 file = self._stripbasepath(l[1:].strip())
251 if not self._exclude(file):
253 if not self._exclude(file):
252 self.changes[rev].add_files.append(file)
254 self.changes[rev].add_files.append(file)
253 # Deleted file (ignore deleted directory)
255 # Deleted file (ignore deleted directory)
254 elif l.startswith('D') and not l.startswith('D/'):
256 elif l.startswith('D') and not l.startswith('D/'):
255 file = self._stripbasepath(l[1:].strip())
257 file = self._stripbasepath(l[1:].strip())
256 if not self._exclude(file):
258 if not self._exclude(file):
257 self.changes[rev].del_files.append(file)
259 self.changes[rev].del_files.append(file)
258 # Modified binary file
260 # Modified binary file
259 elif l.startswith('Mb'):
261 elif l.startswith('Mb'):
260 file = self._stripbasepath(l[2:].strip())
262 file = self._stripbasepath(l[2:].strip())
261 if not self._exclude(file):
263 if not self._exclude(file):
262 self.changes[rev].mod_files.append(file)
264 self.changes[rev].mod_files.append(file)
263 # Modified link
265 # Modified link
264 elif l.startswith('M->'):
266 elif l.startswith('M->'):
265 file = self._stripbasepath(l[3:].strip())
267 file = self._stripbasepath(l[3:].strip())
266 if not self._exclude(file):
268 if not self._exclude(file):
267 self.changes[rev].mod_files.append(file)
269 self.changes[rev].mod_files.append(file)
268 # Modified file
270 # Modified file
269 elif l.startswith('M'):
271 elif l.startswith('M'):
270 file = self._stripbasepath(l[1:].strip())
272 file = self._stripbasepath(l[1:].strip())
271 if not self._exclude(file):
273 if not self._exclude(file):
272 self.changes[rev].mod_files.append(file)
274 self.changes[rev].mod_files.append(file)
273 # Renamed file (or link)
275 # Renamed file (or link)
274 elif l.startswith('=>'):
276 elif l.startswith('=>'):
275 files = l[2:].strip().split(' ')
277 files = l[2:].strip().split(' ')
276 if len(files) == 1:
278 if len(files) == 1:
277 files = l[2:].strip().split('\t')
279 files = l[2:].strip().split('\t')
278 src = self._stripbasepath(files[0])
280 src = self._stripbasepath(files[0])
279 dst = self._stripbasepath(files[1])
281 dst = self._stripbasepath(files[1])
280 if not self._exclude(src) and not self._exclude(dst):
282 if not self._exclude(src) and not self._exclude(dst):
281 self.changes[rev].ren_files[src] = dst
283 self.changes[rev].ren_files[src] = dst
282 # Conversion from file to link or from link to file (modified)
284 # Conversion from file to link or from link to file (modified)
283 elif l.startswith('ch'):
285 elif l.startswith('ch'):
284 file = self._stripbasepath(l[2:].strip())
286 file = self._stripbasepath(l[2:].strip())
285 if not self._exclude(file):
287 if not self._exclude(file):
286 self.changes[rev].mod_files.append(file)
288 self.changes[rev].mod_files.append(file)
287 # Renamed directory
289 # Renamed directory
288 elif l.startswith('/>'):
290 elif l.startswith('/>'):
289 dirs = l[2:].strip().split(' ')
291 dirs = l[2:].strip().split(' ')
290 if len(dirs) == 1:
292 if len(dirs) == 1:
291 dirs = l[2:].strip().split('\t')
293 dirs = l[2:].strip().split('\t')
292 src = self._stripbasepath(dirs[0])
294 src = self._stripbasepath(dirs[0])
293 dst = self._stripbasepath(dirs[1])
295 dst = self._stripbasepath(dirs[1])
294 if not self._exclude(src) and not self._exclude(dst):
296 if not self._exclude(src) and not self._exclude(dst):
295 self.changes[rev].ren_dirs[src] = dst
297 self.changes[rev].ren_dirs[src] = dst
General Comments 0
You need to be logged in to leave comments. Login now