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