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