##// END OF EJS Templates
largefiles: move identical statement to after if/else
Martin von Zweigbergk -
r32301:8a87bfc5 default
parent child Browse files
Show More
@@ -1,1468 +1,1466 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 import (
18 18 archival,
19 19 cmdutil,
20 20 error,
21 21 hg,
22 22 match as matchmod,
23 23 pathutil,
24 24 registrar,
25 25 scmutil,
26 26 smartset,
27 27 util,
28 28 )
29 29
30 30 from . import (
31 31 lfcommands,
32 32 lfutil,
33 33 storefactory,
34 34 )
35 35
36 36 # -- Utility functions: commonly/repeatedly needed functionality ---------------
37 37
38 38 def composelargefilematcher(match, manifest):
39 39 '''create a matcher that matches only the largefiles in the original
40 40 matcher'''
41 41 m = copy.copy(match)
42 42 lfile = lambda f: lfutil.standin(f) in manifest
43 43 m._files = filter(lfile, m._files)
44 44 m._fileroots = set(m._files)
45 45 m._always = False
46 46 origmatchfn = m.matchfn
47 47 m.matchfn = lambda f: lfile(f) and origmatchfn(f)
48 48 return m
49 49
50 50 def composenormalfilematcher(match, manifest, exclude=None):
51 51 excluded = set()
52 52 if exclude is not None:
53 53 excluded.update(exclude)
54 54
55 55 m = copy.copy(match)
56 56 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
57 57 manifest or f in excluded)
58 58 m._files = filter(notlfile, m._files)
59 59 m._fileroots = set(m._files)
60 60 m._always = False
61 61 origmatchfn = m.matchfn
62 62 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
63 63 return m
64 64
65 65 def installnormalfilesmatchfn(manifest):
66 66 '''installmatchfn with a matchfn that ignores all largefiles'''
67 67 def overridematch(ctx, pats=(), opts=None, globbed=False,
68 68 default='relpath', badfn=None):
69 69 if opts is None:
70 70 opts = {}
71 71 match = oldmatch(ctx, pats, opts, globbed, default, badfn=badfn)
72 72 return composenormalfilematcher(match, manifest)
73 73 oldmatch = installmatchfn(overridematch)
74 74
75 75 def installmatchfn(f):
76 76 '''monkey patch the scmutil module with a custom match function.
77 77 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
78 78 oldmatch = scmutil.match
79 79 setattr(f, 'oldmatch', oldmatch)
80 80 scmutil.match = f
81 81 return oldmatch
82 82
83 83 def restorematchfn():
84 84 '''restores scmutil.match to what it was before installmatchfn
85 85 was called. no-op if scmutil.match is its original function.
86 86
87 87 Note that n calls to installmatchfn will require n calls to
88 88 restore the original matchfn.'''
89 89 scmutil.match = getattr(scmutil.match, 'oldmatch')
90 90
91 91 def installmatchandpatsfn(f):
92 92 oldmatchandpats = scmutil.matchandpats
93 93 setattr(f, 'oldmatchandpats', oldmatchandpats)
94 94 scmutil.matchandpats = f
95 95 return oldmatchandpats
96 96
97 97 def restorematchandpatsfn():
98 98 '''restores scmutil.matchandpats to what it was before
99 99 installmatchandpatsfn was called. No-op if scmutil.matchandpats
100 100 is its original function.
101 101
102 102 Note that n calls to installmatchandpatsfn will require n calls
103 103 to restore the original matchfn.'''
104 104 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
105 105 scmutil.matchandpats)
106 106
107 107 def addlargefiles(ui, repo, isaddremove, matcher, **opts):
108 108 large = opts.get(r'large')
109 109 lfsize = lfutil.getminsize(
110 110 ui, lfutil.islfilesrepo(repo), opts.get(r'lfsize'))
111 111
112 112 lfmatcher = None
113 113 if lfutil.islfilesrepo(repo):
114 114 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
115 115 if lfpats:
116 116 lfmatcher = matchmod.match(repo.root, '', list(lfpats))
117 117
118 118 lfnames = []
119 119 m = matcher
120 120
121 121 wctx = repo[None]
122 122 for f in repo.walk(matchmod.badmatch(m, lambda x, y: None)):
123 123 exact = m.exact(f)
124 124 lfile = lfutil.standin(f) in wctx
125 125 nfile = f in wctx
126 126 exists = lfile or nfile
127 127
128 128 # addremove in core gets fancy with the name, add doesn't
129 129 if isaddremove:
130 130 name = m.uipath(f)
131 131 else:
132 132 name = m.rel(f)
133 133
134 134 # Don't warn the user when they attempt to add a normal tracked file.
135 135 # The normal add code will do that for us.
136 136 if exact and exists:
137 137 if lfile:
138 138 ui.warn(_('%s already a largefile\n') % name)
139 139 continue
140 140
141 141 if (exact or not exists) and not lfutil.isstandin(f):
142 142 # In case the file was removed previously, but not committed
143 143 # (issue3507)
144 144 if not repo.wvfs.exists(f):
145 145 continue
146 146
147 147 abovemin = (lfsize and
148 148 repo.wvfs.lstat(f).st_size >= lfsize * 1024 * 1024)
149 149 if large or abovemin or (lfmatcher and lfmatcher(f)):
150 150 lfnames.append(f)
151 151 if ui.verbose or not exact:
152 152 ui.status(_('adding %s as a largefile\n') % name)
153 153
154 154 bad = []
155 155
156 156 # Need to lock, otherwise there could be a race condition between
157 157 # when standins are created and added to the repo.
158 158 with repo.wlock():
159 159 if not opts.get('dry_run'):
160 160 standins = []
161 161 lfdirstate = lfutil.openlfdirstate(ui, repo)
162 162 for f in lfnames:
163 163 standinname = lfutil.standin(f)
164 164 lfutil.writestandin(repo, standinname, hash='',
165 165 executable=lfutil.getexecutable(repo.wjoin(f)))
166 166 standins.append(standinname)
167 167 if lfdirstate[f] == 'r':
168 168 lfdirstate.normallookup(f)
169 169 else:
170 170 lfdirstate.add(f)
171 171 lfdirstate.write()
172 172 bad += [lfutil.splitstandin(f)
173 173 for f in repo[None].add(standins)
174 174 if f in m.files()]
175 175
176 176 added = [f for f in lfnames if f not in bad]
177 177 return added, bad
178 178
179 179 def removelargefiles(ui, repo, isaddremove, matcher, **opts):
180 180 after = opts.get('after')
181 181 m = composelargefilematcher(matcher, repo[None].manifest())
182 182 try:
183 183 repo.lfstatus = True
184 184 s = repo.status(match=m, clean=not isaddremove)
185 185 finally:
186 186 repo.lfstatus = False
187 187 manifest = repo[None].manifest()
188 188 modified, added, deleted, clean = [[f for f in list
189 189 if lfutil.standin(f) in manifest]
190 190 for list in (s.modified, s.added,
191 191 s.deleted, s.clean)]
192 192
193 193 def warn(files, msg):
194 194 for f in files:
195 195 ui.warn(msg % m.rel(f))
196 196 return int(len(files) > 0)
197 197
198 198 result = 0
199 199
200 200 if after:
201 201 remove = deleted
202 202 result = warn(modified + added + clean,
203 203 _('not removing %s: file still exists\n'))
204 204 else:
205 205 remove = deleted + clean
206 206 result = warn(modified, _('not removing %s: file is modified (use -f'
207 207 ' to force removal)\n'))
208 208 result = warn(added, _('not removing %s: file has been marked for add'
209 209 ' (use forget to undo)\n')) or result
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 # addremove in core gets fancy with the name, remove doesn't
218 218 if isaddremove:
219 219 name = m.uipath(f)
220 220 else:
221 221 name = m.rel(f)
222 222 ui.status(_('removing %s\n') % name)
223 223
224 224 if not opts.get('dry_run'):
225 225 if not after:
226 226 repo.wvfs.unlinkpath(f, ignoremissing=True)
227 227
228 228 if opts.get('dry_run'):
229 229 return result
230 230
231 231 remove = [lfutil.standin(f) for f in remove]
232 232 # If this is being called by addremove, let the original addremove
233 233 # function handle this.
234 234 if not isaddremove:
235 235 for f in remove:
236 236 repo.wvfs.unlinkpath(f, ignoremissing=True)
237 237 repo[None].forget(remove)
238 238
239 239 for f in remove:
240 240 lfutil.synclfdirstate(repo, lfdirstate, lfutil.splitstandin(f),
241 241 False)
242 242
243 243 lfdirstate.write()
244 244
245 245 return result
246 246
247 247 # For overriding mercurial.hgweb.webcommands so that largefiles will
248 248 # appear at their right place in the manifests.
249 249 def decodepath(orig, path):
250 250 return lfutil.splitstandin(path) or path
251 251
252 252 # -- Wrappers: modify existing commands --------------------------------
253 253
254 254 def overrideadd(orig, ui, repo, *pats, **opts):
255 255 if opts.get('normal') and opts.get('large'):
256 256 raise error.Abort(_('--normal cannot be used with --large'))
257 257 return orig(ui, repo, *pats, **opts)
258 258
259 259 def cmdutiladd(orig, ui, repo, matcher, prefix, explicitonly, **opts):
260 260 # The --normal flag short circuits this override
261 261 if opts.get(r'normal'):
262 262 return orig(ui, repo, matcher, prefix, explicitonly, **opts)
263 263
264 264 ladded, lbad = addlargefiles(ui, repo, False, matcher, **opts)
265 265 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest(),
266 266 ladded)
267 267 bad = orig(ui, repo, normalmatcher, prefix, explicitonly, **opts)
268 268
269 269 bad.extend(f for f in lbad)
270 270 return bad
271 271
272 272 def cmdutilremove(orig, ui, repo, matcher, prefix, after, force, subrepos):
273 273 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest())
274 274 result = orig(ui, repo, normalmatcher, prefix, after, force, subrepos)
275 275 return removelargefiles(ui, repo, False, matcher, after=after,
276 276 force=force) or result
277 277
278 278 def overridestatusfn(orig, repo, rev2, **opts):
279 279 try:
280 280 repo._repo.lfstatus = True
281 281 return orig(repo, rev2, **opts)
282 282 finally:
283 283 repo._repo.lfstatus = False
284 284
285 285 def overridestatus(orig, ui, repo, *pats, **opts):
286 286 try:
287 287 repo.lfstatus = True
288 288 return orig(ui, repo, *pats, **opts)
289 289 finally:
290 290 repo.lfstatus = False
291 291
292 292 def overridedirty(orig, repo, ignoreupdate=False):
293 293 try:
294 294 repo._repo.lfstatus = True
295 295 return orig(repo, ignoreupdate)
296 296 finally:
297 297 repo._repo.lfstatus = False
298 298
299 299 def overridelog(orig, ui, repo, *pats, **opts):
300 300 def overridematchandpats(ctx, pats=(), opts=None, globbed=False,
301 301 default='relpath', badfn=None):
302 302 """Matcher that merges root directory with .hglf, suitable for log.
303 303 It is still possible to match .hglf directly.
304 304 For any listed files run log on the standin too.
305 305 matchfn tries both the given filename and with .hglf stripped.
306 306 """
307 307 if opts is None:
308 308 opts = {}
309 309 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default,
310 310 badfn=badfn)
311 311 m, p = copy.copy(matchandpats)
312 312
313 313 if m.always():
314 314 # We want to match everything anyway, so there's no benefit trying
315 315 # to add standins.
316 316 return matchandpats
317 317
318 318 pats = set(p)
319 319
320 320 def fixpats(pat, tostandin=lfutil.standin):
321 321 if pat.startswith('set:'):
322 322 return pat
323 323
324 324 kindpat = matchmod._patsplit(pat, None)
325 325
326 326 if kindpat[0] is not None:
327 327 return kindpat[0] + ':' + tostandin(kindpat[1])
328 328 return tostandin(kindpat[1])
329 329
330 330 if m._cwd:
331 331 hglf = lfutil.shortname
332 332 back = util.pconvert(m.rel(hglf)[:-len(hglf)])
333 333
334 334 def tostandin(f):
335 335 # The file may already be a standin, so truncate the back
336 336 # prefix and test before mangling it. This avoids turning
337 337 # 'glob:../.hglf/foo*' into 'glob:../.hglf/../.hglf/foo*'.
338 338 if f.startswith(back) and lfutil.splitstandin(f[len(back):]):
339 339 return f
340 340
341 341 # An absolute path is from outside the repo, so truncate the
342 342 # path to the root before building the standin. Otherwise cwd
343 343 # is somewhere in the repo, relative to root, and needs to be
344 344 # prepended before building the standin.
345 345 if os.path.isabs(m._cwd):
346 346 f = f[len(back):]
347 347 else:
348 348 f = m._cwd + '/' + f
349 349 return back + lfutil.standin(f)
350
351 pats.update(fixpats(f, tostandin) for f in p)
352 350 else:
353 351 def tostandin(f):
354 352 if lfutil.isstandin(f):
355 353 return f
356 354 return lfutil.standin(f)
357 pats.update(fixpats(f, tostandin) for f in p)
355 pats.update(fixpats(f, tostandin) for f in p)
358 356
359 357 for i in range(0, len(m._files)):
360 358 # Don't add '.hglf' to m.files, since that is already covered by '.'
361 359 if m._files[i] == '.':
362 360 continue
363 361 standin = lfutil.standin(m._files[i])
364 362 # If the "standin" is a directory, append instead of replace to
365 363 # support naming a directory on the command line with only
366 364 # largefiles. The original directory is kept to support normal
367 365 # files.
368 366 if standin in ctx:
369 367 m._files[i] = standin
370 368 elif m._files[i] not in ctx and repo.wvfs.isdir(standin):
371 369 m._files.append(standin)
372 370
373 371 m._fileroots = set(m._files)
374 372 m._always = False
375 373 origmatchfn = m.matchfn
376 374 def lfmatchfn(f):
377 375 lf = lfutil.splitstandin(f)
378 376 if lf is not None and origmatchfn(lf):
379 377 return True
380 378 r = origmatchfn(f)
381 379 return r
382 380 m.matchfn = lfmatchfn
383 381
384 382 ui.debug('updated patterns: %s\n' % sorted(pats))
385 383 return m, pats
386 384
387 385 # For hg log --patch, the match object is used in two different senses:
388 386 # (1) to determine what revisions should be printed out, and
389 387 # (2) to determine what files to print out diffs for.
390 388 # The magic matchandpats override should be used for case (1) but not for
391 389 # case (2).
392 390 def overridemakelogfilematcher(repo, pats, opts, badfn=None):
393 391 wctx = repo[None]
394 392 match, pats = oldmatchandpats(wctx, pats, opts, badfn=badfn)
395 393 return lambda rev: match
396 394
397 395 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
398 396 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
399 397 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
400 398
401 399 try:
402 400 return orig(ui, repo, *pats, **opts)
403 401 finally:
404 402 restorematchandpatsfn()
405 403 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
406 404
407 405 def overrideverify(orig, ui, repo, *pats, **opts):
408 406 large = opts.pop('large', False)
409 407 all = opts.pop('lfa', False)
410 408 contents = opts.pop('lfc', False)
411 409
412 410 result = orig(ui, repo, *pats, **opts)
413 411 if large or all or contents:
414 412 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
415 413 return result
416 414
417 415 def overridedebugstate(orig, ui, repo, *pats, **opts):
418 416 large = opts.pop('large', False)
419 417 if large:
420 418 class fakerepo(object):
421 419 dirstate = lfutil.openlfdirstate(ui, repo)
422 420 orig(ui, fakerepo, *pats, **opts)
423 421 else:
424 422 orig(ui, repo, *pats, **opts)
425 423
426 424 # Before starting the manifest merge, merge.updates will call
427 425 # _checkunknownfile to check if there are any files in the merged-in
428 426 # changeset that collide with unknown files in the working copy.
429 427 #
430 428 # The largefiles are seen as unknown, so this prevents us from merging
431 429 # in a file 'foo' if we already have a largefile with the same name.
432 430 #
433 431 # The overridden function filters the unknown files by removing any
434 432 # largefiles. This makes the merge proceed and we can then handle this
435 433 # case further in the overridden calculateupdates function below.
436 434 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
437 435 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
438 436 return False
439 437 return origfn(repo, wctx, mctx, f, f2)
440 438
441 439 # The manifest merge handles conflicts on the manifest level. We want
442 440 # to handle changes in largefile-ness of files at this level too.
443 441 #
444 442 # The strategy is to run the original calculateupdates and then process
445 443 # the action list it outputs. There are two cases we need to deal with:
446 444 #
447 445 # 1. Normal file in p1, largefile in p2. Here the largefile is
448 446 # detected via its standin file, which will enter the working copy
449 447 # with a "get" action. It is not "merge" since the standin is all
450 448 # Mercurial is concerned with at this level -- the link to the
451 449 # existing normal file is not relevant here.
452 450 #
453 451 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
454 452 # since the largefile will be present in the working copy and
455 453 # different from the normal file in p2. Mercurial therefore
456 454 # triggers a merge action.
457 455 #
458 456 # In both cases, we prompt the user and emit new actions to either
459 457 # remove the standin (if the normal file was kept) or to remove the
460 458 # normal file and get the standin (if the largefile was kept). The
461 459 # default prompt answer is to use the largefile version since it was
462 460 # presumably changed on purpose.
463 461 #
464 462 # Finally, the merge.applyupdates function will then take care of
465 463 # writing the files into the working copy and lfcommands.updatelfiles
466 464 # will update the largefiles.
467 465 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
468 466 acceptremote, *args, **kwargs):
469 467 overwrite = force and not branchmerge
470 468 actions, diverge, renamedelete = origfn(
471 469 repo, p1, p2, pas, branchmerge, force, acceptremote, *args, **kwargs)
472 470
473 471 if overwrite:
474 472 return actions, diverge, renamedelete
475 473
476 474 # Convert to dictionary with filename as key and action as value.
477 475 lfiles = set()
478 476 for f in actions:
479 477 splitstandin = lfutil.splitstandin(f)
480 478 if splitstandin in p1:
481 479 lfiles.add(splitstandin)
482 480 elif lfutil.standin(f) in p1:
483 481 lfiles.add(f)
484 482
485 483 for lfile in sorted(lfiles):
486 484 standin = lfutil.standin(lfile)
487 485 (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
488 486 (sm, sargs, smsg) = actions.get(standin, (None, None, None))
489 487 if sm in ('g', 'dc') and lm != 'r':
490 488 if sm == 'dc':
491 489 f1, f2, fa, move, anc = sargs
492 490 sargs = (p2[f2].flags(), False)
493 491 # Case 1: normal file in the working copy, largefile in
494 492 # the second parent
495 493 usermsg = _('remote turned local normal file %s into a largefile\n'
496 494 'use (l)argefile or keep (n)ormal file?'
497 495 '$$ &Largefile $$ &Normal file') % lfile
498 496 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
499 497 actions[lfile] = ('r', None, 'replaced by standin')
500 498 actions[standin] = ('g', sargs, 'replaces standin')
501 499 else: # keep local normal file
502 500 actions[lfile] = ('k', None, 'replaces standin')
503 501 if branchmerge:
504 502 actions[standin] = ('k', None, 'replaced by non-standin')
505 503 else:
506 504 actions[standin] = ('r', None, 'replaced by non-standin')
507 505 elif lm in ('g', 'dc') and sm != 'r':
508 506 if lm == 'dc':
509 507 f1, f2, fa, move, anc = largs
510 508 largs = (p2[f2].flags(), False)
511 509 # Case 2: largefile in the working copy, normal file in
512 510 # the second parent
513 511 usermsg = _('remote turned local largefile %s into a normal file\n'
514 512 'keep (l)argefile or use (n)ormal file?'
515 513 '$$ &Largefile $$ &Normal file') % lfile
516 514 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
517 515 if branchmerge:
518 516 # largefile can be restored from standin safely
519 517 actions[lfile] = ('k', None, 'replaced by standin')
520 518 actions[standin] = ('k', None, 'replaces standin')
521 519 else:
522 520 # "lfile" should be marked as "removed" without
523 521 # removal of itself
524 522 actions[lfile] = ('lfmr', None,
525 523 'forget non-standin largefile')
526 524
527 525 # linear-merge should treat this largefile as 're-added'
528 526 actions[standin] = ('a', None, 'keep standin')
529 527 else: # pick remote normal file
530 528 actions[lfile] = ('g', largs, 'replaces standin')
531 529 actions[standin] = ('r', None, 'replaced by non-standin')
532 530
533 531 return actions, diverge, renamedelete
534 532
535 533 def mergerecordupdates(orig, repo, actions, branchmerge):
536 534 if 'lfmr' in actions:
537 535 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
538 536 for lfile, args, msg in actions['lfmr']:
539 537 # this should be executed before 'orig', to execute 'remove'
540 538 # before all other actions
541 539 repo.dirstate.remove(lfile)
542 540 # make sure lfile doesn't get synclfdirstate'd as normal
543 541 lfdirstate.add(lfile)
544 542 lfdirstate.write()
545 543
546 544 return orig(repo, actions, branchmerge)
547 545
548 546 # Override filemerge to prompt the user about how they wish to merge
549 547 # largefiles. This will handle identical edits without prompting the user.
550 548 def overridefilemerge(origfn, premerge, repo, mynode, orig, fcd, fco, fca,
551 549 labels=None):
552 550 if not lfutil.isstandin(orig) or fcd.isabsent() or fco.isabsent():
553 551 return origfn(premerge, repo, mynode, orig, fcd, fco, fca,
554 552 labels=labels)
555 553
556 554 ahash = lfutil.readasstandin(fca).lower()
557 555 dhash = lfutil.readasstandin(fcd).lower()
558 556 ohash = lfutil.readasstandin(fco).lower()
559 557 if (ohash != ahash and
560 558 ohash != dhash and
561 559 (dhash == ahash or
562 560 repo.ui.promptchoice(
563 561 _('largefile %s has a merge conflict\nancestor was %s\n'
564 562 'keep (l)ocal %s or\ntake (o)ther %s?'
565 563 '$$ &Local $$ &Other') %
566 564 (lfutil.splitstandin(orig), ahash, dhash, ohash),
567 565 0) == 1)):
568 566 repo.wwrite(fcd.path(), fco.data(), fco.flags())
569 567 return True, 0, False
570 568
571 569 def copiespathcopies(orig, ctx1, ctx2, match=None):
572 570 copies = orig(ctx1, ctx2, match=match)
573 571 updated = {}
574 572
575 573 for k, v in copies.iteritems():
576 574 updated[lfutil.splitstandin(k) or k] = lfutil.splitstandin(v) or v
577 575
578 576 return updated
579 577
580 578 # Copy first changes the matchers to match standins instead of
581 579 # largefiles. Then it overrides util.copyfile in that function it
582 580 # checks if the destination largefile already exists. It also keeps a
583 581 # list of copied files so that the largefiles can be copied and the
584 582 # dirstate updated.
585 583 def overridecopy(orig, ui, repo, pats, opts, rename=False):
586 584 # doesn't remove largefile on rename
587 585 if len(pats) < 2:
588 586 # this isn't legal, let the original function deal with it
589 587 return orig(ui, repo, pats, opts, rename)
590 588
591 589 # This could copy both lfiles and normal files in one command,
592 590 # but we don't want to do that. First replace their matcher to
593 591 # only match normal files and run it, then replace it to just
594 592 # match largefiles and run it again.
595 593 nonormalfiles = False
596 594 nolfiles = False
597 595 installnormalfilesmatchfn(repo[None].manifest())
598 596 try:
599 597 result = orig(ui, repo, pats, opts, rename)
600 598 except error.Abort as e:
601 599 if str(e) != _('no files to copy'):
602 600 raise e
603 601 else:
604 602 nonormalfiles = True
605 603 result = 0
606 604 finally:
607 605 restorematchfn()
608 606
609 607 # The first rename can cause our current working directory to be removed.
610 608 # In that case there is nothing left to copy/rename so just quit.
611 609 try:
612 610 repo.getcwd()
613 611 except OSError:
614 612 return result
615 613
616 614 def makestandin(relpath):
617 615 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
618 616 return repo.wvfs.join(lfutil.standin(path))
619 617
620 618 fullpats = scmutil.expandpats(pats)
621 619 dest = fullpats[-1]
622 620
623 621 if os.path.isdir(dest):
624 622 if not os.path.isdir(makestandin(dest)):
625 623 os.makedirs(makestandin(dest))
626 624
627 625 try:
628 626 # When we call orig below it creates the standins but we don't add
629 627 # them to the dir state until later so lock during that time.
630 628 wlock = repo.wlock()
631 629
632 630 manifest = repo[None].manifest()
633 631 def overridematch(ctx, pats=(), opts=None, globbed=False,
634 632 default='relpath', badfn=None):
635 633 if opts is None:
636 634 opts = {}
637 635 newpats = []
638 636 # The patterns were previously mangled to add the standin
639 637 # directory; we need to remove that now
640 638 for pat in pats:
641 639 if matchmod.patkind(pat) is None and lfutil.shortname in pat:
642 640 newpats.append(pat.replace(lfutil.shortname, ''))
643 641 else:
644 642 newpats.append(pat)
645 643 match = oldmatch(ctx, newpats, opts, globbed, default, badfn=badfn)
646 644 m = copy.copy(match)
647 645 lfile = lambda f: lfutil.standin(f) in manifest
648 646 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
649 647 m._fileroots = set(m._files)
650 648 origmatchfn = m.matchfn
651 649 def matchfn(f):
652 650 lfile = lfutil.splitstandin(f)
653 651 return (lfile is not None and
654 652 (f in manifest) and
655 653 origmatchfn(lfile) or
656 654 None)
657 655 m.matchfn = matchfn
658 656 return m
659 657 oldmatch = installmatchfn(overridematch)
660 658 listpats = []
661 659 for pat in pats:
662 660 if matchmod.patkind(pat) is not None:
663 661 listpats.append(pat)
664 662 else:
665 663 listpats.append(makestandin(pat))
666 664
667 665 try:
668 666 origcopyfile = util.copyfile
669 667 copiedfiles = []
670 668 def overridecopyfile(src, dest):
671 669 if (lfutil.shortname in src and
672 670 dest.startswith(repo.wjoin(lfutil.shortname))):
673 671 destlfile = dest.replace(lfutil.shortname, '')
674 672 if not opts['force'] and os.path.exists(destlfile):
675 673 raise IOError('',
676 674 _('destination largefile already exists'))
677 675 copiedfiles.append((src, dest))
678 676 origcopyfile(src, dest)
679 677
680 678 util.copyfile = overridecopyfile
681 679 result += orig(ui, repo, listpats, opts, rename)
682 680 finally:
683 681 util.copyfile = origcopyfile
684 682
685 683 lfdirstate = lfutil.openlfdirstate(ui, repo)
686 684 for (src, dest) in copiedfiles:
687 685 if (lfutil.shortname in src and
688 686 dest.startswith(repo.wjoin(lfutil.shortname))):
689 687 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
690 688 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
691 689 destlfiledir = repo.wvfs.dirname(repo.wjoin(destlfile)) or '.'
692 690 if not os.path.isdir(destlfiledir):
693 691 os.makedirs(destlfiledir)
694 692 if rename:
695 693 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
696 694
697 695 # The file is gone, but this deletes any empty parent
698 696 # directories as a side-effect.
699 697 repo.wvfs.unlinkpath(srclfile, ignoremissing=True)
700 698 lfdirstate.remove(srclfile)
701 699 else:
702 700 util.copyfile(repo.wjoin(srclfile),
703 701 repo.wjoin(destlfile))
704 702
705 703 lfdirstate.add(destlfile)
706 704 lfdirstate.write()
707 705 except error.Abort as e:
708 706 if str(e) != _('no files to copy'):
709 707 raise e
710 708 else:
711 709 nolfiles = True
712 710 finally:
713 711 restorematchfn()
714 712 wlock.release()
715 713
716 714 if nolfiles and nonormalfiles:
717 715 raise error.Abort(_('no files to copy'))
718 716
719 717 return result
720 718
721 719 # When the user calls revert, we have to be careful to not revert any
722 720 # changes to other largefiles accidentally. This means we have to keep
723 721 # track of the largefiles that are being reverted so we only pull down
724 722 # the necessary largefiles.
725 723 #
726 724 # Standins are only updated (to match the hash of largefiles) before
727 725 # commits. Update the standins then run the original revert, changing
728 726 # the matcher to hit standins instead of largefiles. Based on the
729 727 # resulting standins update the largefiles.
730 728 def overriderevert(orig, ui, repo, ctx, parents, *pats, **opts):
731 729 # Because we put the standins in a bad state (by updating them)
732 730 # and then return them to a correct state we need to lock to
733 731 # prevent others from changing them in their incorrect state.
734 732 with repo.wlock():
735 733 lfdirstate = lfutil.openlfdirstate(ui, repo)
736 734 s = lfutil.lfdirstatestatus(lfdirstate, repo)
737 735 lfdirstate.write()
738 736 for lfile in s.modified:
739 737 lfutil.updatestandin(repo, lfile, lfutil.standin(lfile))
740 738 for lfile in s.deleted:
741 739 fstandin = lfutil.standin(lfile)
742 740 if (repo.wvfs.exists(fstandin)):
743 741 repo.wvfs.unlink(fstandin)
744 742
745 743 oldstandins = lfutil.getstandinsstate(repo)
746 744
747 745 def overridematch(mctx, pats=(), opts=None, globbed=False,
748 746 default='relpath', badfn=None):
749 747 if opts is None:
750 748 opts = {}
751 749 match = oldmatch(mctx, pats, opts, globbed, default, badfn=badfn)
752 750 m = copy.copy(match)
753 751
754 752 # revert supports recursing into subrepos, and though largefiles
755 753 # currently doesn't work correctly in that case, this match is
756 754 # called, so the lfdirstate above may not be the correct one for
757 755 # this invocation of match.
758 756 lfdirstate = lfutil.openlfdirstate(mctx.repo().ui, mctx.repo(),
759 757 False)
760 758
761 759 wctx = repo[None]
762 760 matchfiles = []
763 761 for f in m._files:
764 762 standin = lfutil.standin(f)
765 763 if standin in ctx or standin in mctx:
766 764 matchfiles.append(standin)
767 765 elif standin in wctx or lfdirstate[f] == 'r':
768 766 continue
769 767 else:
770 768 matchfiles.append(f)
771 769 m._files = matchfiles
772 770 m._fileroots = set(m._files)
773 771 origmatchfn = m.matchfn
774 772 def matchfn(f):
775 773 lfile = lfutil.splitstandin(f)
776 774 if lfile is not None:
777 775 return (origmatchfn(lfile) and
778 776 (f in ctx or f in mctx))
779 777 return origmatchfn(f)
780 778 m.matchfn = matchfn
781 779 return m
782 780 oldmatch = installmatchfn(overridematch)
783 781 try:
784 782 orig(ui, repo, ctx, parents, *pats, **opts)
785 783 finally:
786 784 restorematchfn()
787 785
788 786 newstandins = lfutil.getstandinsstate(repo)
789 787 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
790 788 # lfdirstate should be 'normallookup'-ed for updated files,
791 789 # because reverting doesn't touch dirstate for 'normal' files
792 790 # when target revision is explicitly specified: in such case,
793 791 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
794 792 # of target (standin) file.
795 793 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
796 794 normallookup=True)
797 795
798 796 # after pulling changesets, we need to take some extra care to get
799 797 # largefiles updated remotely
800 798 def overridepull(orig, ui, repo, source=None, **opts):
801 799 revsprepull = len(repo)
802 800 if not source:
803 801 source = 'default'
804 802 repo.lfpullsource = source
805 803 result = orig(ui, repo, source, **opts)
806 804 revspostpull = len(repo)
807 805 lfrevs = opts.get('lfrev', [])
808 806 if opts.get('all_largefiles'):
809 807 lfrevs.append('pulled()')
810 808 if lfrevs and revspostpull > revsprepull:
811 809 numcached = 0
812 810 repo.firstpulled = revsprepull # for pulled() revset expression
813 811 try:
814 812 for rev in scmutil.revrange(repo, lfrevs):
815 813 ui.note(_('pulling largefiles for revision %s\n') % rev)
816 814 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
817 815 numcached += len(cached)
818 816 finally:
819 817 del repo.firstpulled
820 818 ui.status(_("%d largefiles cached\n") % numcached)
821 819 return result
822 820
823 821 def overridepush(orig, ui, repo, *args, **kwargs):
824 822 """Override push command and store --lfrev parameters in opargs"""
825 823 lfrevs = kwargs.pop('lfrev', None)
826 824 if lfrevs:
827 825 opargs = kwargs.setdefault('opargs', {})
828 826 opargs['lfrevs'] = scmutil.revrange(repo, lfrevs)
829 827 return orig(ui, repo, *args, **kwargs)
830 828
831 829 def exchangepushoperation(orig, *args, **kwargs):
832 830 """Override pushoperation constructor and store lfrevs parameter"""
833 831 lfrevs = kwargs.pop('lfrevs', None)
834 832 pushop = orig(*args, **kwargs)
835 833 pushop.lfrevs = lfrevs
836 834 return pushop
837 835
838 836 revsetpredicate = registrar.revsetpredicate()
839 837
840 838 @revsetpredicate('pulled()')
841 839 def pulledrevsetsymbol(repo, subset, x):
842 840 """Changesets that just has been pulled.
843 841
844 842 Only available with largefiles from pull --lfrev expressions.
845 843
846 844 .. container:: verbose
847 845
848 846 Some examples:
849 847
850 848 - pull largefiles for all new changesets::
851 849
852 850 hg pull -lfrev "pulled()"
853 851
854 852 - pull largefiles for all new branch heads::
855 853
856 854 hg pull -lfrev "head(pulled()) and not closed()"
857 855
858 856 """
859 857
860 858 try:
861 859 firstpulled = repo.firstpulled
862 860 except AttributeError:
863 861 raise error.Abort(_("pulled() only available in --lfrev"))
864 862 return smartset.baseset([r for r in subset if r >= firstpulled])
865 863
866 864 def overrideclone(orig, ui, source, dest=None, **opts):
867 865 d = dest
868 866 if d is None:
869 867 d = hg.defaultdest(source)
870 868 if opts.get('all_largefiles') and not hg.islocal(d):
871 869 raise error.Abort(_(
872 870 '--all-largefiles is incompatible with non-local destination %s') %
873 871 d)
874 872
875 873 return orig(ui, source, dest, **opts)
876 874
877 875 def hgclone(orig, ui, opts, *args, **kwargs):
878 876 result = orig(ui, opts, *args, **kwargs)
879 877
880 878 if result is not None:
881 879 sourcerepo, destrepo = result
882 880 repo = destrepo.local()
883 881
884 882 # When cloning to a remote repo (like through SSH), no repo is available
885 883 # from the peer. Therefore the largefiles can't be downloaded and the
886 884 # hgrc can't be updated.
887 885 if not repo:
888 886 return result
889 887
890 888 # If largefiles is required for this repo, permanently enable it locally
891 889 if 'largefiles' in repo.requirements:
892 890 with repo.vfs('hgrc', 'a', text=True) as fp:
893 891 fp.write('\n[extensions]\nlargefiles=\n')
894 892
895 893 # Caching is implicitly limited to 'rev' option, since the dest repo was
896 894 # truncated at that point. The user may expect a download count with
897 895 # this option, so attempt whether or not this is a largefile repo.
898 896 if opts.get('all_largefiles'):
899 897 success, missing = lfcommands.downloadlfiles(ui, repo, None)
900 898
901 899 if missing != 0:
902 900 return None
903 901
904 902 return result
905 903
906 904 def hgpostshare(orig, sourcerepo, destrepo, bookmarks=True, defaultpath=None):
907 905 orig(sourcerepo, destrepo, bookmarks, defaultpath)
908 906
909 907 # If largefiles is required for this repo, permanently enable it locally
910 908 if 'largefiles' in destrepo.requirements:
911 909 with destrepo.vfs('hgrc', 'a+', text=True) as fp:
912 910 fp.write('\n[extensions]\nlargefiles=\n')
913 911
914 912 def overriderebase(orig, ui, repo, **opts):
915 913 if not util.safehasattr(repo, '_largefilesenabled'):
916 914 return orig(ui, repo, **opts)
917 915
918 916 resuming = opts.get('continue')
919 917 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
920 918 repo._lfstatuswriters.append(lambda *msg, **opts: None)
921 919 try:
922 920 return orig(ui, repo, **opts)
923 921 finally:
924 922 repo._lfstatuswriters.pop()
925 923 repo._lfcommithooks.pop()
926 924
927 925 def overridearchivecmd(orig, ui, repo, dest, **opts):
928 926 repo.unfiltered().lfstatus = True
929 927
930 928 try:
931 929 return orig(ui, repo.unfiltered(), dest, **opts)
932 930 finally:
933 931 repo.unfiltered().lfstatus = False
934 932
935 933 def hgwebarchive(orig, web, req, tmpl):
936 934 web.repo.lfstatus = True
937 935
938 936 try:
939 937 return orig(web, req, tmpl)
940 938 finally:
941 939 web.repo.lfstatus = False
942 940
943 941 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
944 942 prefix='', mtime=None, subrepos=None):
945 943 # For some reason setting repo.lfstatus in hgwebarchive only changes the
946 944 # unfiltered repo's attr, so check that as well.
947 945 if not repo.lfstatus and not repo.unfiltered().lfstatus:
948 946 return orig(repo, dest, node, kind, decode, matchfn, prefix, mtime,
949 947 subrepos)
950 948
951 949 # No need to lock because we are only reading history and
952 950 # largefile caches, neither of which are modified.
953 951 if node is not None:
954 952 lfcommands.cachelfiles(repo.ui, repo, node)
955 953
956 954 if kind not in archival.archivers:
957 955 raise error.Abort(_("unknown archive type '%s'") % kind)
958 956
959 957 ctx = repo[node]
960 958
961 959 if kind == 'files':
962 960 if prefix:
963 961 raise error.Abort(
964 962 _('cannot give prefix when archiving to files'))
965 963 else:
966 964 prefix = archival.tidyprefix(dest, kind, prefix)
967 965
968 966 def write(name, mode, islink, getdata):
969 967 if matchfn and not matchfn(name):
970 968 return
971 969 data = getdata()
972 970 if decode:
973 971 data = repo.wwritedata(name, data)
974 972 archiver.addfile(prefix + name, mode, islink, data)
975 973
976 974 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
977 975
978 976 if repo.ui.configbool("ui", "archivemeta", True):
979 977 write('.hg_archival.txt', 0o644, False,
980 978 lambda: archival.buildmetadata(ctx))
981 979
982 980 for f in ctx:
983 981 ff = ctx.flags(f)
984 982 getdata = ctx[f].data
985 983 lfile = lfutil.splitstandin(f)
986 984 if lfile is not None:
987 985 if node is not None:
988 986 path = lfutil.findfile(repo, getdata().strip())
989 987
990 988 if path is None:
991 989 raise error.Abort(
992 990 _('largefile %s not found in repo store or system cache')
993 991 % lfile)
994 992 else:
995 993 path = lfile
996 994
997 995 f = lfile
998 996
999 997 getdata = lambda: util.readfile(path)
1000 998 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, getdata)
1001 999
1002 1000 if subrepos:
1003 1001 for subpath in sorted(ctx.substate):
1004 1002 sub = ctx.workingsub(subpath)
1005 1003 submatch = matchmod.subdirmatcher(subpath, matchfn)
1006 1004 sub._repo.lfstatus = True
1007 1005 sub.archive(archiver, prefix, submatch)
1008 1006
1009 1007 archiver.done()
1010 1008
1011 1009 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None, decode=True):
1012 1010 if not repo._repo.lfstatus:
1013 1011 return orig(repo, archiver, prefix, match, decode)
1014 1012
1015 1013 repo._get(repo._state + ('hg',))
1016 1014 rev = repo._state[1]
1017 1015 ctx = repo._repo[rev]
1018 1016
1019 1017 if ctx.node() is not None:
1020 1018 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
1021 1019
1022 1020 def write(name, mode, islink, getdata):
1023 1021 # At this point, the standin has been replaced with the largefile name,
1024 1022 # so the normal matcher works here without the lfutil variants.
1025 1023 if match and not match(f):
1026 1024 return
1027 1025 data = getdata()
1028 1026 if decode:
1029 1027 data = repo._repo.wwritedata(name, data)
1030 1028
1031 1029 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
1032 1030
1033 1031 for f in ctx:
1034 1032 ff = ctx.flags(f)
1035 1033 getdata = ctx[f].data
1036 1034 lfile = lfutil.splitstandin(f)
1037 1035 if lfile is not None:
1038 1036 if ctx.node() is not None:
1039 1037 path = lfutil.findfile(repo._repo, getdata().strip())
1040 1038
1041 1039 if path is None:
1042 1040 raise error.Abort(
1043 1041 _('largefile %s not found in repo store or system cache')
1044 1042 % lfile)
1045 1043 else:
1046 1044 path = lfile
1047 1045
1048 1046 f = lfile
1049 1047
1050 1048 getdata = lambda: util.readfile(os.path.join(prefix, path))
1051 1049
1052 1050 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, getdata)
1053 1051
1054 1052 for subpath in sorted(ctx.substate):
1055 1053 sub = ctx.workingsub(subpath)
1056 1054 submatch = matchmod.subdirmatcher(subpath, match)
1057 1055 sub._repo.lfstatus = True
1058 1056 sub.archive(archiver, prefix + repo._path + '/', submatch, decode)
1059 1057
1060 1058 # If a largefile is modified, the change is not reflected in its
1061 1059 # standin until a commit. cmdutil.bailifchanged() raises an exception
1062 1060 # if the repo has uncommitted changes. Wrap it to also check if
1063 1061 # largefiles were changed. This is used by bisect, backout and fetch.
1064 1062 def overridebailifchanged(orig, repo, *args, **kwargs):
1065 1063 orig(repo, *args, **kwargs)
1066 1064 repo.lfstatus = True
1067 1065 s = repo.status()
1068 1066 repo.lfstatus = False
1069 1067 if s.modified or s.added or s.removed or s.deleted:
1070 1068 raise error.Abort(_('uncommitted changes'))
1071 1069
1072 1070 def postcommitstatus(orig, repo, *args, **kwargs):
1073 1071 repo.lfstatus = True
1074 1072 try:
1075 1073 return orig(repo, *args, **kwargs)
1076 1074 finally:
1077 1075 repo.lfstatus = False
1078 1076
1079 1077 def cmdutilforget(orig, ui, repo, match, prefix, explicitonly):
1080 1078 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
1081 1079 bad, forgot = orig(ui, repo, normalmatcher, prefix, explicitonly)
1082 1080 m = composelargefilematcher(match, repo[None].manifest())
1083 1081
1084 1082 try:
1085 1083 repo.lfstatus = True
1086 1084 s = repo.status(match=m, clean=True)
1087 1085 finally:
1088 1086 repo.lfstatus = False
1089 1087 manifest = repo[None].manifest()
1090 1088 forget = sorted(s.modified + s.added + s.deleted + s.clean)
1091 1089 forget = [f for f in forget if lfutil.standin(f) in manifest]
1092 1090
1093 1091 for f in forget:
1094 1092 fstandin = lfutil.standin(f)
1095 1093 if fstandin not in repo.dirstate and not repo.wvfs.isdir(fstandin):
1096 1094 ui.warn(_('not removing %s: file is already untracked\n')
1097 1095 % m.rel(f))
1098 1096 bad.append(f)
1099 1097
1100 1098 for f in forget:
1101 1099 if ui.verbose or not m.exact(f):
1102 1100 ui.status(_('removing %s\n') % m.rel(f))
1103 1101
1104 1102 # Need to lock because standin files are deleted then removed from the
1105 1103 # repository and we could race in-between.
1106 1104 with repo.wlock():
1107 1105 lfdirstate = lfutil.openlfdirstate(ui, repo)
1108 1106 for f in forget:
1109 1107 if lfdirstate[f] == 'a':
1110 1108 lfdirstate.drop(f)
1111 1109 else:
1112 1110 lfdirstate.remove(f)
1113 1111 lfdirstate.write()
1114 1112 standins = [lfutil.standin(f) for f in forget]
1115 1113 for f in standins:
1116 1114 repo.wvfs.unlinkpath(f, ignoremissing=True)
1117 1115 rejected = repo[None].forget(standins)
1118 1116
1119 1117 bad.extend(f for f in rejected if f in m.files())
1120 1118 forgot.extend(f for f in forget if f not in rejected)
1121 1119 return bad, forgot
1122 1120
1123 1121 def _getoutgoings(repo, other, missing, addfunc):
1124 1122 """get pairs of filename and largefile hash in outgoing revisions
1125 1123 in 'missing'.
1126 1124
1127 1125 largefiles already existing on 'other' repository are ignored.
1128 1126
1129 1127 'addfunc' is invoked with each unique pairs of filename and
1130 1128 largefile hash value.
1131 1129 """
1132 1130 knowns = set()
1133 1131 lfhashes = set()
1134 1132 def dedup(fn, lfhash):
1135 1133 k = (fn, lfhash)
1136 1134 if k not in knowns:
1137 1135 knowns.add(k)
1138 1136 lfhashes.add(lfhash)
1139 1137 lfutil.getlfilestoupload(repo, missing, dedup)
1140 1138 if lfhashes:
1141 1139 lfexists = storefactory.openstore(repo, other).exists(lfhashes)
1142 1140 for fn, lfhash in knowns:
1143 1141 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1144 1142 addfunc(fn, lfhash)
1145 1143
1146 1144 def outgoinghook(ui, repo, other, opts, missing):
1147 1145 if opts.pop('large', None):
1148 1146 lfhashes = set()
1149 1147 if ui.debugflag:
1150 1148 toupload = {}
1151 1149 def addfunc(fn, lfhash):
1152 1150 if fn not in toupload:
1153 1151 toupload[fn] = []
1154 1152 toupload[fn].append(lfhash)
1155 1153 lfhashes.add(lfhash)
1156 1154 def showhashes(fn):
1157 1155 for lfhash in sorted(toupload[fn]):
1158 1156 ui.debug(' %s\n' % (lfhash))
1159 1157 else:
1160 1158 toupload = set()
1161 1159 def addfunc(fn, lfhash):
1162 1160 toupload.add(fn)
1163 1161 lfhashes.add(lfhash)
1164 1162 def showhashes(fn):
1165 1163 pass
1166 1164 _getoutgoings(repo, other, missing, addfunc)
1167 1165
1168 1166 if not toupload:
1169 1167 ui.status(_('largefiles: no files to upload\n'))
1170 1168 else:
1171 1169 ui.status(_('largefiles to upload (%d entities):\n')
1172 1170 % (len(lfhashes)))
1173 1171 for file in sorted(toupload):
1174 1172 ui.status(lfutil.splitstandin(file) + '\n')
1175 1173 showhashes(file)
1176 1174 ui.status('\n')
1177 1175
1178 1176 def summaryremotehook(ui, repo, opts, changes):
1179 1177 largeopt = opts.get('large', False)
1180 1178 if changes is None:
1181 1179 if largeopt:
1182 1180 return (False, True) # only outgoing check is needed
1183 1181 else:
1184 1182 return (False, False)
1185 1183 elif largeopt:
1186 1184 url, branch, peer, outgoing = changes[1]
1187 1185 if peer is None:
1188 1186 # i18n: column positioning for "hg summary"
1189 1187 ui.status(_('largefiles: (no remote repo)\n'))
1190 1188 return
1191 1189
1192 1190 toupload = set()
1193 1191 lfhashes = set()
1194 1192 def addfunc(fn, lfhash):
1195 1193 toupload.add(fn)
1196 1194 lfhashes.add(lfhash)
1197 1195 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1198 1196
1199 1197 if not toupload:
1200 1198 # i18n: column positioning for "hg summary"
1201 1199 ui.status(_('largefiles: (no files to upload)\n'))
1202 1200 else:
1203 1201 # i18n: column positioning for "hg summary"
1204 1202 ui.status(_('largefiles: %d entities for %d files to upload\n')
1205 1203 % (len(lfhashes), len(toupload)))
1206 1204
1207 1205 def overridesummary(orig, ui, repo, *pats, **opts):
1208 1206 try:
1209 1207 repo.lfstatus = True
1210 1208 orig(ui, repo, *pats, **opts)
1211 1209 finally:
1212 1210 repo.lfstatus = False
1213 1211
1214 1212 def scmutiladdremove(orig, repo, matcher, prefix, opts=None, dry_run=None,
1215 1213 similarity=None):
1216 1214 if opts is None:
1217 1215 opts = {}
1218 1216 if not lfutil.islfilesrepo(repo):
1219 1217 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1220 1218 # Get the list of missing largefiles so we can remove them
1221 1219 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1222 1220 unsure, s = lfdirstate.status(matchmod.always(repo.root, repo.getcwd()), [],
1223 1221 False, False, False)
1224 1222
1225 1223 # Call into the normal remove code, but the removing of the standin, we want
1226 1224 # to have handled by original addremove. Monkey patching here makes sure
1227 1225 # we don't remove the standin in the largefiles code, preventing a very
1228 1226 # confused state later.
1229 1227 if s.deleted:
1230 1228 m = copy.copy(matcher)
1231 1229
1232 1230 # The m._files and m._map attributes are not changed to the deleted list
1233 1231 # because that affects the m.exact() test, which in turn governs whether
1234 1232 # or not the file name is printed, and how. Simply limit the original
1235 1233 # matches to those in the deleted status list.
1236 1234 matchfn = m.matchfn
1237 1235 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1238 1236
1239 1237 removelargefiles(repo.ui, repo, True, m, **opts)
1240 1238 # Call into the normal add code, and any files that *should* be added as
1241 1239 # largefiles will be
1242 1240 added, bad = addlargefiles(repo.ui, repo, True, matcher, **opts)
1243 1241 # Now that we've handled largefiles, hand off to the original addremove
1244 1242 # function to take care of the rest. Make sure it doesn't do anything with
1245 1243 # largefiles by passing a matcher that will ignore them.
1246 1244 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1247 1245 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1248 1246
1249 1247 # Calling purge with --all will cause the largefiles to be deleted.
1250 1248 # Override repo.status to prevent this from happening.
1251 1249 def overridepurge(orig, ui, repo, *dirs, **opts):
1252 1250 # XXX Monkey patching a repoview will not work. The assigned attribute will
1253 1251 # be set on the unfiltered repo, but we will only lookup attributes in the
1254 1252 # unfiltered repo if the lookup in the repoview object itself fails. As the
1255 1253 # monkey patched method exists on the repoview class the lookup will not
1256 1254 # fail. As a result, the original version will shadow the monkey patched
1257 1255 # one, defeating the monkey patch.
1258 1256 #
1259 1257 # As a work around we use an unfiltered repo here. We should do something
1260 1258 # cleaner instead.
1261 1259 repo = repo.unfiltered()
1262 1260 oldstatus = repo.status
1263 1261 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1264 1262 clean=False, unknown=False, listsubrepos=False):
1265 1263 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1266 1264 listsubrepos)
1267 1265 lfdirstate = lfutil.openlfdirstate(ui, repo)
1268 1266 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1269 1267 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1270 1268 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1271 1269 unknown, ignored, r.clean)
1272 1270 repo.status = overridestatus
1273 1271 orig(ui, repo, *dirs, **opts)
1274 1272 repo.status = oldstatus
1275 1273 def overriderollback(orig, ui, repo, **opts):
1276 1274 with repo.wlock():
1277 1275 before = repo.dirstate.parents()
1278 1276 orphans = set(f for f in repo.dirstate
1279 1277 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1280 1278 result = orig(ui, repo, **opts)
1281 1279 after = repo.dirstate.parents()
1282 1280 if before == after:
1283 1281 return result # no need to restore standins
1284 1282
1285 1283 pctx = repo['.']
1286 1284 for f in repo.dirstate:
1287 1285 if lfutil.isstandin(f):
1288 1286 orphans.discard(f)
1289 1287 if repo.dirstate[f] == 'r':
1290 1288 repo.wvfs.unlinkpath(f, ignoremissing=True)
1291 1289 elif f in pctx:
1292 1290 fctx = pctx[f]
1293 1291 repo.wwrite(f, fctx.data(), fctx.flags())
1294 1292 else:
1295 1293 # content of standin is not so important in 'a',
1296 1294 # 'm' or 'n' (coming from the 2nd parent) cases
1297 1295 lfutil.writestandin(repo, f, '', False)
1298 1296 for standin in orphans:
1299 1297 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1300 1298
1301 1299 lfdirstate = lfutil.openlfdirstate(ui, repo)
1302 1300 orphans = set(lfdirstate)
1303 1301 lfiles = lfutil.listlfiles(repo)
1304 1302 for file in lfiles:
1305 1303 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1306 1304 orphans.discard(file)
1307 1305 for lfile in orphans:
1308 1306 lfdirstate.drop(lfile)
1309 1307 lfdirstate.write()
1310 1308 return result
1311 1309
1312 1310 def overridetransplant(orig, ui, repo, *revs, **opts):
1313 1311 resuming = opts.get('continue')
1314 1312 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1315 1313 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1316 1314 try:
1317 1315 result = orig(ui, repo, *revs, **opts)
1318 1316 finally:
1319 1317 repo._lfstatuswriters.pop()
1320 1318 repo._lfcommithooks.pop()
1321 1319 return result
1322 1320
1323 1321 def overridecat(orig, ui, repo, file1, *pats, **opts):
1324 1322 ctx = scmutil.revsingle(repo, opts.get('rev'))
1325 1323 err = 1
1326 1324 notbad = set()
1327 1325 m = scmutil.match(ctx, (file1,) + pats, opts)
1328 1326 origmatchfn = m.matchfn
1329 1327 def lfmatchfn(f):
1330 1328 if origmatchfn(f):
1331 1329 return True
1332 1330 lf = lfutil.splitstandin(f)
1333 1331 if lf is None:
1334 1332 return False
1335 1333 notbad.add(lf)
1336 1334 return origmatchfn(lf)
1337 1335 m.matchfn = lfmatchfn
1338 1336 origbadfn = m.bad
1339 1337 def lfbadfn(f, msg):
1340 1338 if not f in notbad:
1341 1339 origbadfn(f, msg)
1342 1340 m.bad = lfbadfn
1343 1341
1344 1342 origvisitdirfn = m.visitdir
1345 1343 def lfvisitdirfn(dir):
1346 1344 if dir == lfutil.shortname:
1347 1345 return True
1348 1346 ret = origvisitdirfn(dir)
1349 1347 if ret:
1350 1348 return ret
1351 1349 lf = lfutil.splitstandin(dir)
1352 1350 if lf is None:
1353 1351 return False
1354 1352 return origvisitdirfn(lf)
1355 1353 m.visitdir = lfvisitdirfn
1356 1354
1357 1355 for f in ctx.walk(m):
1358 1356 with cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1359 1357 pathname=f) as fp:
1360 1358 lf = lfutil.splitstandin(f)
1361 1359 if lf is None or origmatchfn(f):
1362 1360 # duplicating unreachable code from commands.cat
1363 1361 data = ctx[f].data()
1364 1362 if opts.get('decode'):
1365 1363 data = repo.wwritedata(f, data)
1366 1364 fp.write(data)
1367 1365 else:
1368 1366 hash = lfutil.readasstandin(ctx[f])
1369 1367 if not lfutil.inusercache(repo.ui, hash):
1370 1368 store = storefactory.openstore(repo)
1371 1369 success, missing = store.get([(lf, hash)])
1372 1370 if len(success) != 1:
1373 1371 raise error.Abort(
1374 1372 _('largefile %s is not in cache and could not be '
1375 1373 'downloaded') % lf)
1376 1374 path = lfutil.usercachepath(repo.ui, hash)
1377 1375 with open(path, "rb") as fpin:
1378 1376 for chunk in util.filechunkiter(fpin):
1379 1377 fp.write(chunk)
1380 1378 err = 0
1381 1379 return err
1382 1380
1383 1381 def mergeupdate(orig, repo, node, branchmerge, force,
1384 1382 *args, **kwargs):
1385 1383 matcher = kwargs.get('matcher', None)
1386 1384 # note if this is a partial update
1387 1385 partial = matcher and not matcher.always()
1388 1386 with repo.wlock():
1389 1387 # branch | | |
1390 1388 # merge | force | partial | action
1391 1389 # -------+-------+---------+--------------
1392 1390 # x | x | x | linear-merge
1393 1391 # o | x | x | branch-merge
1394 1392 # x | o | x | overwrite (as clean update)
1395 1393 # o | o | x | force-branch-merge (*1)
1396 1394 # x | x | o | (*)
1397 1395 # o | x | o | (*)
1398 1396 # x | o | o | overwrite (as revert)
1399 1397 # o | o | o | (*)
1400 1398 #
1401 1399 # (*) don't care
1402 1400 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1403 1401
1404 1402 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1405 1403 unsure, s = lfdirstate.status(matchmod.always(repo.root,
1406 1404 repo.getcwd()),
1407 1405 [], False, True, False)
1408 1406 oldclean = set(s.clean)
1409 1407 pctx = repo['.']
1410 1408 dctx = repo[node]
1411 1409 for lfile in unsure + s.modified:
1412 1410 lfileabs = repo.wvfs.join(lfile)
1413 1411 if not repo.wvfs.exists(lfileabs):
1414 1412 continue
1415 1413 lfhash = lfutil.hashfile(lfileabs)
1416 1414 standin = lfutil.standin(lfile)
1417 1415 lfutil.writestandin(repo, standin, lfhash,
1418 1416 lfutil.getexecutable(lfileabs))
1419 1417 if (standin in pctx and
1420 1418 lfhash == lfutil.readasstandin(pctx[standin])):
1421 1419 oldclean.add(lfile)
1422 1420 for lfile in s.added:
1423 1421 fstandin = lfutil.standin(lfile)
1424 1422 if fstandin not in dctx:
1425 1423 # in this case, content of standin file is meaningless
1426 1424 # (in dctx, lfile is unknown, or normal file)
1427 1425 continue
1428 1426 lfutil.updatestandin(repo, lfile, fstandin)
1429 1427 # mark all clean largefiles as dirty, just in case the update gets
1430 1428 # interrupted before largefiles and lfdirstate are synchronized
1431 1429 for lfile in oldclean:
1432 1430 lfdirstate.normallookup(lfile)
1433 1431 lfdirstate.write()
1434 1432
1435 1433 oldstandins = lfutil.getstandinsstate(repo)
1436 1434
1437 1435 result = orig(repo, node, branchmerge, force, *args, **kwargs)
1438 1436
1439 1437 newstandins = lfutil.getstandinsstate(repo)
1440 1438 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1441 1439
1442 1440 # to avoid leaving all largefiles as dirty and thus rehash them, mark
1443 1441 # all the ones that didn't change as clean
1444 1442 for lfile in oldclean.difference(filelist):
1445 1443 lfdirstate.normal(lfile)
1446 1444 lfdirstate.write()
1447 1445
1448 1446 if branchmerge or force or partial:
1449 1447 filelist.extend(s.deleted + s.removed)
1450 1448
1451 1449 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1452 1450 normallookup=partial)
1453 1451
1454 1452 return result
1455 1453
1456 1454 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1457 1455 result = orig(repo, files, *args, **kwargs)
1458 1456
1459 1457 filelist = []
1460 1458 for f in files:
1461 1459 lf = lfutil.splitstandin(f)
1462 1460 if lf is not None:
1463 1461 filelist.append(lf)
1464 1462 if filelist:
1465 1463 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1466 1464 printmessage=False, normallookup=True)
1467 1465
1468 1466 return result
General Comments 0
You need to be logged in to leave comments. Login now