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