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