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