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