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