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