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