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