##// END OF EJS Templates
largefile: use `set_untracked` in the override of copy/rename...
marmoute -
r48455:0ab58b1f default
parent child Browse files
Show More
@@ -1,1860 +1,1860 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 from __future__ import absolute_import
11 11
12 12 import copy
13 13 import os
14 14
15 15 from mercurial.i18n import _
16 16
17 17 from mercurial.pycompat import open
18 18
19 19 from mercurial.hgweb import webcommands
20 20
21 21 from mercurial import (
22 22 archival,
23 23 cmdutil,
24 24 copies as copiesmod,
25 25 error,
26 26 exchange,
27 27 extensions,
28 28 exthelper,
29 29 filemerge,
30 30 hg,
31 31 logcmdutil,
32 32 match as matchmod,
33 33 merge,
34 34 mergestate as mergestatemod,
35 35 pathutil,
36 36 pycompat,
37 37 scmutil,
38 38 smartset,
39 39 subrepo,
40 40 url as urlmod,
41 41 util,
42 42 )
43 43
44 44 from mercurial.upgrade_utils import (
45 45 actions as upgrade_actions,
46 46 )
47 47
48 48 from . import (
49 49 lfcommands,
50 50 lfutil,
51 51 storefactory,
52 52 )
53 53
54 54 eh = exthelper.exthelper()
55 55
56 56 lfstatus = lfutil.lfstatus
57 57
58 58 MERGE_ACTION_LARGEFILE_MARK_REMOVED = b'lfmr'
59 59
60 60 # -- Utility functions: commonly/repeatedly needed functionality ---------------
61 61
62 62
63 63 def composelargefilematcher(match, manifest):
64 64 """create a matcher that matches only the largefiles in the original
65 65 matcher"""
66 66 m = copy.copy(match)
67 67 lfile = lambda f: lfutil.standin(f) in manifest
68 68 m._files = [lf for lf in m._files if lfile(lf)]
69 69 m._fileset = set(m._files)
70 70 m.always = lambda: False
71 71 origmatchfn = m.matchfn
72 72 m.matchfn = lambda f: lfile(f) and origmatchfn(f)
73 73 return m
74 74
75 75
76 76 def composenormalfilematcher(match, manifest, exclude=None):
77 77 excluded = set()
78 78 if exclude is not None:
79 79 excluded.update(exclude)
80 80
81 81 m = copy.copy(match)
82 82 notlfile = lambda f: not (
83 83 lfutil.isstandin(f) or lfutil.standin(f) in manifest or f in excluded
84 84 )
85 85 m._files = [lf for lf in m._files if notlfile(lf)]
86 86 m._fileset = set(m._files)
87 87 m.always = lambda: False
88 88 origmatchfn = m.matchfn
89 89 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
90 90 return m
91 91
92 92
93 93 def addlargefiles(ui, repo, isaddremove, matcher, uipathfn, **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
99 99 lfmatcher = None
100 100 if lfutil.islfilesrepo(repo):
101 101 lfpats = ui.configlist(lfutil.longname, b'patterns')
102 102 if lfpats:
103 103 lfmatcher = matchmod.match(repo.root, b'', list(lfpats))
104 104
105 105 lfnames = []
106 106 m = matcher
107 107
108 108 wctx = repo[None]
109 109 for f in wctx.walk(matchmod.badmatch(m, lambda x, y: None)):
110 110 exact = m.exact(f)
111 111 lfile = lfutil.standin(f) in wctx
112 112 nfile = f in wctx
113 113 exists = lfile or nfile
114 114
115 115 # Don't warn the user when they attempt to add a normal tracked file.
116 116 # The normal add code will do that for us.
117 117 if exact and exists:
118 118 if lfile:
119 119 ui.warn(_(b'%s already a largefile\n') % uipathfn(f))
120 120 continue
121 121
122 122 if (exact or not exists) and not lfutil.isstandin(f):
123 123 # In case the file was removed previously, but not committed
124 124 # (issue3507)
125 125 if not repo.wvfs.exists(f):
126 126 continue
127 127
128 128 abovemin = (
129 129 lfsize and repo.wvfs.lstat(f).st_size >= lfsize * 1024 * 1024
130 130 )
131 131 if large or abovemin or (lfmatcher and lfmatcher(f)):
132 132 lfnames.append(f)
133 133 if ui.verbose or not exact:
134 134 ui.status(_(b'adding %s as a largefile\n') % uipathfn(f))
135 135
136 136 bad = []
137 137
138 138 # Need to lock, otherwise there could be a race condition between
139 139 # when standins are created and added to the repo.
140 140 with repo.wlock():
141 141 if not opts.get('dry_run'):
142 142 standins = []
143 143 lfdirstate = lfutil.openlfdirstate(ui, repo)
144 144 for f in lfnames:
145 145 standinname = lfutil.standin(f)
146 146 lfutil.writestandin(
147 147 repo,
148 148 standinname,
149 149 hash=b'',
150 150 executable=lfutil.getexecutable(repo.wjoin(f)),
151 151 )
152 152 standins.append(standinname)
153 153 lfdirstate.set_tracked(f)
154 154 lfdirstate.write()
155 155 bad += [
156 156 lfutil.splitstandin(f)
157 157 for f in repo[None].add(standins)
158 158 if f in m.files()
159 159 ]
160 160
161 161 added = [f for f in lfnames if f not in bad]
162 162 return added, bad
163 163
164 164
165 165 def removelargefiles(ui, repo, isaddremove, matcher, uipathfn, dryrun, **opts):
166 166 after = opts.get('after')
167 167 m = composelargefilematcher(matcher, repo[None].manifest())
168 168 with lfstatus(repo):
169 169 s = repo.status(match=m, clean=not isaddremove)
170 170 manifest = repo[None].manifest()
171 171 modified, added, deleted, clean = [
172 172 [f for f in list if lfutil.standin(f) in manifest]
173 173 for list in (s.modified, s.added, s.deleted, s.clean)
174 174 ]
175 175
176 176 def warn(files, msg):
177 177 for f in files:
178 178 ui.warn(msg % uipathfn(f))
179 179 return int(len(files) > 0)
180 180
181 181 if after:
182 182 remove = deleted
183 183 result = warn(
184 184 modified + added + clean, _(b'not removing %s: file still exists\n')
185 185 )
186 186 else:
187 187 remove = deleted + clean
188 188 result = warn(
189 189 modified,
190 190 _(
191 191 b'not removing %s: file is modified (use -f'
192 192 b' to force removal)\n'
193 193 ),
194 194 )
195 195 result = (
196 196 warn(
197 197 added,
198 198 _(
199 199 b'not removing %s: file has been marked for add'
200 200 b' (use forget to undo)\n'
201 201 ),
202 202 )
203 203 or result
204 204 )
205 205
206 206 # Need to lock because standin files are deleted then removed from the
207 207 # repository and we could race in-between.
208 208 with repo.wlock():
209 209 lfdirstate = lfutil.openlfdirstate(ui, repo)
210 210 for f in sorted(remove):
211 211 if ui.verbose or not m.exact(f):
212 212 ui.status(_(b'removing %s\n') % uipathfn(f))
213 213
214 214 if not dryrun:
215 215 if not after:
216 216 repo.wvfs.unlinkpath(f, ignoremissing=True)
217 217
218 218 if dryrun:
219 219 return result
220 220
221 221 remove = [lfutil.standin(f) for f in remove]
222 222 # If this is being called by addremove, let the original addremove
223 223 # function handle this.
224 224 if not isaddremove:
225 225 for f in remove:
226 226 repo.wvfs.unlinkpath(f, ignoremissing=True)
227 227 repo[None].forget(remove)
228 228
229 229 for f in remove:
230 230 lfdirstate.set_untracked(lfutil.splitstandin(f))
231 231
232 232 lfdirstate.write()
233 233
234 234 return result
235 235
236 236
237 237 # For overriding mercurial.hgweb.webcommands so that largefiles will
238 238 # appear at their right place in the manifests.
239 239 @eh.wrapfunction(webcommands, b'decodepath')
240 240 def decodepath(orig, path):
241 241 return lfutil.splitstandin(path) or path
242 242
243 243
244 244 # -- Wrappers: modify existing commands --------------------------------
245 245
246 246
247 247 @eh.wrapcommand(
248 248 b'add',
249 249 opts=[
250 250 (b'', b'large', None, _(b'add as largefile')),
251 251 (b'', b'normal', None, _(b'add as normal file')),
252 252 (
253 253 b'',
254 254 b'lfsize',
255 255 b'',
256 256 _(
257 257 b'add all files above this size (in megabytes) '
258 258 b'as largefiles (default: 10)'
259 259 ),
260 260 ),
261 261 ],
262 262 )
263 263 def overrideadd(orig, ui, repo, *pats, **opts):
264 264 if opts.get('normal') and opts.get('large'):
265 265 raise error.Abort(_(b'--normal cannot be used with --large'))
266 266 return orig(ui, repo, *pats, **opts)
267 267
268 268
269 269 @eh.wrapfunction(cmdutil, b'add')
270 270 def cmdutiladd(orig, ui, repo, matcher, prefix, uipathfn, explicitonly, **opts):
271 271 # The --normal flag short circuits this override
272 272 if opts.get('normal'):
273 273 return orig(ui, repo, matcher, prefix, uipathfn, explicitonly, **opts)
274 274
275 275 ladded, lbad = addlargefiles(ui, repo, False, matcher, uipathfn, **opts)
276 276 normalmatcher = composenormalfilematcher(
277 277 matcher, repo[None].manifest(), ladded
278 278 )
279 279 bad = orig(ui, repo, normalmatcher, prefix, uipathfn, explicitonly, **opts)
280 280
281 281 bad.extend(f for f in lbad)
282 282 return bad
283 283
284 284
285 285 @eh.wrapfunction(cmdutil, b'remove')
286 286 def cmdutilremove(
287 287 orig, ui, repo, matcher, prefix, uipathfn, after, force, subrepos, dryrun
288 288 ):
289 289 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest())
290 290 result = orig(
291 291 ui,
292 292 repo,
293 293 normalmatcher,
294 294 prefix,
295 295 uipathfn,
296 296 after,
297 297 force,
298 298 subrepos,
299 299 dryrun,
300 300 )
301 301 return (
302 302 removelargefiles(
303 303 ui, repo, False, matcher, uipathfn, dryrun, after=after, force=force
304 304 )
305 305 or result
306 306 )
307 307
308 308
309 309 @eh.wrapfunction(subrepo.hgsubrepo, b'status')
310 310 def overridestatusfn(orig, repo, rev2, **opts):
311 311 with lfstatus(repo._repo):
312 312 return orig(repo, rev2, **opts)
313 313
314 314
315 315 @eh.wrapcommand(b'status')
316 316 def overridestatus(orig, ui, repo, *pats, **opts):
317 317 with lfstatus(repo):
318 318 return orig(ui, repo, *pats, **opts)
319 319
320 320
321 321 @eh.wrapfunction(subrepo.hgsubrepo, b'dirty')
322 322 def overridedirty(orig, repo, ignoreupdate=False, missing=False):
323 323 with lfstatus(repo._repo):
324 324 return orig(repo, ignoreupdate=ignoreupdate, missing=missing)
325 325
326 326
327 327 @eh.wrapcommand(b'log')
328 328 def overridelog(orig, ui, repo, *pats, **opts):
329 329 def overridematchandpats(
330 330 orig,
331 331 ctx,
332 332 pats=(),
333 333 opts=None,
334 334 globbed=False,
335 335 default=b'relpath',
336 336 badfn=None,
337 337 ):
338 338 """Matcher that merges root directory with .hglf, suitable for log.
339 339 It is still possible to match .hglf directly.
340 340 For any listed files run log on the standin too.
341 341 matchfn tries both the given filename and with .hglf stripped.
342 342 """
343 343 if opts is None:
344 344 opts = {}
345 345 matchandpats = orig(ctx, pats, opts, globbed, default, badfn=badfn)
346 346 m, p = copy.copy(matchandpats)
347 347
348 348 if m.always():
349 349 # We want to match everything anyway, so there's no benefit trying
350 350 # to add standins.
351 351 return matchandpats
352 352
353 353 pats = set(p)
354 354
355 355 def fixpats(pat, tostandin=lfutil.standin):
356 356 if pat.startswith(b'set:'):
357 357 return pat
358 358
359 359 kindpat = matchmod._patsplit(pat, None)
360 360
361 361 if kindpat[0] is not None:
362 362 return kindpat[0] + b':' + tostandin(kindpat[1])
363 363 return tostandin(kindpat[1])
364 364
365 365 cwd = repo.getcwd()
366 366 if cwd:
367 367 hglf = lfutil.shortname
368 368 back = util.pconvert(repo.pathto(hglf)[: -len(hglf)])
369 369
370 370 def tostandin(f):
371 371 # The file may already be a standin, so truncate the back
372 372 # prefix and test before mangling it. This avoids turning
373 373 # 'glob:../.hglf/foo*' into 'glob:../.hglf/../.hglf/foo*'.
374 374 if f.startswith(back) and lfutil.splitstandin(f[len(back) :]):
375 375 return f
376 376
377 377 # An absolute path is from outside the repo, so truncate the
378 378 # path to the root before building the standin. Otherwise cwd
379 379 # is somewhere in the repo, relative to root, and needs to be
380 380 # prepended before building the standin.
381 381 if os.path.isabs(cwd):
382 382 f = f[len(back) :]
383 383 else:
384 384 f = cwd + b'/' + f
385 385 return back + lfutil.standin(f)
386 386
387 387 else:
388 388
389 389 def tostandin(f):
390 390 if lfutil.isstandin(f):
391 391 return f
392 392 return lfutil.standin(f)
393 393
394 394 pats.update(fixpats(f, tostandin) for f in p)
395 395
396 396 for i in range(0, len(m._files)):
397 397 # Don't add '.hglf' to m.files, since that is already covered by '.'
398 398 if m._files[i] == b'.':
399 399 continue
400 400 standin = lfutil.standin(m._files[i])
401 401 # If the "standin" is a directory, append instead of replace to
402 402 # support naming a directory on the command line with only
403 403 # largefiles. The original directory is kept to support normal
404 404 # files.
405 405 if standin in ctx:
406 406 m._files[i] = standin
407 407 elif m._files[i] not in ctx and repo.wvfs.isdir(standin):
408 408 m._files.append(standin)
409 409
410 410 m._fileset = set(m._files)
411 411 m.always = lambda: False
412 412 origmatchfn = m.matchfn
413 413
414 414 def lfmatchfn(f):
415 415 lf = lfutil.splitstandin(f)
416 416 if lf is not None and origmatchfn(lf):
417 417 return True
418 418 r = origmatchfn(f)
419 419 return r
420 420
421 421 m.matchfn = lfmatchfn
422 422
423 423 ui.debug(b'updated patterns: %s\n' % b', '.join(sorted(pats)))
424 424 return m, pats
425 425
426 426 # For hg log --patch, the match object is used in two different senses:
427 427 # (1) to determine what revisions should be printed out, and
428 428 # (2) to determine what files to print out diffs for.
429 429 # The magic matchandpats override should be used for case (1) but not for
430 430 # case (2).
431 431 oldmatchandpats = scmutil.matchandpats
432 432
433 433 def overridemakefilematcher(orig, repo, pats, opts, badfn=None):
434 434 wctx = repo[None]
435 435 match, pats = oldmatchandpats(wctx, pats, opts, badfn=badfn)
436 436 return lambda ctx: match
437 437
438 438 wrappedmatchandpats = extensions.wrappedfunction(
439 439 scmutil, b'matchandpats', overridematchandpats
440 440 )
441 441 wrappedmakefilematcher = extensions.wrappedfunction(
442 442 logcmdutil, b'_makenofollowfilematcher', overridemakefilematcher
443 443 )
444 444 with wrappedmatchandpats, wrappedmakefilematcher:
445 445 return orig(ui, repo, *pats, **opts)
446 446
447 447
448 448 @eh.wrapcommand(
449 449 b'verify',
450 450 opts=[
451 451 (
452 452 b'',
453 453 b'large',
454 454 None,
455 455 _(b'verify that all largefiles in current revision exists'),
456 456 ),
457 457 (
458 458 b'',
459 459 b'lfa',
460 460 None,
461 461 _(b'verify largefiles in all revisions, not just current'),
462 462 ),
463 463 (
464 464 b'',
465 465 b'lfc',
466 466 None,
467 467 _(b'verify local largefile contents, not just existence'),
468 468 ),
469 469 ],
470 470 )
471 471 def overrideverify(orig, ui, repo, *pats, **opts):
472 472 large = opts.pop('large', False)
473 473 all = opts.pop('lfa', False)
474 474 contents = opts.pop('lfc', False)
475 475
476 476 result = orig(ui, repo, *pats, **opts)
477 477 if large or all or contents:
478 478 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
479 479 return result
480 480
481 481
482 482 @eh.wrapcommand(
483 483 b'debugstate',
484 484 opts=[(b'', b'large', None, _(b'display largefiles dirstate'))],
485 485 )
486 486 def overridedebugstate(orig, ui, repo, *pats, **opts):
487 487 large = opts.pop('large', False)
488 488 if large:
489 489
490 490 class fakerepo(object):
491 491 dirstate = lfutil.openlfdirstate(ui, repo)
492 492
493 493 orig(ui, fakerepo, *pats, **opts)
494 494 else:
495 495 orig(ui, repo, *pats, **opts)
496 496
497 497
498 498 # Before starting the manifest merge, merge.updates will call
499 499 # _checkunknownfile to check if there are any files in the merged-in
500 500 # changeset that collide with unknown files in the working copy.
501 501 #
502 502 # The largefiles are seen as unknown, so this prevents us from merging
503 503 # in a file 'foo' if we already have a largefile with the same name.
504 504 #
505 505 # The overridden function filters the unknown files by removing any
506 506 # largefiles. This makes the merge proceed and we can then handle this
507 507 # case further in the overridden calculateupdates function below.
508 508 @eh.wrapfunction(merge, b'_checkunknownfile')
509 509 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
510 510 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
511 511 return False
512 512 return origfn(repo, wctx, mctx, f, f2)
513 513
514 514
515 515 # The manifest merge handles conflicts on the manifest level. We want
516 516 # to handle changes in largefile-ness of files at this level too.
517 517 #
518 518 # The strategy is to run the original calculateupdates and then process
519 519 # the action list it outputs. There are two cases we need to deal with:
520 520 #
521 521 # 1. Normal file in p1, largefile in p2. Here the largefile is
522 522 # detected via its standin file, which will enter the working copy
523 523 # with a "get" action. It is not "merge" since the standin is all
524 524 # Mercurial is concerned with at this level -- the link to the
525 525 # existing normal file is not relevant here.
526 526 #
527 527 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
528 528 # since the largefile will be present in the working copy and
529 529 # different from the normal file in p2. Mercurial therefore
530 530 # triggers a merge action.
531 531 #
532 532 # In both cases, we prompt the user and emit new actions to either
533 533 # remove the standin (if the normal file was kept) or to remove the
534 534 # normal file and get the standin (if the largefile was kept). The
535 535 # default prompt answer is to use the largefile version since it was
536 536 # presumably changed on purpose.
537 537 #
538 538 # Finally, the merge.applyupdates function will then take care of
539 539 # writing the files into the working copy and lfcommands.updatelfiles
540 540 # will update the largefiles.
541 541 @eh.wrapfunction(merge, b'calculateupdates')
542 542 def overridecalculateupdates(
543 543 origfn, repo, p1, p2, pas, branchmerge, force, acceptremote, *args, **kwargs
544 544 ):
545 545 overwrite = force and not branchmerge
546 546 mresult = origfn(
547 547 repo, p1, p2, pas, branchmerge, force, acceptremote, *args, **kwargs
548 548 )
549 549
550 550 if overwrite:
551 551 return mresult
552 552
553 553 # Convert to dictionary with filename as key and action as value.
554 554 lfiles = set()
555 555 for f in mresult.files():
556 556 splitstandin = lfutil.splitstandin(f)
557 557 if splitstandin is not None and splitstandin in p1:
558 558 lfiles.add(splitstandin)
559 559 elif lfutil.standin(f) in p1:
560 560 lfiles.add(f)
561 561
562 562 for lfile in sorted(lfiles):
563 563 standin = lfutil.standin(lfile)
564 564 (lm, largs, lmsg) = mresult.getfile(lfile, (None, None, None))
565 565 (sm, sargs, smsg) = mresult.getfile(standin, (None, None, None))
566 566 if sm in (b'g', b'dc') and lm != b'r':
567 567 if sm == b'dc':
568 568 f1, f2, fa, move, anc = sargs
569 569 sargs = (p2[f2].flags(), False)
570 570 # Case 1: normal file in the working copy, largefile in
571 571 # the second parent
572 572 usermsg = (
573 573 _(
574 574 b'remote turned local normal file %s into a largefile\n'
575 575 b'use (l)argefile or keep (n)ormal file?'
576 576 b'$$ &Largefile $$ &Normal file'
577 577 )
578 578 % lfile
579 579 )
580 580 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
581 581 mresult.addfile(lfile, b'r', None, b'replaced by standin')
582 582 mresult.addfile(standin, b'g', sargs, b'replaces standin')
583 583 else: # keep local normal file
584 584 mresult.addfile(lfile, b'k', None, b'replaces standin')
585 585 if branchmerge:
586 586 mresult.addfile(
587 587 standin,
588 588 b'k',
589 589 None,
590 590 b'replaced by non-standin',
591 591 )
592 592 else:
593 593 mresult.addfile(
594 594 standin,
595 595 b'r',
596 596 None,
597 597 b'replaced by non-standin',
598 598 )
599 599 elif lm in (b'g', b'dc') and sm != b'r':
600 600 if lm == b'dc':
601 601 f1, f2, fa, move, anc = largs
602 602 largs = (p2[f2].flags(), False)
603 603 # Case 2: largefile in the working copy, normal file in
604 604 # the second parent
605 605 usermsg = (
606 606 _(
607 607 b'remote turned local largefile %s into a normal file\n'
608 608 b'keep (l)argefile or use (n)ormal file?'
609 609 b'$$ &Largefile $$ &Normal file'
610 610 )
611 611 % lfile
612 612 )
613 613 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
614 614 if branchmerge:
615 615 # largefile can be restored from standin safely
616 616 mresult.addfile(
617 617 lfile,
618 618 b'k',
619 619 None,
620 620 b'replaced by standin',
621 621 )
622 622 mresult.addfile(standin, b'k', None, b'replaces standin')
623 623 else:
624 624 # "lfile" should be marked as "removed" without
625 625 # removal of itself
626 626 mresult.addfile(
627 627 lfile,
628 628 MERGE_ACTION_LARGEFILE_MARK_REMOVED,
629 629 None,
630 630 b'forget non-standin largefile',
631 631 )
632 632
633 633 # linear-merge should treat this largefile as 're-added'
634 634 mresult.addfile(standin, b'a', None, b'keep standin')
635 635 else: # pick remote normal file
636 636 mresult.addfile(lfile, b'g', largs, b'replaces standin')
637 637 mresult.addfile(
638 638 standin,
639 639 b'r',
640 640 None,
641 641 b'replaced by non-standin',
642 642 )
643 643
644 644 return mresult
645 645
646 646
647 647 @eh.wrapfunction(mergestatemod, b'recordupdates')
648 648 def mergerecordupdates(orig, repo, actions, branchmerge, getfiledata):
649 649 if MERGE_ACTION_LARGEFILE_MARK_REMOVED in actions:
650 650 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
651 651 with lfdirstate.parentchange():
652 652 for lfile, args, msg in actions[
653 653 MERGE_ACTION_LARGEFILE_MARK_REMOVED
654 654 ]:
655 655 # this should be executed before 'orig', to execute 'remove'
656 656 # before all other actions
657 657 repo.dirstate.remove(lfile)
658 658 # make sure lfile doesn't get synclfdirstate'd as normal
659 659 lfdirstate.add(lfile)
660 660 lfdirstate.write()
661 661
662 662 return orig(repo, actions, branchmerge, getfiledata)
663 663
664 664
665 665 # Override filemerge to prompt the user about how they wish to merge
666 666 # largefiles. This will handle identical edits without prompting the user.
667 667 @eh.wrapfunction(filemerge, b'_filemerge')
668 668 def overridefilemerge(
669 669 origfn, premerge, repo, wctx, mynode, orig, fcd, fco, fca, labels=None
670 670 ):
671 671 if not lfutil.isstandin(orig) or fcd.isabsent() or fco.isabsent():
672 672 return origfn(
673 673 premerge, repo, wctx, mynode, orig, fcd, fco, fca, labels=labels
674 674 )
675 675
676 676 ahash = lfutil.readasstandin(fca).lower()
677 677 dhash = lfutil.readasstandin(fcd).lower()
678 678 ohash = lfutil.readasstandin(fco).lower()
679 679 if (
680 680 ohash != ahash
681 681 and ohash != dhash
682 682 and (
683 683 dhash == ahash
684 684 or repo.ui.promptchoice(
685 685 _(
686 686 b'largefile %s has a merge conflict\nancestor was %s\n'
687 687 b'you can keep (l)ocal %s or take (o)ther %s.\n'
688 688 b'what do you want to do?'
689 689 b'$$ &Local $$ &Other'
690 690 )
691 691 % (lfutil.splitstandin(orig), ahash, dhash, ohash),
692 692 0,
693 693 )
694 694 == 1
695 695 )
696 696 ):
697 697 repo.wwrite(fcd.path(), fco.data(), fco.flags())
698 698 return True, 0, False
699 699
700 700
701 701 @eh.wrapfunction(copiesmod, b'pathcopies')
702 702 def copiespathcopies(orig, ctx1, ctx2, match=None):
703 703 copies = orig(ctx1, ctx2, match=match)
704 704 updated = {}
705 705
706 706 for k, v in pycompat.iteritems(copies):
707 707 updated[lfutil.splitstandin(k) or k] = lfutil.splitstandin(v) or v
708 708
709 709 return updated
710 710
711 711
712 712 # Copy first changes the matchers to match standins instead of
713 713 # largefiles. Then it overrides util.copyfile in that function it
714 714 # checks if the destination largefile already exists. It also keeps a
715 715 # list of copied files so that the largefiles can be copied and the
716 716 # dirstate updated.
717 717 @eh.wrapfunction(cmdutil, b'copy')
718 718 def overridecopy(orig, ui, repo, pats, opts, rename=False):
719 719 # doesn't remove largefile on rename
720 720 if len(pats) < 2:
721 721 # this isn't legal, let the original function deal with it
722 722 return orig(ui, repo, pats, opts, rename)
723 723
724 724 # This could copy both lfiles and normal files in one command,
725 725 # but we don't want to do that. First replace their matcher to
726 726 # only match normal files and run it, then replace it to just
727 727 # match largefiles and run it again.
728 728 nonormalfiles = False
729 729 nolfiles = False
730 730 manifest = repo[None].manifest()
731 731
732 732 def normalfilesmatchfn(
733 733 orig,
734 734 ctx,
735 735 pats=(),
736 736 opts=None,
737 737 globbed=False,
738 738 default=b'relpath',
739 739 badfn=None,
740 740 ):
741 741 if opts is None:
742 742 opts = {}
743 743 match = orig(ctx, pats, opts, globbed, default, badfn=badfn)
744 744 return composenormalfilematcher(match, manifest)
745 745
746 746 with extensions.wrappedfunction(scmutil, b'match', normalfilesmatchfn):
747 747 try:
748 748 result = orig(ui, repo, pats, opts, rename)
749 749 except error.Abort as e:
750 750 if e.message != _(b'no files to copy'):
751 751 raise e
752 752 else:
753 753 nonormalfiles = True
754 754 result = 0
755 755
756 756 # The first rename can cause our current working directory to be removed.
757 757 # In that case there is nothing left to copy/rename so just quit.
758 758 try:
759 759 repo.getcwd()
760 760 except OSError:
761 761 return result
762 762
763 763 def makestandin(relpath):
764 764 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
765 765 return repo.wvfs.join(lfutil.standin(path))
766 766
767 767 fullpats = scmutil.expandpats(pats)
768 768 dest = fullpats[-1]
769 769
770 770 if os.path.isdir(dest):
771 771 if not os.path.isdir(makestandin(dest)):
772 772 os.makedirs(makestandin(dest))
773 773
774 774 try:
775 775 # When we call orig below it creates the standins but we don't add
776 776 # them to the dir state until later so lock during that time.
777 777 wlock = repo.wlock()
778 778
779 779 manifest = repo[None].manifest()
780 780
781 781 def overridematch(
782 782 orig,
783 783 ctx,
784 784 pats=(),
785 785 opts=None,
786 786 globbed=False,
787 787 default=b'relpath',
788 788 badfn=None,
789 789 ):
790 790 if opts is None:
791 791 opts = {}
792 792 newpats = []
793 793 # The patterns were previously mangled to add the standin
794 794 # directory; we need to remove that now
795 795 for pat in pats:
796 796 if matchmod.patkind(pat) is None and lfutil.shortname in pat:
797 797 newpats.append(pat.replace(lfutil.shortname, b''))
798 798 else:
799 799 newpats.append(pat)
800 800 match = orig(ctx, newpats, opts, globbed, default, badfn=badfn)
801 801 m = copy.copy(match)
802 802 lfile = lambda f: lfutil.standin(f) in manifest
803 803 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
804 804 m._fileset = set(m._files)
805 805 origmatchfn = m.matchfn
806 806
807 807 def matchfn(f):
808 808 lfile = lfutil.splitstandin(f)
809 809 return (
810 810 lfile is not None
811 811 and (f in manifest)
812 812 and origmatchfn(lfile)
813 813 or None
814 814 )
815 815
816 816 m.matchfn = matchfn
817 817 return m
818 818
819 819 listpats = []
820 820 for pat in pats:
821 821 if matchmod.patkind(pat) is not None:
822 822 listpats.append(pat)
823 823 else:
824 824 listpats.append(makestandin(pat))
825 825
826 826 copiedfiles = []
827 827
828 828 def overridecopyfile(orig, src, dest, *args, **kwargs):
829 829 if lfutil.shortname in src and dest.startswith(
830 830 repo.wjoin(lfutil.shortname)
831 831 ):
832 832 destlfile = dest.replace(lfutil.shortname, b'')
833 833 if not opts[b'force'] and os.path.exists(destlfile):
834 834 raise IOError(
835 835 b'', _(b'destination largefile already exists')
836 836 )
837 837 copiedfiles.append((src, dest))
838 838 orig(src, dest, *args, **kwargs)
839 839
840 840 with extensions.wrappedfunction(util, b'copyfile', overridecopyfile):
841 841 with extensions.wrappedfunction(scmutil, b'match', overridematch):
842 842 result += orig(ui, repo, listpats, opts, rename)
843 843
844 844 lfdirstate = lfutil.openlfdirstate(ui, repo)
845 845 for (src, dest) in copiedfiles:
846 846 if lfutil.shortname in src and dest.startswith(
847 847 repo.wjoin(lfutil.shortname)
848 848 ):
849 849 srclfile = src.replace(repo.wjoin(lfutil.standin(b'')), b'')
850 850 destlfile = dest.replace(repo.wjoin(lfutil.standin(b'')), b'')
851 851 destlfiledir = repo.wvfs.dirname(repo.wjoin(destlfile)) or b'.'
852 852 if not os.path.isdir(destlfiledir):
853 853 os.makedirs(destlfiledir)
854 854 if rename:
855 855 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
856 856
857 857 # The file is gone, but this deletes any empty parent
858 858 # directories as a side-effect.
859 859 repo.wvfs.unlinkpath(srclfile, ignoremissing=True)
860 lfdirstate.remove(srclfile)
860 lfdirstate.set_untracked(srclfile)
861 861 else:
862 862 util.copyfile(repo.wjoin(srclfile), repo.wjoin(destlfile))
863 863
864 864 lfdirstate.set_tracked(destlfile)
865 865 lfdirstate.write()
866 866 except error.Abort as e:
867 867 if e.message != _(b'no files to copy'):
868 868 raise e
869 869 else:
870 870 nolfiles = True
871 871 finally:
872 872 wlock.release()
873 873
874 874 if nolfiles and nonormalfiles:
875 875 raise error.Abort(_(b'no files to copy'))
876 876
877 877 return result
878 878
879 879
880 880 # When the user calls revert, we have to be careful to not revert any
881 881 # changes to other largefiles accidentally. This means we have to keep
882 882 # track of the largefiles that are being reverted so we only pull down
883 883 # the necessary largefiles.
884 884 #
885 885 # Standins are only updated (to match the hash of largefiles) before
886 886 # commits. Update the standins then run the original revert, changing
887 887 # the matcher to hit standins instead of largefiles. Based on the
888 888 # resulting standins update the largefiles.
889 889 @eh.wrapfunction(cmdutil, b'revert')
890 890 def overriderevert(orig, ui, repo, ctx, *pats, **opts):
891 891 # Because we put the standins in a bad state (by updating them)
892 892 # and then return them to a correct state we need to lock to
893 893 # prevent others from changing them in their incorrect state.
894 894 with repo.wlock():
895 895 lfdirstate = lfutil.openlfdirstate(ui, repo)
896 896 s = lfutil.lfdirstatestatus(lfdirstate, repo)
897 897 lfdirstate.write()
898 898 for lfile in s.modified:
899 899 lfutil.updatestandin(repo, lfile, lfutil.standin(lfile))
900 900 for lfile in s.deleted:
901 901 fstandin = lfutil.standin(lfile)
902 902 if repo.wvfs.exists(fstandin):
903 903 repo.wvfs.unlink(fstandin)
904 904
905 905 oldstandins = lfutil.getstandinsstate(repo)
906 906
907 907 def overridematch(
908 908 orig,
909 909 mctx,
910 910 pats=(),
911 911 opts=None,
912 912 globbed=False,
913 913 default=b'relpath',
914 914 badfn=None,
915 915 ):
916 916 if opts is None:
917 917 opts = {}
918 918 match = orig(mctx, pats, opts, globbed, default, badfn=badfn)
919 919 m = copy.copy(match)
920 920
921 921 # revert supports recursing into subrepos, and though largefiles
922 922 # currently doesn't work correctly in that case, this match is
923 923 # called, so the lfdirstate above may not be the correct one for
924 924 # this invocation of match.
925 925 lfdirstate = lfutil.openlfdirstate(
926 926 mctx.repo().ui, mctx.repo(), False
927 927 )
928 928
929 929 wctx = repo[None]
930 930 matchfiles = []
931 931 for f in m._files:
932 932 standin = lfutil.standin(f)
933 933 if standin in ctx or standin in mctx:
934 934 matchfiles.append(standin)
935 935 elif standin in wctx or lfdirstate[f] == b'r':
936 936 continue
937 937 else:
938 938 matchfiles.append(f)
939 939 m._files = matchfiles
940 940 m._fileset = set(m._files)
941 941 origmatchfn = m.matchfn
942 942
943 943 def matchfn(f):
944 944 lfile = lfutil.splitstandin(f)
945 945 if lfile is not None:
946 946 return origmatchfn(lfile) and (f in ctx or f in mctx)
947 947 return origmatchfn(f)
948 948
949 949 m.matchfn = matchfn
950 950 return m
951 951
952 952 with extensions.wrappedfunction(scmutil, b'match', overridematch):
953 953 orig(ui, repo, ctx, *pats, **opts)
954 954
955 955 newstandins = lfutil.getstandinsstate(repo)
956 956 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
957 957 # lfdirstate should be 'normallookup'-ed for updated files,
958 958 # because reverting doesn't touch dirstate for 'normal' files
959 959 # when target revision is explicitly specified: in such case,
960 960 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
961 961 # of target (standin) file.
962 962 lfcommands.updatelfiles(
963 963 ui, repo, filelist, printmessage=False, normallookup=True
964 964 )
965 965
966 966
967 967 # after pulling changesets, we need to take some extra care to get
968 968 # largefiles updated remotely
969 969 @eh.wrapcommand(
970 970 b'pull',
971 971 opts=[
972 972 (
973 973 b'',
974 974 b'all-largefiles',
975 975 None,
976 976 _(b'download all pulled versions of largefiles (DEPRECATED)'),
977 977 ),
978 978 (
979 979 b'',
980 980 b'lfrev',
981 981 [],
982 982 _(b'download largefiles for these revisions'),
983 983 _(b'REV'),
984 984 ),
985 985 ],
986 986 )
987 987 def overridepull(orig, ui, repo, source=None, **opts):
988 988 revsprepull = len(repo)
989 989 if not source:
990 990 source = b'default'
991 991 repo.lfpullsource = source
992 992 result = orig(ui, repo, source, **opts)
993 993 revspostpull = len(repo)
994 994 lfrevs = opts.get('lfrev', [])
995 995 if opts.get('all_largefiles'):
996 996 lfrevs.append(b'pulled()')
997 997 if lfrevs and revspostpull > revsprepull:
998 998 numcached = 0
999 999 repo.firstpulled = revsprepull # for pulled() revset expression
1000 1000 try:
1001 1001 for rev in scmutil.revrange(repo, lfrevs):
1002 1002 ui.note(_(b'pulling largefiles for revision %d\n') % rev)
1003 1003 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
1004 1004 numcached += len(cached)
1005 1005 finally:
1006 1006 del repo.firstpulled
1007 1007 ui.status(_(b"%d largefiles cached\n") % numcached)
1008 1008 return result
1009 1009
1010 1010
1011 1011 @eh.wrapcommand(
1012 1012 b'push',
1013 1013 opts=[
1014 1014 (
1015 1015 b'',
1016 1016 b'lfrev',
1017 1017 [],
1018 1018 _(b'upload largefiles for these revisions'),
1019 1019 _(b'REV'),
1020 1020 )
1021 1021 ],
1022 1022 )
1023 1023 def overridepush(orig, ui, repo, *args, **kwargs):
1024 1024 """Override push command and store --lfrev parameters in opargs"""
1025 1025 lfrevs = kwargs.pop('lfrev', None)
1026 1026 if lfrevs:
1027 1027 opargs = kwargs.setdefault('opargs', {})
1028 1028 opargs[b'lfrevs'] = scmutil.revrange(repo, lfrevs)
1029 1029 return orig(ui, repo, *args, **kwargs)
1030 1030
1031 1031
1032 1032 @eh.wrapfunction(exchange, b'pushoperation')
1033 1033 def exchangepushoperation(orig, *args, **kwargs):
1034 1034 """Override pushoperation constructor and store lfrevs parameter"""
1035 1035 lfrevs = kwargs.pop('lfrevs', None)
1036 1036 pushop = orig(*args, **kwargs)
1037 1037 pushop.lfrevs = lfrevs
1038 1038 return pushop
1039 1039
1040 1040
1041 1041 @eh.revsetpredicate(b'pulled()')
1042 1042 def pulledrevsetsymbol(repo, subset, x):
1043 1043 """Changesets that just has been pulled.
1044 1044
1045 1045 Only available with largefiles from pull --lfrev expressions.
1046 1046
1047 1047 .. container:: verbose
1048 1048
1049 1049 Some examples:
1050 1050
1051 1051 - pull largefiles for all new changesets::
1052 1052
1053 1053 hg pull -lfrev "pulled()"
1054 1054
1055 1055 - pull largefiles for all new branch heads::
1056 1056
1057 1057 hg pull -lfrev "head(pulled()) and not closed()"
1058 1058
1059 1059 """
1060 1060
1061 1061 try:
1062 1062 firstpulled = repo.firstpulled
1063 1063 except AttributeError:
1064 1064 raise error.Abort(_(b"pulled() only available in --lfrev"))
1065 1065 return smartset.baseset([r for r in subset if r >= firstpulled])
1066 1066
1067 1067
1068 1068 @eh.wrapcommand(
1069 1069 b'clone',
1070 1070 opts=[
1071 1071 (
1072 1072 b'',
1073 1073 b'all-largefiles',
1074 1074 None,
1075 1075 _(b'download all versions of all largefiles'),
1076 1076 )
1077 1077 ],
1078 1078 )
1079 1079 def overrideclone(orig, ui, source, dest=None, **opts):
1080 1080 d = dest
1081 1081 if d is None:
1082 1082 d = hg.defaultdest(source)
1083 1083 if opts.get('all_largefiles') and not hg.islocal(d):
1084 1084 raise error.Abort(
1085 1085 _(b'--all-largefiles is incompatible with non-local destination %s')
1086 1086 % d
1087 1087 )
1088 1088
1089 1089 return orig(ui, source, dest, **opts)
1090 1090
1091 1091
1092 1092 @eh.wrapfunction(hg, b'clone')
1093 1093 def hgclone(orig, ui, opts, *args, **kwargs):
1094 1094 result = orig(ui, opts, *args, **kwargs)
1095 1095
1096 1096 if result is not None:
1097 1097 sourcerepo, destrepo = result
1098 1098 repo = destrepo.local()
1099 1099
1100 1100 # When cloning to a remote repo (like through SSH), no repo is available
1101 1101 # from the peer. Therefore the largefiles can't be downloaded and the
1102 1102 # hgrc can't be updated.
1103 1103 if not repo:
1104 1104 return result
1105 1105
1106 1106 # Caching is implicitly limited to 'rev' option, since the dest repo was
1107 1107 # truncated at that point. The user may expect a download count with
1108 1108 # this option, so attempt whether or not this is a largefile repo.
1109 1109 if opts.get(b'all_largefiles'):
1110 1110 success, missing = lfcommands.downloadlfiles(ui, repo)
1111 1111
1112 1112 if missing != 0:
1113 1113 return None
1114 1114
1115 1115 return result
1116 1116
1117 1117
1118 1118 @eh.wrapcommand(b'rebase', extension=b'rebase')
1119 1119 def overriderebasecmd(orig, ui, repo, **opts):
1120 1120 if not util.safehasattr(repo, b'_largefilesenabled'):
1121 1121 return orig(ui, repo, **opts)
1122 1122
1123 1123 resuming = opts.get('continue')
1124 1124 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1125 1125 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1126 1126 try:
1127 1127 with ui.configoverride(
1128 1128 {(b'rebase', b'experimental.inmemory'): False}, b"largefiles"
1129 1129 ):
1130 1130 return orig(ui, repo, **opts)
1131 1131 finally:
1132 1132 repo._lfstatuswriters.pop()
1133 1133 repo._lfcommithooks.pop()
1134 1134
1135 1135
1136 1136 @eh.extsetup
1137 1137 def overriderebase(ui):
1138 1138 try:
1139 1139 rebase = extensions.find(b'rebase')
1140 1140 except KeyError:
1141 1141 pass
1142 1142 else:
1143 1143
1144 1144 def _dorebase(orig, *args, **kwargs):
1145 1145 kwargs['inmemory'] = False
1146 1146 return orig(*args, **kwargs)
1147 1147
1148 1148 extensions.wrapfunction(rebase, b'_dorebase', _dorebase)
1149 1149
1150 1150
1151 1151 @eh.wrapcommand(b'archive')
1152 1152 def overridearchivecmd(orig, ui, repo, dest, **opts):
1153 1153 with lfstatus(repo.unfiltered()):
1154 1154 return orig(ui, repo.unfiltered(), dest, **opts)
1155 1155
1156 1156
1157 1157 @eh.wrapfunction(webcommands, b'archive')
1158 1158 def hgwebarchive(orig, web):
1159 1159 with lfstatus(web.repo):
1160 1160 return orig(web)
1161 1161
1162 1162
1163 1163 @eh.wrapfunction(archival, b'archive')
1164 1164 def overridearchive(
1165 1165 orig,
1166 1166 repo,
1167 1167 dest,
1168 1168 node,
1169 1169 kind,
1170 1170 decode=True,
1171 1171 match=None,
1172 1172 prefix=b'',
1173 1173 mtime=None,
1174 1174 subrepos=None,
1175 1175 ):
1176 1176 # For some reason setting repo.lfstatus in hgwebarchive only changes the
1177 1177 # unfiltered repo's attr, so check that as well.
1178 1178 if not repo.lfstatus and not repo.unfiltered().lfstatus:
1179 1179 return orig(
1180 1180 repo, dest, node, kind, decode, match, prefix, mtime, subrepos
1181 1181 )
1182 1182
1183 1183 # No need to lock because we are only reading history and
1184 1184 # largefile caches, neither of which are modified.
1185 1185 if node is not None:
1186 1186 lfcommands.cachelfiles(repo.ui, repo, node)
1187 1187
1188 1188 if kind not in archival.archivers:
1189 1189 raise error.Abort(_(b"unknown archive type '%s'") % kind)
1190 1190
1191 1191 ctx = repo[node]
1192 1192
1193 1193 if kind == b'files':
1194 1194 if prefix:
1195 1195 raise error.Abort(_(b'cannot give prefix when archiving to files'))
1196 1196 else:
1197 1197 prefix = archival.tidyprefix(dest, kind, prefix)
1198 1198
1199 1199 def write(name, mode, islink, getdata):
1200 1200 if match and not match(name):
1201 1201 return
1202 1202 data = getdata()
1203 1203 if decode:
1204 1204 data = repo.wwritedata(name, data)
1205 1205 archiver.addfile(prefix + name, mode, islink, data)
1206 1206
1207 1207 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
1208 1208
1209 1209 if repo.ui.configbool(b"ui", b"archivemeta"):
1210 1210 write(
1211 1211 b'.hg_archival.txt',
1212 1212 0o644,
1213 1213 False,
1214 1214 lambda: archival.buildmetadata(ctx),
1215 1215 )
1216 1216
1217 1217 for f in ctx:
1218 1218 ff = ctx.flags(f)
1219 1219 getdata = ctx[f].data
1220 1220 lfile = lfutil.splitstandin(f)
1221 1221 if lfile is not None:
1222 1222 if node is not None:
1223 1223 path = lfutil.findfile(repo, getdata().strip())
1224 1224
1225 1225 if path is None:
1226 1226 raise error.Abort(
1227 1227 _(
1228 1228 b'largefile %s not found in repo store or system cache'
1229 1229 )
1230 1230 % lfile
1231 1231 )
1232 1232 else:
1233 1233 path = lfile
1234 1234
1235 1235 f = lfile
1236 1236
1237 1237 getdata = lambda: util.readfile(path)
1238 1238 write(f, b'x' in ff and 0o755 or 0o644, b'l' in ff, getdata)
1239 1239
1240 1240 if subrepos:
1241 1241 for subpath in sorted(ctx.substate):
1242 1242 sub = ctx.workingsub(subpath)
1243 1243 submatch = matchmod.subdirmatcher(subpath, match)
1244 1244 subprefix = prefix + subpath + b'/'
1245 1245
1246 1246 # TODO: Only hgsubrepo instances have `_repo`, so figure out how to
1247 1247 # infer and possibly set lfstatus in hgsubrepoarchive. That would
1248 1248 # allow only hgsubrepos to set this, instead of the current scheme
1249 1249 # where the parent sets this for the child.
1250 1250 with (
1251 1251 util.safehasattr(sub, '_repo')
1252 1252 and lfstatus(sub._repo)
1253 1253 or util.nullcontextmanager()
1254 1254 ):
1255 1255 sub.archive(archiver, subprefix, submatch)
1256 1256
1257 1257 archiver.done()
1258 1258
1259 1259
1260 1260 @eh.wrapfunction(subrepo.hgsubrepo, b'archive')
1261 1261 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None, decode=True):
1262 1262 lfenabled = util.safehasattr(repo._repo, b'_largefilesenabled')
1263 1263 if not lfenabled or not repo._repo.lfstatus:
1264 1264 return orig(repo, archiver, prefix, match, decode)
1265 1265
1266 1266 repo._get(repo._state + (b'hg',))
1267 1267 rev = repo._state[1]
1268 1268 ctx = repo._repo[rev]
1269 1269
1270 1270 if ctx.node() is not None:
1271 1271 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
1272 1272
1273 1273 def write(name, mode, islink, getdata):
1274 1274 # At this point, the standin has been replaced with the largefile name,
1275 1275 # so the normal matcher works here without the lfutil variants.
1276 1276 if match and not match(f):
1277 1277 return
1278 1278 data = getdata()
1279 1279 if decode:
1280 1280 data = repo._repo.wwritedata(name, data)
1281 1281
1282 1282 archiver.addfile(prefix + name, mode, islink, data)
1283 1283
1284 1284 for f in ctx:
1285 1285 ff = ctx.flags(f)
1286 1286 getdata = ctx[f].data
1287 1287 lfile = lfutil.splitstandin(f)
1288 1288 if lfile is not None:
1289 1289 if ctx.node() is not None:
1290 1290 path = lfutil.findfile(repo._repo, getdata().strip())
1291 1291
1292 1292 if path is None:
1293 1293 raise error.Abort(
1294 1294 _(
1295 1295 b'largefile %s not found in repo store or system cache'
1296 1296 )
1297 1297 % lfile
1298 1298 )
1299 1299 else:
1300 1300 path = lfile
1301 1301
1302 1302 f = lfile
1303 1303
1304 1304 getdata = lambda: util.readfile(os.path.join(prefix, path))
1305 1305
1306 1306 write(f, b'x' in ff and 0o755 or 0o644, b'l' in ff, getdata)
1307 1307
1308 1308 for subpath in sorted(ctx.substate):
1309 1309 sub = ctx.workingsub(subpath)
1310 1310 submatch = matchmod.subdirmatcher(subpath, match)
1311 1311 subprefix = prefix + subpath + b'/'
1312 1312 # TODO: Only hgsubrepo instances have `_repo`, so figure out how to
1313 1313 # infer and possibly set lfstatus at the top of this function. That
1314 1314 # would allow only hgsubrepos to set this, instead of the current scheme
1315 1315 # where the parent sets this for the child.
1316 1316 with (
1317 1317 util.safehasattr(sub, '_repo')
1318 1318 and lfstatus(sub._repo)
1319 1319 or util.nullcontextmanager()
1320 1320 ):
1321 1321 sub.archive(archiver, subprefix, submatch, decode)
1322 1322
1323 1323
1324 1324 # If a largefile is modified, the change is not reflected in its
1325 1325 # standin until a commit. cmdutil.bailifchanged() raises an exception
1326 1326 # if the repo has uncommitted changes. Wrap it to also check if
1327 1327 # largefiles were changed. This is used by bisect, backout and fetch.
1328 1328 @eh.wrapfunction(cmdutil, b'bailifchanged')
1329 1329 def overridebailifchanged(orig, repo, *args, **kwargs):
1330 1330 orig(repo, *args, **kwargs)
1331 1331 with lfstatus(repo):
1332 1332 s = repo.status()
1333 1333 if s.modified or s.added or s.removed or s.deleted:
1334 1334 raise error.Abort(_(b'uncommitted changes'))
1335 1335
1336 1336
1337 1337 @eh.wrapfunction(cmdutil, b'postcommitstatus')
1338 1338 def postcommitstatus(orig, repo, *args, **kwargs):
1339 1339 with lfstatus(repo):
1340 1340 return orig(repo, *args, **kwargs)
1341 1341
1342 1342
1343 1343 @eh.wrapfunction(cmdutil, b'forget')
1344 1344 def cmdutilforget(
1345 1345 orig, ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
1346 1346 ):
1347 1347 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
1348 1348 bad, forgot = orig(
1349 1349 ui,
1350 1350 repo,
1351 1351 normalmatcher,
1352 1352 prefix,
1353 1353 uipathfn,
1354 1354 explicitonly,
1355 1355 dryrun,
1356 1356 interactive,
1357 1357 )
1358 1358 m = composelargefilematcher(match, repo[None].manifest())
1359 1359
1360 1360 with lfstatus(repo):
1361 1361 s = repo.status(match=m, clean=True)
1362 1362 manifest = repo[None].manifest()
1363 1363 forget = sorted(s.modified + s.added + s.deleted + s.clean)
1364 1364 forget = [f for f in forget if lfutil.standin(f) in manifest]
1365 1365
1366 1366 for f in forget:
1367 1367 fstandin = lfutil.standin(f)
1368 1368 if fstandin not in repo.dirstate and not repo.wvfs.isdir(fstandin):
1369 1369 ui.warn(
1370 1370 _(b'not removing %s: file is already untracked\n') % uipathfn(f)
1371 1371 )
1372 1372 bad.append(f)
1373 1373
1374 1374 for f in forget:
1375 1375 if ui.verbose or not m.exact(f):
1376 1376 ui.status(_(b'removing %s\n') % uipathfn(f))
1377 1377
1378 1378 # Need to lock because standin files are deleted then removed from the
1379 1379 # repository and we could race in-between.
1380 1380 with repo.wlock():
1381 1381 lfdirstate = lfutil.openlfdirstate(ui, repo)
1382 1382 for f in forget:
1383 1383 lfdirstate.set_untracked(f)
1384 1384 lfdirstate.write()
1385 1385 standins = [lfutil.standin(f) for f in forget]
1386 1386 for f in standins:
1387 1387 repo.wvfs.unlinkpath(f, ignoremissing=True)
1388 1388 rejected = repo[None].forget(standins)
1389 1389
1390 1390 bad.extend(f for f in rejected if f in m.files())
1391 1391 forgot.extend(f for f in forget if f not in rejected)
1392 1392 return bad, forgot
1393 1393
1394 1394
1395 1395 def _getoutgoings(repo, other, missing, addfunc):
1396 1396 """get pairs of filename and largefile hash in outgoing revisions
1397 1397 in 'missing'.
1398 1398
1399 1399 largefiles already existing on 'other' repository are ignored.
1400 1400
1401 1401 'addfunc' is invoked with each unique pairs of filename and
1402 1402 largefile hash value.
1403 1403 """
1404 1404 knowns = set()
1405 1405 lfhashes = set()
1406 1406
1407 1407 def dedup(fn, lfhash):
1408 1408 k = (fn, lfhash)
1409 1409 if k not in knowns:
1410 1410 knowns.add(k)
1411 1411 lfhashes.add(lfhash)
1412 1412
1413 1413 lfutil.getlfilestoupload(repo, missing, dedup)
1414 1414 if lfhashes:
1415 1415 lfexists = storefactory.openstore(repo, other).exists(lfhashes)
1416 1416 for fn, lfhash in knowns:
1417 1417 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1418 1418 addfunc(fn, lfhash)
1419 1419
1420 1420
1421 1421 def outgoinghook(ui, repo, other, opts, missing):
1422 1422 if opts.pop(b'large', None):
1423 1423 lfhashes = set()
1424 1424 if ui.debugflag:
1425 1425 toupload = {}
1426 1426
1427 1427 def addfunc(fn, lfhash):
1428 1428 if fn not in toupload:
1429 1429 toupload[fn] = []
1430 1430 toupload[fn].append(lfhash)
1431 1431 lfhashes.add(lfhash)
1432 1432
1433 1433 def showhashes(fn):
1434 1434 for lfhash in sorted(toupload[fn]):
1435 1435 ui.debug(b' %s\n' % lfhash)
1436 1436
1437 1437 else:
1438 1438 toupload = set()
1439 1439
1440 1440 def addfunc(fn, lfhash):
1441 1441 toupload.add(fn)
1442 1442 lfhashes.add(lfhash)
1443 1443
1444 1444 def showhashes(fn):
1445 1445 pass
1446 1446
1447 1447 _getoutgoings(repo, other, missing, addfunc)
1448 1448
1449 1449 if not toupload:
1450 1450 ui.status(_(b'largefiles: no files to upload\n'))
1451 1451 else:
1452 1452 ui.status(
1453 1453 _(b'largefiles to upload (%d entities):\n') % (len(lfhashes))
1454 1454 )
1455 1455 for file in sorted(toupload):
1456 1456 ui.status(lfutil.splitstandin(file) + b'\n')
1457 1457 showhashes(file)
1458 1458 ui.status(b'\n')
1459 1459
1460 1460
1461 1461 @eh.wrapcommand(
1462 1462 b'outgoing', opts=[(b'', b'large', None, _(b'display outgoing largefiles'))]
1463 1463 )
1464 1464 def _outgoingcmd(orig, *args, **kwargs):
1465 1465 # Nothing to do here other than add the extra help option- the hook above
1466 1466 # processes it.
1467 1467 return orig(*args, **kwargs)
1468 1468
1469 1469
1470 1470 def summaryremotehook(ui, repo, opts, changes):
1471 1471 largeopt = opts.get(b'large', False)
1472 1472 if changes is None:
1473 1473 if largeopt:
1474 1474 return (False, True) # only outgoing check is needed
1475 1475 else:
1476 1476 return (False, False)
1477 1477 elif largeopt:
1478 1478 url, branch, peer, outgoing = changes[1]
1479 1479 if peer is None:
1480 1480 # i18n: column positioning for "hg summary"
1481 1481 ui.status(_(b'largefiles: (no remote repo)\n'))
1482 1482 return
1483 1483
1484 1484 toupload = set()
1485 1485 lfhashes = set()
1486 1486
1487 1487 def addfunc(fn, lfhash):
1488 1488 toupload.add(fn)
1489 1489 lfhashes.add(lfhash)
1490 1490
1491 1491 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1492 1492
1493 1493 if not toupload:
1494 1494 # i18n: column positioning for "hg summary"
1495 1495 ui.status(_(b'largefiles: (no files to upload)\n'))
1496 1496 else:
1497 1497 # i18n: column positioning for "hg summary"
1498 1498 ui.status(
1499 1499 _(b'largefiles: %d entities for %d files to upload\n')
1500 1500 % (len(lfhashes), len(toupload))
1501 1501 )
1502 1502
1503 1503
1504 1504 @eh.wrapcommand(
1505 1505 b'summary', opts=[(b'', b'large', None, _(b'display outgoing largefiles'))]
1506 1506 )
1507 1507 def overridesummary(orig, ui, repo, *pats, **opts):
1508 1508 with lfstatus(repo):
1509 1509 orig(ui, repo, *pats, **opts)
1510 1510
1511 1511
1512 1512 @eh.wrapfunction(scmutil, b'addremove')
1513 1513 def scmutiladdremove(orig, repo, matcher, prefix, uipathfn, opts=None):
1514 1514 if opts is None:
1515 1515 opts = {}
1516 1516 if not lfutil.islfilesrepo(repo):
1517 1517 return orig(repo, matcher, prefix, uipathfn, opts)
1518 1518 # Get the list of missing largefiles so we can remove them
1519 1519 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1520 1520 unsure, s = lfdirstate.status(
1521 1521 matchmod.always(),
1522 1522 subrepos=[],
1523 1523 ignored=False,
1524 1524 clean=False,
1525 1525 unknown=False,
1526 1526 )
1527 1527
1528 1528 # Call into the normal remove code, but the removing of the standin, we want
1529 1529 # to have handled by original addremove. Monkey patching here makes sure
1530 1530 # we don't remove the standin in the largefiles code, preventing a very
1531 1531 # confused state later.
1532 1532 if s.deleted:
1533 1533 m = copy.copy(matcher)
1534 1534
1535 1535 # The m._files and m._map attributes are not changed to the deleted list
1536 1536 # because that affects the m.exact() test, which in turn governs whether
1537 1537 # or not the file name is printed, and how. Simply limit the original
1538 1538 # matches to those in the deleted status list.
1539 1539 matchfn = m.matchfn
1540 1540 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1541 1541
1542 1542 removelargefiles(
1543 1543 repo.ui,
1544 1544 repo,
1545 1545 True,
1546 1546 m,
1547 1547 uipathfn,
1548 1548 opts.get(b'dry_run'),
1549 1549 **pycompat.strkwargs(opts)
1550 1550 )
1551 1551 # Call into the normal add code, and any files that *should* be added as
1552 1552 # largefiles will be
1553 1553 added, bad = addlargefiles(
1554 1554 repo.ui, repo, True, matcher, uipathfn, **pycompat.strkwargs(opts)
1555 1555 )
1556 1556 # Now that we've handled largefiles, hand off to the original addremove
1557 1557 # function to take care of the rest. Make sure it doesn't do anything with
1558 1558 # largefiles by passing a matcher that will ignore them.
1559 1559 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1560 1560 return orig(repo, matcher, prefix, uipathfn, opts)
1561 1561
1562 1562
1563 1563 # Calling purge with --all will cause the largefiles to be deleted.
1564 1564 # Override repo.status to prevent this from happening.
1565 1565 @eh.wrapcommand(b'purge')
1566 1566 def overridepurge(orig, ui, repo, *dirs, **opts):
1567 1567 # XXX Monkey patching a repoview will not work. The assigned attribute will
1568 1568 # be set on the unfiltered repo, but we will only lookup attributes in the
1569 1569 # unfiltered repo if the lookup in the repoview object itself fails. As the
1570 1570 # monkey patched method exists on the repoview class the lookup will not
1571 1571 # fail. As a result, the original version will shadow the monkey patched
1572 1572 # one, defeating the monkey patch.
1573 1573 #
1574 1574 # As a work around we use an unfiltered repo here. We should do something
1575 1575 # cleaner instead.
1576 1576 repo = repo.unfiltered()
1577 1577 oldstatus = repo.status
1578 1578
1579 1579 def overridestatus(
1580 1580 node1=b'.',
1581 1581 node2=None,
1582 1582 match=None,
1583 1583 ignored=False,
1584 1584 clean=False,
1585 1585 unknown=False,
1586 1586 listsubrepos=False,
1587 1587 ):
1588 1588 r = oldstatus(
1589 1589 node1, node2, match, ignored, clean, unknown, listsubrepos
1590 1590 )
1591 1591 lfdirstate = lfutil.openlfdirstate(ui, repo)
1592 1592 unknown = [f for f in r.unknown if lfdirstate[f] == b'?']
1593 1593 ignored = [f for f in r.ignored if lfdirstate[f] == b'?']
1594 1594 return scmutil.status(
1595 1595 r.modified, r.added, r.removed, r.deleted, unknown, ignored, r.clean
1596 1596 )
1597 1597
1598 1598 repo.status = overridestatus
1599 1599 orig(ui, repo, *dirs, **opts)
1600 1600 repo.status = oldstatus
1601 1601
1602 1602
1603 1603 @eh.wrapcommand(b'rollback')
1604 1604 def overriderollback(orig, ui, repo, **opts):
1605 1605 with repo.wlock():
1606 1606 before = repo.dirstate.parents()
1607 1607 orphans = {
1608 1608 f
1609 1609 for f in repo.dirstate
1610 1610 if lfutil.isstandin(f) and repo.dirstate[f] != b'r'
1611 1611 }
1612 1612 result = orig(ui, repo, **opts)
1613 1613 after = repo.dirstate.parents()
1614 1614 if before == after:
1615 1615 return result # no need to restore standins
1616 1616
1617 1617 pctx = repo[b'.']
1618 1618 for f in repo.dirstate:
1619 1619 if lfutil.isstandin(f):
1620 1620 orphans.discard(f)
1621 1621 if repo.dirstate[f] == b'r':
1622 1622 repo.wvfs.unlinkpath(f, ignoremissing=True)
1623 1623 elif f in pctx:
1624 1624 fctx = pctx[f]
1625 1625 repo.wwrite(f, fctx.data(), fctx.flags())
1626 1626 else:
1627 1627 # content of standin is not so important in 'a',
1628 1628 # 'm' or 'n' (coming from the 2nd parent) cases
1629 1629 lfutil.writestandin(repo, f, b'', False)
1630 1630 for standin in orphans:
1631 1631 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1632 1632
1633 1633 lfdirstate = lfutil.openlfdirstate(ui, repo)
1634 1634 with lfdirstate.parentchange():
1635 1635 orphans = set(lfdirstate)
1636 1636 lfiles = lfutil.listlfiles(repo)
1637 1637 for file in lfiles:
1638 1638 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1639 1639 orphans.discard(file)
1640 1640 for lfile in orphans:
1641 1641 lfdirstate.drop(lfile)
1642 1642 lfdirstate.write()
1643 1643 return result
1644 1644
1645 1645
1646 1646 @eh.wrapcommand(b'transplant', extension=b'transplant')
1647 1647 def overridetransplant(orig, ui, repo, *revs, **opts):
1648 1648 resuming = opts.get('continue')
1649 1649 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1650 1650 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1651 1651 try:
1652 1652 result = orig(ui, repo, *revs, **opts)
1653 1653 finally:
1654 1654 repo._lfstatuswriters.pop()
1655 1655 repo._lfcommithooks.pop()
1656 1656 return result
1657 1657
1658 1658
1659 1659 @eh.wrapcommand(b'cat')
1660 1660 def overridecat(orig, ui, repo, file1, *pats, **opts):
1661 1661 opts = pycompat.byteskwargs(opts)
1662 1662 ctx = scmutil.revsingle(repo, opts.get(b'rev'))
1663 1663 err = 1
1664 1664 notbad = set()
1665 1665 m = scmutil.match(ctx, (file1,) + pats, opts)
1666 1666 origmatchfn = m.matchfn
1667 1667
1668 1668 def lfmatchfn(f):
1669 1669 if origmatchfn(f):
1670 1670 return True
1671 1671 lf = lfutil.splitstandin(f)
1672 1672 if lf is None:
1673 1673 return False
1674 1674 notbad.add(lf)
1675 1675 return origmatchfn(lf)
1676 1676
1677 1677 m.matchfn = lfmatchfn
1678 1678 origbadfn = m.bad
1679 1679
1680 1680 def lfbadfn(f, msg):
1681 1681 if not f in notbad:
1682 1682 origbadfn(f, msg)
1683 1683
1684 1684 m.bad = lfbadfn
1685 1685
1686 1686 origvisitdirfn = m.visitdir
1687 1687
1688 1688 def lfvisitdirfn(dir):
1689 1689 if dir == lfutil.shortname:
1690 1690 return True
1691 1691 ret = origvisitdirfn(dir)
1692 1692 if ret:
1693 1693 return ret
1694 1694 lf = lfutil.splitstandin(dir)
1695 1695 if lf is None:
1696 1696 return False
1697 1697 return origvisitdirfn(lf)
1698 1698
1699 1699 m.visitdir = lfvisitdirfn
1700 1700
1701 1701 for f in ctx.walk(m):
1702 1702 with cmdutil.makefileobj(ctx, opts.get(b'output'), pathname=f) as fp:
1703 1703 lf = lfutil.splitstandin(f)
1704 1704 if lf is None or origmatchfn(f):
1705 1705 # duplicating unreachable code from commands.cat
1706 1706 data = ctx[f].data()
1707 1707 if opts.get(b'decode'):
1708 1708 data = repo.wwritedata(f, data)
1709 1709 fp.write(data)
1710 1710 else:
1711 1711 hash = lfutil.readasstandin(ctx[f])
1712 1712 if not lfutil.inusercache(repo.ui, hash):
1713 1713 store = storefactory.openstore(repo)
1714 1714 success, missing = store.get([(lf, hash)])
1715 1715 if len(success) != 1:
1716 1716 raise error.Abort(
1717 1717 _(
1718 1718 b'largefile %s is not in cache and could not be '
1719 1719 b'downloaded'
1720 1720 )
1721 1721 % lf
1722 1722 )
1723 1723 path = lfutil.usercachepath(repo.ui, hash)
1724 1724 with open(path, b"rb") as fpin:
1725 1725 for chunk in util.filechunkiter(fpin):
1726 1726 fp.write(chunk)
1727 1727 err = 0
1728 1728 return err
1729 1729
1730 1730
1731 1731 @eh.wrapfunction(merge, b'_update')
1732 1732 def mergeupdate(orig, repo, node, branchmerge, force, *args, **kwargs):
1733 1733 matcher = kwargs.get('matcher', None)
1734 1734 # note if this is a partial update
1735 1735 partial = matcher and not matcher.always()
1736 1736 with repo.wlock():
1737 1737 # branch | | |
1738 1738 # merge | force | partial | action
1739 1739 # -------+-------+---------+--------------
1740 1740 # x | x | x | linear-merge
1741 1741 # o | x | x | branch-merge
1742 1742 # x | o | x | overwrite (as clean update)
1743 1743 # o | o | x | force-branch-merge (*1)
1744 1744 # x | x | o | (*)
1745 1745 # o | x | o | (*)
1746 1746 # x | o | o | overwrite (as revert)
1747 1747 # o | o | o | (*)
1748 1748 #
1749 1749 # (*) don't care
1750 1750 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1751 1751
1752 1752 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1753 1753 unsure, s = lfdirstate.status(
1754 1754 matchmod.always(),
1755 1755 subrepos=[],
1756 1756 ignored=False,
1757 1757 clean=True,
1758 1758 unknown=False,
1759 1759 )
1760 1760 oldclean = set(s.clean)
1761 1761 pctx = repo[b'.']
1762 1762 dctx = repo[node]
1763 1763 for lfile in unsure + s.modified:
1764 1764 lfileabs = repo.wvfs.join(lfile)
1765 1765 if not repo.wvfs.exists(lfileabs):
1766 1766 continue
1767 1767 lfhash = lfutil.hashfile(lfileabs)
1768 1768 standin = lfutil.standin(lfile)
1769 1769 lfutil.writestandin(
1770 1770 repo, standin, lfhash, lfutil.getexecutable(lfileabs)
1771 1771 )
1772 1772 if standin in pctx and lfhash == lfutil.readasstandin(
1773 1773 pctx[standin]
1774 1774 ):
1775 1775 oldclean.add(lfile)
1776 1776 for lfile in s.added:
1777 1777 fstandin = lfutil.standin(lfile)
1778 1778 if fstandin not in dctx:
1779 1779 # in this case, content of standin file is meaningless
1780 1780 # (in dctx, lfile is unknown, or normal file)
1781 1781 continue
1782 1782 lfutil.updatestandin(repo, lfile, fstandin)
1783 1783 # mark all clean largefiles as dirty, just in case the update gets
1784 1784 # interrupted before largefiles and lfdirstate are synchronized
1785 1785 for lfile in oldclean:
1786 1786 lfdirstate.normallookup(lfile)
1787 1787 lfdirstate.write()
1788 1788
1789 1789 oldstandins = lfutil.getstandinsstate(repo)
1790 1790 wc = kwargs.get('wc')
1791 1791 if wc and wc.isinmemory():
1792 1792 # largefiles is not a good candidate for in-memory merge (large
1793 1793 # files, custom dirstate, matcher usage).
1794 1794 raise error.ProgrammingError(
1795 1795 b'largefiles is not compatible with in-memory merge'
1796 1796 )
1797 1797 result = orig(repo, node, branchmerge, force, *args, **kwargs)
1798 1798
1799 1799 newstandins = lfutil.getstandinsstate(repo)
1800 1800 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1801 1801
1802 1802 # to avoid leaving all largefiles as dirty and thus rehash them, mark
1803 1803 # all the ones that didn't change as clean
1804 1804 for lfile in oldclean.difference(filelist):
1805 1805 lfdirstate.normal(lfile)
1806 1806 lfdirstate.write()
1807 1807
1808 1808 if branchmerge or force or partial:
1809 1809 filelist.extend(s.deleted + s.removed)
1810 1810
1811 1811 lfcommands.updatelfiles(
1812 1812 repo.ui, repo, filelist=filelist, normallookup=partial
1813 1813 )
1814 1814
1815 1815 return result
1816 1816
1817 1817
1818 1818 @eh.wrapfunction(scmutil, b'marktouched')
1819 1819 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1820 1820 result = orig(repo, files, *args, **kwargs)
1821 1821
1822 1822 filelist = []
1823 1823 for f in files:
1824 1824 lf = lfutil.splitstandin(f)
1825 1825 if lf is not None:
1826 1826 filelist.append(lf)
1827 1827 if filelist:
1828 1828 lfcommands.updatelfiles(
1829 1829 repo.ui,
1830 1830 repo,
1831 1831 filelist=filelist,
1832 1832 printmessage=False,
1833 1833 normallookup=True,
1834 1834 )
1835 1835
1836 1836 return result
1837 1837
1838 1838
1839 1839 @eh.wrapfunction(upgrade_actions, b'preservedrequirements')
1840 1840 @eh.wrapfunction(upgrade_actions, b'supporteddestrequirements')
1841 1841 def upgraderequirements(orig, repo):
1842 1842 reqs = orig(repo)
1843 1843 if b'largefiles' in repo.requirements:
1844 1844 reqs.add(b'largefiles')
1845 1845 return reqs
1846 1846
1847 1847
1848 1848 _lfscheme = b'largefile://'
1849 1849
1850 1850
1851 1851 @eh.wrapfunction(urlmod, b'open')
1852 1852 def openlargefile(orig, ui, url_, data=None, **kwargs):
1853 1853 if url_.startswith(_lfscheme):
1854 1854 if data:
1855 1855 msg = b"cannot use data on a 'largefile://' url"
1856 1856 raise error.ProgrammingError(msg)
1857 1857 lfid = url_[len(_lfscheme) :]
1858 1858 return storefactory.getlfile(ui, lfid)
1859 1859 else:
1860 1860 return orig(ui, url_, data=data, **kwargs)
General Comments 0
You need to be logged in to leave comments. Login now