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