##// END OF EJS Templates
largefile: use `update_file` instead of `remove` in `mergerecordupdates`...
marmoute -
r48498:9fa1085f default
parent child Browse files
Show More
@@ -1,1860 +1,1862
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 repo.dirstate.remove(lfile)
657 repo.dirstate.update_file(
658 lfile, p1_tracked=True, wc_tracked=False
659 )
658 660 # make sure lfile doesn't get synclfdirstate'd as normal
659 661 lfdirstate.add(lfile)
660 662 lfdirstate.write()
661 663
662 664 return orig(repo, actions, branchmerge, getfiledata)
663 665
664 666
665 667 # Override filemerge to prompt the user about how they wish to merge
666 668 # largefiles. This will handle identical edits without prompting the user.
667 669 @eh.wrapfunction(filemerge, b'_filemerge')
668 670 def overridefilemerge(
669 671 origfn, premerge, repo, wctx, mynode, orig, fcd, fco, fca, labels=None
670 672 ):
671 673 if not lfutil.isstandin(orig) or fcd.isabsent() or fco.isabsent():
672 674 return origfn(
673 675 premerge, repo, wctx, mynode, orig, fcd, fco, fca, labels=labels
674 676 )
675 677
676 678 ahash = lfutil.readasstandin(fca).lower()
677 679 dhash = lfutil.readasstandin(fcd).lower()
678 680 ohash = lfutil.readasstandin(fco).lower()
679 681 if (
680 682 ohash != ahash
681 683 and ohash != dhash
682 684 and (
683 685 dhash == ahash
684 686 or repo.ui.promptchoice(
685 687 _(
686 688 b'largefile %s has a merge conflict\nancestor was %s\n'
687 689 b'you can keep (l)ocal %s or take (o)ther %s.\n'
688 690 b'what do you want to do?'
689 691 b'$$ &Local $$ &Other'
690 692 )
691 693 % (lfutil.splitstandin(orig), ahash, dhash, ohash),
692 694 0,
693 695 )
694 696 == 1
695 697 )
696 698 ):
697 699 repo.wwrite(fcd.path(), fco.data(), fco.flags())
698 700 return True, 0, False
699 701
700 702
701 703 @eh.wrapfunction(copiesmod, b'pathcopies')
702 704 def copiespathcopies(orig, ctx1, ctx2, match=None):
703 705 copies = orig(ctx1, ctx2, match=match)
704 706 updated = {}
705 707
706 708 for k, v in pycompat.iteritems(copies):
707 709 updated[lfutil.splitstandin(k) or k] = lfutil.splitstandin(v) or v
708 710
709 711 return updated
710 712
711 713
712 714 # Copy first changes the matchers to match standins instead of
713 715 # largefiles. Then it overrides util.copyfile in that function it
714 716 # checks if the destination largefile already exists. It also keeps a
715 717 # list of copied files so that the largefiles can be copied and the
716 718 # dirstate updated.
717 719 @eh.wrapfunction(cmdutil, b'copy')
718 720 def overridecopy(orig, ui, repo, pats, opts, rename=False):
719 721 # doesn't remove largefile on rename
720 722 if len(pats) < 2:
721 723 # this isn't legal, let the original function deal with it
722 724 return orig(ui, repo, pats, opts, rename)
723 725
724 726 # This could copy both lfiles and normal files in one command,
725 727 # but we don't want to do that. First replace their matcher to
726 728 # only match normal files and run it, then replace it to just
727 729 # match largefiles and run it again.
728 730 nonormalfiles = False
729 731 nolfiles = False
730 732 manifest = repo[None].manifest()
731 733
732 734 def normalfilesmatchfn(
733 735 orig,
734 736 ctx,
735 737 pats=(),
736 738 opts=None,
737 739 globbed=False,
738 740 default=b'relpath',
739 741 badfn=None,
740 742 ):
741 743 if opts is None:
742 744 opts = {}
743 745 match = orig(ctx, pats, opts, globbed, default, badfn=badfn)
744 746 return composenormalfilematcher(match, manifest)
745 747
746 748 with extensions.wrappedfunction(scmutil, b'match', normalfilesmatchfn):
747 749 try:
748 750 result = orig(ui, repo, pats, opts, rename)
749 751 except error.Abort as e:
750 752 if e.message != _(b'no files to copy'):
751 753 raise e
752 754 else:
753 755 nonormalfiles = True
754 756 result = 0
755 757
756 758 # The first rename can cause our current working directory to be removed.
757 759 # In that case there is nothing left to copy/rename so just quit.
758 760 try:
759 761 repo.getcwd()
760 762 except OSError:
761 763 return result
762 764
763 765 def makestandin(relpath):
764 766 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
765 767 return repo.wvfs.join(lfutil.standin(path))
766 768
767 769 fullpats = scmutil.expandpats(pats)
768 770 dest = fullpats[-1]
769 771
770 772 if os.path.isdir(dest):
771 773 if not os.path.isdir(makestandin(dest)):
772 774 os.makedirs(makestandin(dest))
773 775
774 776 try:
775 777 # When we call orig below it creates the standins but we don't add
776 778 # them to the dir state until later so lock during that time.
777 779 wlock = repo.wlock()
778 780
779 781 manifest = repo[None].manifest()
780 782
781 783 def overridematch(
782 784 orig,
783 785 ctx,
784 786 pats=(),
785 787 opts=None,
786 788 globbed=False,
787 789 default=b'relpath',
788 790 badfn=None,
789 791 ):
790 792 if opts is None:
791 793 opts = {}
792 794 newpats = []
793 795 # The patterns were previously mangled to add the standin
794 796 # directory; we need to remove that now
795 797 for pat in pats:
796 798 if matchmod.patkind(pat) is None and lfutil.shortname in pat:
797 799 newpats.append(pat.replace(lfutil.shortname, b''))
798 800 else:
799 801 newpats.append(pat)
800 802 match = orig(ctx, newpats, opts, globbed, default, badfn=badfn)
801 803 m = copy.copy(match)
802 804 lfile = lambda f: lfutil.standin(f) in manifest
803 805 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
804 806 m._fileset = set(m._files)
805 807 origmatchfn = m.matchfn
806 808
807 809 def matchfn(f):
808 810 lfile = lfutil.splitstandin(f)
809 811 return (
810 812 lfile is not None
811 813 and (f in manifest)
812 814 and origmatchfn(lfile)
813 815 or None
814 816 )
815 817
816 818 m.matchfn = matchfn
817 819 return m
818 820
819 821 listpats = []
820 822 for pat in pats:
821 823 if matchmod.patkind(pat) is not None:
822 824 listpats.append(pat)
823 825 else:
824 826 listpats.append(makestandin(pat))
825 827
826 828 copiedfiles = []
827 829
828 830 def overridecopyfile(orig, src, dest, *args, **kwargs):
829 831 if lfutil.shortname in src and dest.startswith(
830 832 repo.wjoin(lfutil.shortname)
831 833 ):
832 834 destlfile = dest.replace(lfutil.shortname, b'')
833 835 if not opts[b'force'] and os.path.exists(destlfile):
834 836 raise IOError(
835 837 b'', _(b'destination largefile already exists')
836 838 )
837 839 copiedfiles.append((src, dest))
838 840 orig(src, dest, *args, **kwargs)
839 841
840 842 with extensions.wrappedfunction(util, b'copyfile', overridecopyfile):
841 843 with extensions.wrappedfunction(scmutil, b'match', overridematch):
842 844 result += orig(ui, repo, listpats, opts, rename)
843 845
844 846 lfdirstate = lfutil.openlfdirstate(ui, repo)
845 847 for (src, dest) in copiedfiles:
846 848 if lfutil.shortname in src and dest.startswith(
847 849 repo.wjoin(lfutil.shortname)
848 850 ):
849 851 srclfile = src.replace(repo.wjoin(lfutil.standin(b'')), b'')
850 852 destlfile = dest.replace(repo.wjoin(lfutil.standin(b'')), b'')
851 853 destlfiledir = repo.wvfs.dirname(repo.wjoin(destlfile)) or b'.'
852 854 if not os.path.isdir(destlfiledir):
853 855 os.makedirs(destlfiledir)
854 856 if rename:
855 857 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
856 858
857 859 # The file is gone, but this deletes any empty parent
858 860 # directories as a side-effect.
859 861 repo.wvfs.unlinkpath(srclfile, ignoremissing=True)
860 862 lfdirstate.set_untracked(srclfile)
861 863 else:
862 864 util.copyfile(repo.wjoin(srclfile), repo.wjoin(destlfile))
863 865
864 866 lfdirstate.set_tracked(destlfile)
865 867 lfdirstate.write()
866 868 except error.Abort as e:
867 869 if e.message != _(b'no files to copy'):
868 870 raise e
869 871 else:
870 872 nolfiles = True
871 873 finally:
872 874 wlock.release()
873 875
874 876 if nolfiles and nonormalfiles:
875 877 raise error.Abort(_(b'no files to copy'))
876 878
877 879 return result
878 880
879 881
880 882 # When the user calls revert, we have to be careful to not revert any
881 883 # changes to other largefiles accidentally. This means we have to keep
882 884 # track of the largefiles that are being reverted so we only pull down
883 885 # the necessary largefiles.
884 886 #
885 887 # Standins are only updated (to match the hash of largefiles) before
886 888 # commits. Update the standins then run the original revert, changing
887 889 # the matcher to hit standins instead of largefiles. Based on the
888 890 # resulting standins update the largefiles.
889 891 @eh.wrapfunction(cmdutil, b'revert')
890 892 def overriderevert(orig, ui, repo, ctx, *pats, **opts):
891 893 # Because we put the standins in a bad state (by updating them)
892 894 # and then return them to a correct state we need to lock to
893 895 # prevent others from changing them in their incorrect state.
894 896 with repo.wlock():
895 897 lfdirstate = lfutil.openlfdirstate(ui, repo)
896 898 s = lfutil.lfdirstatestatus(lfdirstate, repo)
897 899 lfdirstate.write()
898 900 for lfile in s.modified:
899 901 lfutil.updatestandin(repo, lfile, lfutil.standin(lfile))
900 902 for lfile in s.deleted:
901 903 fstandin = lfutil.standin(lfile)
902 904 if repo.wvfs.exists(fstandin):
903 905 repo.wvfs.unlink(fstandin)
904 906
905 907 oldstandins = lfutil.getstandinsstate(repo)
906 908
907 909 def overridematch(
908 910 orig,
909 911 mctx,
910 912 pats=(),
911 913 opts=None,
912 914 globbed=False,
913 915 default=b'relpath',
914 916 badfn=None,
915 917 ):
916 918 if opts is None:
917 919 opts = {}
918 920 match = orig(mctx, pats, opts, globbed, default, badfn=badfn)
919 921 m = copy.copy(match)
920 922
921 923 # revert supports recursing into subrepos, and though largefiles
922 924 # currently doesn't work correctly in that case, this match is
923 925 # called, so the lfdirstate above may not be the correct one for
924 926 # this invocation of match.
925 927 lfdirstate = lfutil.openlfdirstate(
926 928 mctx.repo().ui, mctx.repo(), False
927 929 )
928 930
929 931 wctx = repo[None]
930 932 matchfiles = []
931 933 for f in m._files:
932 934 standin = lfutil.standin(f)
933 935 if standin in ctx or standin in mctx:
934 936 matchfiles.append(standin)
935 937 elif standin in wctx or lfdirstate[f] == b'r':
936 938 continue
937 939 else:
938 940 matchfiles.append(f)
939 941 m._files = matchfiles
940 942 m._fileset = set(m._files)
941 943 origmatchfn = m.matchfn
942 944
943 945 def matchfn(f):
944 946 lfile = lfutil.splitstandin(f)
945 947 if lfile is not None:
946 948 return origmatchfn(lfile) and (f in ctx or f in mctx)
947 949 return origmatchfn(f)
948 950
949 951 m.matchfn = matchfn
950 952 return m
951 953
952 954 with extensions.wrappedfunction(scmutil, b'match', overridematch):
953 955 orig(ui, repo, ctx, *pats, **opts)
954 956
955 957 newstandins = lfutil.getstandinsstate(repo)
956 958 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
957 959 # lfdirstate should be 'normallookup'-ed for updated files,
958 960 # because reverting doesn't touch dirstate for 'normal' files
959 961 # when target revision is explicitly specified: in such case,
960 962 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
961 963 # of target (standin) file.
962 964 lfcommands.updatelfiles(
963 965 ui, repo, filelist, printmessage=False, normallookup=True
964 966 )
965 967
966 968
967 969 # after pulling changesets, we need to take some extra care to get
968 970 # largefiles updated remotely
969 971 @eh.wrapcommand(
970 972 b'pull',
971 973 opts=[
972 974 (
973 975 b'',
974 976 b'all-largefiles',
975 977 None,
976 978 _(b'download all pulled versions of largefiles (DEPRECATED)'),
977 979 ),
978 980 (
979 981 b'',
980 982 b'lfrev',
981 983 [],
982 984 _(b'download largefiles for these revisions'),
983 985 _(b'REV'),
984 986 ),
985 987 ],
986 988 )
987 989 def overridepull(orig, ui, repo, source=None, **opts):
988 990 revsprepull = len(repo)
989 991 if not source:
990 992 source = b'default'
991 993 repo.lfpullsource = source
992 994 result = orig(ui, repo, source, **opts)
993 995 revspostpull = len(repo)
994 996 lfrevs = opts.get('lfrev', [])
995 997 if opts.get('all_largefiles'):
996 998 lfrevs.append(b'pulled()')
997 999 if lfrevs and revspostpull > revsprepull:
998 1000 numcached = 0
999 1001 repo.firstpulled = revsprepull # for pulled() revset expression
1000 1002 try:
1001 1003 for rev in scmutil.revrange(repo, lfrevs):
1002 1004 ui.note(_(b'pulling largefiles for revision %d\n') % rev)
1003 1005 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
1004 1006 numcached += len(cached)
1005 1007 finally:
1006 1008 del repo.firstpulled
1007 1009 ui.status(_(b"%d largefiles cached\n") % numcached)
1008 1010 return result
1009 1011
1010 1012
1011 1013 @eh.wrapcommand(
1012 1014 b'push',
1013 1015 opts=[
1014 1016 (
1015 1017 b'',
1016 1018 b'lfrev',
1017 1019 [],
1018 1020 _(b'upload largefiles for these revisions'),
1019 1021 _(b'REV'),
1020 1022 )
1021 1023 ],
1022 1024 )
1023 1025 def overridepush(orig, ui, repo, *args, **kwargs):
1024 1026 """Override push command and store --lfrev parameters in opargs"""
1025 1027 lfrevs = kwargs.pop('lfrev', None)
1026 1028 if lfrevs:
1027 1029 opargs = kwargs.setdefault('opargs', {})
1028 1030 opargs[b'lfrevs'] = scmutil.revrange(repo, lfrevs)
1029 1031 return orig(ui, repo, *args, **kwargs)
1030 1032
1031 1033
1032 1034 @eh.wrapfunction(exchange, b'pushoperation')
1033 1035 def exchangepushoperation(orig, *args, **kwargs):
1034 1036 """Override pushoperation constructor and store lfrevs parameter"""
1035 1037 lfrevs = kwargs.pop('lfrevs', None)
1036 1038 pushop = orig(*args, **kwargs)
1037 1039 pushop.lfrevs = lfrevs
1038 1040 return pushop
1039 1041
1040 1042
1041 1043 @eh.revsetpredicate(b'pulled()')
1042 1044 def pulledrevsetsymbol(repo, subset, x):
1043 1045 """Changesets that just has been pulled.
1044 1046
1045 1047 Only available with largefiles from pull --lfrev expressions.
1046 1048
1047 1049 .. container:: verbose
1048 1050
1049 1051 Some examples:
1050 1052
1051 1053 - pull largefiles for all new changesets::
1052 1054
1053 1055 hg pull -lfrev "pulled()"
1054 1056
1055 1057 - pull largefiles for all new branch heads::
1056 1058
1057 1059 hg pull -lfrev "head(pulled()) and not closed()"
1058 1060
1059 1061 """
1060 1062
1061 1063 try:
1062 1064 firstpulled = repo.firstpulled
1063 1065 except AttributeError:
1064 1066 raise error.Abort(_(b"pulled() only available in --lfrev"))
1065 1067 return smartset.baseset([r for r in subset if r >= firstpulled])
1066 1068
1067 1069
1068 1070 @eh.wrapcommand(
1069 1071 b'clone',
1070 1072 opts=[
1071 1073 (
1072 1074 b'',
1073 1075 b'all-largefiles',
1074 1076 None,
1075 1077 _(b'download all versions of all largefiles'),
1076 1078 )
1077 1079 ],
1078 1080 )
1079 1081 def overrideclone(orig, ui, source, dest=None, **opts):
1080 1082 d = dest
1081 1083 if d is None:
1082 1084 d = hg.defaultdest(source)
1083 1085 if opts.get('all_largefiles') and not hg.islocal(d):
1084 1086 raise error.Abort(
1085 1087 _(b'--all-largefiles is incompatible with non-local destination %s')
1086 1088 % d
1087 1089 )
1088 1090
1089 1091 return orig(ui, source, dest, **opts)
1090 1092
1091 1093
1092 1094 @eh.wrapfunction(hg, b'clone')
1093 1095 def hgclone(orig, ui, opts, *args, **kwargs):
1094 1096 result = orig(ui, opts, *args, **kwargs)
1095 1097
1096 1098 if result is not None:
1097 1099 sourcerepo, destrepo = result
1098 1100 repo = destrepo.local()
1099 1101
1100 1102 # When cloning to a remote repo (like through SSH), no repo is available
1101 1103 # from the peer. Therefore the largefiles can't be downloaded and the
1102 1104 # hgrc can't be updated.
1103 1105 if not repo:
1104 1106 return result
1105 1107
1106 1108 # Caching is implicitly limited to 'rev' option, since the dest repo was
1107 1109 # truncated at that point. The user may expect a download count with
1108 1110 # this option, so attempt whether or not this is a largefile repo.
1109 1111 if opts.get(b'all_largefiles'):
1110 1112 success, missing = lfcommands.downloadlfiles(ui, repo)
1111 1113
1112 1114 if missing != 0:
1113 1115 return None
1114 1116
1115 1117 return result
1116 1118
1117 1119
1118 1120 @eh.wrapcommand(b'rebase', extension=b'rebase')
1119 1121 def overriderebasecmd(orig, ui, repo, **opts):
1120 1122 if not util.safehasattr(repo, b'_largefilesenabled'):
1121 1123 return orig(ui, repo, **opts)
1122 1124
1123 1125 resuming = opts.get('continue')
1124 1126 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1125 1127 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1126 1128 try:
1127 1129 with ui.configoverride(
1128 1130 {(b'rebase', b'experimental.inmemory'): False}, b"largefiles"
1129 1131 ):
1130 1132 return orig(ui, repo, **opts)
1131 1133 finally:
1132 1134 repo._lfstatuswriters.pop()
1133 1135 repo._lfcommithooks.pop()
1134 1136
1135 1137
1136 1138 @eh.extsetup
1137 1139 def overriderebase(ui):
1138 1140 try:
1139 1141 rebase = extensions.find(b'rebase')
1140 1142 except KeyError:
1141 1143 pass
1142 1144 else:
1143 1145
1144 1146 def _dorebase(orig, *args, **kwargs):
1145 1147 kwargs['inmemory'] = False
1146 1148 return orig(*args, **kwargs)
1147 1149
1148 1150 extensions.wrapfunction(rebase, b'_dorebase', _dorebase)
1149 1151
1150 1152
1151 1153 @eh.wrapcommand(b'archive')
1152 1154 def overridearchivecmd(orig, ui, repo, dest, **opts):
1153 1155 with lfstatus(repo.unfiltered()):
1154 1156 return orig(ui, repo.unfiltered(), dest, **opts)
1155 1157
1156 1158
1157 1159 @eh.wrapfunction(webcommands, b'archive')
1158 1160 def hgwebarchive(orig, web):
1159 1161 with lfstatus(web.repo):
1160 1162 return orig(web)
1161 1163
1162 1164
1163 1165 @eh.wrapfunction(archival, b'archive')
1164 1166 def overridearchive(
1165 1167 orig,
1166 1168 repo,
1167 1169 dest,
1168 1170 node,
1169 1171 kind,
1170 1172 decode=True,
1171 1173 match=None,
1172 1174 prefix=b'',
1173 1175 mtime=None,
1174 1176 subrepos=None,
1175 1177 ):
1176 1178 # For some reason setting repo.lfstatus in hgwebarchive only changes the
1177 1179 # unfiltered repo's attr, so check that as well.
1178 1180 if not repo.lfstatus and not repo.unfiltered().lfstatus:
1179 1181 return orig(
1180 1182 repo, dest, node, kind, decode, match, prefix, mtime, subrepos
1181 1183 )
1182 1184
1183 1185 # No need to lock because we are only reading history and
1184 1186 # largefile caches, neither of which are modified.
1185 1187 if node is not None:
1186 1188 lfcommands.cachelfiles(repo.ui, repo, node)
1187 1189
1188 1190 if kind not in archival.archivers:
1189 1191 raise error.Abort(_(b"unknown archive type '%s'") % kind)
1190 1192
1191 1193 ctx = repo[node]
1192 1194
1193 1195 if kind == b'files':
1194 1196 if prefix:
1195 1197 raise error.Abort(_(b'cannot give prefix when archiving to files'))
1196 1198 else:
1197 1199 prefix = archival.tidyprefix(dest, kind, prefix)
1198 1200
1199 1201 def write(name, mode, islink, getdata):
1200 1202 if match and not match(name):
1201 1203 return
1202 1204 data = getdata()
1203 1205 if decode:
1204 1206 data = repo.wwritedata(name, data)
1205 1207 archiver.addfile(prefix + name, mode, islink, data)
1206 1208
1207 1209 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
1208 1210
1209 1211 if repo.ui.configbool(b"ui", b"archivemeta"):
1210 1212 write(
1211 1213 b'.hg_archival.txt',
1212 1214 0o644,
1213 1215 False,
1214 1216 lambda: archival.buildmetadata(ctx),
1215 1217 )
1216 1218
1217 1219 for f in ctx:
1218 1220 ff = ctx.flags(f)
1219 1221 getdata = ctx[f].data
1220 1222 lfile = lfutil.splitstandin(f)
1221 1223 if lfile is not None:
1222 1224 if node is not None:
1223 1225 path = lfutil.findfile(repo, getdata().strip())
1224 1226
1225 1227 if path is None:
1226 1228 raise error.Abort(
1227 1229 _(
1228 1230 b'largefile %s not found in repo store or system cache'
1229 1231 )
1230 1232 % lfile
1231 1233 )
1232 1234 else:
1233 1235 path = lfile
1234 1236
1235 1237 f = lfile
1236 1238
1237 1239 getdata = lambda: util.readfile(path)
1238 1240 write(f, b'x' in ff and 0o755 or 0o644, b'l' in ff, getdata)
1239 1241
1240 1242 if subrepos:
1241 1243 for subpath in sorted(ctx.substate):
1242 1244 sub = ctx.workingsub(subpath)
1243 1245 submatch = matchmod.subdirmatcher(subpath, match)
1244 1246 subprefix = prefix + subpath + b'/'
1245 1247
1246 1248 # TODO: Only hgsubrepo instances have `_repo`, so figure out how to
1247 1249 # infer and possibly set lfstatus in hgsubrepoarchive. That would
1248 1250 # allow only hgsubrepos to set this, instead of the current scheme
1249 1251 # where the parent sets this for the child.
1250 1252 with (
1251 1253 util.safehasattr(sub, '_repo')
1252 1254 and lfstatus(sub._repo)
1253 1255 or util.nullcontextmanager()
1254 1256 ):
1255 1257 sub.archive(archiver, subprefix, submatch)
1256 1258
1257 1259 archiver.done()
1258 1260
1259 1261
1260 1262 @eh.wrapfunction(subrepo.hgsubrepo, b'archive')
1261 1263 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None, decode=True):
1262 1264 lfenabled = util.safehasattr(repo._repo, b'_largefilesenabled')
1263 1265 if not lfenabled or not repo._repo.lfstatus:
1264 1266 return orig(repo, archiver, prefix, match, decode)
1265 1267
1266 1268 repo._get(repo._state + (b'hg',))
1267 1269 rev = repo._state[1]
1268 1270 ctx = repo._repo[rev]
1269 1271
1270 1272 if ctx.node() is not None:
1271 1273 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
1272 1274
1273 1275 def write(name, mode, islink, getdata):
1274 1276 # At this point, the standin has been replaced with the largefile name,
1275 1277 # so the normal matcher works here without the lfutil variants.
1276 1278 if match and not match(f):
1277 1279 return
1278 1280 data = getdata()
1279 1281 if decode:
1280 1282 data = repo._repo.wwritedata(name, data)
1281 1283
1282 1284 archiver.addfile(prefix + name, mode, islink, data)
1283 1285
1284 1286 for f in ctx:
1285 1287 ff = ctx.flags(f)
1286 1288 getdata = ctx[f].data
1287 1289 lfile = lfutil.splitstandin(f)
1288 1290 if lfile is not None:
1289 1291 if ctx.node() is not None:
1290 1292 path = lfutil.findfile(repo._repo, getdata().strip())
1291 1293
1292 1294 if path is None:
1293 1295 raise error.Abort(
1294 1296 _(
1295 1297 b'largefile %s not found in repo store or system cache'
1296 1298 )
1297 1299 % lfile
1298 1300 )
1299 1301 else:
1300 1302 path = lfile
1301 1303
1302 1304 f = lfile
1303 1305
1304 1306 getdata = lambda: util.readfile(os.path.join(prefix, path))
1305 1307
1306 1308 write(f, b'x' in ff and 0o755 or 0o644, b'l' in ff, getdata)
1307 1309
1308 1310 for subpath in sorted(ctx.substate):
1309 1311 sub = ctx.workingsub(subpath)
1310 1312 submatch = matchmod.subdirmatcher(subpath, match)
1311 1313 subprefix = prefix + subpath + b'/'
1312 1314 # TODO: Only hgsubrepo instances have `_repo`, so figure out how to
1313 1315 # infer and possibly set lfstatus at the top of this function. That
1314 1316 # would allow only hgsubrepos to set this, instead of the current scheme
1315 1317 # where the parent sets this for the child.
1316 1318 with (
1317 1319 util.safehasattr(sub, '_repo')
1318 1320 and lfstatus(sub._repo)
1319 1321 or util.nullcontextmanager()
1320 1322 ):
1321 1323 sub.archive(archiver, subprefix, submatch, decode)
1322 1324
1323 1325
1324 1326 # If a largefile is modified, the change is not reflected in its
1325 1327 # standin until a commit. cmdutil.bailifchanged() raises an exception
1326 1328 # if the repo has uncommitted changes. Wrap it to also check if
1327 1329 # largefiles were changed. This is used by bisect, backout and fetch.
1328 1330 @eh.wrapfunction(cmdutil, b'bailifchanged')
1329 1331 def overridebailifchanged(orig, repo, *args, **kwargs):
1330 1332 orig(repo, *args, **kwargs)
1331 1333 with lfstatus(repo):
1332 1334 s = repo.status()
1333 1335 if s.modified or s.added or s.removed or s.deleted:
1334 1336 raise error.Abort(_(b'uncommitted changes'))
1335 1337
1336 1338
1337 1339 @eh.wrapfunction(cmdutil, b'postcommitstatus')
1338 1340 def postcommitstatus(orig, repo, *args, **kwargs):
1339 1341 with lfstatus(repo):
1340 1342 return orig(repo, *args, **kwargs)
1341 1343
1342 1344
1343 1345 @eh.wrapfunction(cmdutil, b'forget')
1344 1346 def cmdutilforget(
1345 1347 orig, ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
1346 1348 ):
1347 1349 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
1348 1350 bad, forgot = orig(
1349 1351 ui,
1350 1352 repo,
1351 1353 normalmatcher,
1352 1354 prefix,
1353 1355 uipathfn,
1354 1356 explicitonly,
1355 1357 dryrun,
1356 1358 interactive,
1357 1359 )
1358 1360 m = composelargefilematcher(match, repo[None].manifest())
1359 1361
1360 1362 with lfstatus(repo):
1361 1363 s = repo.status(match=m, clean=True)
1362 1364 manifest = repo[None].manifest()
1363 1365 forget = sorted(s.modified + s.added + s.deleted + s.clean)
1364 1366 forget = [f for f in forget if lfutil.standin(f) in manifest]
1365 1367
1366 1368 for f in forget:
1367 1369 fstandin = lfutil.standin(f)
1368 1370 if fstandin not in repo.dirstate and not repo.wvfs.isdir(fstandin):
1369 1371 ui.warn(
1370 1372 _(b'not removing %s: file is already untracked\n') % uipathfn(f)
1371 1373 )
1372 1374 bad.append(f)
1373 1375
1374 1376 for f in forget:
1375 1377 if ui.verbose or not m.exact(f):
1376 1378 ui.status(_(b'removing %s\n') % uipathfn(f))
1377 1379
1378 1380 # Need to lock because standin files are deleted then removed from the
1379 1381 # repository and we could race in-between.
1380 1382 with repo.wlock():
1381 1383 lfdirstate = lfutil.openlfdirstate(ui, repo)
1382 1384 for f in forget:
1383 1385 lfdirstate.set_untracked(f)
1384 1386 lfdirstate.write()
1385 1387 standins = [lfutil.standin(f) for f in forget]
1386 1388 for f in standins:
1387 1389 repo.wvfs.unlinkpath(f, ignoremissing=True)
1388 1390 rejected = repo[None].forget(standins)
1389 1391
1390 1392 bad.extend(f for f in rejected if f in m.files())
1391 1393 forgot.extend(f for f in forget if f not in rejected)
1392 1394 return bad, forgot
1393 1395
1394 1396
1395 1397 def _getoutgoings(repo, other, missing, addfunc):
1396 1398 """get pairs of filename and largefile hash in outgoing revisions
1397 1399 in 'missing'.
1398 1400
1399 1401 largefiles already existing on 'other' repository are ignored.
1400 1402
1401 1403 'addfunc' is invoked with each unique pairs of filename and
1402 1404 largefile hash value.
1403 1405 """
1404 1406 knowns = set()
1405 1407 lfhashes = set()
1406 1408
1407 1409 def dedup(fn, lfhash):
1408 1410 k = (fn, lfhash)
1409 1411 if k not in knowns:
1410 1412 knowns.add(k)
1411 1413 lfhashes.add(lfhash)
1412 1414
1413 1415 lfutil.getlfilestoupload(repo, missing, dedup)
1414 1416 if lfhashes:
1415 1417 lfexists = storefactory.openstore(repo, other).exists(lfhashes)
1416 1418 for fn, lfhash in knowns:
1417 1419 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1418 1420 addfunc(fn, lfhash)
1419 1421
1420 1422
1421 1423 def outgoinghook(ui, repo, other, opts, missing):
1422 1424 if opts.pop(b'large', None):
1423 1425 lfhashes = set()
1424 1426 if ui.debugflag:
1425 1427 toupload = {}
1426 1428
1427 1429 def addfunc(fn, lfhash):
1428 1430 if fn not in toupload:
1429 1431 toupload[fn] = []
1430 1432 toupload[fn].append(lfhash)
1431 1433 lfhashes.add(lfhash)
1432 1434
1433 1435 def showhashes(fn):
1434 1436 for lfhash in sorted(toupload[fn]):
1435 1437 ui.debug(b' %s\n' % lfhash)
1436 1438
1437 1439 else:
1438 1440 toupload = set()
1439 1441
1440 1442 def addfunc(fn, lfhash):
1441 1443 toupload.add(fn)
1442 1444 lfhashes.add(lfhash)
1443 1445
1444 1446 def showhashes(fn):
1445 1447 pass
1446 1448
1447 1449 _getoutgoings(repo, other, missing, addfunc)
1448 1450
1449 1451 if not toupload:
1450 1452 ui.status(_(b'largefiles: no files to upload\n'))
1451 1453 else:
1452 1454 ui.status(
1453 1455 _(b'largefiles to upload (%d entities):\n') % (len(lfhashes))
1454 1456 )
1455 1457 for file in sorted(toupload):
1456 1458 ui.status(lfutil.splitstandin(file) + b'\n')
1457 1459 showhashes(file)
1458 1460 ui.status(b'\n')
1459 1461
1460 1462
1461 1463 @eh.wrapcommand(
1462 1464 b'outgoing', opts=[(b'', b'large', None, _(b'display outgoing largefiles'))]
1463 1465 )
1464 1466 def _outgoingcmd(orig, *args, **kwargs):
1465 1467 # Nothing to do here other than add the extra help option- the hook above
1466 1468 # processes it.
1467 1469 return orig(*args, **kwargs)
1468 1470
1469 1471
1470 1472 def summaryremotehook(ui, repo, opts, changes):
1471 1473 largeopt = opts.get(b'large', False)
1472 1474 if changes is None:
1473 1475 if largeopt:
1474 1476 return (False, True) # only outgoing check is needed
1475 1477 else:
1476 1478 return (False, False)
1477 1479 elif largeopt:
1478 1480 url, branch, peer, outgoing = changes[1]
1479 1481 if peer is None:
1480 1482 # i18n: column positioning for "hg summary"
1481 1483 ui.status(_(b'largefiles: (no remote repo)\n'))
1482 1484 return
1483 1485
1484 1486 toupload = set()
1485 1487 lfhashes = set()
1486 1488
1487 1489 def addfunc(fn, lfhash):
1488 1490 toupload.add(fn)
1489 1491 lfhashes.add(lfhash)
1490 1492
1491 1493 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1492 1494
1493 1495 if not toupload:
1494 1496 # i18n: column positioning for "hg summary"
1495 1497 ui.status(_(b'largefiles: (no files to upload)\n'))
1496 1498 else:
1497 1499 # i18n: column positioning for "hg summary"
1498 1500 ui.status(
1499 1501 _(b'largefiles: %d entities for %d files to upload\n')
1500 1502 % (len(lfhashes), len(toupload))
1501 1503 )
1502 1504
1503 1505
1504 1506 @eh.wrapcommand(
1505 1507 b'summary', opts=[(b'', b'large', None, _(b'display outgoing largefiles'))]
1506 1508 )
1507 1509 def overridesummary(orig, ui, repo, *pats, **opts):
1508 1510 with lfstatus(repo):
1509 1511 orig(ui, repo, *pats, **opts)
1510 1512
1511 1513
1512 1514 @eh.wrapfunction(scmutil, b'addremove')
1513 1515 def scmutiladdremove(orig, repo, matcher, prefix, uipathfn, opts=None):
1514 1516 if opts is None:
1515 1517 opts = {}
1516 1518 if not lfutil.islfilesrepo(repo):
1517 1519 return orig(repo, matcher, prefix, uipathfn, opts)
1518 1520 # Get the list of missing largefiles so we can remove them
1519 1521 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1520 1522 unsure, s = lfdirstate.status(
1521 1523 matchmod.always(),
1522 1524 subrepos=[],
1523 1525 ignored=False,
1524 1526 clean=False,
1525 1527 unknown=False,
1526 1528 )
1527 1529
1528 1530 # Call into the normal remove code, but the removing of the standin, we want
1529 1531 # to have handled by original addremove. Monkey patching here makes sure
1530 1532 # we don't remove the standin in the largefiles code, preventing a very
1531 1533 # confused state later.
1532 1534 if s.deleted:
1533 1535 m = copy.copy(matcher)
1534 1536
1535 1537 # The m._files and m._map attributes are not changed to the deleted list
1536 1538 # because that affects the m.exact() test, which in turn governs whether
1537 1539 # or not the file name is printed, and how. Simply limit the original
1538 1540 # matches to those in the deleted status list.
1539 1541 matchfn = m.matchfn
1540 1542 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1541 1543
1542 1544 removelargefiles(
1543 1545 repo.ui,
1544 1546 repo,
1545 1547 True,
1546 1548 m,
1547 1549 uipathfn,
1548 1550 opts.get(b'dry_run'),
1549 1551 **pycompat.strkwargs(opts)
1550 1552 )
1551 1553 # Call into the normal add code, and any files that *should* be added as
1552 1554 # largefiles will be
1553 1555 added, bad = addlargefiles(
1554 1556 repo.ui, repo, True, matcher, uipathfn, **pycompat.strkwargs(opts)
1555 1557 )
1556 1558 # Now that we've handled largefiles, hand off to the original addremove
1557 1559 # function to take care of the rest. Make sure it doesn't do anything with
1558 1560 # largefiles by passing a matcher that will ignore them.
1559 1561 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1560 1562 return orig(repo, matcher, prefix, uipathfn, opts)
1561 1563
1562 1564
1563 1565 # Calling purge with --all will cause the largefiles to be deleted.
1564 1566 # Override repo.status to prevent this from happening.
1565 1567 @eh.wrapcommand(b'purge')
1566 1568 def overridepurge(orig, ui, repo, *dirs, **opts):
1567 1569 # XXX Monkey patching a repoview will not work. The assigned attribute will
1568 1570 # be set on the unfiltered repo, but we will only lookup attributes in the
1569 1571 # unfiltered repo if the lookup in the repoview object itself fails. As the
1570 1572 # monkey patched method exists on the repoview class the lookup will not
1571 1573 # fail. As a result, the original version will shadow the monkey patched
1572 1574 # one, defeating the monkey patch.
1573 1575 #
1574 1576 # As a work around we use an unfiltered repo here. We should do something
1575 1577 # cleaner instead.
1576 1578 repo = repo.unfiltered()
1577 1579 oldstatus = repo.status
1578 1580
1579 1581 def overridestatus(
1580 1582 node1=b'.',
1581 1583 node2=None,
1582 1584 match=None,
1583 1585 ignored=False,
1584 1586 clean=False,
1585 1587 unknown=False,
1586 1588 listsubrepos=False,
1587 1589 ):
1588 1590 r = oldstatus(
1589 1591 node1, node2, match, ignored, clean, unknown, listsubrepos
1590 1592 )
1591 1593 lfdirstate = lfutil.openlfdirstate(ui, repo)
1592 1594 unknown = [f for f in r.unknown if lfdirstate[f] == b'?']
1593 1595 ignored = [f for f in r.ignored if lfdirstate[f] == b'?']
1594 1596 return scmutil.status(
1595 1597 r.modified, r.added, r.removed, r.deleted, unknown, ignored, r.clean
1596 1598 )
1597 1599
1598 1600 repo.status = overridestatus
1599 1601 orig(ui, repo, *dirs, **opts)
1600 1602 repo.status = oldstatus
1601 1603
1602 1604
1603 1605 @eh.wrapcommand(b'rollback')
1604 1606 def overriderollback(orig, ui, repo, **opts):
1605 1607 with repo.wlock():
1606 1608 before = repo.dirstate.parents()
1607 1609 orphans = {
1608 1610 f
1609 1611 for f in repo.dirstate
1610 1612 if lfutil.isstandin(f) and repo.dirstate[f] != b'r'
1611 1613 }
1612 1614 result = orig(ui, repo, **opts)
1613 1615 after = repo.dirstate.parents()
1614 1616 if before == after:
1615 1617 return result # no need to restore standins
1616 1618
1617 1619 pctx = repo[b'.']
1618 1620 for f in repo.dirstate:
1619 1621 if lfutil.isstandin(f):
1620 1622 orphans.discard(f)
1621 1623 if repo.dirstate[f] == b'r':
1622 1624 repo.wvfs.unlinkpath(f, ignoremissing=True)
1623 1625 elif f in pctx:
1624 1626 fctx = pctx[f]
1625 1627 repo.wwrite(f, fctx.data(), fctx.flags())
1626 1628 else:
1627 1629 # content of standin is not so important in 'a',
1628 1630 # 'm' or 'n' (coming from the 2nd parent) cases
1629 1631 lfutil.writestandin(repo, f, b'', False)
1630 1632 for standin in orphans:
1631 1633 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1632 1634
1633 1635 lfdirstate = lfutil.openlfdirstate(ui, repo)
1634 1636 with lfdirstate.parentchange():
1635 1637 orphans = set(lfdirstate)
1636 1638 lfiles = lfutil.listlfiles(repo)
1637 1639 for file in lfiles:
1638 1640 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1639 1641 orphans.discard(file)
1640 1642 for lfile in orphans:
1641 1643 lfdirstate.drop(lfile)
1642 1644 lfdirstate.write()
1643 1645 return result
1644 1646
1645 1647
1646 1648 @eh.wrapcommand(b'transplant', extension=b'transplant')
1647 1649 def overridetransplant(orig, ui, repo, *revs, **opts):
1648 1650 resuming = opts.get('continue')
1649 1651 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1650 1652 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1651 1653 try:
1652 1654 result = orig(ui, repo, *revs, **opts)
1653 1655 finally:
1654 1656 repo._lfstatuswriters.pop()
1655 1657 repo._lfcommithooks.pop()
1656 1658 return result
1657 1659
1658 1660
1659 1661 @eh.wrapcommand(b'cat')
1660 1662 def overridecat(orig, ui, repo, file1, *pats, **opts):
1661 1663 opts = pycompat.byteskwargs(opts)
1662 1664 ctx = scmutil.revsingle(repo, opts.get(b'rev'))
1663 1665 err = 1
1664 1666 notbad = set()
1665 1667 m = scmutil.match(ctx, (file1,) + pats, opts)
1666 1668 origmatchfn = m.matchfn
1667 1669
1668 1670 def lfmatchfn(f):
1669 1671 if origmatchfn(f):
1670 1672 return True
1671 1673 lf = lfutil.splitstandin(f)
1672 1674 if lf is None:
1673 1675 return False
1674 1676 notbad.add(lf)
1675 1677 return origmatchfn(lf)
1676 1678
1677 1679 m.matchfn = lfmatchfn
1678 1680 origbadfn = m.bad
1679 1681
1680 1682 def lfbadfn(f, msg):
1681 1683 if not f in notbad:
1682 1684 origbadfn(f, msg)
1683 1685
1684 1686 m.bad = lfbadfn
1685 1687
1686 1688 origvisitdirfn = m.visitdir
1687 1689
1688 1690 def lfvisitdirfn(dir):
1689 1691 if dir == lfutil.shortname:
1690 1692 return True
1691 1693 ret = origvisitdirfn(dir)
1692 1694 if ret:
1693 1695 return ret
1694 1696 lf = lfutil.splitstandin(dir)
1695 1697 if lf is None:
1696 1698 return False
1697 1699 return origvisitdirfn(lf)
1698 1700
1699 1701 m.visitdir = lfvisitdirfn
1700 1702
1701 1703 for f in ctx.walk(m):
1702 1704 with cmdutil.makefileobj(ctx, opts.get(b'output'), pathname=f) as fp:
1703 1705 lf = lfutil.splitstandin(f)
1704 1706 if lf is None or origmatchfn(f):
1705 1707 # duplicating unreachable code from commands.cat
1706 1708 data = ctx[f].data()
1707 1709 if opts.get(b'decode'):
1708 1710 data = repo.wwritedata(f, data)
1709 1711 fp.write(data)
1710 1712 else:
1711 1713 hash = lfutil.readasstandin(ctx[f])
1712 1714 if not lfutil.inusercache(repo.ui, hash):
1713 1715 store = storefactory.openstore(repo)
1714 1716 success, missing = store.get([(lf, hash)])
1715 1717 if len(success) != 1:
1716 1718 raise error.Abort(
1717 1719 _(
1718 1720 b'largefile %s is not in cache and could not be '
1719 1721 b'downloaded'
1720 1722 )
1721 1723 % lf
1722 1724 )
1723 1725 path = lfutil.usercachepath(repo.ui, hash)
1724 1726 with open(path, b"rb") as fpin:
1725 1727 for chunk in util.filechunkiter(fpin):
1726 1728 fp.write(chunk)
1727 1729 err = 0
1728 1730 return err
1729 1731
1730 1732
1731 1733 @eh.wrapfunction(merge, b'_update')
1732 1734 def mergeupdate(orig, repo, node, branchmerge, force, *args, **kwargs):
1733 1735 matcher = kwargs.get('matcher', None)
1734 1736 # note if this is a partial update
1735 1737 partial = matcher and not matcher.always()
1736 1738 with repo.wlock():
1737 1739 # branch | | |
1738 1740 # merge | force | partial | action
1739 1741 # -------+-------+---------+--------------
1740 1742 # x | x | x | linear-merge
1741 1743 # o | x | x | branch-merge
1742 1744 # x | o | x | overwrite (as clean update)
1743 1745 # o | o | x | force-branch-merge (*1)
1744 1746 # x | x | o | (*)
1745 1747 # o | x | o | (*)
1746 1748 # x | o | o | overwrite (as revert)
1747 1749 # o | o | o | (*)
1748 1750 #
1749 1751 # (*) don't care
1750 1752 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1751 1753
1752 1754 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1753 1755 unsure, s = lfdirstate.status(
1754 1756 matchmod.always(),
1755 1757 subrepos=[],
1756 1758 ignored=False,
1757 1759 clean=True,
1758 1760 unknown=False,
1759 1761 )
1760 1762 oldclean = set(s.clean)
1761 1763 pctx = repo[b'.']
1762 1764 dctx = repo[node]
1763 1765 for lfile in unsure + s.modified:
1764 1766 lfileabs = repo.wvfs.join(lfile)
1765 1767 if not repo.wvfs.exists(lfileabs):
1766 1768 continue
1767 1769 lfhash = lfutil.hashfile(lfileabs)
1768 1770 standin = lfutil.standin(lfile)
1769 1771 lfutil.writestandin(
1770 1772 repo, standin, lfhash, lfutil.getexecutable(lfileabs)
1771 1773 )
1772 1774 if standin in pctx and lfhash == lfutil.readasstandin(
1773 1775 pctx[standin]
1774 1776 ):
1775 1777 oldclean.add(lfile)
1776 1778 for lfile in s.added:
1777 1779 fstandin = lfutil.standin(lfile)
1778 1780 if fstandin not in dctx:
1779 1781 # in this case, content of standin file is meaningless
1780 1782 # (in dctx, lfile is unknown, or normal file)
1781 1783 continue
1782 1784 lfutil.updatestandin(repo, lfile, fstandin)
1783 1785 # mark all clean largefiles as dirty, just in case the update gets
1784 1786 # interrupted before largefiles and lfdirstate are synchronized
1785 1787 for lfile in oldclean:
1786 1788 lfdirstate.normallookup(lfile)
1787 1789 lfdirstate.write()
1788 1790
1789 1791 oldstandins = lfutil.getstandinsstate(repo)
1790 1792 wc = kwargs.get('wc')
1791 1793 if wc and wc.isinmemory():
1792 1794 # largefiles is not a good candidate for in-memory merge (large
1793 1795 # files, custom dirstate, matcher usage).
1794 1796 raise error.ProgrammingError(
1795 1797 b'largefiles is not compatible with in-memory merge'
1796 1798 )
1797 1799 result = orig(repo, node, branchmerge, force, *args, **kwargs)
1798 1800
1799 1801 newstandins = lfutil.getstandinsstate(repo)
1800 1802 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1801 1803
1802 1804 # to avoid leaving all largefiles as dirty and thus rehash them, mark
1803 1805 # all the ones that didn't change as clean
1804 1806 for lfile in oldclean.difference(filelist):
1805 1807 lfdirstate.normal(lfile)
1806 1808 lfdirstate.write()
1807 1809
1808 1810 if branchmerge or force or partial:
1809 1811 filelist.extend(s.deleted + s.removed)
1810 1812
1811 1813 lfcommands.updatelfiles(
1812 1814 repo.ui, repo, filelist=filelist, normallookup=partial
1813 1815 )
1814 1816
1815 1817 return result
1816 1818
1817 1819
1818 1820 @eh.wrapfunction(scmutil, b'marktouched')
1819 1821 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1820 1822 result = orig(repo, files, *args, **kwargs)
1821 1823
1822 1824 filelist = []
1823 1825 for f in files:
1824 1826 lf = lfutil.splitstandin(f)
1825 1827 if lf is not None:
1826 1828 filelist.append(lf)
1827 1829 if filelist:
1828 1830 lfcommands.updatelfiles(
1829 1831 repo.ui,
1830 1832 repo,
1831 1833 filelist=filelist,
1832 1834 printmessage=False,
1833 1835 normallookup=True,
1834 1836 )
1835 1837
1836 1838 return result
1837 1839
1838 1840
1839 1841 @eh.wrapfunction(upgrade_actions, b'preservedrequirements')
1840 1842 @eh.wrapfunction(upgrade_actions, b'supporteddestrequirements')
1841 1843 def upgraderequirements(orig, repo):
1842 1844 reqs = orig(repo)
1843 1845 if b'largefiles' in repo.requirements:
1844 1846 reqs.add(b'largefiles')
1845 1847 return reqs
1846 1848
1847 1849
1848 1850 _lfscheme = b'largefile://'
1849 1851
1850 1852
1851 1853 @eh.wrapfunction(urlmod, b'open')
1852 1854 def openlargefile(orig, ui, url_, data=None, **kwargs):
1853 1855 if url_.startswith(_lfscheme):
1854 1856 if data:
1855 1857 msg = b"cannot use data on a 'largefile://' url"
1856 1858 raise error.ProgrammingError(msg)
1857 1859 lfid = url_[len(_lfscheme) :]
1858 1860 return storefactory.getlfile(ui, lfid)
1859 1861 else:
1860 1862 return orig(ui, url_, data=data, **kwargs)
General Comments 0
You need to be logged in to leave comments. Login now