##// END OF EJS Templates
largefiles: replace invocation of os.path module by vfs in reposetup.py...
liscju -
r28716:5c14af47 default
parent child Browse files
Show More
@@ -1,372 +1,371 b''
1 1 # Copyright 2009-2010 Gregory P. Ward
2 2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 3 # Copyright 2010-2011 Fog Creek Software
4 4 # Copyright 2010-2011 Unity Technologies
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 '''setup for largefiles repositories: reposetup'''
10 10 import copy
11 import os
12 11
13 12 from mercurial import error, match as match_, error
14 13 from mercurial.i18n import _
15 14 from mercurial import scmutil, localrepo
16 15
17 16 import lfcommands
18 17 import lfutil
19 18
20 19 def reposetup(ui, repo):
21 20 # wire repositories should be given new wireproto functions
22 21 # by "proto.wirereposetup()" via "hg.wirepeersetupfuncs"
23 22 if not repo.local():
24 23 return
25 24
26 25 class lfilesrepo(repo.__class__):
27 26 # the mark to examine whether "repo" object enables largefiles or not
28 27 _largefilesenabled = True
29 28
30 29 lfstatus = False
31 30 def status_nolfiles(self, *args, **kwargs):
32 31 return super(lfilesrepo, self).status(*args, **kwargs)
33 32
34 33 # When lfstatus is set, return a context that gives the names
35 34 # of largefiles instead of their corresponding standins and
36 35 # identifies the largefiles as always binary, regardless of
37 36 # their actual contents.
38 37 def __getitem__(self, changeid):
39 38 ctx = super(lfilesrepo, self).__getitem__(changeid)
40 39 if self.lfstatus:
41 40 class lfilesctx(ctx.__class__):
42 41 def files(self):
43 42 filenames = super(lfilesctx, self).files()
44 43 return [lfutil.splitstandin(f) or f for f in filenames]
45 44 def manifest(self):
46 45 man1 = super(lfilesctx, self).manifest()
47 46 class lfilesmanifest(man1.__class__):
48 47 def __contains__(self, filename):
49 48 orig = super(lfilesmanifest, self).__contains__
50 49 return (orig(filename) or
51 50 orig(lfutil.standin(filename)))
52 51 man1.__class__ = lfilesmanifest
53 52 return man1
54 53 def filectx(self, path, fileid=None, filelog=None):
55 54 orig = super(lfilesctx, self).filectx
56 55 try:
57 56 if filelog is not None:
58 57 result = orig(path, fileid, filelog)
59 58 else:
60 59 result = orig(path, fileid)
61 60 except error.LookupError:
62 61 # Adding a null character will cause Mercurial to
63 62 # identify this as a binary file.
64 63 if filelog is not None:
65 64 result = orig(lfutil.standin(path), fileid,
66 65 filelog)
67 66 else:
68 67 result = orig(lfutil.standin(path), fileid)
69 68 olddata = result.data
70 69 result.data = lambda: olddata() + '\0'
71 70 return result
72 71 ctx.__class__ = lfilesctx
73 72 return ctx
74 73
75 74 # Figure out the status of big files and insert them into the
76 75 # appropriate list in the result. Also removes standin files
77 76 # from the listing. Revert to the original status if
78 77 # self.lfstatus is False.
79 78 # XXX large file status is buggy when used on repo proxy.
80 79 # XXX this needs to be investigated.
81 80 @localrepo.unfilteredmethod
82 81 def status(self, node1='.', node2=None, match=None, ignored=False,
83 82 clean=False, unknown=False, listsubrepos=False):
84 83 listignored, listclean, listunknown = ignored, clean, unknown
85 84 orig = super(lfilesrepo, self).status
86 85 if not self.lfstatus:
87 86 return orig(node1, node2, match, listignored, listclean,
88 87 listunknown, listsubrepos)
89 88
90 89 # some calls in this function rely on the old version of status
91 90 self.lfstatus = False
92 91 ctx1 = self[node1]
93 92 ctx2 = self[node2]
94 93 working = ctx2.rev() is None
95 94 parentworking = working and ctx1 == self['.']
96 95
97 96 if match is None:
98 97 match = match_.always(self.root, self.getcwd())
99 98
100 99 wlock = None
101 100 try:
102 101 try:
103 102 # updating the dirstate is optional
104 103 # so we don't wait on the lock
105 104 wlock = self.wlock(False)
106 105 except error.LockError:
107 106 pass
108 107
109 108 # First check if paths or patterns were specified on the
110 109 # command line. If there were, and they don't match any
111 110 # largefiles, we should just bail here and let super
112 111 # handle it -- thus gaining a big performance boost.
113 112 lfdirstate = lfutil.openlfdirstate(ui, self)
114 113 if not match.always():
115 114 for f in lfdirstate:
116 115 if match(f):
117 116 break
118 117 else:
119 118 return orig(node1, node2, match, listignored, listclean,
120 119 listunknown, listsubrepos)
121 120
122 121 # Create a copy of match that matches standins instead
123 122 # of largefiles.
124 123 def tostandins(files):
125 124 if not working:
126 125 return files
127 126 newfiles = []
128 127 dirstate = self.dirstate
129 128 for f in files:
130 129 sf = lfutil.standin(f)
131 130 if sf in dirstate:
132 131 newfiles.append(sf)
133 132 elif sf in dirstate.dirs():
134 133 # Directory entries could be regular or
135 134 # standin, check both
136 135 newfiles.extend((f, sf))
137 136 else:
138 137 newfiles.append(f)
139 138 return newfiles
140 139
141 140 m = copy.copy(match)
142 141 m._files = tostandins(m._files)
143 142
144 143 result = orig(node1, node2, m, ignored, clean, unknown,
145 144 listsubrepos)
146 145 if working:
147 146
148 147 def sfindirstate(f):
149 148 sf = lfutil.standin(f)
150 149 dirstate = self.dirstate
151 150 return sf in dirstate or sf in dirstate.dirs()
152 151
153 152 match._files = [f for f in match._files
154 153 if sfindirstate(f)]
155 154 # Don't waste time getting the ignored and unknown
156 155 # files from lfdirstate
157 156 unsure, s = lfdirstate.status(match, [], False, listclean,
158 157 False)
159 158 (modified, added, removed, clean) = (s.modified, s.added,
160 159 s.removed, s.clean)
161 160 if parentworking:
162 161 for lfile in unsure:
163 162 standin = lfutil.standin(lfile)
164 163 if standin not in ctx1:
165 164 # from second parent
166 165 modified.append(lfile)
167 166 elif ctx1[standin].data().strip() \
168 167 != lfutil.hashfile(self.wjoin(lfile)):
169 168 modified.append(lfile)
170 169 else:
171 170 if listclean:
172 171 clean.append(lfile)
173 172 lfdirstate.normal(lfile)
174 173 else:
175 174 tocheck = unsure + modified + added + clean
176 175 modified, added, clean = [], [], []
177 176 checkexec = self.dirstate._checkexec
178 177
179 178 for lfile in tocheck:
180 179 standin = lfutil.standin(lfile)
181 180 if standin in ctx1:
182 181 abslfile = self.wjoin(lfile)
183 182 if ((ctx1[standin].data().strip() !=
184 183 lfutil.hashfile(abslfile)) or
185 184 (checkexec and
186 185 ('x' in ctx1.flags(standin)) !=
187 186 bool(lfutil.getexecutable(abslfile)))):
188 187 modified.append(lfile)
189 188 elif listclean:
190 189 clean.append(lfile)
191 190 else:
192 191 added.append(lfile)
193 192
194 193 # at this point, 'removed' contains largefiles
195 194 # marked as 'R' in the working context.
196 195 # then, largefiles not managed also in the target
197 196 # context should be excluded from 'removed'.
198 197 removed = [lfile for lfile in removed
199 198 if lfutil.standin(lfile) in ctx1]
200 199
201 200 # Standins no longer found in lfdirstate has been
202 201 # removed
203 202 for standin in ctx1.walk(lfutil.getstandinmatcher(self)):
204 203 lfile = lfutil.splitstandin(standin)
205 204 if not match(lfile):
206 205 continue
207 206 if lfile not in lfdirstate:
208 207 removed.append(lfile)
209 208
210 209 # Filter result lists
211 210 result = list(result)
212 211
213 212 # Largefiles are not really removed when they're
214 213 # still in the normal dirstate. Likewise, normal
215 214 # files are not really removed if they are still in
216 215 # lfdirstate. This happens in merges where files
217 216 # change type.
218 217 removed = [f for f in removed
219 218 if f not in self.dirstate]
220 219 result[2] = [f for f in result[2]
221 220 if f not in lfdirstate]
222 221
223 222 lfiles = set(lfdirstate._map)
224 223 # Unknown files
225 224 result[4] = set(result[4]).difference(lfiles)
226 225 # Ignored files
227 226 result[5] = set(result[5]).difference(lfiles)
228 227 # combine normal files and largefiles
229 228 normals = [[fn for fn in filelist
230 229 if not lfutil.isstandin(fn)]
231 230 for filelist in result]
232 231 lfstatus = (modified, added, removed, s.deleted, [], [],
233 232 clean)
234 233 result = [sorted(list1 + list2)
235 234 for (list1, list2) in zip(normals, lfstatus)]
236 235 else: # not against working directory
237 236 result = [[lfutil.splitstandin(f) or f for f in items]
238 237 for items in result]
239 238
240 239 if wlock:
241 240 lfdirstate.write()
242 241
243 242 finally:
244 243 if wlock:
245 244 wlock.release()
246 245
247 246 self.lfstatus = True
248 247 return scmutil.status(*result)
249 248
250 249 def commitctx(self, ctx, *args, **kwargs):
251 250 node = super(lfilesrepo, self).commitctx(ctx, *args, **kwargs)
252 251 class lfilesctx(ctx.__class__):
253 252 def markcommitted(self, node):
254 253 orig = super(lfilesctx, self).markcommitted
255 254 return lfutil.markcommitted(orig, self, node)
256 255 ctx.__class__ = lfilesctx
257 256 return node
258 257
259 258 # Before commit, largefile standins have not had their
260 259 # contents updated to reflect the hash of their largefile.
261 260 # Do that here.
262 261 def commit(self, text="", user=None, date=None, match=None,
263 262 force=False, editor=False, extra={}):
264 263 orig = super(lfilesrepo, self).commit
265 264
266 265 with self.wlock():
267 266 lfcommithook = self._lfcommithooks[-1]
268 267 match = lfcommithook(self, match)
269 268 result = orig(text=text, user=user, date=date, match=match,
270 269 force=force, editor=editor, extra=extra)
271 270 return result
272 271
273 272 def push(self, remote, force=False, revs=None, newbranch=False):
274 273 if remote.local():
275 274 missing = set(self.requirements) - remote.local().supported
276 275 if missing:
277 276 msg = _("required features are not"
278 277 " supported in the destination:"
279 278 " %s") % (', '.join(sorted(missing)))
280 279 raise error.Abort(msg)
281 280 return super(lfilesrepo, self).push(remote, force=force, revs=revs,
282 281 newbranch=newbranch)
283 282
284 283 # TODO: _subdirlfs should be moved into "lfutil.py", because
285 284 # it is referred only from "lfutil.updatestandinsbymatch"
286 285 def _subdirlfs(self, files, lfiles):
287 286 '''
288 287 Adjust matched file list
289 288 If we pass a directory to commit whose only committable files
290 289 are largefiles, the core commit code aborts before finding
291 290 the largefiles.
292 291 So we do the following:
293 292 For directories that only have largefiles as matches,
294 293 we explicitly add the largefiles to the match list and remove
295 294 the directory.
296 295 In other cases, we leave the match list unmodified.
297 296 '''
298 297 actualfiles = []
299 298 dirs = []
300 299 regulars = []
301 300
302 301 for f in files:
303 302 if lfutil.isstandin(f + '/'):
304 303 raise error.Abort(
305 304 _('file "%s" is a largefile standin') % f,
306 305 hint=('commit the largefile itself instead'))
307 306 # Scan directories
308 if os.path.isdir(self.wjoin(f)):
307 if self.wvfs.isdir(f):
309 308 dirs.append(f)
310 309 else:
311 310 regulars.append(f)
312 311
313 312 for f in dirs:
314 313 matcheddir = False
315 314 d = self.dirstate.normalize(f) + '/'
316 315 # Check for matched normal files
317 316 for mf in regulars:
318 317 if self.dirstate.normalize(mf).startswith(d):
319 318 actualfiles.append(f)
320 319 matcheddir = True
321 320 break
322 321 if not matcheddir:
323 322 # If no normal match, manually append
324 323 # any matching largefiles
325 324 for lf in lfiles:
326 325 if self.dirstate.normalize(lf).startswith(d):
327 326 actualfiles.append(lf)
328 327 if not matcheddir:
329 328 # There may still be normal files in the dir, so
330 329 # add a directory to the list, which
331 330 # forces status/dirstate to walk all files and
332 331 # call the match function on the matcher, even
333 332 # on case sensitive filesystems.
334 333 actualfiles.append('.')
335 334 matcheddir = True
336 335 # Nothing in dir, so readd it
337 336 # and let commit reject it
338 337 if not matcheddir:
339 338 actualfiles.append(f)
340 339
341 340 # Always add normal files
342 341 actualfiles += regulars
343 342 return actualfiles
344 343
345 344 repo.__class__ = lfilesrepo
346 345
347 346 # stack of hooks being executed before committing.
348 347 # only last element ("_lfcommithooks[-1]") is used for each committing.
349 348 repo._lfcommithooks = [lfutil.updatestandinsbymatch]
350 349
351 350 # Stack of status writer functions taking "*msg, **opts" arguments
352 351 # like "ui.status()". Only last element ("_lfstatuswriters[-1]")
353 352 # is used to write status out.
354 353 repo._lfstatuswriters = [ui.status]
355 354
356 355 def prepushoutgoinghook(local, remote, outgoing):
357 356 if outgoing.missing:
358 357 toupload = set()
359 358 addfunc = lambda fn, lfhash: toupload.add(lfhash)
360 359 lfutil.getlfilestoupload(local, outgoing.missing, addfunc)
361 360 lfcommands.uploadlfiles(ui, local, remote, toupload)
362 361 repo.prepushoutgoinghooks.add("largefiles", prepushoutgoinghook)
363 362
364 363 def checkrequireslfiles(ui, repo, **kwargs):
365 364 if 'largefiles' not in repo.requirements and any(
366 365 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
367 366 repo.requirements.add('largefiles')
368 367 repo._writerequirements()
369 368
370 369 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles,
371 370 'largefiles')
372 371 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles, 'largefiles')
General Comments 0
You need to be logged in to leave comments. Login now