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