##// END OF EJS Templates
largefiles: remove a mutable default argument...
Pierre-Yves David -
r26343:019559aa default
parent child Browse files
Show More
@@ -1,1408 +1,1410 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 620 def overridematch(ctx, pats=(), opts=None, globbed=False,
621 621 default='relpath', badfn=None):
622 622 if opts is None:
623 623 opts = {}
624 624 newpats = []
625 625 # The patterns were previously mangled to add the standin
626 626 # directory; we need to remove that now
627 627 for pat in pats:
628 628 if match_.patkind(pat) is None and lfutil.shortname in pat:
629 629 newpats.append(pat.replace(lfutil.shortname, ''))
630 630 else:
631 631 newpats.append(pat)
632 632 match = oldmatch(ctx, newpats, opts, globbed, default, badfn=badfn)
633 633 m = copy.copy(match)
634 634 lfile = lambda f: lfutil.standin(f) in manifest
635 635 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
636 636 m._fileroots = set(m._files)
637 637 origmatchfn = m.matchfn
638 638 m.matchfn = lambda f: (lfutil.isstandin(f) and
639 639 (f in manifest) and
640 640 origmatchfn(lfutil.splitstandin(f)) or
641 641 None)
642 642 return m
643 643 oldmatch = installmatchfn(overridematch)
644 644 listpats = []
645 645 for pat in pats:
646 646 if match_.patkind(pat) is not None:
647 647 listpats.append(pat)
648 648 else:
649 649 listpats.append(makestandin(pat))
650 650
651 651 try:
652 652 origcopyfile = util.copyfile
653 653 copiedfiles = []
654 654 def overridecopyfile(src, dest):
655 655 if (lfutil.shortname in src and
656 656 dest.startswith(repo.wjoin(lfutil.shortname))):
657 657 destlfile = dest.replace(lfutil.shortname, '')
658 658 if not opts['force'] and os.path.exists(destlfile):
659 659 raise IOError('',
660 660 _('destination largefile already exists'))
661 661 copiedfiles.append((src, dest))
662 662 origcopyfile(src, dest)
663 663
664 664 util.copyfile = overridecopyfile
665 665 result += orig(ui, repo, listpats, opts, rename)
666 666 finally:
667 667 util.copyfile = origcopyfile
668 668
669 669 lfdirstate = lfutil.openlfdirstate(ui, repo)
670 670 for (src, dest) in copiedfiles:
671 671 if (lfutil.shortname in src and
672 672 dest.startswith(repo.wjoin(lfutil.shortname))):
673 673 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
674 674 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
675 675 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
676 676 if not os.path.isdir(destlfiledir):
677 677 os.makedirs(destlfiledir)
678 678 if rename:
679 679 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
680 680
681 681 # The file is gone, but this deletes any empty parent
682 682 # directories as a side-effect.
683 683 util.unlinkpath(repo.wjoin(srclfile), True)
684 684 lfdirstate.remove(srclfile)
685 685 else:
686 686 util.copyfile(repo.wjoin(srclfile),
687 687 repo.wjoin(destlfile))
688 688
689 689 lfdirstate.add(destlfile)
690 690 lfdirstate.write()
691 691 except util.Abort as e:
692 692 if str(e) != _('no files to copy'):
693 693 raise e
694 694 else:
695 695 nolfiles = True
696 696 finally:
697 697 restorematchfn()
698 698 wlock.release()
699 699
700 700 if nolfiles and nonormalfiles:
701 701 raise util.Abort(_('no files to copy'))
702 702
703 703 return result
704 704
705 705 # When the user calls revert, we have to be careful to not revert any
706 706 # changes to other largefiles accidentally. This means we have to keep
707 707 # track of the largefiles that are being reverted so we only pull down
708 708 # the necessary largefiles.
709 709 #
710 710 # Standins are only updated (to match the hash of largefiles) before
711 711 # commits. Update the standins then run the original revert, changing
712 712 # the matcher to hit standins instead of largefiles. Based on the
713 713 # resulting standins update the largefiles.
714 714 def overriderevert(orig, ui, repo, ctx, parents, *pats, **opts):
715 715 # Because we put the standins in a bad state (by updating them)
716 716 # and then return them to a correct state we need to lock to
717 717 # prevent others from changing them in their incorrect state.
718 718 wlock = repo.wlock()
719 719 try:
720 720 lfdirstate = lfutil.openlfdirstate(ui, repo)
721 721 s = lfutil.lfdirstatestatus(lfdirstate, repo)
722 722 lfdirstate.write()
723 723 for lfile in s.modified:
724 724 lfutil.updatestandin(repo, lfutil.standin(lfile))
725 725 for lfile in s.deleted:
726 726 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
727 727 os.unlink(repo.wjoin(lfutil.standin(lfile)))
728 728
729 729 oldstandins = lfutil.getstandinsstate(repo)
730 730
731 def overridematch(mctx, pats=(), opts={}, globbed=False,
731 def overridematch(mctx, pats=(), opts=None, globbed=False,
732 732 default='relpath', badfn=None):
733 if opts is None:
734 opts = {}
733 735 match = oldmatch(mctx, pats, opts, globbed, default, badfn=badfn)
734 736 m = copy.copy(match)
735 737
736 738 # revert supports recursing into subrepos, and though largefiles
737 739 # currently doesn't work correctly in that case, this match is
738 740 # called, so the lfdirstate above may not be the correct one for
739 741 # this invocation of match.
740 742 lfdirstate = lfutil.openlfdirstate(mctx.repo().ui, mctx.repo(),
741 743 False)
742 744
743 745 def tostandin(f):
744 746 standin = lfutil.standin(f)
745 747 if standin in ctx or standin in mctx:
746 748 return standin
747 749 elif standin in repo[None] or lfdirstate[f] == 'r':
748 750 return None
749 751 return f
750 752 m._files = [tostandin(f) for f in m._files]
751 753 m._files = [f for f in m._files if f is not None]
752 754 m._fileroots = set(m._files)
753 755 origmatchfn = m.matchfn
754 756 def matchfn(f):
755 757 if lfutil.isstandin(f):
756 758 return (origmatchfn(lfutil.splitstandin(f)) and
757 759 (f in ctx or f in mctx))
758 760 return origmatchfn(f)
759 761 m.matchfn = matchfn
760 762 return m
761 763 oldmatch = installmatchfn(overridematch)
762 764 try:
763 765 orig(ui, repo, ctx, parents, *pats, **opts)
764 766 finally:
765 767 restorematchfn()
766 768
767 769 newstandins = lfutil.getstandinsstate(repo)
768 770 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
769 771 # lfdirstate should be 'normallookup'-ed for updated files,
770 772 # because reverting doesn't touch dirstate for 'normal' files
771 773 # when target revision is explicitly specified: in such case,
772 774 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
773 775 # of target (standin) file.
774 776 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
775 777 normallookup=True)
776 778
777 779 finally:
778 780 wlock.release()
779 781
780 782 # after pulling changesets, we need to take some extra care to get
781 783 # largefiles updated remotely
782 784 def overridepull(orig, ui, repo, source=None, **opts):
783 785 revsprepull = len(repo)
784 786 if not source:
785 787 source = 'default'
786 788 repo.lfpullsource = source
787 789 result = orig(ui, repo, source, **opts)
788 790 revspostpull = len(repo)
789 791 lfrevs = opts.get('lfrev', [])
790 792 if opts.get('all_largefiles'):
791 793 lfrevs.append('pulled()')
792 794 if lfrevs and revspostpull > revsprepull:
793 795 numcached = 0
794 796 repo.firstpulled = revsprepull # for pulled() revset expression
795 797 try:
796 798 for rev in scmutil.revrange(repo, lfrevs):
797 799 ui.note(_('pulling largefiles for revision %s\n') % rev)
798 800 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
799 801 numcached += len(cached)
800 802 finally:
801 803 del repo.firstpulled
802 804 ui.status(_("%d largefiles cached\n") % numcached)
803 805 return result
804 806
805 807 def pulledrevsetsymbol(repo, subset, x):
806 808 """``pulled()``
807 809 Changesets that just has been pulled.
808 810
809 811 Only available with largefiles from pull --lfrev expressions.
810 812
811 813 .. container:: verbose
812 814
813 815 Some examples:
814 816
815 817 - pull largefiles for all new changesets::
816 818
817 819 hg pull -lfrev "pulled()"
818 820
819 821 - pull largefiles for all new branch heads::
820 822
821 823 hg pull -lfrev "head(pulled()) and not closed()"
822 824
823 825 """
824 826
825 827 try:
826 828 firstpulled = repo.firstpulled
827 829 except AttributeError:
828 830 raise util.Abort(_("pulled() only available in --lfrev"))
829 831 return revset.baseset([r for r in subset if r >= firstpulled])
830 832
831 833 def overrideclone(orig, ui, source, dest=None, **opts):
832 834 d = dest
833 835 if d is None:
834 836 d = hg.defaultdest(source)
835 837 if opts.get('all_largefiles') and not hg.islocal(d):
836 838 raise util.Abort(_(
837 839 '--all-largefiles is incompatible with non-local destination %s') %
838 840 d)
839 841
840 842 return orig(ui, source, dest, **opts)
841 843
842 844 def hgclone(orig, ui, opts, *args, **kwargs):
843 845 result = orig(ui, opts, *args, **kwargs)
844 846
845 847 if result is not None:
846 848 sourcerepo, destrepo = result
847 849 repo = destrepo.local()
848 850
849 851 # When cloning to a remote repo (like through SSH), no repo is available
850 852 # from the peer. Therefore the largefiles can't be downloaded and the
851 853 # hgrc can't be updated.
852 854 if not repo:
853 855 return result
854 856
855 857 # If largefiles is required for this repo, permanently enable it locally
856 858 if 'largefiles' in repo.requirements:
857 859 fp = repo.vfs('hgrc', 'a', text=True)
858 860 try:
859 861 fp.write('\n[extensions]\nlargefiles=\n')
860 862 finally:
861 863 fp.close()
862 864
863 865 # Caching is implicitly limited to 'rev' option, since the dest repo was
864 866 # truncated at that point. The user may expect a download count with
865 867 # this option, so attempt whether or not this is a largefile repo.
866 868 if opts.get('all_largefiles'):
867 869 success, missing = lfcommands.downloadlfiles(ui, repo, None)
868 870
869 871 if missing != 0:
870 872 return None
871 873
872 874 return result
873 875
874 876 def overriderebase(orig, ui, repo, **opts):
875 877 if not util.safehasattr(repo, '_largefilesenabled'):
876 878 return orig(ui, repo, **opts)
877 879
878 880 resuming = opts.get('continue')
879 881 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
880 882 repo._lfstatuswriters.append(lambda *msg, **opts: None)
881 883 try:
882 884 return orig(ui, repo, **opts)
883 885 finally:
884 886 repo._lfstatuswriters.pop()
885 887 repo._lfcommithooks.pop()
886 888
887 889 def overridearchivecmd(orig, ui, repo, dest, **opts):
888 890 repo.unfiltered().lfstatus = True
889 891
890 892 try:
891 893 return orig(ui, repo.unfiltered(), dest, **opts)
892 894 finally:
893 895 repo.unfiltered().lfstatus = False
894 896
895 897 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
896 898 prefix='', mtime=None, subrepos=None):
897 899 if not repo.lfstatus:
898 900 return orig(repo, dest, node, kind, decode, matchfn, prefix, mtime,
899 901 subrepos)
900 902
901 903 # No need to lock because we are only reading history and
902 904 # largefile caches, neither of which are modified.
903 905 if node is not None:
904 906 lfcommands.cachelfiles(repo.ui, repo, node)
905 907
906 908 if kind not in archival.archivers:
907 909 raise util.Abort(_("unknown archive type '%s'") % kind)
908 910
909 911 ctx = repo[node]
910 912
911 913 if kind == 'files':
912 914 if prefix:
913 915 raise util.Abort(
914 916 _('cannot give prefix when archiving to files'))
915 917 else:
916 918 prefix = archival.tidyprefix(dest, kind, prefix)
917 919
918 920 def write(name, mode, islink, getdata):
919 921 if matchfn and not matchfn(name):
920 922 return
921 923 data = getdata()
922 924 if decode:
923 925 data = repo.wwritedata(name, data)
924 926 archiver.addfile(prefix + name, mode, islink, data)
925 927
926 928 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
927 929
928 930 if repo.ui.configbool("ui", "archivemeta", True):
929 931 write('.hg_archival.txt', 0o644, False,
930 932 lambda: archival.buildmetadata(ctx))
931 933
932 934 for f in ctx:
933 935 ff = ctx.flags(f)
934 936 getdata = ctx[f].data
935 937 if lfutil.isstandin(f):
936 938 if node is not None:
937 939 path = lfutil.findfile(repo, getdata().strip())
938 940
939 941 if path is None:
940 942 raise util.Abort(
941 943 _('largefile %s not found in repo store or system cache')
942 944 % lfutil.splitstandin(f))
943 945 else:
944 946 path = lfutil.splitstandin(f)
945 947
946 948 f = lfutil.splitstandin(f)
947 949
948 950 def getdatafn():
949 951 fd = None
950 952 try:
951 953 fd = open(path, 'rb')
952 954 return fd.read()
953 955 finally:
954 956 if fd:
955 957 fd.close()
956 958
957 959 getdata = getdatafn
958 960 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, getdata)
959 961
960 962 if subrepos:
961 963 for subpath in sorted(ctx.substate):
962 964 sub = ctx.workingsub(subpath)
963 965 submatch = match_.narrowmatcher(subpath, matchfn)
964 966 sub._repo.lfstatus = True
965 967 sub.archive(archiver, prefix, submatch)
966 968
967 969 archiver.done()
968 970
969 971 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None):
970 972 if not repo._repo.lfstatus:
971 973 return orig(repo, archiver, prefix, match)
972 974
973 975 repo._get(repo._state + ('hg',))
974 976 rev = repo._state[1]
975 977 ctx = repo._repo[rev]
976 978
977 979 if ctx.node() is not None:
978 980 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
979 981
980 982 def write(name, mode, islink, getdata):
981 983 # At this point, the standin has been replaced with the largefile name,
982 984 # so the normal matcher works here without the lfutil variants.
983 985 if match and not match(f):
984 986 return
985 987 data = getdata()
986 988
987 989 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
988 990
989 991 for f in ctx:
990 992 ff = ctx.flags(f)
991 993 getdata = ctx[f].data
992 994 if lfutil.isstandin(f):
993 995 if ctx.node() is not None:
994 996 path = lfutil.findfile(repo._repo, getdata().strip())
995 997
996 998 if path is None:
997 999 raise util.Abort(
998 1000 _('largefile %s not found in repo store or system cache')
999 1001 % lfutil.splitstandin(f))
1000 1002 else:
1001 1003 path = lfutil.splitstandin(f)
1002 1004
1003 1005 f = lfutil.splitstandin(f)
1004 1006
1005 1007 def getdatafn():
1006 1008 fd = None
1007 1009 try:
1008 1010 fd = open(os.path.join(prefix, path), 'rb')
1009 1011 return fd.read()
1010 1012 finally:
1011 1013 if fd:
1012 1014 fd.close()
1013 1015
1014 1016 getdata = getdatafn
1015 1017
1016 1018 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, getdata)
1017 1019
1018 1020 for subpath in sorted(ctx.substate):
1019 1021 sub = ctx.workingsub(subpath)
1020 1022 submatch = match_.narrowmatcher(subpath, match)
1021 1023 sub._repo.lfstatus = True
1022 1024 sub.archive(archiver, prefix + repo._path + '/', submatch)
1023 1025
1024 1026 # If a largefile is modified, the change is not reflected in its
1025 1027 # standin until a commit. cmdutil.bailifchanged() raises an exception
1026 1028 # if the repo has uncommitted changes. Wrap it to also check if
1027 1029 # largefiles were changed. This is used by bisect, backout and fetch.
1028 1030 def overridebailifchanged(orig, repo, *args, **kwargs):
1029 1031 orig(repo, *args, **kwargs)
1030 1032 repo.lfstatus = True
1031 1033 s = repo.status()
1032 1034 repo.lfstatus = False
1033 1035 if s.modified or s.added or s.removed or s.deleted:
1034 1036 raise util.Abort(_('uncommitted changes'))
1035 1037
1036 1038 def cmdutilforget(orig, ui, repo, match, prefix, explicitonly):
1037 1039 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
1038 1040 bad, forgot = orig(ui, repo, normalmatcher, prefix, explicitonly)
1039 1041 m = composelargefilematcher(match, repo[None].manifest())
1040 1042
1041 1043 try:
1042 1044 repo.lfstatus = True
1043 1045 s = repo.status(match=m, clean=True)
1044 1046 finally:
1045 1047 repo.lfstatus = False
1046 1048 forget = sorted(s.modified + s.added + s.deleted + s.clean)
1047 1049 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
1048 1050
1049 1051 for f in forget:
1050 1052 if lfutil.standin(f) not in repo.dirstate and not \
1051 1053 repo.wvfs.isdir(lfutil.standin(f)):
1052 1054 ui.warn(_('not removing %s: file is already untracked\n')
1053 1055 % m.rel(f))
1054 1056 bad.append(f)
1055 1057
1056 1058 for f in forget:
1057 1059 if ui.verbose or not m.exact(f):
1058 1060 ui.status(_('removing %s\n') % m.rel(f))
1059 1061
1060 1062 # Need to lock because standin files are deleted then removed from the
1061 1063 # repository and we could race in-between.
1062 1064 wlock = repo.wlock()
1063 1065 try:
1064 1066 lfdirstate = lfutil.openlfdirstate(ui, repo)
1065 1067 for f in forget:
1066 1068 if lfdirstate[f] == 'a':
1067 1069 lfdirstate.drop(f)
1068 1070 else:
1069 1071 lfdirstate.remove(f)
1070 1072 lfdirstate.write()
1071 1073 standins = [lfutil.standin(f) for f in forget]
1072 1074 for f in standins:
1073 1075 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1074 1076 rejected = repo[None].forget(standins)
1075 1077 finally:
1076 1078 wlock.release()
1077 1079
1078 1080 bad.extend(f for f in rejected if f in m.files())
1079 1081 forgot.extend(f for f in forget if f not in rejected)
1080 1082 return bad, forgot
1081 1083
1082 1084 def _getoutgoings(repo, other, missing, addfunc):
1083 1085 """get pairs of filename and largefile hash in outgoing revisions
1084 1086 in 'missing'.
1085 1087
1086 1088 largefiles already existing on 'other' repository are ignored.
1087 1089
1088 1090 'addfunc' is invoked with each unique pairs of filename and
1089 1091 largefile hash value.
1090 1092 """
1091 1093 knowns = set()
1092 1094 lfhashes = set()
1093 1095 def dedup(fn, lfhash):
1094 1096 k = (fn, lfhash)
1095 1097 if k not in knowns:
1096 1098 knowns.add(k)
1097 1099 lfhashes.add(lfhash)
1098 1100 lfutil.getlfilestoupload(repo, missing, dedup)
1099 1101 if lfhashes:
1100 1102 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1101 1103 for fn, lfhash in knowns:
1102 1104 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1103 1105 addfunc(fn, lfhash)
1104 1106
1105 1107 def outgoinghook(ui, repo, other, opts, missing):
1106 1108 if opts.pop('large', None):
1107 1109 lfhashes = set()
1108 1110 if ui.debugflag:
1109 1111 toupload = {}
1110 1112 def addfunc(fn, lfhash):
1111 1113 if fn not in toupload:
1112 1114 toupload[fn] = []
1113 1115 toupload[fn].append(lfhash)
1114 1116 lfhashes.add(lfhash)
1115 1117 def showhashes(fn):
1116 1118 for lfhash in sorted(toupload[fn]):
1117 1119 ui.debug(' %s\n' % (lfhash))
1118 1120 else:
1119 1121 toupload = set()
1120 1122 def addfunc(fn, lfhash):
1121 1123 toupload.add(fn)
1122 1124 lfhashes.add(lfhash)
1123 1125 def showhashes(fn):
1124 1126 pass
1125 1127 _getoutgoings(repo, other, missing, addfunc)
1126 1128
1127 1129 if not toupload:
1128 1130 ui.status(_('largefiles: no files to upload\n'))
1129 1131 else:
1130 1132 ui.status(_('largefiles to upload (%d entities):\n')
1131 1133 % (len(lfhashes)))
1132 1134 for file in sorted(toupload):
1133 1135 ui.status(lfutil.splitstandin(file) + '\n')
1134 1136 showhashes(file)
1135 1137 ui.status('\n')
1136 1138
1137 1139 def summaryremotehook(ui, repo, opts, changes):
1138 1140 largeopt = opts.get('large', False)
1139 1141 if changes is None:
1140 1142 if largeopt:
1141 1143 return (False, True) # only outgoing check is needed
1142 1144 else:
1143 1145 return (False, False)
1144 1146 elif largeopt:
1145 1147 url, branch, peer, outgoing = changes[1]
1146 1148 if peer is None:
1147 1149 # i18n: column positioning for "hg summary"
1148 1150 ui.status(_('largefiles: (no remote repo)\n'))
1149 1151 return
1150 1152
1151 1153 toupload = set()
1152 1154 lfhashes = set()
1153 1155 def addfunc(fn, lfhash):
1154 1156 toupload.add(fn)
1155 1157 lfhashes.add(lfhash)
1156 1158 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1157 1159
1158 1160 if not toupload:
1159 1161 # i18n: column positioning for "hg summary"
1160 1162 ui.status(_('largefiles: (no files to upload)\n'))
1161 1163 else:
1162 1164 # i18n: column positioning for "hg summary"
1163 1165 ui.status(_('largefiles: %d entities for %d files to upload\n')
1164 1166 % (len(lfhashes), len(toupload)))
1165 1167
1166 1168 def overridesummary(orig, ui, repo, *pats, **opts):
1167 1169 try:
1168 1170 repo.lfstatus = True
1169 1171 orig(ui, repo, *pats, **opts)
1170 1172 finally:
1171 1173 repo.lfstatus = False
1172 1174
1173 1175 def scmutiladdremove(orig, repo, matcher, prefix, opts={}, dry_run=None,
1174 1176 similarity=None):
1175 1177 if not lfutil.islfilesrepo(repo):
1176 1178 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1177 1179 # Get the list of missing largefiles so we can remove them
1178 1180 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1179 1181 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1180 1182 False, False, False)
1181 1183
1182 1184 # Call into the normal remove code, but the removing of the standin, we want
1183 1185 # to have handled by original addremove. Monkey patching here makes sure
1184 1186 # we don't remove the standin in the largefiles code, preventing a very
1185 1187 # confused state later.
1186 1188 if s.deleted:
1187 1189 m = copy.copy(matcher)
1188 1190
1189 1191 # The m._files and m._map attributes are not changed to the deleted list
1190 1192 # because that affects the m.exact() test, which in turn governs whether
1191 1193 # or not the file name is printed, and how. Simply limit the original
1192 1194 # matches to those in the deleted status list.
1193 1195 matchfn = m.matchfn
1194 1196 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1195 1197
1196 1198 removelargefiles(repo.ui, repo, True, m, **opts)
1197 1199 # Call into the normal add code, and any files that *should* be added as
1198 1200 # largefiles will be
1199 1201 added, bad = addlargefiles(repo.ui, repo, True, matcher, **opts)
1200 1202 # Now that we've handled largefiles, hand off to the original addremove
1201 1203 # function to take care of the rest. Make sure it doesn't do anything with
1202 1204 # largefiles by passing a matcher that will ignore them.
1203 1205 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1204 1206 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1205 1207
1206 1208 # Calling purge with --all will cause the largefiles to be deleted.
1207 1209 # Override repo.status to prevent this from happening.
1208 1210 def overridepurge(orig, ui, repo, *dirs, **opts):
1209 1211 # XXX Monkey patching a repoview will not work. The assigned attribute will
1210 1212 # be set on the unfiltered repo, but we will only lookup attributes in the
1211 1213 # unfiltered repo if the lookup in the repoview object itself fails. As the
1212 1214 # monkey patched method exists on the repoview class the lookup will not
1213 1215 # fail. As a result, the original version will shadow the monkey patched
1214 1216 # one, defeating the monkey patch.
1215 1217 #
1216 1218 # As a work around we use an unfiltered repo here. We should do something
1217 1219 # cleaner instead.
1218 1220 repo = repo.unfiltered()
1219 1221 oldstatus = repo.status
1220 1222 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1221 1223 clean=False, unknown=False, listsubrepos=False):
1222 1224 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1223 1225 listsubrepos)
1224 1226 lfdirstate = lfutil.openlfdirstate(ui, repo)
1225 1227 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1226 1228 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1227 1229 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1228 1230 unknown, ignored, r.clean)
1229 1231 repo.status = overridestatus
1230 1232 orig(ui, repo, *dirs, **opts)
1231 1233 repo.status = oldstatus
1232 1234 def overriderollback(orig, ui, repo, **opts):
1233 1235 wlock = repo.wlock()
1234 1236 try:
1235 1237 before = repo.dirstate.parents()
1236 1238 orphans = set(f for f in repo.dirstate
1237 1239 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1238 1240 result = orig(ui, repo, **opts)
1239 1241 after = repo.dirstate.parents()
1240 1242 if before == after:
1241 1243 return result # no need to restore standins
1242 1244
1243 1245 pctx = repo['.']
1244 1246 for f in repo.dirstate:
1245 1247 if lfutil.isstandin(f):
1246 1248 orphans.discard(f)
1247 1249 if repo.dirstate[f] == 'r':
1248 1250 repo.wvfs.unlinkpath(f, ignoremissing=True)
1249 1251 elif f in pctx:
1250 1252 fctx = pctx[f]
1251 1253 repo.wwrite(f, fctx.data(), fctx.flags())
1252 1254 else:
1253 1255 # content of standin is not so important in 'a',
1254 1256 # 'm' or 'n' (coming from the 2nd parent) cases
1255 1257 lfutil.writestandin(repo, f, '', False)
1256 1258 for standin in orphans:
1257 1259 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1258 1260
1259 1261 lfdirstate = lfutil.openlfdirstate(ui, repo)
1260 1262 orphans = set(lfdirstate)
1261 1263 lfiles = lfutil.listlfiles(repo)
1262 1264 for file in lfiles:
1263 1265 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1264 1266 orphans.discard(file)
1265 1267 for lfile in orphans:
1266 1268 lfdirstate.drop(lfile)
1267 1269 lfdirstate.write()
1268 1270 finally:
1269 1271 wlock.release()
1270 1272 return result
1271 1273
1272 1274 def overridetransplant(orig, ui, repo, *revs, **opts):
1273 1275 resuming = opts.get('continue')
1274 1276 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1275 1277 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1276 1278 try:
1277 1279 result = orig(ui, repo, *revs, **opts)
1278 1280 finally:
1279 1281 repo._lfstatuswriters.pop()
1280 1282 repo._lfcommithooks.pop()
1281 1283 return result
1282 1284
1283 1285 def overridecat(orig, ui, repo, file1, *pats, **opts):
1284 1286 ctx = scmutil.revsingle(repo, opts.get('rev'))
1285 1287 err = 1
1286 1288 notbad = set()
1287 1289 m = scmutil.match(ctx, (file1,) + pats, opts)
1288 1290 origmatchfn = m.matchfn
1289 1291 def lfmatchfn(f):
1290 1292 if origmatchfn(f):
1291 1293 return True
1292 1294 lf = lfutil.splitstandin(f)
1293 1295 if lf is None:
1294 1296 return False
1295 1297 notbad.add(lf)
1296 1298 return origmatchfn(lf)
1297 1299 m.matchfn = lfmatchfn
1298 1300 origbadfn = m.bad
1299 1301 def lfbadfn(f, msg):
1300 1302 if not f in notbad:
1301 1303 origbadfn(f, msg)
1302 1304 m.bad = lfbadfn
1303 1305
1304 1306 origvisitdirfn = m.visitdir
1305 1307 def lfvisitdirfn(dir):
1306 1308 if dir == lfutil.shortname:
1307 1309 return True
1308 1310 ret = origvisitdirfn(dir)
1309 1311 if ret:
1310 1312 return ret
1311 1313 lf = lfutil.splitstandin(dir)
1312 1314 if lf is None:
1313 1315 return False
1314 1316 return origvisitdirfn(lf)
1315 1317 m.visitdir = lfvisitdirfn
1316 1318
1317 1319 for f in ctx.walk(m):
1318 1320 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1319 1321 pathname=f)
1320 1322 lf = lfutil.splitstandin(f)
1321 1323 if lf is None or origmatchfn(f):
1322 1324 # duplicating unreachable code from commands.cat
1323 1325 data = ctx[f].data()
1324 1326 if opts.get('decode'):
1325 1327 data = repo.wwritedata(f, data)
1326 1328 fp.write(data)
1327 1329 else:
1328 1330 hash = lfutil.readstandin(repo, lf, ctx.rev())
1329 1331 if not lfutil.inusercache(repo.ui, hash):
1330 1332 store = basestore._openstore(repo)
1331 1333 success, missing = store.get([(lf, hash)])
1332 1334 if len(success) != 1:
1333 1335 raise util.Abort(
1334 1336 _('largefile %s is not in cache and could not be '
1335 1337 'downloaded') % lf)
1336 1338 path = lfutil.usercachepath(repo.ui, hash)
1337 1339 fpin = open(path, "rb")
1338 1340 for chunk in util.filechunkiter(fpin, 128 * 1024):
1339 1341 fp.write(chunk)
1340 1342 fpin.close()
1341 1343 fp.close()
1342 1344 err = 0
1343 1345 return err
1344 1346
1345 1347 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1346 1348 *args, **kwargs):
1347 1349 wlock = repo.wlock()
1348 1350 try:
1349 1351 # branch | | |
1350 1352 # merge | force | partial | action
1351 1353 # -------+-------+---------+--------------
1352 1354 # x | x | x | linear-merge
1353 1355 # o | x | x | branch-merge
1354 1356 # x | o | x | overwrite (as clean update)
1355 1357 # o | o | x | force-branch-merge (*1)
1356 1358 # x | x | o | (*)
1357 1359 # o | x | o | (*)
1358 1360 # x | o | o | overwrite (as revert)
1359 1361 # o | o | o | (*)
1360 1362 #
1361 1363 # (*) don't care
1362 1364 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1363 1365
1364 1366 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1365 1367 unsure, s = lfdirstate.status(match_.always(repo.root,
1366 1368 repo.getcwd()),
1367 1369 [], False, False, False)
1368 1370 pctx = repo['.']
1369 1371 for lfile in unsure + s.modified:
1370 1372 lfileabs = repo.wvfs.join(lfile)
1371 1373 if not os.path.exists(lfileabs):
1372 1374 continue
1373 1375 lfhash = lfutil.hashrepofile(repo, lfile)
1374 1376 standin = lfutil.standin(lfile)
1375 1377 lfutil.writestandin(repo, standin, lfhash,
1376 1378 lfutil.getexecutable(lfileabs))
1377 1379 if (standin in pctx and
1378 1380 lfhash == lfutil.readstandin(repo, lfile, '.')):
1379 1381 lfdirstate.normal(lfile)
1380 1382 for lfile in s.added:
1381 1383 lfutil.updatestandin(repo, lfutil.standin(lfile))
1382 1384 lfdirstate.write()
1383 1385
1384 1386 oldstandins = lfutil.getstandinsstate(repo)
1385 1387
1386 1388 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1387 1389
1388 1390 newstandins = lfutil.getstandinsstate(repo)
1389 1391 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1390 1392 if branchmerge or force or partial:
1391 1393 filelist.extend(s.deleted + s.removed)
1392 1394
1393 1395 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1394 1396 normallookup=partial)
1395 1397
1396 1398 return result
1397 1399 finally:
1398 1400 wlock.release()
1399 1401
1400 1402 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1401 1403 result = orig(repo, files, *args, **kwargs)
1402 1404
1403 1405 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1404 1406 if filelist:
1405 1407 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1406 1408 printmessage=False, normallookup=True)
1407 1409
1408 1410 return result
General Comments 0
You need to be logged in to leave comments. Login now