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