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