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