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