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