##// END OF EJS Templates
with: use context manager for wlock in overriderevert
Bryan O'Sullivan -
r27823:37552634 default
parent child Browse files
Show More
@@ -1,1411 +1,1407 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 '''Overridden Mercurial commands and functions for the largefiles extension'''
10 10
11 11 import os
12 12 import copy
13 13
14 14 from mercurial import hg, util, cmdutil, scmutil, match as match_, \
15 15 archival, pathutil, revset, error
16 16 from mercurial.i18n import _
17 17
18 18 import lfutil
19 19 import lfcommands
20 20 import basestore
21 21
22 22 # -- Utility functions: commonly/repeatedly needed functionality ---------------
23 23
24 24 def composelargefilematcher(match, manifest):
25 25 '''create a matcher that matches only the largefiles in the original
26 26 matcher'''
27 27 m = copy.copy(match)
28 28 lfile = lambda f: lfutil.standin(f) in manifest
29 29 m._files = filter(lfile, m._files)
30 30 m._fileroots = set(m._files)
31 31 m._always = False
32 32 origmatchfn = m.matchfn
33 33 m.matchfn = lambda f: lfile(f) and origmatchfn(f)
34 34 return m
35 35
36 36 def composenormalfilematcher(match, manifest, exclude=None):
37 37 excluded = set()
38 38 if exclude is not None:
39 39 excluded.update(exclude)
40 40
41 41 m = copy.copy(match)
42 42 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
43 43 manifest or f in excluded)
44 44 m._files = filter(notlfile, m._files)
45 45 m._fileroots = set(m._files)
46 46 m._always = False
47 47 origmatchfn = m.matchfn
48 48 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
49 49 return m
50 50
51 51 def installnormalfilesmatchfn(manifest):
52 52 '''installmatchfn with a matchfn that ignores all largefiles'''
53 53 def overridematch(ctx, pats=(), opts=None, globbed=False,
54 54 default='relpath', badfn=None):
55 55 if opts is None:
56 56 opts = {}
57 57 match = oldmatch(ctx, pats, opts, globbed, default, badfn=badfn)
58 58 return composenormalfilematcher(match, manifest)
59 59 oldmatch = installmatchfn(overridematch)
60 60
61 61 def installmatchfn(f):
62 62 '''monkey patch the scmutil module with a custom match function.
63 63 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
64 64 oldmatch = scmutil.match
65 65 setattr(f, 'oldmatch', oldmatch)
66 66 scmutil.match = f
67 67 return oldmatch
68 68
69 69 def restorematchfn():
70 70 '''restores scmutil.match to what it was before installmatchfn
71 71 was called. no-op if scmutil.match is its original function.
72 72
73 73 Note that n calls to installmatchfn will require n calls to
74 74 restore the original matchfn.'''
75 75 scmutil.match = getattr(scmutil.match, 'oldmatch')
76 76
77 77 def installmatchandpatsfn(f):
78 78 oldmatchandpats = scmutil.matchandpats
79 79 setattr(f, 'oldmatchandpats', oldmatchandpats)
80 80 scmutil.matchandpats = f
81 81 return oldmatchandpats
82 82
83 83 def restorematchandpatsfn():
84 84 '''restores scmutil.matchandpats to what it was before
85 85 installmatchandpatsfn was called. No-op if scmutil.matchandpats
86 86 is its original function.
87 87
88 88 Note that n calls to installmatchandpatsfn will require n calls
89 89 to restore the original matchfn.'''
90 90 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
91 91 scmutil.matchandpats)
92 92
93 93 def addlargefiles(ui, repo, isaddremove, matcher, **opts):
94 94 large = opts.get('large')
95 95 lfsize = lfutil.getminsize(
96 96 ui, lfutil.islfilesrepo(repo), opts.get('lfsize'))
97 97
98 98 lfmatcher = None
99 99 if lfutil.islfilesrepo(repo):
100 100 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
101 101 if lfpats:
102 102 lfmatcher = match_.match(repo.root, '', list(lfpats))
103 103
104 104 lfnames = []
105 105 m = matcher
106 106
107 107 wctx = repo[None]
108 108 for f in repo.walk(match_.badmatch(m, lambda x, y: None)):
109 109 exact = m.exact(f)
110 110 lfile = lfutil.standin(f) in wctx
111 111 nfile = f in wctx
112 112 exists = lfile or nfile
113 113
114 114 # addremove in core gets fancy with the name, add doesn't
115 115 if isaddremove:
116 116 name = m.uipath(f)
117 117 else:
118 118 name = m.rel(f)
119 119
120 120 # Don't warn the user when they attempt to add a normal tracked file.
121 121 # The normal add code will do that for us.
122 122 if exact and exists:
123 123 if lfile:
124 124 ui.warn(_('%s already a largefile\n') % name)
125 125 continue
126 126
127 127 if (exact or not exists) and not lfutil.isstandin(f):
128 128 # In case the file was removed previously, but not committed
129 129 # (issue3507)
130 130 if not repo.wvfs.exists(f):
131 131 continue
132 132
133 133 abovemin = (lfsize and
134 134 repo.wvfs.lstat(f).st_size >= lfsize * 1024 * 1024)
135 135 if large or abovemin or (lfmatcher and lfmatcher(f)):
136 136 lfnames.append(f)
137 137 if ui.verbose or not exact:
138 138 ui.status(_('adding %s as a largefile\n') % name)
139 139
140 140 bad = []
141 141
142 142 # Need to lock, otherwise there could be a race condition between
143 143 # when standins are created and added to the repo.
144 144 with repo.wlock():
145 145 if not opts.get('dry_run'):
146 146 standins = []
147 147 lfdirstate = lfutil.openlfdirstate(ui, repo)
148 148 for f in lfnames:
149 149 standinname = lfutil.standin(f)
150 150 lfutil.writestandin(repo, standinname, hash='',
151 151 executable=lfutil.getexecutable(repo.wjoin(f)))
152 152 standins.append(standinname)
153 153 if lfdirstate[f] == 'r':
154 154 lfdirstate.normallookup(f)
155 155 else:
156 156 lfdirstate.add(f)
157 157 lfdirstate.write()
158 158 bad += [lfutil.splitstandin(f)
159 159 for f in repo[None].add(standins)
160 160 if f in m.files()]
161 161
162 162 added = [f for f in lfnames if f not in bad]
163 163 return added, bad
164 164
165 165 def removelargefiles(ui, repo, isaddremove, matcher, **opts):
166 166 after = opts.get('after')
167 167 m = composelargefilematcher(matcher, repo[None].manifest())
168 168 try:
169 169 repo.lfstatus = True
170 170 s = repo.status(match=m, clean=not isaddremove)
171 171 finally:
172 172 repo.lfstatus = False
173 173 manifest = repo[None].manifest()
174 174 modified, added, deleted, clean = [[f for f in list
175 175 if lfutil.standin(f) in manifest]
176 176 for list in (s.modified, s.added,
177 177 s.deleted, s.clean)]
178 178
179 179 def warn(files, msg):
180 180 for f in files:
181 181 ui.warn(msg % m.rel(f))
182 182 return int(len(files) > 0)
183 183
184 184 result = 0
185 185
186 186 if after:
187 187 remove = deleted
188 188 result = warn(modified + added + clean,
189 189 _('not removing %s: file still exists\n'))
190 190 else:
191 191 remove = deleted + clean
192 192 result = warn(modified, _('not removing %s: file is modified (use -f'
193 193 ' to force removal)\n'))
194 194 result = warn(added, _('not removing %s: file has been marked for add'
195 195 ' (use forget to undo)\n')) or result
196 196
197 197 # Need to lock because standin files are deleted then removed from the
198 198 # repository and we could race in-between.
199 199 with repo.wlock():
200 200 lfdirstate = lfutil.openlfdirstate(ui, repo)
201 201 for f in sorted(remove):
202 202 if ui.verbose or not m.exact(f):
203 203 # addremove in core gets fancy with the name, remove doesn't
204 204 if isaddremove:
205 205 name = m.uipath(f)
206 206 else:
207 207 name = m.rel(f)
208 208 ui.status(_('removing %s\n') % name)
209 209
210 210 if not opts.get('dry_run'):
211 211 if not after:
212 212 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
213 213
214 214 if opts.get('dry_run'):
215 215 return result
216 216
217 217 remove = [lfutil.standin(f) for f in remove]
218 218 # If this is being called by addremove, let the original addremove
219 219 # function handle this.
220 220 if not isaddremove:
221 221 for f in remove:
222 222 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
223 223 repo[None].forget(remove)
224 224
225 225 for f in remove:
226 226 lfutil.synclfdirstate(repo, lfdirstate, lfutil.splitstandin(f),
227 227 False)
228 228
229 229 lfdirstate.write()
230 230
231 231 return result
232 232
233 233 # For overriding mercurial.hgweb.webcommands so that largefiles will
234 234 # appear at their right place in the manifests.
235 235 def decodepath(orig, path):
236 236 return lfutil.splitstandin(path) or path
237 237
238 238 # -- Wrappers: modify existing commands --------------------------------
239 239
240 240 def overrideadd(orig, ui, repo, *pats, **opts):
241 241 if opts.get('normal') and opts.get('large'):
242 242 raise error.Abort(_('--normal cannot be used with --large'))
243 243 return orig(ui, repo, *pats, **opts)
244 244
245 245 def cmdutiladd(orig, ui, repo, matcher, prefix, explicitonly, **opts):
246 246 # The --normal flag short circuits this override
247 247 if opts.get('normal'):
248 248 return orig(ui, repo, matcher, prefix, explicitonly, **opts)
249 249
250 250 ladded, lbad = addlargefiles(ui, repo, False, matcher, **opts)
251 251 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest(),
252 252 ladded)
253 253 bad = orig(ui, repo, normalmatcher, prefix, explicitonly, **opts)
254 254
255 255 bad.extend(f for f in lbad)
256 256 return bad
257 257
258 258 def cmdutilremove(orig, ui, repo, matcher, prefix, after, force, subrepos):
259 259 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest())
260 260 result = orig(ui, repo, normalmatcher, prefix, after, force, subrepos)
261 261 return removelargefiles(ui, repo, False, matcher, after=after,
262 262 force=force) or result
263 263
264 264 def overridestatusfn(orig, repo, rev2, **opts):
265 265 try:
266 266 repo._repo.lfstatus = True
267 267 return orig(repo, rev2, **opts)
268 268 finally:
269 269 repo._repo.lfstatus = False
270 270
271 271 def overridestatus(orig, ui, repo, *pats, **opts):
272 272 try:
273 273 repo.lfstatus = True
274 274 return orig(ui, repo, *pats, **opts)
275 275 finally:
276 276 repo.lfstatus = False
277 277
278 278 def overridedirty(orig, repo, ignoreupdate=False):
279 279 try:
280 280 repo._repo.lfstatus = True
281 281 return orig(repo, ignoreupdate)
282 282 finally:
283 283 repo._repo.lfstatus = False
284 284
285 285 def overridelog(orig, ui, repo, *pats, **opts):
286 286 def overridematchandpats(ctx, pats=(), opts=None, globbed=False,
287 287 default='relpath', badfn=None):
288 288 """Matcher that merges root directory with .hglf, suitable for log.
289 289 It is still possible to match .hglf directly.
290 290 For any listed files run log on the standin too.
291 291 matchfn tries both the given filename and with .hglf stripped.
292 292 """
293 293 if opts is None:
294 294 opts = {}
295 295 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default,
296 296 badfn=badfn)
297 297 m, p = copy.copy(matchandpats)
298 298
299 299 if m.always():
300 300 # We want to match everything anyway, so there's no benefit trying
301 301 # to add standins.
302 302 return matchandpats
303 303
304 304 pats = set(p)
305 305
306 306 def fixpats(pat, tostandin=lfutil.standin):
307 307 if pat.startswith('set:'):
308 308 return pat
309 309
310 310 kindpat = match_._patsplit(pat, None)
311 311
312 312 if kindpat[0] is not None:
313 313 return kindpat[0] + ':' + tostandin(kindpat[1])
314 314 return tostandin(kindpat[1])
315 315
316 316 if m._cwd:
317 317 hglf = lfutil.shortname
318 318 back = util.pconvert(m.rel(hglf)[:-len(hglf)])
319 319
320 320 def tostandin(f):
321 321 # The file may already be a standin, so truncate the back
322 322 # prefix and test before mangling it. This avoids turning
323 323 # 'glob:../.hglf/foo*' into 'glob:../.hglf/../.hglf/foo*'.
324 324 if f.startswith(back) and lfutil.splitstandin(f[len(back):]):
325 325 return f
326 326
327 327 # An absolute path is from outside the repo, so truncate the
328 328 # path to the root before building the standin. Otherwise cwd
329 329 # is somewhere in the repo, relative to root, and needs to be
330 330 # prepended before building the standin.
331 331 if os.path.isabs(m._cwd):
332 332 f = f[len(back):]
333 333 else:
334 334 f = m._cwd + '/' + f
335 335 return back + lfutil.standin(f)
336 336
337 337 pats.update(fixpats(f, tostandin) for f in p)
338 338 else:
339 339 def tostandin(f):
340 340 if lfutil.splitstandin(f):
341 341 return f
342 342 return lfutil.standin(f)
343 343 pats.update(fixpats(f, tostandin) for f in p)
344 344
345 345 for i in range(0, len(m._files)):
346 346 # Don't add '.hglf' to m.files, since that is already covered by '.'
347 347 if m._files[i] == '.':
348 348 continue
349 349 standin = lfutil.standin(m._files[i])
350 350 # If the "standin" is a directory, append instead of replace to
351 351 # support naming a directory on the command line with only
352 352 # largefiles. The original directory is kept to support normal
353 353 # files.
354 354 if standin in repo[ctx.node()]:
355 355 m._files[i] = standin
356 356 elif m._files[i] not in repo[ctx.node()] \
357 357 and repo.wvfs.isdir(standin):
358 358 m._files.append(standin)
359 359
360 360 m._fileroots = set(m._files)
361 361 m._always = False
362 362 origmatchfn = m.matchfn
363 363 def lfmatchfn(f):
364 364 lf = lfutil.splitstandin(f)
365 365 if lf is not None and origmatchfn(lf):
366 366 return True
367 367 r = origmatchfn(f)
368 368 return r
369 369 m.matchfn = lfmatchfn
370 370
371 371 ui.debug('updated patterns: %s\n' % sorted(pats))
372 372 return m, pats
373 373
374 374 # For hg log --patch, the match object is used in two different senses:
375 375 # (1) to determine what revisions should be printed out, and
376 376 # (2) to determine what files to print out diffs for.
377 377 # The magic matchandpats override should be used for case (1) but not for
378 378 # case (2).
379 379 def overridemakelogfilematcher(repo, pats, opts, badfn=None):
380 380 wctx = repo[None]
381 381 match, pats = oldmatchandpats(wctx, pats, opts, badfn=badfn)
382 382 return lambda rev: match
383 383
384 384 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
385 385 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
386 386 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
387 387
388 388 try:
389 389 return orig(ui, repo, *pats, **opts)
390 390 finally:
391 391 restorematchandpatsfn()
392 392 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
393 393
394 394 def overrideverify(orig, ui, repo, *pats, **opts):
395 395 large = opts.pop('large', False)
396 396 all = opts.pop('lfa', False)
397 397 contents = opts.pop('lfc', False)
398 398
399 399 result = orig(ui, repo, *pats, **opts)
400 400 if large or all or contents:
401 401 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
402 402 return result
403 403
404 404 def overridedebugstate(orig, ui, repo, *pats, **opts):
405 405 large = opts.pop('large', False)
406 406 if large:
407 407 class fakerepo(object):
408 408 dirstate = lfutil.openlfdirstate(ui, repo)
409 409 orig(ui, fakerepo, *pats, **opts)
410 410 else:
411 411 orig(ui, repo, *pats, **opts)
412 412
413 413 # Before starting the manifest merge, merge.updates will call
414 414 # _checkunknownfile to check if there are any files in the merged-in
415 415 # changeset that collide with unknown files in the working copy.
416 416 #
417 417 # The largefiles are seen as unknown, so this prevents us from merging
418 418 # in a file 'foo' if we already have a largefile with the same name.
419 419 #
420 420 # The overridden function filters the unknown files by removing any
421 421 # largefiles. This makes the merge proceed and we can then handle this
422 422 # case further in the overridden calculateupdates function below.
423 423 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
424 424 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
425 425 return False
426 426 return origfn(repo, wctx, mctx, f, f2)
427 427
428 428 # The manifest merge handles conflicts on the manifest level. We want
429 429 # to handle changes in largefile-ness of files at this level too.
430 430 #
431 431 # The strategy is to run the original calculateupdates and then process
432 432 # the action list it outputs. There are two cases we need to deal with:
433 433 #
434 434 # 1. Normal file in p1, largefile in p2. Here the largefile is
435 435 # detected via its standin file, which will enter the working copy
436 436 # with a "get" action. It is not "merge" since the standin is all
437 437 # Mercurial is concerned with at this level -- the link to the
438 438 # existing normal file is not relevant here.
439 439 #
440 440 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
441 441 # since the largefile will be present in the working copy and
442 442 # different from the normal file in p2. Mercurial therefore
443 443 # triggers a merge action.
444 444 #
445 445 # In both cases, we prompt the user and emit new actions to either
446 446 # remove the standin (if the normal file was kept) or to remove the
447 447 # normal file and get the standin (if the largefile was kept). The
448 448 # default prompt answer is to use the largefile version since it was
449 449 # presumably changed on purpose.
450 450 #
451 451 # Finally, the merge.applyupdates function will then take care of
452 452 # writing the files into the working copy and lfcommands.updatelfiles
453 453 # will update the largefiles.
454 454 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
455 455 acceptremote, followcopies, matcher=None):
456 456 overwrite = force and not branchmerge
457 457 actions, diverge, renamedelete = origfn(
458 458 repo, p1, p2, pas, branchmerge, force, acceptremote,
459 459 followcopies, matcher=matcher)
460 460
461 461 if overwrite:
462 462 return actions, diverge, renamedelete
463 463
464 464 # Convert to dictionary with filename as key and action as value.
465 465 lfiles = set()
466 466 for f in actions:
467 467 splitstandin = f and lfutil.splitstandin(f)
468 468 if splitstandin in p1:
469 469 lfiles.add(splitstandin)
470 470 elif lfutil.standin(f) in p1:
471 471 lfiles.add(f)
472 472
473 473 for lfile in lfiles:
474 474 standin = lfutil.standin(lfile)
475 475 (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
476 476 (sm, sargs, smsg) = actions.get(standin, (None, None, None))
477 477 if sm in ('g', 'dc') and lm != 'r':
478 478 if sm == 'dc':
479 479 f1, f2, fa, move, anc = sargs
480 480 sargs = (p2[f2].flags(), False)
481 481 # Case 1: normal file in the working copy, largefile in
482 482 # the second parent
483 483 usermsg = _('remote turned local normal file %s into a largefile\n'
484 484 'use (l)argefile or keep (n)ormal file?'
485 485 '$$ &Largefile $$ &Normal file') % lfile
486 486 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
487 487 actions[lfile] = ('r', None, 'replaced by standin')
488 488 actions[standin] = ('g', sargs, 'replaces standin')
489 489 else: # keep local normal file
490 490 actions[lfile] = ('k', None, 'replaces standin')
491 491 if branchmerge:
492 492 actions[standin] = ('k', None, 'replaced by non-standin')
493 493 else:
494 494 actions[standin] = ('r', None, 'replaced by non-standin')
495 495 elif lm in ('g', 'dc') and sm != 'r':
496 496 if lm == 'dc':
497 497 f1, f2, fa, move, anc = largs
498 498 largs = (p2[f2].flags(), False)
499 499 # Case 2: largefile in the working copy, normal file in
500 500 # the second parent
501 501 usermsg = _('remote turned local largefile %s into a normal file\n'
502 502 'keep (l)argefile or use (n)ormal file?'
503 503 '$$ &Largefile $$ &Normal file') % lfile
504 504 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
505 505 if branchmerge:
506 506 # largefile can be restored from standin safely
507 507 actions[lfile] = ('k', None, 'replaced by standin')
508 508 actions[standin] = ('k', None, 'replaces standin')
509 509 else:
510 510 # "lfile" should be marked as "removed" without
511 511 # removal of itself
512 512 actions[lfile] = ('lfmr', None,
513 513 'forget non-standin largefile')
514 514
515 515 # linear-merge should treat this largefile as 're-added'
516 516 actions[standin] = ('a', None, 'keep standin')
517 517 else: # pick remote normal file
518 518 actions[lfile] = ('g', largs, 'replaces standin')
519 519 actions[standin] = ('r', None, 'replaced by non-standin')
520 520
521 521 return actions, diverge, renamedelete
522 522
523 523 def mergerecordupdates(orig, repo, actions, branchmerge):
524 524 if 'lfmr' in actions:
525 525 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
526 526 for lfile, args, msg in actions['lfmr']:
527 527 # this should be executed before 'orig', to execute 'remove'
528 528 # before all other actions
529 529 repo.dirstate.remove(lfile)
530 530 # make sure lfile doesn't get synclfdirstate'd as normal
531 531 lfdirstate.add(lfile)
532 532 lfdirstate.write()
533 533
534 534 return orig(repo, actions, branchmerge)
535 535
536 536
537 537 # Override filemerge to prompt the user about how they wish to merge
538 538 # largefiles. This will handle identical edits without prompting the user.
539 539 def overridefilemerge(origfn, premerge, repo, mynode, orig, fcd, fco, fca,
540 540 labels=None):
541 541 if not lfutil.isstandin(orig) or fcd.isabsent() or fco.isabsent():
542 542 return origfn(premerge, repo, mynode, orig, fcd, fco, fca,
543 543 labels=labels)
544 544
545 545 ahash = fca.data().strip().lower()
546 546 dhash = fcd.data().strip().lower()
547 547 ohash = fco.data().strip().lower()
548 548 if (ohash != ahash and
549 549 ohash != dhash and
550 550 (dhash == ahash or
551 551 repo.ui.promptchoice(
552 552 _('largefile %s has a merge conflict\nancestor was %s\n'
553 553 'keep (l)ocal %s or\ntake (o)ther %s?'
554 554 '$$ &Local $$ &Other') %
555 555 (lfutil.splitstandin(orig), ahash, dhash, ohash),
556 556 0) == 1)):
557 557 repo.wwrite(fcd.path(), fco.data(), fco.flags())
558 558 return True, 0, False
559 559
560 560 def copiespathcopies(orig, ctx1, ctx2, match=None):
561 561 copies = orig(ctx1, ctx2, match=match)
562 562 updated = {}
563 563
564 564 for k, v in copies.iteritems():
565 565 updated[lfutil.splitstandin(k) or k] = lfutil.splitstandin(v) or v
566 566
567 567 return updated
568 568
569 569 # Copy first changes the matchers to match standins instead of
570 570 # largefiles. Then it overrides util.copyfile in that function it
571 571 # checks if the destination largefile already exists. It also keeps a
572 572 # list of copied files so that the largefiles can be copied and the
573 573 # dirstate updated.
574 574 def overridecopy(orig, ui, repo, pats, opts, rename=False):
575 575 # doesn't remove largefile on rename
576 576 if len(pats) < 2:
577 577 # this isn't legal, let the original function deal with it
578 578 return orig(ui, repo, pats, opts, rename)
579 579
580 580 # This could copy both lfiles and normal files in one command,
581 581 # but we don't want to do that. First replace their matcher to
582 582 # only match normal files and run it, then replace it to just
583 583 # match largefiles and run it again.
584 584 nonormalfiles = False
585 585 nolfiles = False
586 586 installnormalfilesmatchfn(repo[None].manifest())
587 587 try:
588 588 result = orig(ui, repo, pats, opts, rename)
589 589 except error.Abort as e:
590 590 if str(e) != _('no files to copy'):
591 591 raise e
592 592 else:
593 593 nonormalfiles = True
594 594 result = 0
595 595 finally:
596 596 restorematchfn()
597 597
598 598 # The first rename can cause our current working directory to be removed.
599 599 # In that case there is nothing left to copy/rename so just quit.
600 600 try:
601 601 repo.getcwd()
602 602 except OSError:
603 603 return result
604 604
605 605 def makestandin(relpath):
606 606 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
607 607 return os.path.join(repo.wjoin(lfutil.standin(path)))
608 608
609 609 fullpats = scmutil.expandpats(pats)
610 610 dest = fullpats[-1]
611 611
612 612 if os.path.isdir(dest):
613 613 if not os.path.isdir(makestandin(dest)):
614 614 os.makedirs(makestandin(dest))
615 615
616 616 try:
617 617 # When we call orig below it creates the standins but we don't add
618 618 # them to the dir state until later so lock during that time.
619 619 wlock = repo.wlock()
620 620
621 621 manifest = repo[None].manifest()
622 622 def overridematch(ctx, pats=(), opts=None, globbed=False,
623 623 default='relpath', badfn=None):
624 624 if opts is None:
625 625 opts = {}
626 626 newpats = []
627 627 # The patterns were previously mangled to add the standin
628 628 # directory; we need to remove that now
629 629 for pat in pats:
630 630 if match_.patkind(pat) is None and lfutil.shortname in pat:
631 631 newpats.append(pat.replace(lfutil.shortname, ''))
632 632 else:
633 633 newpats.append(pat)
634 634 match = oldmatch(ctx, newpats, opts, globbed, default, badfn=badfn)
635 635 m = copy.copy(match)
636 636 lfile = lambda f: lfutil.standin(f) in manifest
637 637 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
638 638 m._fileroots = set(m._files)
639 639 origmatchfn = m.matchfn
640 640 m.matchfn = lambda f: (lfutil.isstandin(f) and
641 641 (f in manifest) and
642 642 origmatchfn(lfutil.splitstandin(f)) or
643 643 None)
644 644 return m
645 645 oldmatch = installmatchfn(overridematch)
646 646 listpats = []
647 647 for pat in pats:
648 648 if match_.patkind(pat) is not None:
649 649 listpats.append(pat)
650 650 else:
651 651 listpats.append(makestandin(pat))
652 652
653 653 try:
654 654 origcopyfile = util.copyfile
655 655 copiedfiles = []
656 656 def overridecopyfile(src, dest):
657 657 if (lfutil.shortname in src and
658 658 dest.startswith(repo.wjoin(lfutil.shortname))):
659 659 destlfile = dest.replace(lfutil.shortname, '')
660 660 if not opts['force'] and os.path.exists(destlfile):
661 661 raise IOError('',
662 662 _('destination largefile already exists'))
663 663 copiedfiles.append((src, dest))
664 664 origcopyfile(src, dest)
665 665
666 666 util.copyfile = overridecopyfile
667 667 result += orig(ui, repo, listpats, opts, rename)
668 668 finally:
669 669 util.copyfile = origcopyfile
670 670
671 671 lfdirstate = lfutil.openlfdirstate(ui, repo)
672 672 for (src, dest) in copiedfiles:
673 673 if (lfutil.shortname in src and
674 674 dest.startswith(repo.wjoin(lfutil.shortname))):
675 675 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
676 676 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
677 677 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
678 678 if not os.path.isdir(destlfiledir):
679 679 os.makedirs(destlfiledir)
680 680 if rename:
681 681 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
682 682
683 683 # The file is gone, but this deletes any empty parent
684 684 # directories as a side-effect.
685 685 util.unlinkpath(repo.wjoin(srclfile), True)
686 686 lfdirstate.remove(srclfile)
687 687 else:
688 688 util.copyfile(repo.wjoin(srclfile),
689 689 repo.wjoin(destlfile))
690 690
691 691 lfdirstate.add(destlfile)
692 692 lfdirstate.write()
693 693 except error.Abort as e:
694 694 if str(e) != _('no files to copy'):
695 695 raise e
696 696 else:
697 697 nolfiles = True
698 698 finally:
699 699 restorematchfn()
700 700 wlock.release()
701 701
702 702 if nolfiles and nonormalfiles:
703 703 raise error.Abort(_('no files to copy'))
704 704
705 705 return result
706 706
707 707 # When the user calls revert, we have to be careful to not revert any
708 708 # changes to other largefiles accidentally. This means we have to keep
709 709 # track of the largefiles that are being reverted so we only pull down
710 710 # the necessary largefiles.
711 711 #
712 712 # Standins are only updated (to match the hash of largefiles) before
713 713 # commits. Update the standins then run the original revert, changing
714 714 # the matcher to hit standins instead of largefiles. Based on the
715 715 # resulting standins update the largefiles.
716 716 def overriderevert(orig, ui, repo, ctx, parents, *pats, **opts):
717 717 # Because we put the standins in a bad state (by updating them)
718 718 # and then return them to a correct state we need to lock to
719 719 # prevent others from changing them in their incorrect state.
720 wlock = repo.wlock()
721 try:
720 with repo.wlock():
722 721 lfdirstate = lfutil.openlfdirstate(ui, repo)
723 722 s = lfutil.lfdirstatestatus(lfdirstate, repo)
724 723 lfdirstate.write()
725 724 for lfile in s.modified:
726 725 lfutil.updatestandin(repo, lfutil.standin(lfile))
727 726 for lfile in s.deleted:
728 727 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
729 728 os.unlink(repo.wjoin(lfutil.standin(lfile)))
730 729
731 730 oldstandins = lfutil.getstandinsstate(repo)
732 731
733 732 def overridematch(mctx, pats=(), opts=None, globbed=False,
734 733 default='relpath', badfn=None):
735 734 if opts is None:
736 735 opts = {}
737 736 match = oldmatch(mctx, pats, opts, globbed, default, badfn=badfn)
738 737 m = copy.copy(match)
739 738
740 739 # revert supports recursing into subrepos, and though largefiles
741 740 # currently doesn't work correctly in that case, this match is
742 741 # called, so the lfdirstate above may not be the correct one for
743 742 # this invocation of match.
744 743 lfdirstate = lfutil.openlfdirstate(mctx.repo().ui, mctx.repo(),
745 744 False)
746 745
747 746 def tostandin(f):
748 747 standin = lfutil.standin(f)
749 748 if standin in ctx or standin in mctx:
750 749 return standin
751 750 elif standin in repo[None] or lfdirstate[f] == 'r':
752 751 return None
753 752 return f
754 753 m._files = [tostandin(f) for f in m._files]
755 754 m._files = [f for f in m._files if f is not None]
756 755 m._fileroots = set(m._files)
757 756 origmatchfn = m.matchfn
758 757 def matchfn(f):
759 758 if lfutil.isstandin(f):
760 759 return (origmatchfn(lfutil.splitstandin(f)) and
761 760 (f in ctx or f in mctx))
762 761 return origmatchfn(f)
763 762 m.matchfn = matchfn
764 763 return m
765 764 oldmatch = installmatchfn(overridematch)
766 765 try:
767 766 orig(ui, repo, ctx, parents, *pats, **opts)
768 767 finally:
769 768 restorematchfn()
770 769
771 770 newstandins = lfutil.getstandinsstate(repo)
772 771 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
773 772 # lfdirstate should be 'normallookup'-ed for updated files,
774 773 # because reverting doesn't touch dirstate for 'normal' files
775 774 # when target revision is explicitly specified: in such case,
776 775 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
777 776 # of target (standin) file.
778 777 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
779 778 normallookup=True)
780 779
781 finally:
782 wlock.release()
783
784 780 # after pulling changesets, we need to take some extra care to get
785 781 # largefiles updated remotely
786 782 def overridepull(orig, ui, repo, source=None, **opts):
787 783 revsprepull = len(repo)
788 784 if not source:
789 785 source = 'default'
790 786 repo.lfpullsource = source
791 787 result = orig(ui, repo, source, **opts)
792 788 revspostpull = len(repo)
793 789 lfrevs = opts.get('lfrev', [])
794 790 if opts.get('all_largefiles'):
795 791 lfrevs.append('pulled()')
796 792 if lfrevs and revspostpull > revsprepull:
797 793 numcached = 0
798 794 repo.firstpulled = revsprepull # for pulled() revset expression
799 795 try:
800 796 for rev in scmutil.revrange(repo, lfrevs):
801 797 ui.note(_('pulling largefiles for revision %s\n') % rev)
802 798 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
803 799 numcached += len(cached)
804 800 finally:
805 801 del repo.firstpulled
806 802 ui.status(_("%d largefiles cached\n") % numcached)
807 803 return result
808 804
809 805 revsetpredicate = revset.extpredicate()
810 806
811 807 @revsetpredicate('pulled()')
812 808 def pulledrevsetsymbol(repo, subset, x):
813 809 """Changesets that just has been pulled.
814 810
815 811 Only available with largefiles from pull --lfrev expressions.
816 812
817 813 .. container:: verbose
818 814
819 815 Some examples:
820 816
821 817 - pull largefiles for all new changesets::
822 818
823 819 hg pull -lfrev "pulled()"
824 820
825 821 - pull largefiles for all new branch heads::
826 822
827 823 hg pull -lfrev "head(pulled()) and not closed()"
828 824
829 825 """
830 826
831 827 try:
832 828 firstpulled = repo.firstpulled
833 829 except AttributeError:
834 830 raise error.Abort(_("pulled() only available in --lfrev"))
835 831 return revset.baseset([r for r in subset if r >= firstpulled])
836 832
837 833 def overrideclone(orig, ui, source, dest=None, **opts):
838 834 d = dest
839 835 if d is None:
840 836 d = hg.defaultdest(source)
841 837 if opts.get('all_largefiles') and not hg.islocal(d):
842 838 raise error.Abort(_(
843 839 '--all-largefiles is incompatible with non-local destination %s') %
844 840 d)
845 841
846 842 return orig(ui, source, dest, **opts)
847 843
848 844 def hgclone(orig, ui, opts, *args, **kwargs):
849 845 result = orig(ui, opts, *args, **kwargs)
850 846
851 847 if result is not None:
852 848 sourcerepo, destrepo = result
853 849 repo = destrepo.local()
854 850
855 851 # When cloning to a remote repo (like through SSH), no repo is available
856 852 # from the peer. Therefore the largefiles can't be downloaded and the
857 853 # hgrc can't be updated.
858 854 if not repo:
859 855 return result
860 856
861 857 # If largefiles is required for this repo, permanently enable it locally
862 858 if 'largefiles' in repo.requirements:
863 859 fp = repo.vfs('hgrc', 'a', text=True)
864 860 try:
865 861 fp.write('\n[extensions]\nlargefiles=\n')
866 862 finally:
867 863 fp.close()
868 864
869 865 # Caching is implicitly limited to 'rev' option, since the dest repo was
870 866 # truncated at that point. The user may expect a download count with
871 867 # this option, so attempt whether or not this is a largefile repo.
872 868 if opts.get('all_largefiles'):
873 869 success, missing = lfcommands.downloadlfiles(ui, repo, None)
874 870
875 871 if missing != 0:
876 872 return None
877 873
878 874 return result
879 875
880 876 def overriderebase(orig, ui, repo, **opts):
881 877 if not util.safehasattr(repo, '_largefilesenabled'):
882 878 return orig(ui, repo, **opts)
883 879
884 880 resuming = opts.get('continue')
885 881 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
886 882 repo._lfstatuswriters.append(lambda *msg, **opts: None)
887 883 try:
888 884 return orig(ui, repo, **opts)
889 885 finally:
890 886 repo._lfstatuswriters.pop()
891 887 repo._lfcommithooks.pop()
892 888
893 889 def overridearchivecmd(orig, ui, repo, dest, **opts):
894 890 repo.unfiltered().lfstatus = True
895 891
896 892 try:
897 893 return orig(ui, repo.unfiltered(), dest, **opts)
898 894 finally:
899 895 repo.unfiltered().lfstatus = False
900 896
901 897 def hgwebarchive(orig, web, req, tmpl):
902 898 web.repo.lfstatus = True
903 899
904 900 try:
905 901 return orig(web, req, tmpl)
906 902 finally:
907 903 web.repo.lfstatus = False
908 904
909 905 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
910 906 prefix='', mtime=None, subrepos=None):
911 907 # For some reason setting repo.lfstatus in hgwebarchive only changes the
912 908 # unfiltered repo's attr, so check that as well.
913 909 if not repo.lfstatus and not repo.unfiltered().lfstatus:
914 910 return orig(repo, dest, node, kind, decode, matchfn, prefix, mtime,
915 911 subrepos)
916 912
917 913 # No need to lock because we are only reading history and
918 914 # largefile caches, neither of which are modified.
919 915 if node is not None:
920 916 lfcommands.cachelfiles(repo.ui, repo, node)
921 917
922 918 if kind not in archival.archivers:
923 919 raise error.Abort(_("unknown archive type '%s'") % kind)
924 920
925 921 ctx = repo[node]
926 922
927 923 if kind == 'files':
928 924 if prefix:
929 925 raise error.Abort(
930 926 _('cannot give prefix when archiving to files'))
931 927 else:
932 928 prefix = archival.tidyprefix(dest, kind, prefix)
933 929
934 930 def write(name, mode, islink, getdata):
935 931 if matchfn and not matchfn(name):
936 932 return
937 933 data = getdata()
938 934 if decode:
939 935 data = repo.wwritedata(name, data)
940 936 archiver.addfile(prefix + name, mode, islink, data)
941 937
942 938 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
943 939
944 940 if repo.ui.configbool("ui", "archivemeta", True):
945 941 write('.hg_archival.txt', 0o644, False,
946 942 lambda: archival.buildmetadata(ctx))
947 943
948 944 for f in ctx:
949 945 ff = ctx.flags(f)
950 946 getdata = ctx[f].data
951 947 if lfutil.isstandin(f):
952 948 if node is not None:
953 949 path = lfutil.findfile(repo, getdata().strip())
954 950
955 951 if path is None:
956 952 raise error.Abort(
957 953 _('largefile %s not found in repo store or system cache')
958 954 % lfutil.splitstandin(f))
959 955 else:
960 956 path = lfutil.splitstandin(f)
961 957
962 958 f = lfutil.splitstandin(f)
963 959
964 960 getdata = lambda: util.readfile(path)
965 961 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, getdata)
966 962
967 963 if subrepos:
968 964 for subpath in sorted(ctx.substate):
969 965 sub = ctx.workingsub(subpath)
970 966 submatch = match_.narrowmatcher(subpath, matchfn)
971 967 sub._repo.lfstatus = True
972 968 sub.archive(archiver, prefix, submatch)
973 969
974 970 archiver.done()
975 971
976 972 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None):
977 973 if not repo._repo.lfstatus:
978 974 return orig(repo, archiver, prefix, match)
979 975
980 976 repo._get(repo._state + ('hg',))
981 977 rev = repo._state[1]
982 978 ctx = repo._repo[rev]
983 979
984 980 if ctx.node() is not None:
985 981 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
986 982
987 983 def write(name, mode, islink, getdata):
988 984 # At this point, the standin has been replaced with the largefile name,
989 985 # so the normal matcher works here without the lfutil variants.
990 986 if match and not match(f):
991 987 return
992 988 data = getdata()
993 989
994 990 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
995 991
996 992 for f in ctx:
997 993 ff = ctx.flags(f)
998 994 getdata = ctx[f].data
999 995 if lfutil.isstandin(f):
1000 996 if ctx.node() is not None:
1001 997 path = lfutil.findfile(repo._repo, getdata().strip())
1002 998
1003 999 if path is None:
1004 1000 raise error.Abort(
1005 1001 _('largefile %s not found in repo store or system cache')
1006 1002 % lfutil.splitstandin(f))
1007 1003 else:
1008 1004 path = lfutil.splitstandin(f)
1009 1005
1010 1006 f = lfutil.splitstandin(f)
1011 1007
1012 1008 getdata = lambda: util.readfile(os.path.join(prefix, path))
1013 1009
1014 1010 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, getdata)
1015 1011
1016 1012 for subpath in sorted(ctx.substate):
1017 1013 sub = ctx.workingsub(subpath)
1018 1014 submatch = match_.narrowmatcher(subpath, match)
1019 1015 sub._repo.lfstatus = True
1020 1016 sub.archive(archiver, prefix + repo._path + '/', submatch)
1021 1017
1022 1018 # If a largefile is modified, the change is not reflected in its
1023 1019 # standin until a commit. cmdutil.bailifchanged() raises an exception
1024 1020 # if the repo has uncommitted changes. Wrap it to also check if
1025 1021 # largefiles were changed. This is used by bisect, backout and fetch.
1026 1022 def overridebailifchanged(orig, repo, *args, **kwargs):
1027 1023 orig(repo, *args, **kwargs)
1028 1024 repo.lfstatus = True
1029 1025 s = repo.status()
1030 1026 repo.lfstatus = False
1031 1027 if s.modified or s.added or s.removed or s.deleted:
1032 1028 raise error.Abort(_('uncommitted changes'))
1033 1029
1034 1030 def cmdutilforget(orig, ui, repo, match, prefix, explicitonly):
1035 1031 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
1036 1032 bad, forgot = orig(ui, repo, normalmatcher, prefix, explicitonly)
1037 1033 m = composelargefilematcher(match, repo[None].manifest())
1038 1034
1039 1035 try:
1040 1036 repo.lfstatus = True
1041 1037 s = repo.status(match=m, clean=True)
1042 1038 finally:
1043 1039 repo.lfstatus = False
1044 1040 forget = sorted(s.modified + s.added + s.deleted + s.clean)
1045 1041 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
1046 1042
1047 1043 for f in forget:
1048 1044 if lfutil.standin(f) not in repo.dirstate and not \
1049 1045 repo.wvfs.isdir(lfutil.standin(f)):
1050 1046 ui.warn(_('not removing %s: file is already untracked\n')
1051 1047 % m.rel(f))
1052 1048 bad.append(f)
1053 1049
1054 1050 for f in forget:
1055 1051 if ui.verbose or not m.exact(f):
1056 1052 ui.status(_('removing %s\n') % m.rel(f))
1057 1053
1058 1054 # Need to lock because standin files are deleted then removed from the
1059 1055 # repository and we could race in-between.
1060 1056 wlock = repo.wlock()
1061 1057 try:
1062 1058 lfdirstate = lfutil.openlfdirstate(ui, repo)
1063 1059 for f in forget:
1064 1060 if lfdirstate[f] == 'a':
1065 1061 lfdirstate.drop(f)
1066 1062 else:
1067 1063 lfdirstate.remove(f)
1068 1064 lfdirstate.write()
1069 1065 standins = [lfutil.standin(f) for f in forget]
1070 1066 for f in standins:
1071 1067 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1072 1068 rejected = repo[None].forget(standins)
1073 1069 finally:
1074 1070 wlock.release()
1075 1071
1076 1072 bad.extend(f for f in rejected if f in m.files())
1077 1073 forgot.extend(f for f in forget if f not in rejected)
1078 1074 return bad, forgot
1079 1075
1080 1076 def _getoutgoings(repo, other, missing, addfunc):
1081 1077 """get pairs of filename and largefile hash in outgoing revisions
1082 1078 in 'missing'.
1083 1079
1084 1080 largefiles already existing on 'other' repository are ignored.
1085 1081
1086 1082 'addfunc' is invoked with each unique pairs of filename and
1087 1083 largefile hash value.
1088 1084 """
1089 1085 knowns = set()
1090 1086 lfhashes = set()
1091 1087 def dedup(fn, lfhash):
1092 1088 k = (fn, lfhash)
1093 1089 if k not in knowns:
1094 1090 knowns.add(k)
1095 1091 lfhashes.add(lfhash)
1096 1092 lfutil.getlfilestoupload(repo, missing, dedup)
1097 1093 if lfhashes:
1098 1094 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1099 1095 for fn, lfhash in knowns:
1100 1096 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1101 1097 addfunc(fn, lfhash)
1102 1098
1103 1099 def outgoinghook(ui, repo, other, opts, missing):
1104 1100 if opts.pop('large', None):
1105 1101 lfhashes = set()
1106 1102 if ui.debugflag:
1107 1103 toupload = {}
1108 1104 def addfunc(fn, lfhash):
1109 1105 if fn not in toupload:
1110 1106 toupload[fn] = []
1111 1107 toupload[fn].append(lfhash)
1112 1108 lfhashes.add(lfhash)
1113 1109 def showhashes(fn):
1114 1110 for lfhash in sorted(toupload[fn]):
1115 1111 ui.debug(' %s\n' % (lfhash))
1116 1112 else:
1117 1113 toupload = set()
1118 1114 def addfunc(fn, lfhash):
1119 1115 toupload.add(fn)
1120 1116 lfhashes.add(lfhash)
1121 1117 def showhashes(fn):
1122 1118 pass
1123 1119 _getoutgoings(repo, other, missing, addfunc)
1124 1120
1125 1121 if not toupload:
1126 1122 ui.status(_('largefiles: no files to upload\n'))
1127 1123 else:
1128 1124 ui.status(_('largefiles to upload (%d entities):\n')
1129 1125 % (len(lfhashes)))
1130 1126 for file in sorted(toupload):
1131 1127 ui.status(lfutil.splitstandin(file) + '\n')
1132 1128 showhashes(file)
1133 1129 ui.status('\n')
1134 1130
1135 1131 def summaryremotehook(ui, repo, opts, changes):
1136 1132 largeopt = opts.get('large', False)
1137 1133 if changes is None:
1138 1134 if largeopt:
1139 1135 return (False, True) # only outgoing check is needed
1140 1136 else:
1141 1137 return (False, False)
1142 1138 elif largeopt:
1143 1139 url, branch, peer, outgoing = changes[1]
1144 1140 if peer is None:
1145 1141 # i18n: column positioning for "hg summary"
1146 1142 ui.status(_('largefiles: (no remote repo)\n'))
1147 1143 return
1148 1144
1149 1145 toupload = set()
1150 1146 lfhashes = set()
1151 1147 def addfunc(fn, lfhash):
1152 1148 toupload.add(fn)
1153 1149 lfhashes.add(lfhash)
1154 1150 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1155 1151
1156 1152 if not toupload:
1157 1153 # i18n: column positioning for "hg summary"
1158 1154 ui.status(_('largefiles: (no files to upload)\n'))
1159 1155 else:
1160 1156 # i18n: column positioning for "hg summary"
1161 1157 ui.status(_('largefiles: %d entities for %d files to upload\n')
1162 1158 % (len(lfhashes), len(toupload)))
1163 1159
1164 1160 def overridesummary(orig, ui, repo, *pats, **opts):
1165 1161 try:
1166 1162 repo.lfstatus = True
1167 1163 orig(ui, repo, *pats, **opts)
1168 1164 finally:
1169 1165 repo.lfstatus = False
1170 1166
1171 1167 def scmutiladdremove(orig, repo, matcher, prefix, opts=None, dry_run=None,
1172 1168 similarity=None):
1173 1169 if opts is None:
1174 1170 opts = {}
1175 1171 if not lfutil.islfilesrepo(repo):
1176 1172 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1177 1173 # Get the list of missing largefiles so we can remove them
1178 1174 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1179 1175 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1180 1176 False, False, False)
1181 1177
1182 1178 # Call into the normal remove code, but the removing of the standin, we want
1183 1179 # to have handled by original addremove. Monkey patching here makes sure
1184 1180 # we don't remove the standin in the largefiles code, preventing a very
1185 1181 # confused state later.
1186 1182 if s.deleted:
1187 1183 m = copy.copy(matcher)
1188 1184
1189 1185 # The m._files and m._map attributes are not changed to the deleted list
1190 1186 # because that affects the m.exact() test, which in turn governs whether
1191 1187 # or not the file name is printed, and how. Simply limit the original
1192 1188 # matches to those in the deleted status list.
1193 1189 matchfn = m.matchfn
1194 1190 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1195 1191
1196 1192 removelargefiles(repo.ui, repo, True, m, **opts)
1197 1193 # Call into the normal add code, and any files that *should* be added as
1198 1194 # largefiles will be
1199 1195 added, bad = addlargefiles(repo.ui, repo, True, matcher, **opts)
1200 1196 # Now that we've handled largefiles, hand off to the original addremove
1201 1197 # function to take care of the rest. Make sure it doesn't do anything with
1202 1198 # largefiles by passing a matcher that will ignore them.
1203 1199 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1204 1200 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1205 1201
1206 1202 # Calling purge with --all will cause the largefiles to be deleted.
1207 1203 # Override repo.status to prevent this from happening.
1208 1204 def overridepurge(orig, ui, repo, *dirs, **opts):
1209 1205 # XXX Monkey patching a repoview will not work. The assigned attribute will
1210 1206 # be set on the unfiltered repo, but we will only lookup attributes in the
1211 1207 # unfiltered repo if the lookup in the repoview object itself fails. As the
1212 1208 # monkey patched method exists on the repoview class the lookup will not
1213 1209 # fail. As a result, the original version will shadow the monkey patched
1214 1210 # one, defeating the monkey patch.
1215 1211 #
1216 1212 # As a work around we use an unfiltered repo here. We should do something
1217 1213 # cleaner instead.
1218 1214 repo = repo.unfiltered()
1219 1215 oldstatus = repo.status
1220 1216 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1221 1217 clean=False, unknown=False, listsubrepos=False):
1222 1218 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1223 1219 listsubrepos)
1224 1220 lfdirstate = lfutil.openlfdirstate(ui, repo)
1225 1221 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1226 1222 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1227 1223 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1228 1224 unknown, ignored, r.clean)
1229 1225 repo.status = overridestatus
1230 1226 orig(ui, repo, *dirs, **opts)
1231 1227 repo.status = oldstatus
1232 1228 def overriderollback(orig, ui, repo, **opts):
1233 1229 wlock = repo.wlock()
1234 1230 try:
1235 1231 before = repo.dirstate.parents()
1236 1232 orphans = set(f for f in repo.dirstate
1237 1233 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1238 1234 result = orig(ui, repo, **opts)
1239 1235 after = repo.dirstate.parents()
1240 1236 if before == after:
1241 1237 return result # no need to restore standins
1242 1238
1243 1239 pctx = repo['.']
1244 1240 for f in repo.dirstate:
1245 1241 if lfutil.isstandin(f):
1246 1242 orphans.discard(f)
1247 1243 if repo.dirstate[f] == 'r':
1248 1244 repo.wvfs.unlinkpath(f, ignoremissing=True)
1249 1245 elif f in pctx:
1250 1246 fctx = pctx[f]
1251 1247 repo.wwrite(f, fctx.data(), fctx.flags())
1252 1248 else:
1253 1249 # content of standin is not so important in 'a',
1254 1250 # 'm' or 'n' (coming from the 2nd parent) cases
1255 1251 lfutil.writestandin(repo, f, '', False)
1256 1252 for standin in orphans:
1257 1253 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1258 1254
1259 1255 lfdirstate = lfutil.openlfdirstate(ui, repo)
1260 1256 orphans = set(lfdirstate)
1261 1257 lfiles = lfutil.listlfiles(repo)
1262 1258 for file in lfiles:
1263 1259 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1264 1260 orphans.discard(file)
1265 1261 for lfile in orphans:
1266 1262 lfdirstate.drop(lfile)
1267 1263 lfdirstate.write()
1268 1264 finally:
1269 1265 wlock.release()
1270 1266 return result
1271 1267
1272 1268 def overridetransplant(orig, ui, repo, *revs, **opts):
1273 1269 resuming = opts.get('continue')
1274 1270 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1275 1271 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1276 1272 try:
1277 1273 result = orig(ui, repo, *revs, **opts)
1278 1274 finally:
1279 1275 repo._lfstatuswriters.pop()
1280 1276 repo._lfcommithooks.pop()
1281 1277 return result
1282 1278
1283 1279 def overridecat(orig, ui, repo, file1, *pats, **opts):
1284 1280 ctx = scmutil.revsingle(repo, opts.get('rev'))
1285 1281 err = 1
1286 1282 notbad = set()
1287 1283 m = scmutil.match(ctx, (file1,) + pats, opts)
1288 1284 origmatchfn = m.matchfn
1289 1285 def lfmatchfn(f):
1290 1286 if origmatchfn(f):
1291 1287 return True
1292 1288 lf = lfutil.splitstandin(f)
1293 1289 if lf is None:
1294 1290 return False
1295 1291 notbad.add(lf)
1296 1292 return origmatchfn(lf)
1297 1293 m.matchfn = lfmatchfn
1298 1294 origbadfn = m.bad
1299 1295 def lfbadfn(f, msg):
1300 1296 if not f in notbad:
1301 1297 origbadfn(f, msg)
1302 1298 m.bad = lfbadfn
1303 1299
1304 1300 origvisitdirfn = m.visitdir
1305 1301 def lfvisitdirfn(dir):
1306 1302 if dir == lfutil.shortname:
1307 1303 return True
1308 1304 ret = origvisitdirfn(dir)
1309 1305 if ret:
1310 1306 return ret
1311 1307 lf = lfutil.splitstandin(dir)
1312 1308 if lf is None:
1313 1309 return False
1314 1310 return origvisitdirfn(lf)
1315 1311 m.visitdir = lfvisitdirfn
1316 1312
1317 1313 for f in ctx.walk(m):
1318 1314 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1319 1315 pathname=f)
1320 1316 lf = lfutil.splitstandin(f)
1321 1317 if lf is None or origmatchfn(f):
1322 1318 # duplicating unreachable code from commands.cat
1323 1319 data = ctx[f].data()
1324 1320 if opts.get('decode'):
1325 1321 data = repo.wwritedata(f, data)
1326 1322 fp.write(data)
1327 1323 else:
1328 1324 hash = lfutil.readstandin(repo, lf, ctx.rev())
1329 1325 if not lfutil.inusercache(repo.ui, hash):
1330 1326 store = basestore._openstore(repo)
1331 1327 success, missing = store.get([(lf, hash)])
1332 1328 if len(success) != 1:
1333 1329 raise error.Abort(
1334 1330 _('largefile %s is not in cache and could not be '
1335 1331 'downloaded') % lf)
1336 1332 path = lfutil.usercachepath(repo.ui, hash)
1337 1333 fpin = open(path, "rb")
1338 1334 for chunk in util.filechunkiter(fpin, 128 * 1024):
1339 1335 fp.write(chunk)
1340 1336 fpin.close()
1341 1337 fp.close()
1342 1338 err = 0
1343 1339 return err
1344 1340
1345 1341 def mergeupdate(orig, repo, node, branchmerge, force,
1346 1342 *args, **kwargs):
1347 1343 matcher = kwargs.get('matcher', None)
1348 1344 # note if this is a partial update
1349 1345 partial = matcher and not matcher.always()
1350 1346 wlock = repo.wlock()
1351 1347 try:
1352 1348 # branch | | |
1353 1349 # merge | force | partial | action
1354 1350 # -------+-------+---------+--------------
1355 1351 # x | x | x | linear-merge
1356 1352 # o | x | x | branch-merge
1357 1353 # x | o | x | overwrite (as clean update)
1358 1354 # o | o | x | force-branch-merge (*1)
1359 1355 # x | x | o | (*)
1360 1356 # o | x | o | (*)
1361 1357 # x | o | o | overwrite (as revert)
1362 1358 # o | o | o | (*)
1363 1359 #
1364 1360 # (*) don't care
1365 1361 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1366 1362
1367 1363 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1368 1364 unsure, s = lfdirstate.status(match_.always(repo.root,
1369 1365 repo.getcwd()),
1370 1366 [], False, False, False)
1371 1367 pctx = repo['.']
1372 1368 for lfile in unsure + s.modified:
1373 1369 lfileabs = repo.wvfs.join(lfile)
1374 1370 if not os.path.exists(lfileabs):
1375 1371 continue
1376 1372 lfhash = lfutil.hashrepofile(repo, lfile)
1377 1373 standin = lfutil.standin(lfile)
1378 1374 lfutil.writestandin(repo, standin, lfhash,
1379 1375 lfutil.getexecutable(lfileabs))
1380 1376 if (standin in pctx and
1381 1377 lfhash == lfutil.readstandin(repo, lfile, '.')):
1382 1378 lfdirstate.normal(lfile)
1383 1379 for lfile in s.added:
1384 1380 lfutil.updatestandin(repo, lfutil.standin(lfile))
1385 1381 lfdirstate.write()
1386 1382
1387 1383 oldstandins = lfutil.getstandinsstate(repo)
1388 1384
1389 1385 result = orig(repo, node, branchmerge, force, *args, **kwargs)
1390 1386
1391 1387 newstandins = lfutil.getstandinsstate(repo)
1392 1388 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1393 1389 if branchmerge or force or partial:
1394 1390 filelist.extend(s.deleted + s.removed)
1395 1391
1396 1392 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1397 1393 normallookup=partial)
1398 1394
1399 1395 return result
1400 1396 finally:
1401 1397 wlock.release()
1402 1398
1403 1399 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1404 1400 result = orig(repo, files, *args, **kwargs)
1405 1401
1406 1402 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1407 1403 if filelist:
1408 1404 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1409 1405 printmessage=False, normallookup=True)
1410 1406
1411 1407 return result
General Comments 0
You need to be logged in to leave comments. Login now