##// END OF EJS Templates
largefiles: override calculateupdates instead of manifestmerge...
Mads Kiilerich -
r20638:b228ad1f default
parent child Browse files
Show More
@@ -1,1194 +1,1194 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, commands, util, cmdutil, scmutil, match as match_, \
15 15 node, archival, error, merge, discovery, pathutil, revset
16 16 from mercurial.i18n import _
17 17 from mercurial.node import hex
18 18 from hgext import rebase
19 19
20 20 import lfutil
21 21 import lfcommands
22 22 import basestore
23 23
24 24 # -- Utility functions: commonly/repeatedly needed functionality ---------------
25 25
26 26 def installnormalfilesmatchfn(manifest):
27 27 '''overrides scmutil.match so that the matcher it returns will ignore all
28 28 largefiles'''
29 29 oldmatch = None # for the closure
30 30 def overridematch(ctx, pats=[], opts={}, globbed=False,
31 31 default='relpath'):
32 32 match = oldmatch(ctx, pats, opts, globbed, default)
33 33 m = copy.copy(match)
34 34 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
35 35 manifest)
36 36 m._files = filter(notlfile, m._files)
37 37 m._fmap = set(m._files)
38 38 m._always = False
39 39 origmatchfn = m.matchfn
40 40 m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None
41 41 return m
42 42 oldmatch = installmatchfn(overridematch)
43 43
44 44 def installmatchfn(f):
45 45 oldmatch = scmutil.match
46 46 setattr(f, 'oldmatch', oldmatch)
47 47 scmutil.match = f
48 48 return oldmatch
49 49
50 50 def restorematchfn():
51 51 '''restores scmutil.match to what it was before installnormalfilesmatchfn
52 52 was called. no-op if scmutil.match is its original function.
53 53
54 54 Note that n calls to installnormalfilesmatchfn will require n calls to
55 55 restore matchfn to reverse'''
56 56 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
57 57
58 58 def addlargefiles(ui, repo, *pats, **opts):
59 59 large = opts.pop('large', None)
60 60 lfsize = lfutil.getminsize(
61 61 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
62 62
63 63 lfmatcher = None
64 64 if lfutil.islfilesrepo(repo):
65 65 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
66 66 if lfpats:
67 67 lfmatcher = match_.match(repo.root, '', list(lfpats))
68 68
69 69 lfnames = []
70 70 m = scmutil.match(repo[None], pats, opts)
71 71 m.bad = lambda x, y: None
72 72 wctx = repo[None]
73 73 for f in repo.walk(m):
74 74 exact = m.exact(f)
75 75 lfile = lfutil.standin(f) in wctx
76 76 nfile = f in wctx
77 77 exists = lfile or nfile
78 78
79 79 # Don't warn the user when they attempt to add a normal tracked file.
80 80 # The normal add code will do that for us.
81 81 if exact and exists:
82 82 if lfile:
83 83 ui.warn(_('%s already a largefile\n') % f)
84 84 continue
85 85
86 86 if (exact or not exists) and not lfutil.isstandin(f):
87 87 wfile = repo.wjoin(f)
88 88
89 89 # In case the file was removed previously, but not committed
90 90 # (issue3507)
91 91 if not os.path.exists(wfile):
92 92 continue
93 93
94 94 abovemin = (lfsize and
95 95 os.lstat(wfile).st_size >= lfsize * 1024 * 1024)
96 96 if large or abovemin or (lfmatcher and lfmatcher(f)):
97 97 lfnames.append(f)
98 98 if ui.verbose or not exact:
99 99 ui.status(_('adding %s as a largefile\n') % m.rel(f))
100 100
101 101 bad = []
102 102 standins = []
103 103
104 104 # Need to lock, otherwise there could be a race condition between
105 105 # when standins are created and added to the repo.
106 106 wlock = repo.wlock()
107 107 try:
108 108 if not opts.get('dry_run'):
109 109 lfdirstate = lfutil.openlfdirstate(ui, repo)
110 110 for f in lfnames:
111 111 standinname = lfutil.standin(f)
112 112 lfutil.writestandin(repo, standinname, hash='',
113 113 executable=lfutil.getexecutable(repo.wjoin(f)))
114 114 standins.append(standinname)
115 115 if lfdirstate[f] == 'r':
116 116 lfdirstate.normallookup(f)
117 117 else:
118 118 lfdirstate.add(f)
119 119 lfdirstate.write()
120 120 bad += [lfutil.splitstandin(f)
121 121 for f in repo[None].add(standins)
122 122 if f in m.files()]
123 123 finally:
124 124 wlock.release()
125 125 return bad
126 126
127 127 def removelargefiles(ui, repo, *pats, **opts):
128 128 after = opts.get('after')
129 129 if not pats and not after:
130 130 raise util.Abort(_('no files specified'))
131 131 m = scmutil.match(repo[None], pats, opts)
132 132 try:
133 133 repo.lfstatus = True
134 134 s = repo.status(match=m, clean=True)
135 135 finally:
136 136 repo.lfstatus = False
137 137 manifest = repo[None].manifest()
138 138 modified, added, deleted, clean = [[f for f in list
139 139 if lfutil.standin(f) in manifest]
140 140 for list in [s[0], s[1], s[3], s[6]]]
141 141
142 142 def warn(files, msg):
143 143 for f in files:
144 144 ui.warn(msg % m.rel(f))
145 145 return int(len(files) > 0)
146 146
147 147 result = 0
148 148
149 149 if after:
150 150 remove, forget = deleted, []
151 151 result = warn(modified + added + clean,
152 152 _('not removing %s: file still exists\n'))
153 153 else:
154 154 remove, forget = deleted + clean, []
155 155 result = warn(modified, _('not removing %s: file is modified (use -f'
156 156 ' to force removal)\n'))
157 157 result = warn(added, _('not removing %s: file has been marked for add'
158 158 ' (use forget to undo)\n')) or result
159 159
160 160 for f in sorted(remove + forget):
161 161 if ui.verbose or not m.exact(f):
162 162 ui.status(_('removing %s\n') % m.rel(f))
163 163
164 164 # Need to lock because standin files are deleted then removed from the
165 165 # repository and we could race in-between.
166 166 wlock = repo.wlock()
167 167 try:
168 168 lfdirstate = lfutil.openlfdirstate(ui, repo)
169 169 for f in remove:
170 170 if not after:
171 171 # If this is being called by addremove, notify the user that we
172 172 # are removing the file.
173 173 if getattr(repo, "_isaddremove", False):
174 174 ui.status(_('removing %s\n') % f)
175 175 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
176 176 lfdirstate.remove(f)
177 177 lfdirstate.write()
178 178 forget = [lfutil.standin(f) for f in forget]
179 179 remove = [lfutil.standin(f) for f in remove]
180 180 repo[None].forget(forget)
181 181 # If this is being called by addremove, let the original addremove
182 182 # function handle this.
183 183 if not getattr(repo, "_isaddremove", False):
184 184 for f in remove:
185 185 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
186 186 repo[None].forget(remove)
187 187 finally:
188 188 wlock.release()
189 189
190 190 return result
191 191
192 192 # For overriding mercurial.hgweb.webcommands so that largefiles will
193 193 # appear at their right place in the manifests.
194 194 def decodepath(orig, path):
195 195 return lfutil.splitstandin(path) or path
196 196
197 197 # -- Wrappers: modify existing commands --------------------------------
198 198
199 199 # Add works by going through the files that the user wanted to add and
200 200 # checking if they should be added as largefiles. Then it makes a new
201 201 # matcher which matches only the normal files and runs the original
202 202 # version of add.
203 203 def overrideadd(orig, ui, repo, *pats, **opts):
204 204 normal = opts.pop('normal')
205 205 if normal:
206 206 if opts.get('large'):
207 207 raise util.Abort(_('--normal cannot be used with --large'))
208 208 return orig(ui, repo, *pats, **opts)
209 209 bad = addlargefiles(ui, repo, *pats, **opts)
210 210 installnormalfilesmatchfn(repo[None].manifest())
211 211 result = orig(ui, repo, *pats, **opts)
212 212 restorematchfn()
213 213
214 214 return (result == 1 or bad) and 1 or 0
215 215
216 216 def overrideremove(orig, ui, repo, *pats, **opts):
217 217 installnormalfilesmatchfn(repo[None].manifest())
218 218 result = orig(ui, repo, *pats, **opts)
219 219 restorematchfn()
220 220 return removelargefiles(ui, repo, *pats, **opts) or result
221 221
222 222 def overridestatusfn(orig, repo, rev2, **opts):
223 223 try:
224 224 repo._repo.lfstatus = True
225 225 return orig(repo, rev2, **opts)
226 226 finally:
227 227 repo._repo.lfstatus = False
228 228
229 229 def overridestatus(orig, ui, repo, *pats, **opts):
230 230 try:
231 231 repo.lfstatus = True
232 232 return orig(ui, repo, *pats, **opts)
233 233 finally:
234 234 repo.lfstatus = False
235 235
236 236 def overridedirty(orig, repo, ignoreupdate=False):
237 237 try:
238 238 repo._repo.lfstatus = True
239 239 return orig(repo, ignoreupdate)
240 240 finally:
241 241 repo._repo.lfstatus = False
242 242
243 243 def overridelog(orig, ui, repo, *pats, **opts):
244 244 def overridematch(ctx, pats=[], opts={}, globbed=False,
245 245 default='relpath'):
246 246 """Matcher that merges root directory with .hglf, suitable for log.
247 247 It is still possible to match .hglf directly.
248 248 For any listed files run log on the standin too.
249 249 matchfn tries both the given filename and with .hglf stripped.
250 250 """
251 251 match = oldmatch(ctx, pats, opts, globbed, default)
252 252 m = copy.copy(match)
253 253 for i in range(0, len(m._files)):
254 254 standin = lfutil.standin(m._files[i])
255 255 if standin in repo[ctx.node()]:
256 256 m._files[i] = standin
257 257 m._fmap = set(m._files)
258 258 m._always = False
259 259 origmatchfn = m.matchfn
260 260 def lfmatchfn(f):
261 261 lf = lfutil.splitstandin(f)
262 262 if lf is not None and origmatchfn(lf):
263 263 return True
264 264 r = origmatchfn(f)
265 265 return r
266 266 m.matchfn = lfmatchfn
267 267 return m
268 268 oldmatch = installmatchfn(overridematch)
269 269 try:
270 270 repo.lfstatus = True
271 271 return orig(ui, repo, *pats, **opts)
272 272 finally:
273 273 repo.lfstatus = False
274 274 restorematchfn()
275 275
276 276 def overrideverify(orig, ui, repo, *pats, **opts):
277 277 large = opts.pop('large', False)
278 278 all = opts.pop('lfa', False)
279 279 contents = opts.pop('lfc', False)
280 280
281 281 result = orig(ui, repo, *pats, **opts)
282 282 if large or all or contents:
283 283 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
284 284 return result
285 285
286 286 def overridedebugstate(orig, ui, repo, *pats, **opts):
287 287 large = opts.pop('large', False)
288 288 if large:
289 289 lfcommands.debugdirstate(ui, repo)
290 290 else:
291 291 orig(ui, repo, *pats, **opts)
292 292
293 293 # Override needs to refresh standins so that update's normal merge
294 294 # will go through properly. Then the other update hook (overriding repo.update)
295 295 # will get the new files. Filemerge is also overridden so that the merge
296 296 # will merge standins correctly.
297 297 def overrideupdate(orig, ui, repo, *pats, **opts):
298 298 lfdirstate = lfutil.openlfdirstate(ui, repo)
299 299 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
300 300 False, False)
301 301 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
302 302
303 303 # Need to lock between the standins getting updated and their
304 304 # largefiles getting updated
305 305 wlock = repo.wlock()
306 306 try:
307 307 if opts['check']:
308 308 mod = len(modified) > 0
309 309 for lfile in unsure:
310 310 standin = lfutil.standin(lfile)
311 311 if repo['.'][standin].data().strip() != \
312 312 lfutil.hashfile(repo.wjoin(lfile)):
313 313 mod = True
314 314 else:
315 315 lfdirstate.normal(lfile)
316 316 lfdirstate.write()
317 317 if mod:
318 318 raise util.Abort(_('uncommitted changes'))
319 319 # XXX handle removed differently
320 320 if not opts['clean']:
321 321 for lfile in unsure + modified + added:
322 322 lfutil.updatestandin(repo, lfutil.standin(lfile))
323 323 finally:
324 324 wlock.release()
325 325 return orig(ui, repo, *pats, **opts)
326 326
327 327 # Before starting the manifest merge, merge.updates will call
328 328 # _checkunknown to check if there are any files in the merged-in
329 329 # changeset that collide with unknown files in the working copy.
330 330 #
331 331 # The largefiles are seen as unknown, so this prevents us from merging
332 332 # in a file 'foo' if we already have a largefile with the same name.
333 333 #
334 334 # The overridden function filters the unknown files by removing any
335 335 # largefiles. This makes the merge proceed and we can then handle this
336 336 # case further in the overridden manifestmerge function below.
337 337 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
338 338 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
339 339 return False
340 340 return origfn(repo, wctx, mctx, f)
341 341
342 342 # The manifest merge handles conflicts on the manifest level. We want
343 343 # to handle changes in largefile-ness of files at this level too.
344 344 #
345 345 # The strategy is to run the original manifestmerge and then process
346 346 # the action list it outputs. There are two cases we need to deal with:
347 347 #
348 348 # 1. Normal file in p1, largefile in p2. Here the largefile is
349 349 # detected via its standin file, which will enter the working copy
350 350 # with a "get" action. It is not "merge" since the standin is all
351 351 # Mercurial is concerned with at this level -- the link to the
352 352 # existing normal file is not relevant here.
353 353 #
354 354 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
355 355 # since the largefile will be present in the working copy and
356 356 # different from the normal file in p2. Mercurial therefore
357 357 # triggers a merge action.
358 358 #
359 359 # In both cases, we prompt the user and emit new actions to either
360 360 # remove the standin (if the normal file was kept) or to remove the
361 361 # normal file and get the standin (if the largefile was kept). The
362 362 # default prompt answer is to use the largefile version since it was
363 363 # presumably changed on purpose.
364 364 #
365 365 # Finally, the merge.applyupdates function will then take care of
366 366 # writing the files into the working copy and lfcommands.updatelfiles
367 367 # will update the largefiles.
368 def overridemanifestmerge(origfn, repo, p1, p2, pa, branchmerge, force,
369 partial, acceptremote=False):
368 def overridecalculateupdates(origfn, repo, p1, p2, pa, branchmerge, force,
369 partial, acceptremote=False):
370 370 overwrite = force and not branchmerge
371 371 actions = origfn(repo, p1, p2, pa, branchmerge, force, partial,
372 372 acceptremote)
373 373
374 374 if overwrite:
375 375 return actions
376 376
377 377 removes = set(a[0] for a in actions if a[1] == 'r')
378 378 processed = []
379 379
380 380 for action in actions:
381 381 f, m, args, msg = action
382 382
383 383 splitstandin = f and lfutil.splitstandin(f)
384 384 if (m == "g" and splitstandin is not None and
385 385 splitstandin in p1 and splitstandin not in removes):
386 386 # Case 1: normal file in the working copy, largefile in
387 387 # the second parent
388 388 lfile = splitstandin
389 389 standin = f
390 390 msg = _('remote turned local normal file %s into a largefile\n'
391 391 'use (l)argefile or keep (n)ormal file?'
392 392 '$$ &Largefile $$ &Normal file') % lfile
393 393 if repo.ui.promptchoice(msg, 0) == 0:
394 394 processed.append((lfile, "r", None, msg))
395 395 processed.append((standin, "g", (p2.flags(standin),), msg))
396 396 else:
397 397 processed.append((standin, "r", None, msg))
398 398 elif (m == "g" and
399 399 lfutil.standin(f) in p1 and lfutil.standin(f) not in removes):
400 400 # Case 2: largefile in the working copy, normal file in
401 401 # the second parent
402 402 standin = lfutil.standin(f)
403 403 lfile = f
404 404 msg = _('remote turned local largefile %s into a normal file\n'
405 405 'keep (l)argefile or use (n)ormal file?'
406 406 '$$ &Largefile $$ &Normal file') % lfile
407 407 if repo.ui.promptchoice(msg, 0) == 0:
408 408 processed.append((lfile, "r", None, msg))
409 409 else:
410 410 processed.append((standin, "r", None, msg))
411 411 processed.append((lfile, "g", (p2.flags(lfile),), msg))
412 412 else:
413 413 processed.append(action)
414 414
415 415 return processed
416 416
417 417 # Override filemerge to prompt the user about how they wish to merge
418 418 # largefiles. This will handle identical edits without prompting the user.
419 419 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca):
420 420 if not lfutil.isstandin(orig):
421 421 return origfn(repo, mynode, orig, fcd, fco, fca)
422 422
423 423 if not fco.cmp(fcd): # files identical?
424 424 return None
425 425
426 426 if repo.ui.promptchoice(
427 427 _('largefile %s has a merge conflict\nancestor was %s\n'
428 428 'keep (l)ocal %s or\ntake (o)ther %s?'
429 429 '$$ &Local $$ &Other') %
430 430 (lfutil.splitstandin(orig),
431 431 fca.data().strip(), fcd.data().strip(), fco.data().strip()),
432 432 0) == 1:
433 433 repo.wwrite(fcd.path(), fco.data(), fco.flags())
434 434 return 0
435 435
436 436 # Copy first changes the matchers to match standins instead of
437 437 # largefiles. Then it overrides util.copyfile in that function it
438 438 # checks if the destination largefile already exists. It also keeps a
439 439 # list of copied files so that the largefiles can be copied and the
440 440 # dirstate updated.
441 441 def overridecopy(orig, ui, repo, pats, opts, rename=False):
442 442 # doesn't remove largefile on rename
443 443 if len(pats) < 2:
444 444 # this isn't legal, let the original function deal with it
445 445 return orig(ui, repo, pats, opts, rename)
446 446
447 447 def makestandin(relpath):
448 448 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
449 449 return os.path.join(repo.wjoin(lfutil.standin(path)))
450 450
451 451 fullpats = scmutil.expandpats(pats)
452 452 dest = fullpats[-1]
453 453
454 454 if os.path.isdir(dest):
455 455 if not os.path.isdir(makestandin(dest)):
456 456 os.makedirs(makestandin(dest))
457 457 # This could copy both lfiles and normal files in one command,
458 458 # but we don't want to do that. First replace their matcher to
459 459 # only match normal files and run it, then replace it to just
460 460 # match largefiles and run it again.
461 461 nonormalfiles = False
462 462 nolfiles = False
463 463 try:
464 464 try:
465 465 installnormalfilesmatchfn(repo[None].manifest())
466 466 result = orig(ui, repo, pats, opts, rename)
467 467 except util.Abort, e:
468 468 if str(e) != _('no files to copy'):
469 469 raise e
470 470 else:
471 471 nonormalfiles = True
472 472 result = 0
473 473 finally:
474 474 restorematchfn()
475 475
476 476 # The first rename can cause our current working directory to be removed.
477 477 # In that case there is nothing left to copy/rename so just quit.
478 478 try:
479 479 repo.getcwd()
480 480 except OSError:
481 481 return result
482 482
483 483 try:
484 484 try:
485 485 # When we call orig below it creates the standins but we don't add
486 486 # them to the dir state until later so lock during that time.
487 487 wlock = repo.wlock()
488 488
489 489 manifest = repo[None].manifest()
490 490 oldmatch = None # for the closure
491 491 def overridematch(ctx, pats=[], opts={}, globbed=False,
492 492 default='relpath'):
493 493 newpats = []
494 494 # The patterns were previously mangled to add the standin
495 495 # directory; we need to remove that now
496 496 for pat in pats:
497 497 if match_.patkind(pat) is None and lfutil.shortname in pat:
498 498 newpats.append(pat.replace(lfutil.shortname, ''))
499 499 else:
500 500 newpats.append(pat)
501 501 match = oldmatch(ctx, newpats, opts, globbed, default)
502 502 m = copy.copy(match)
503 503 lfile = lambda f: lfutil.standin(f) in manifest
504 504 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
505 505 m._fmap = set(m._files)
506 506 m._always = False
507 507 origmatchfn = m.matchfn
508 508 m.matchfn = lambda f: (lfutil.isstandin(f) and
509 509 (f in manifest) and
510 510 origmatchfn(lfutil.splitstandin(f)) or
511 511 None)
512 512 return m
513 513 oldmatch = installmatchfn(overridematch)
514 514 listpats = []
515 515 for pat in pats:
516 516 if match_.patkind(pat) is not None:
517 517 listpats.append(pat)
518 518 else:
519 519 listpats.append(makestandin(pat))
520 520
521 521 try:
522 522 origcopyfile = util.copyfile
523 523 copiedfiles = []
524 524 def overridecopyfile(src, dest):
525 525 if (lfutil.shortname in src and
526 526 dest.startswith(repo.wjoin(lfutil.shortname))):
527 527 destlfile = dest.replace(lfutil.shortname, '')
528 528 if not opts['force'] and os.path.exists(destlfile):
529 529 raise IOError('',
530 530 _('destination largefile already exists'))
531 531 copiedfiles.append((src, dest))
532 532 origcopyfile(src, dest)
533 533
534 534 util.copyfile = overridecopyfile
535 535 result += orig(ui, repo, listpats, opts, rename)
536 536 finally:
537 537 util.copyfile = origcopyfile
538 538
539 539 lfdirstate = lfutil.openlfdirstate(ui, repo)
540 540 for (src, dest) in copiedfiles:
541 541 if (lfutil.shortname in src and
542 542 dest.startswith(repo.wjoin(lfutil.shortname))):
543 543 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
544 544 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
545 545 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
546 546 if not os.path.isdir(destlfiledir):
547 547 os.makedirs(destlfiledir)
548 548 if rename:
549 549 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
550 550 lfdirstate.remove(srclfile)
551 551 else:
552 552 util.copyfile(repo.wjoin(srclfile),
553 553 repo.wjoin(destlfile))
554 554
555 555 lfdirstate.add(destlfile)
556 556 lfdirstate.write()
557 557 except util.Abort, e:
558 558 if str(e) != _('no files to copy'):
559 559 raise e
560 560 else:
561 561 nolfiles = True
562 562 finally:
563 563 restorematchfn()
564 564 wlock.release()
565 565
566 566 if nolfiles and nonormalfiles:
567 567 raise util.Abort(_('no files to copy'))
568 568
569 569 return result
570 570
571 571 # When the user calls revert, we have to be careful to not revert any
572 572 # changes to other largefiles accidentally. This means we have to keep
573 573 # track of the largefiles that are being reverted so we only pull down
574 574 # the necessary largefiles.
575 575 #
576 576 # Standins are only updated (to match the hash of largefiles) before
577 577 # commits. Update the standins then run the original revert, changing
578 578 # the matcher to hit standins instead of largefiles. Based on the
579 579 # resulting standins update the largefiles. Then return the standins
580 580 # to their proper state
581 581 def overriderevert(orig, ui, repo, *pats, **opts):
582 582 # Because we put the standins in a bad state (by updating them)
583 583 # and then return them to a correct state we need to lock to
584 584 # prevent others from changing them in their incorrect state.
585 585 wlock = repo.wlock()
586 586 try:
587 587 lfdirstate = lfutil.openlfdirstate(ui, repo)
588 588 (modified, added, removed, missing, unknown, ignored, clean) = \
589 589 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
590 590 lfdirstate.write()
591 591 for lfile in modified:
592 592 lfutil.updatestandin(repo, lfutil.standin(lfile))
593 593 for lfile in missing:
594 594 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
595 595 os.unlink(repo.wjoin(lfutil.standin(lfile)))
596 596
597 597 try:
598 598 ctx = scmutil.revsingle(repo, opts.get('rev'))
599 599 oldmatch = None # for the closure
600 600 def overridematch(ctx, pats=[], opts={}, globbed=False,
601 601 default='relpath'):
602 602 match = oldmatch(ctx, pats, opts, globbed, default)
603 603 m = copy.copy(match)
604 604 def tostandin(f):
605 605 if lfutil.standin(f) in ctx:
606 606 return lfutil.standin(f)
607 607 elif lfutil.standin(f) in repo[None]:
608 608 return None
609 609 return f
610 610 m._files = [tostandin(f) for f in m._files]
611 611 m._files = [f for f in m._files if f is not None]
612 612 m._fmap = set(m._files)
613 613 m._always = False
614 614 origmatchfn = m.matchfn
615 615 def matchfn(f):
616 616 if lfutil.isstandin(f):
617 617 # We need to keep track of what largefiles are being
618 618 # matched so we know which ones to update later --
619 619 # otherwise we accidentally revert changes to other
620 620 # largefiles. This is repo-specific, so duckpunch the
621 621 # repo object to keep the list of largefiles for us
622 622 # later.
623 623 if origmatchfn(lfutil.splitstandin(f)) and \
624 624 (f in repo[None] or f in ctx):
625 625 lfileslist = getattr(repo, '_lfilestoupdate', [])
626 626 lfileslist.append(lfutil.splitstandin(f))
627 627 repo._lfilestoupdate = lfileslist
628 628 return True
629 629 else:
630 630 return False
631 631 return origmatchfn(f)
632 632 m.matchfn = matchfn
633 633 return m
634 634 oldmatch = installmatchfn(overridematch)
635 635 scmutil.match
636 636 matches = overridematch(repo[None], pats, opts)
637 637 orig(ui, repo, *pats, **opts)
638 638 finally:
639 639 restorematchfn()
640 640 lfileslist = getattr(repo, '_lfilestoupdate', [])
641 641 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
642 642 printmessage=False)
643 643
644 644 # empty out the largefiles list so we start fresh next time
645 645 repo._lfilestoupdate = []
646 646 for lfile in modified:
647 647 if lfile in lfileslist:
648 648 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
649 649 in repo['.']:
650 650 lfutil.writestandin(repo, lfutil.standin(lfile),
651 651 repo['.'][lfile].data().strip(),
652 652 'x' in repo['.'][lfile].flags())
653 653 lfdirstate = lfutil.openlfdirstate(ui, repo)
654 654 for lfile in added:
655 655 standin = lfutil.standin(lfile)
656 656 if standin not in ctx and (standin in matches or opts.get('all')):
657 657 if lfile in lfdirstate:
658 658 lfdirstate.drop(lfile)
659 659 util.unlinkpath(repo.wjoin(standin))
660 660 lfdirstate.write()
661 661 finally:
662 662 wlock.release()
663 663
664 664 def hgupdaterepo(orig, repo, node, overwrite):
665 665 if not overwrite:
666 666 # Only call updatelfiles on the standins that have changed to save time
667 667 oldstandins = lfutil.getstandinsstate(repo)
668 668
669 669 result = orig(repo, node, overwrite)
670 670
671 671 filelist = None
672 672 if not overwrite:
673 673 newstandins = lfutil.getstandinsstate(repo)
674 674 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
675 675 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist)
676 676 return result
677 677
678 678 def hgmerge(orig, repo, node, force=None, remind=True):
679 679 result = orig(repo, node, force, remind)
680 680 lfcommands.updatelfiles(repo.ui, repo)
681 681 return result
682 682
683 683 # When we rebase a repository with remotely changed largefiles, we need to
684 684 # take some extra care so that the largefiles are correctly updated in the
685 685 # working copy
686 686 def overridepull(orig, ui, repo, source=None, **opts):
687 687 revsprepull = len(repo)
688 688 if not source:
689 689 source = 'default'
690 690 repo.lfpullsource = source
691 691 if opts.get('rebase', False):
692 692 repo._isrebasing = True
693 693 try:
694 694 if opts.get('update'):
695 695 del opts['update']
696 696 ui.debug('--update and --rebase are not compatible, ignoring '
697 697 'the update flag\n')
698 698 del opts['rebase']
699 699 origpostincoming = commands.postincoming
700 700 def _dummy(*args, **kwargs):
701 701 pass
702 702 commands.postincoming = _dummy
703 703 try:
704 704 result = commands.pull(ui, repo, source, **opts)
705 705 finally:
706 706 commands.postincoming = origpostincoming
707 707 revspostpull = len(repo)
708 708 if revspostpull > revsprepull:
709 709 result = result or rebase.rebase(ui, repo)
710 710 finally:
711 711 repo._isrebasing = False
712 712 else:
713 713 result = orig(ui, repo, source, **opts)
714 714 revspostpull = len(repo)
715 715 lfrevs = opts.get('lfrev', [])
716 716 if opts.get('all_largefiles'):
717 717 lfrevs.append('pulled()')
718 718 if lfrevs and revspostpull > revsprepull:
719 719 numcached = 0
720 720 repo.firstpulled = revsprepull # for pulled() revset expression
721 721 try:
722 722 for rev in scmutil.revrange(repo, lfrevs):
723 723 ui.note(_('pulling largefiles for revision %s\n') % rev)
724 724 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
725 725 numcached += len(cached)
726 726 finally:
727 727 del repo.firstpulled
728 728 ui.status(_("%d largefiles cached\n") % numcached)
729 729 return result
730 730
731 731 def pulledrevsetsymbol(repo, subset, x):
732 732 """``pulled()``
733 733 Changesets that just has been pulled.
734 734
735 735 Only available with largefiles from pull --lfrev expressions.
736 736
737 737 .. container:: verbose
738 738
739 739 Some examples:
740 740
741 741 - pull largefiles for all new changesets::
742 742
743 743 hg pull -lfrev "pulled()"
744 744
745 745 - pull largefiles for all new branch heads::
746 746
747 747 hg pull -lfrev "head(pulled()) and not closed()"
748 748
749 749 """
750 750
751 751 try:
752 752 firstpulled = repo.firstpulled
753 753 except AttributeError:
754 754 raise util.Abort(_("pulled() only available in --lfrev"))
755 755 return revset.baseset([r for r in subset if r >= firstpulled])
756 756
757 757 def overrideclone(orig, ui, source, dest=None, **opts):
758 758 d = dest
759 759 if d is None:
760 760 d = hg.defaultdest(source)
761 761 if opts.get('all_largefiles') and not hg.islocal(d):
762 762 raise util.Abort(_(
763 763 '--all-largefiles is incompatible with non-local destination %s' %
764 764 d))
765 765
766 766 return orig(ui, source, dest, **opts)
767 767
768 768 def hgclone(orig, ui, opts, *args, **kwargs):
769 769 result = orig(ui, opts, *args, **kwargs)
770 770
771 771 if result is not None:
772 772 sourcerepo, destrepo = result
773 773 repo = destrepo.local()
774 774
775 775 # Caching is implicitly limited to 'rev' option, since the dest repo was
776 776 # truncated at that point. The user may expect a download count with
777 777 # this option, so attempt whether or not this is a largefile repo.
778 778 if opts.get('all_largefiles'):
779 779 success, missing = lfcommands.downloadlfiles(ui, repo, None)
780 780
781 781 if missing != 0:
782 782 return None
783 783
784 784 return result
785 785
786 786 def overriderebase(orig, ui, repo, **opts):
787 787 repo._isrebasing = True
788 788 try:
789 789 return orig(ui, repo, **opts)
790 790 finally:
791 791 repo._isrebasing = False
792 792
793 793 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
794 794 prefix=None, mtime=None, subrepos=None):
795 795 # No need to lock because we are only reading history and
796 796 # largefile caches, neither of which are modified.
797 797 lfcommands.cachelfiles(repo.ui, repo, node)
798 798
799 799 if kind not in archival.archivers:
800 800 raise util.Abort(_("unknown archive type '%s'") % kind)
801 801
802 802 ctx = repo[node]
803 803
804 804 if kind == 'files':
805 805 if prefix:
806 806 raise util.Abort(
807 807 _('cannot give prefix when archiving to files'))
808 808 else:
809 809 prefix = archival.tidyprefix(dest, kind, prefix)
810 810
811 811 def write(name, mode, islink, getdata):
812 812 if matchfn and not matchfn(name):
813 813 return
814 814 data = getdata()
815 815 if decode:
816 816 data = repo.wwritedata(name, data)
817 817 archiver.addfile(prefix + name, mode, islink, data)
818 818
819 819 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
820 820
821 821 if repo.ui.configbool("ui", "archivemeta", True):
822 822 def metadata():
823 823 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
824 824 hex(repo.changelog.node(0)), hex(node), ctx.branch())
825 825
826 826 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
827 827 if repo.tagtype(t) == 'global')
828 828 if not tags:
829 829 repo.ui.pushbuffer()
830 830 opts = {'template': '{latesttag}\n{latesttagdistance}',
831 831 'style': '', 'patch': None, 'git': None}
832 832 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
833 833 ltags, dist = repo.ui.popbuffer().split('\n')
834 834 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
835 835 tags += 'latesttagdistance: %s\n' % dist
836 836
837 837 return base + tags
838 838
839 839 write('.hg_archival.txt', 0644, False, metadata)
840 840
841 841 for f in ctx:
842 842 ff = ctx.flags(f)
843 843 getdata = ctx[f].data
844 844 if lfutil.isstandin(f):
845 845 path = lfutil.findfile(repo, getdata().strip())
846 846 if path is None:
847 847 raise util.Abort(
848 848 _('largefile %s not found in repo store or system cache')
849 849 % lfutil.splitstandin(f))
850 850 f = lfutil.splitstandin(f)
851 851
852 852 def getdatafn():
853 853 fd = None
854 854 try:
855 855 fd = open(path, 'rb')
856 856 return fd.read()
857 857 finally:
858 858 if fd:
859 859 fd.close()
860 860
861 861 getdata = getdatafn
862 862 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
863 863
864 864 if subrepos:
865 865 for subpath in sorted(ctx.substate):
866 866 sub = ctx.sub(subpath)
867 867 submatch = match_.narrowmatcher(subpath, matchfn)
868 868 sub.archive(repo.ui, archiver, prefix, submatch)
869 869
870 870 archiver.done()
871 871
872 872 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
873 873 repo._get(repo._state + ('hg',))
874 874 rev = repo._state[1]
875 875 ctx = repo._repo[rev]
876 876
877 877 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
878 878
879 879 def write(name, mode, islink, getdata):
880 880 # At this point, the standin has been replaced with the largefile name,
881 881 # so the normal matcher works here without the lfutil variants.
882 882 if match and not match(f):
883 883 return
884 884 data = getdata()
885 885
886 886 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
887 887
888 888 for f in ctx:
889 889 ff = ctx.flags(f)
890 890 getdata = ctx[f].data
891 891 if lfutil.isstandin(f):
892 892 path = lfutil.findfile(repo._repo, getdata().strip())
893 893 if path is None:
894 894 raise util.Abort(
895 895 _('largefile %s not found in repo store or system cache')
896 896 % lfutil.splitstandin(f))
897 897 f = lfutil.splitstandin(f)
898 898
899 899 def getdatafn():
900 900 fd = None
901 901 try:
902 902 fd = open(os.path.join(prefix, path), 'rb')
903 903 return fd.read()
904 904 finally:
905 905 if fd:
906 906 fd.close()
907 907
908 908 getdata = getdatafn
909 909
910 910 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
911 911
912 912 for subpath in sorted(ctx.substate):
913 913 sub = ctx.sub(subpath)
914 914 submatch = match_.narrowmatcher(subpath, match)
915 915 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
916 916 submatch)
917 917
918 918 # If a largefile is modified, the change is not reflected in its
919 919 # standin until a commit. cmdutil.bailifchanged() raises an exception
920 920 # if the repo has uncommitted changes. Wrap it to also check if
921 921 # largefiles were changed. This is used by bisect and backout.
922 922 def overridebailifchanged(orig, repo):
923 923 orig(repo)
924 924 repo.lfstatus = True
925 925 modified, added, removed, deleted = repo.status()[:4]
926 926 repo.lfstatus = False
927 927 if modified or added or removed or deleted:
928 928 raise util.Abort(_('uncommitted changes'))
929 929
930 930 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
931 931 def overridefetch(orig, ui, repo, *pats, **opts):
932 932 repo.lfstatus = True
933 933 modified, added, removed, deleted = repo.status()[:4]
934 934 repo.lfstatus = False
935 935 if modified or added or removed or deleted:
936 936 raise util.Abort(_('uncommitted changes'))
937 937 return orig(ui, repo, *pats, **opts)
938 938
939 939 def overrideforget(orig, ui, repo, *pats, **opts):
940 940 installnormalfilesmatchfn(repo[None].manifest())
941 941 result = orig(ui, repo, *pats, **opts)
942 942 restorematchfn()
943 943 m = scmutil.match(repo[None], pats, opts)
944 944
945 945 try:
946 946 repo.lfstatus = True
947 947 s = repo.status(match=m, clean=True)
948 948 finally:
949 949 repo.lfstatus = False
950 950 forget = sorted(s[0] + s[1] + s[3] + s[6])
951 951 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
952 952
953 953 for f in forget:
954 954 if lfutil.standin(f) not in repo.dirstate and not \
955 955 os.path.isdir(m.rel(lfutil.standin(f))):
956 956 ui.warn(_('not removing %s: file is already untracked\n')
957 957 % m.rel(f))
958 958 result = 1
959 959
960 960 for f in forget:
961 961 if ui.verbose or not m.exact(f):
962 962 ui.status(_('removing %s\n') % m.rel(f))
963 963
964 964 # Need to lock because standin files are deleted then removed from the
965 965 # repository and we could race in-between.
966 966 wlock = repo.wlock()
967 967 try:
968 968 lfdirstate = lfutil.openlfdirstate(ui, repo)
969 969 for f in forget:
970 970 if lfdirstate[f] == 'a':
971 971 lfdirstate.drop(f)
972 972 else:
973 973 lfdirstate.remove(f)
974 974 lfdirstate.write()
975 975 standins = [lfutil.standin(f) for f in forget]
976 976 for f in standins:
977 977 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
978 978 repo[None].forget(standins)
979 979 finally:
980 980 wlock.release()
981 981
982 982 return result
983 983
984 984 def getoutgoinglfiles(ui, repo, dest=None, **opts):
985 985 dest = ui.expandpath(dest or 'default-push', dest or 'default')
986 986 dest, branches = hg.parseurl(dest, opts.get('branch'))
987 987 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
988 988 if revs:
989 989 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
990 990
991 991 try:
992 992 remote = hg.peer(repo, opts, dest)
993 993 except error.RepoError:
994 994 return None
995 995 outgoing = discovery.findcommonoutgoing(repo, remote.peer(), force=False)
996 996 if not outgoing.missing:
997 997 return outgoing.missing
998 998 o = repo.changelog.nodesbetween(outgoing.missing, revs)[0]
999 999 if opts.get('newest_first'):
1000 1000 o.reverse()
1001 1001
1002 1002 toupload = set()
1003 1003 for n in o:
1004 1004 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
1005 1005 ctx = repo[n]
1006 1006 files = set(ctx.files())
1007 1007 if len(parents) == 2:
1008 1008 mc = ctx.manifest()
1009 1009 mp1 = ctx.parents()[0].manifest()
1010 1010 mp2 = ctx.parents()[1].manifest()
1011 1011 for f in mp1:
1012 1012 if f not in mc:
1013 1013 files.add(f)
1014 1014 for f in mp2:
1015 1015 if f not in mc:
1016 1016 files.add(f)
1017 1017 for f in mc:
1018 1018 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
1019 1019 files.add(f)
1020 1020 toupload = toupload.union(
1021 1021 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
1022 1022 return sorted(toupload)
1023 1023
1024 1024 def overrideoutgoing(orig, ui, repo, dest=None, **opts):
1025 1025 result = orig(ui, repo, dest, **opts)
1026 1026
1027 1027 if opts.pop('large', None):
1028 1028 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
1029 1029 if toupload is None:
1030 1030 ui.status(_('largefiles: No remote repo\n'))
1031 1031 elif not toupload:
1032 1032 ui.status(_('largefiles: no files to upload\n'))
1033 1033 else:
1034 1034 ui.status(_('largefiles to upload:\n'))
1035 1035 for file in toupload:
1036 1036 ui.status(lfutil.splitstandin(file) + '\n')
1037 1037 ui.status('\n')
1038 1038
1039 1039 return result
1040 1040
1041 1041 def overridesummary(orig, ui, repo, *pats, **opts):
1042 1042 try:
1043 1043 repo.lfstatus = True
1044 1044 orig(ui, repo, *pats, **opts)
1045 1045 finally:
1046 1046 repo.lfstatus = False
1047 1047
1048 1048 if opts.pop('large', None):
1049 1049 toupload = getoutgoinglfiles(ui, repo, None, **opts)
1050 1050 if toupload is None:
1051 1051 # i18n: column positioning for "hg summary"
1052 1052 ui.status(_('largefiles: (no remote repo)\n'))
1053 1053 elif not toupload:
1054 1054 # i18n: column positioning for "hg summary"
1055 1055 ui.status(_('largefiles: (no files to upload)\n'))
1056 1056 else:
1057 1057 # i18n: column positioning for "hg summary"
1058 1058 ui.status(_('largefiles: %d to upload\n') % len(toupload))
1059 1059
1060 1060 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1061 1061 similarity=None):
1062 1062 if not lfutil.islfilesrepo(repo):
1063 1063 return orig(repo, pats, opts, dry_run, similarity)
1064 1064 # Get the list of missing largefiles so we can remove them
1065 1065 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1066 1066 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
1067 1067 False, False)
1068 1068 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
1069 1069
1070 1070 # Call into the normal remove code, but the removing of the standin, we want
1071 1071 # to have handled by original addremove. Monkey patching here makes sure
1072 1072 # we don't remove the standin in the largefiles code, preventing a very
1073 1073 # confused state later.
1074 1074 if missing:
1075 1075 m = [repo.wjoin(f) for f in missing]
1076 1076 repo._isaddremove = True
1077 1077 removelargefiles(repo.ui, repo, *m, **opts)
1078 1078 repo._isaddremove = False
1079 1079 # Call into the normal add code, and any files that *should* be added as
1080 1080 # largefiles will be
1081 1081 addlargefiles(repo.ui, repo, *pats, **opts)
1082 1082 # Now that we've handled largefiles, hand off to the original addremove
1083 1083 # function to take care of the rest. Make sure it doesn't do anything with
1084 1084 # largefiles by installing a matcher that will ignore them.
1085 1085 installnormalfilesmatchfn(repo[None].manifest())
1086 1086 result = orig(repo, pats, opts, dry_run, similarity)
1087 1087 restorematchfn()
1088 1088 return result
1089 1089
1090 1090 # Calling purge with --all will cause the largefiles to be deleted.
1091 1091 # Override repo.status to prevent this from happening.
1092 1092 def overridepurge(orig, ui, repo, *dirs, **opts):
1093 1093 # XXX large file status is buggy when used on repo proxy.
1094 1094 # XXX this needs to be investigate.
1095 1095 repo = repo.unfiltered()
1096 1096 oldstatus = repo.status
1097 1097 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1098 1098 clean=False, unknown=False, listsubrepos=False):
1099 1099 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1100 1100 listsubrepos)
1101 1101 lfdirstate = lfutil.openlfdirstate(ui, repo)
1102 1102 modified, added, removed, deleted, unknown, ignored, clean = r
1103 1103 unknown = [f for f in unknown if lfdirstate[f] == '?']
1104 1104 ignored = [f for f in ignored if lfdirstate[f] == '?']
1105 1105 return modified, added, removed, deleted, unknown, ignored, clean
1106 1106 repo.status = overridestatus
1107 1107 orig(ui, repo, *dirs, **opts)
1108 1108 repo.status = oldstatus
1109 1109
1110 1110 def overriderollback(orig, ui, repo, **opts):
1111 1111 result = orig(ui, repo, **opts)
1112 1112 merge.update(repo, node=None, branchmerge=False, force=True,
1113 1113 partial=lfutil.isstandin)
1114 1114 wlock = repo.wlock()
1115 1115 try:
1116 1116 lfdirstate = lfutil.openlfdirstate(ui, repo)
1117 1117 lfiles = lfutil.listlfiles(repo)
1118 1118 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
1119 1119 for file in lfiles:
1120 1120 if file in oldlfiles:
1121 1121 lfdirstate.normallookup(file)
1122 1122 else:
1123 1123 lfdirstate.add(file)
1124 1124 lfdirstate.write()
1125 1125 finally:
1126 1126 wlock.release()
1127 1127 return result
1128 1128
1129 1129 def overridetransplant(orig, ui, repo, *revs, **opts):
1130 1130 try:
1131 1131 oldstandins = lfutil.getstandinsstate(repo)
1132 1132 repo._istransplanting = True
1133 1133 result = orig(ui, repo, *revs, **opts)
1134 1134 newstandins = lfutil.getstandinsstate(repo)
1135 1135 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1136 1136 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1137 1137 printmessage=True)
1138 1138 finally:
1139 1139 repo._istransplanting = False
1140 1140 return result
1141 1141
1142 1142 def overridecat(orig, ui, repo, file1, *pats, **opts):
1143 1143 ctx = scmutil.revsingle(repo, opts.get('rev'))
1144 1144 err = 1
1145 1145 notbad = set()
1146 1146 m = scmutil.match(ctx, (file1,) + pats, opts)
1147 1147 origmatchfn = m.matchfn
1148 1148 def lfmatchfn(f):
1149 1149 lf = lfutil.splitstandin(f)
1150 1150 if lf is None:
1151 1151 return origmatchfn(f)
1152 1152 notbad.add(lf)
1153 1153 return origmatchfn(lf)
1154 1154 m.matchfn = lfmatchfn
1155 1155 origbadfn = m.bad
1156 1156 def lfbadfn(f, msg):
1157 1157 if not f in notbad:
1158 1158 return origbadfn(f, msg)
1159 1159 m.bad = lfbadfn
1160 1160 for f in ctx.walk(m):
1161 1161 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1162 1162 pathname=f)
1163 1163 lf = lfutil.splitstandin(f)
1164 1164 if lf is None:
1165 1165 # duplicating unreachable code from commands.cat
1166 1166 data = ctx[f].data()
1167 1167 if opts.get('decode'):
1168 1168 data = repo.wwritedata(f, data)
1169 1169 fp.write(data)
1170 1170 else:
1171 1171 hash = lfutil.readstandin(repo, lf, ctx.rev())
1172 1172 if not lfutil.inusercache(repo.ui, hash):
1173 1173 store = basestore._openstore(repo)
1174 1174 success, missing = store.get([(lf, hash)])
1175 1175 if len(success) != 1:
1176 1176 raise util.Abort(
1177 1177 _('largefile %s is not in cache and could not be '
1178 1178 'downloaded') % lf)
1179 1179 path = lfutil.usercachepath(repo.ui, hash)
1180 1180 fpin = open(path, "rb")
1181 1181 for chunk in util.filechunkiter(fpin, 128 * 1024):
1182 1182 fp.write(chunk)
1183 1183 fpin.close()
1184 1184 fp.close()
1185 1185 err = 0
1186 1186 return err
1187 1187
1188 1188 def mercurialsinkbefore(orig, sink):
1189 1189 sink.repo._isconverting = True
1190 1190 orig(sink)
1191 1191
1192 1192 def mercurialsinkafter(orig, sink):
1193 1193 sink.repo._isconverting = False
1194 1194 orig(sink)
@@ -1,175 +1,175 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
13 13 from mercurial.i18n import _
14 14 from mercurial.hgweb import hgweb_mod, webcommands
15 15 from mercurial.subrepo import hgsubrepo
16 16
17 17 import overrides
18 18 import proto
19 19
20 20 def uisetup(ui):
21 21 # Disable auto-status for some commands which assume that all
22 22 # files in the result are under Mercurial's control
23 23
24 24 entry = extensions.wrapcommand(commands.table, 'add',
25 25 overrides.overrideadd)
26 26 addopt = [('', 'large', None, _('add as largefile')),
27 27 ('', 'normal', None, _('add as normal file')),
28 28 ('', 'lfsize', '', _('add all files above this size '
29 29 '(in megabytes) as largefiles '
30 30 '(default: 10)'))]
31 31 entry[1].extend(addopt)
32 32
33 33 # The scmutil function is called both by the (trivial) addremove command,
34 34 # and in the process of handling commit -A (issue3542)
35 35 entry = extensions.wrapfunction(scmutil, 'addremove',
36 36 overrides.scmutiladdremove)
37 37 entry = extensions.wrapcommand(commands.table, 'remove',
38 38 overrides.overrideremove)
39 39 entry = extensions.wrapcommand(commands.table, 'forget',
40 40 overrides.overrideforget)
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(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 entry = extensions.wrapcommand(commands.table, 'outgoing',
69 69 overrides.overrideoutgoing)
70 70 outgoingopt = [('', 'large', None, _('display outgoing largefiles'))]
71 71 entry[1].extend(outgoingopt)
72 72 entry = extensions.wrapcommand(commands.table, 'summary',
73 73 overrides.overridesummary)
74 74 summaryopt = [('', 'large', None, _('display outgoing largefiles'))]
75 75 entry[1].extend(summaryopt)
76 76
77 77 entry = extensions.wrapcommand(commands.table, 'update',
78 78 overrides.overrideupdate)
79 79 entry = extensions.wrapcommand(commands.table, 'pull',
80 80 overrides.overridepull)
81 81 pullopt = [('', 'all-largefiles', None,
82 82 _('download all pulled versions of largefiles (DEPRECATED)')),
83 83 ('', 'lfrev', [],
84 84 _('download largefiles for these revisions'), _('REV'))]
85 85 entry[1].extend(pullopt)
86 86 revset.symbols['pulled'] = overrides.pulledrevsetsymbol
87 87
88 88 entry = extensions.wrapcommand(commands.table, 'clone',
89 89 overrides.overrideclone)
90 90 cloneopt = [('', 'all-largefiles', None,
91 91 _('download all versions of all largefiles'))]
92 92 entry[1].extend(cloneopt)
93 93 entry = extensions.wrapfunction(hg, 'clone', overrides.hgclone)
94 94
95 95 entry = extensions.wrapcommand(commands.table, 'cat',
96 96 overrides.overridecat)
97 97 entry = extensions.wrapfunction(merge, '_checkunknownfile',
98 98 overrides.overridecheckunknownfile)
99 entry = extensions.wrapfunction(merge, 'manifestmerge',
100 overrides.overridemanifestmerge)
99 entry = extensions.wrapfunction(merge, 'calculateupdates',
100 overrides.overridecalculateupdates)
101 101 entry = extensions.wrapfunction(filemerge, 'filemerge',
102 102 overrides.overridefilemerge)
103 103 entry = extensions.wrapfunction(cmdutil, 'copy',
104 104 overrides.overridecopy)
105 105
106 106 # Summary calls dirty on the subrepos
107 107 entry = extensions.wrapfunction(hgsubrepo, 'dirty',
108 108 overrides.overridedirty)
109 109
110 110 # Backout calls revert so we need to override both the command and the
111 111 # function
112 112 entry = extensions.wrapcommand(commands.table, 'revert',
113 113 overrides.overriderevert)
114 114 entry = extensions.wrapfunction(commands, 'revert',
115 115 overrides.overriderevert)
116 116
117 117 extensions.wrapfunction(hg, 'updaterepo', overrides.hgupdaterepo)
118 118 extensions.wrapfunction(hg, 'merge', overrides.hgmerge)
119 119
120 120 extensions.wrapfunction(archival, 'archive', overrides.overridearchive)
121 121 extensions.wrapfunction(hgsubrepo, 'archive', overrides.hgsubrepoarchive)
122 122 extensions.wrapfunction(cmdutil, 'bailifchanged',
123 123 overrides.overridebailifchanged)
124 124
125 125 # create the new wireproto commands ...
126 126 wireproto.commands['putlfile'] = (proto.putlfile, 'sha')
127 127 wireproto.commands['getlfile'] = (proto.getlfile, 'sha')
128 128 wireproto.commands['statlfile'] = (proto.statlfile, 'sha')
129 129
130 130 # ... and wrap some existing ones
131 131 wireproto.commands['capabilities'] = (proto.capabilities, '')
132 132 wireproto.commands['heads'] = (proto.heads, '')
133 133 wireproto.commands['lheads'] = (wireproto.heads, '')
134 134
135 135 # make putlfile behave the same as push and {get,stat}lfile behave
136 136 # the same as pull w.r.t. permissions checks
137 137 hgweb_mod.perms['putlfile'] = 'push'
138 138 hgweb_mod.perms['getlfile'] = 'pull'
139 139 hgweb_mod.perms['statlfile'] = 'pull'
140 140
141 141 extensions.wrapfunction(webcommands, 'decodepath', overrides.decodepath)
142 142
143 143 # the hello wireproto command uses wireproto.capabilities, so it won't see
144 144 # our largefiles capability unless we replace the actual function as well.
145 145 proto.capabilitiesorig = wireproto.capabilities
146 146 wireproto.capabilities = proto.capabilities
147 147
148 148 # can't do this in reposetup because it needs to have happened before
149 149 # wirerepo.__init__ is called
150 150 proto.ssholdcallstream = sshpeer.sshpeer._callstream
151 151 proto.httpoldcallstream = httppeer.httppeer._callstream
152 152 sshpeer.sshpeer._callstream = proto.sshrepocallstream
153 153 httppeer.httppeer._callstream = proto.httprepocallstream
154 154
155 155 # override some extensions' stuff as well
156 156 for name, module in extensions.extensions():
157 157 if name == 'fetch':
158 158 extensions.wrapcommand(getattr(module, 'cmdtable'), 'fetch',
159 159 overrides.overridefetch)
160 160 if name == 'purge':
161 161 extensions.wrapcommand(getattr(module, 'cmdtable'), 'purge',
162 162 overrides.overridepurge)
163 163 if name == 'rebase':
164 164 extensions.wrapcommand(getattr(module, 'cmdtable'), 'rebase',
165 165 overrides.overriderebase)
166 166 if name == 'transplant':
167 167 extensions.wrapcommand(getattr(module, 'cmdtable'), 'transplant',
168 168 overrides.overridetransplant)
169 169 if name == 'convert':
170 170 convcmd = getattr(module, 'convcmd')
171 171 hgsink = getattr(convcmd, 'mercurial_sink')
172 172 extensions.wrapfunction(hgsink, 'before',
173 173 overrides.mercurialsinkbefore)
174 174 extensions.wrapfunction(hgsink, 'after',
175 175 overrides.mercurialsinkafter)
General Comments 0
You need to be logged in to leave comments. Login now