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