##// END OF EJS Templates
largefiles: replace repo._isaddremove hack with a simple function parameter
Mads Kiilerich -
r23038:3f581bfb default
parent child Browse files
Show More
@@ -1,1325 +1,1323
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 118 standins = []
119 119
120 120 # Need to lock, otherwise there could be a race condition between
121 121 # when standins are created and added to the repo.
122 122 wlock = repo.wlock()
123 123 try:
124 124 if not opts.get('dry_run'):
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 def removelargefiles(ui, repo, *pats, **opts):
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 if getattr(repo, "_isaddremove", False):
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 if not getattr(repo, "_isaddremove", False):
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 return removelargefiles(ui, repo, *pats, **opts) or result
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 lfdirstate = lfutil.openlfdirstate(ui, repo)
355 355 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()),
356 356 [], False, False, False)
357 357
358 358 if opts['check']:
359 359 mod = len(s.modified) > 0
360 360 for lfile in unsure:
361 361 standin = lfutil.standin(lfile)
362 362 if repo['.'][standin].data().strip() != \
363 363 lfutil.hashfile(repo.wjoin(lfile)):
364 364 mod = True
365 365 else:
366 366 lfdirstate.normal(lfile)
367 367 lfdirstate.write()
368 368 if mod:
369 369 raise util.Abort(_('uncommitted changes'))
370 370 return orig(ui, repo, *pats, **opts)
371 371 finally:
372 372 wlock.release()
373 373
374 374 # Before starting the manifest merge, merge.updates will call
375 375 # _checkunknown to check if there are any files in the merged-in
376 376 # changeset that collide with unknown files in the working copy.
377 377 #
378 378 # The largefiles are seen as unknown, so this prevents us from merging
379 379 # in a file 'foo' if we already have a largefile with the same name.
380 380 #
381 381 # The overridden function filters the unknown files by removing any
382 382 # largefiles. This makes the merge proceed and we can then handle this
383 383 # case further in the overridden manifestmerge function below.
384 384 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
385 385 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
386 386 return False
387 387 return origfn(repo, wctx, mctx, f)
388 388
389 389 # The manifest merge handles conflicts on the manifest level. We want
390 390 # to handle changes in largefile-ness of files at this level too.
391 391 #
392 392 # The strategy is to run the original manifestmerge and then process
393 393 # the action list it outputs. There are two cases we need to deal with:
394 394 #
395 395 # 1. Normal file in p1, largefile in p2. Here the largefile is
396 396 # detected via its standin file, which will enter the working copy
397 397 # with a "get" action. It is not "merge" since the standin is all
398 398 # Mercurial is concerned with at this level -- the link to the
399 399 # existing normal file is not relevant here.
400 400 #
401 401 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
402 402 # since the largefile will be present in the working copy and
403 403 # different from the normal file in p2. Mercurial therefore
404 404 # triggers a merge action.
405 405 #
406 406 # In both cases, we prompt the user and emit new actions to either
407 407 # remove the standin (if the normal file was kept) or to remove the
408 408 # normal file and get the standin (if the largefile was kept). The
409 409 # default prompt answer is to use the largefile version since it was
410 410 # presumably changed on purpose.
411 411 #
412 412 # Finally, the merge.applyupdates function will then take care of
413 413 # writing the files into the working copy and lfcommands.updatelfiles
414 414 # will update the largefiles.
415 415 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
416 416 partial, acceptremote, followcopies):
417 417 overwrite = force and not branchmerge
418 418 actions = origfn(repo, p1, p2, pas, branchmerge, force, partial,
419 419 acceptremote, followcopies)
420 420
421 421 if overwrite:
422 422 return actions
423 423
424 424 removes = set(a[0] for a in actions['r'])
425 425
426 426 newglist = []
427 427 lfmr = [] # LargeFiles: Mark as Removed
428 428 for action in actions['g']:
429 429 f, args, msg = action
430 430 splitstandin = f and lfutil.splitstandin(f)
431 431 if (splitstandin is not None and
432 432 splitstandin in p1 and splitstandin not in removes):
433 433 # Case 1: normal file in the working copy, largefile in
434 434 # the second parent
435 435 lfile = splitstandin
436 436 standin = f
437 437 msg = _('remote turned local normal file %s into a largefile\n'
438 438 'use (l)argefile or keep (n)ormal file?'
439 439 '$$ &Largefile $$ &Normal file') % lfile
440 440 if repo.ui.promptchoice(msg, 0) == 0:
441 441 actions['r'].append((lfile, None, msg))
442 442 newglist.append((standin, (p2.flags(standin),), msg))
443 443 else:
444 444 actions['r'].append((standin, None, msg))
445 445 elif lfutil.standin(f) in p1 and lfutil.standin(f) not in removes:
446 446 # Case 2: largefile in the working copy, normal file in
447 447 # the second parent
448 448 standin = lfutil.standin(f)
449 449 lfile = f
450 450 msg = _('remote turned local largefile %s into a normal file\n'
451 451 'keep (l)argefile or use (n)ormal file?'
452 452 '$$ &Largefile $$ &Normal file') % lfile
453 453 if repo.ui.promptchoice(msg, 0) == 0:
454 454 if branchmerge:
455 455 # largefile can be restored from standin safely
456 456 actions['r'].append((lfile, None, msg))
457 457 else:
458 458 # "lfile" should be marked as "removed" without
459 459 # removal of itself
460 460 lfmr.append((lfile, None, msg))
461 461
462 462 # linear-merge should treat this largefile as 're-added'
463 463 actions['a'].append((standin, None, msg))
464 464 else:
465 465 actions['r'].append((standin, None, msg))
466 466 newglist.append((lfile, (p2.flags(lfile),), msg))
467 467 else:
468 468 newglist.append(action)
469 469
470 470 newglist.sort()
471 471 actions['g'] = newglist
472 472 if lfmr:
473 473 lfmr.sort()
474 474 actions['lfmr'] = lfmr
475 475
476 476 return actions
477 477
478 478 def mergerecordupdates(orig, repo, actions, branchmerge):
479 479 if 'lfmr' in actions:
480 480 # this should be executed before 'orig', to execute 'remove'
481 481 # before all other actions
482 482 for lfile, args, msg in actions['lfmr']:
483 483 repo.dirstate.remove(lfile)
484 484
485 485 return orig(repo, actions, branchmerge)
486 486
487 487
488 488 # Override filemerge to prompt the user about how they wish to merge
489 489 # largefiles. This will handle identical edits without prompting the user.
490 490 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
491 491 if not lfutil.isstandin(orig):
492 492 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
493 493
494 494 ahash = fca.data().strip().lower()
495 495 dhash = fcd.data().strip().lower()
496 496 ohash = fco.data().strip().lower()
497 497 if (ohash != ahash and
498 498 ohash != dhash and
499 499 (dhash == ahash or
500 500 repo.ui.promptchoice(
501 501 _('largefile %s has a merge conflict\nancestor was %s\n'
502 502 'keep (l)ocal %s or\ntake (o)ther %s?'
503 503 '$$ &Local $$ &Other') %
504 504 (lfutil.splitstandin(orig), ahash, dhash, ohash),
505 505 0) == 1)):
506 506 repo.wwrite(fcd.path(), fco.data(), fco.flags())
507 507 return 0
508 508
509 509 # Copy first changes the matchers to match standins instead of
510 510 # largefiles. Then it overrides util.copyfile in that function it
511 511 # checks if the destination largefile already exists. It also keeps a
512 512 # list of copied files so that the largefiles can be copied and the
513 513 # dirstate updated.
514 514 def overridecopy(orig, ui, repo, pats, opts, rename=False):
515 515 # doesn't remove largefile on rename
516 516 if len(pats) < 2:
517 517 # this isn't legal, let the original function deal with it
518 518 return orig(ui, repo, pats, opts, rename)
519 519
520 520 def makestandin(relpath):
521 521 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
522 522 return os.path.join(repo.wjoin(lfutil.standin(path)))
523 523
524 524 fullpats = scmutil.expandpats(pats)
525 525 dest = fullpats[-1]
526 526
527 527 if os.path.isdir(dest):
528 528 if not os.path.isdir(makestandin(dest)):
529 529 os.makedirs(makestandin(dest))
530 530 # This could copy both lfiles and normal files in one command,
531 531 # but we don't want to do that. First replace their matcher to
532 532 # only match normal files and run it, then replace it to just
533 533 # match largefiles and run it again.
534 534 nonormalfiles = False
535 535 nolfiles = False
536 536 installnormalfilesmatchfn(repo[None].manifest())
537 537 try:
538 538 try:
539 539 result = orig(ui, repo, pats, opts, rename)
540 540 except util.Abort, e:
541 541 if str(e) != _('no files to copy'):
542 542 raise e
543 543 else:
544 544 nonormalfiles = True
545 545 result = 0
546 546 finally:
547 547 restorematchfn()
548 548
549 549 # The first rename can cause our current working directory to be removed.
550 550 # In that case there is nothing left to copy/rename so just quit.
551 551 try:
552 552 repo.getcwd()
553 553 except OSError:
554 554 return result
555 555
556 556 try:
557 557 try:
558 558 # When we call orig below it creates the standins but we don't add
559 559 # them to the dir state until later so lock during that time.
560 560 wlock = repo.wlock()
561 561
562 562 manifest = repo[None].manifest()
563 563 def overridematch(ctx, pats=[], opts={}, globbed=False,
564 564 default='relpath'):
565 565 newpats = []
566 566 # The patterns were previously mangled to add the standin
567 567 # directory; we need to remove that now
568 568 for pat in pats:
569 569 if match_.patkind(pat) is None and lfutil.shortname in pat:
570 570 newpats.append(pat.replace(lfutil.shortname, ''))
571 571 else:
572 572 newpats.append(pat)
573 573 match = oldmatch(ctx, newpats, opts, globbed, default)
574 574 m = copy.copy(match)
575 575 lfile = lambda f: lfutil.standin(f) in manifest
576 576 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
577 577 m._fmap = set(m._files)
578 578 m._always = False
579 579 origmatchfn = m.matchfn
580 580 m.matchfn = lambda f: (lfutil.isstandin(f) and
581 581 (f in manifest) and
582 582 origmatchfn(lfutil.splitstandin(f)) or
583 583 None)
584 584 return m
585 585 oldmatch = installmatchfn(overridematch)
586 586 listpats = []
587 587 for pat in pats:
588 588 if match_.patkind(pat) is not None:
589 589 listpats.append(pat)
590 590 else:
591 591 listpats.append(makestandin(pat))
592 592
593 593 try:
594 594 origcopyfile = util.copyfile
595 595 copiedfiles = []
596 596 def overridecopyfile(src, dest):
597 597 if (lfutil.shortname in src and
598 598 dest.startswith(repo.wjoin(lfutil.shortname))):
599 599 destlfile = dest.replace(lfutil.shortname, '')
600 600 if not opts['force'] and os.path.exists(destlfile):
601 601 raise IOError('',
602 602 _('destination largefile already exists'))
603 603 copiedfiles.append((src, dest))
604 604 origcopyfile(src, dest)
605 605
606 606 util.copyfile = overridecopyfile
607 607 result += orig(ui, repo, listpats, opts, rename)
608 608 finally:
609 609 util.copyfile = origcopyfile
610 610
611 611 lfdirstate = lfutil.openlfdirstate(ui, repo)
612 612 for (src, dest) in copiedfiles:
613 613 if (lfutil.shortname in src and
614 614 dest.startswith(repo.wjoin(lfutil.shortname))):
615 615 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
616 616 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
617 617 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
618 618 if not os.path.isdir(destlfiledir):
619 619 os.makedirs(destlfiledir)
620 620 if rename:
621 621 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
622 622
623 623 # The file is gone, but this deletes any empty parent
624 624 # directories as a side-effect.
625 625 util.unlinkpath(repo.wjoin(srclfile), True)
626 626 lfdirstate.remove(srclfile)
627 627 else:
628 628 util.copyfile(repo.wjoin(srclfile),
629 629 repo.wjoin(destlfile))
630 630
631 631 lfdirstate.add(destlfile)
632 632 lfdirstate.write()
633 633 except util.Abort, e:
634 634 if str(e) != _('no files to copy'):
635 635 raise e
636 636 else:
637 637 nolfiles = True
638 638 finally:
639 639 restorematchfn()
640 640 wlock.release()
641 641
642 642 if nolfiles and nonormalfiles:
643 643 raise util.Abort(_('no files to copy'))
644 644
645 645 return result
646 646
647 647 # When the user calls revert, we have to be careful to not revert any
648 648 # changes to other largefiles accidentally. This means we have to keep
649 649 # track of the largefiles that are being reverted so we only pull down
650 650 # the necessary largefiles.
651 651 #
652 652 # Standins are only updated (to match the hash of largefiles) before
653 653 # commits. Update the standins then run the original revert, changing
654 654 # the matcher to hit standins instead of largefiles. Based on the
655 655 # resulting standins update the largefiles.
656 656 def overriderevert(orig, ui, repo, *pats, **opts):
657 657 # Because we put the standins in a bad state (by updating them)
658 658 # and then return them to a correct state we need to lock to
659 659 # prevent others from changing them in their incorrect state.
660 660 wlock = repo.wlock()
661 661 try:
662 662 lfdirstate = lfutil.openlfdirstate(ui, repo)
663 663 s = lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
664 664 lfdirstate.write()
665 665 for lfile in s.modified:
666 666 lfutil.updatestandin(repo, lfutil.standin(lfile))
667 667 for lfile in s.deleted:
668 668 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
669 669 os.unlink(repo.wjoin(lfutil.standin(lfile)))
670 670
671 671 oldstandins = lfutil.getstandinsstate(repo)
672 672
673 673 def overridematch(ctx, pats=[], opts={}, globbed=False,
674 674 default='relpath'):
675 675 match = oldmatch(ctx, pats, opts, globbed, default)
676 676 m = copy.copy(match)
677 677 def tostandin(f):
678 678 if lfutil.standin(f) in ctx:
679 679 return lfutil.standin(f)
680 680 elif lfutil.standin(f) in repo[None]:
681 681 return None
682 682 return f
683 683 m._files = [tostandin(f) for f in m._files]
684 684 m._files = [f for f in m._files if f is not None]
685 685 m._fmap = set(m._files)
686 686 m._always = False
687 687 origmatchfn = m.matchfn
688 688 def matchfn(f):
689 689 if lfutil.isstandin(f):
690 690 return (origmatchfn(lfutil.splitstandin(f)) and
691 691 (f in repo[None] or f in ctx))
692 692 return origmatchfn(f)
693 693 m.matchfn = matchfn
694 694 return m
695 695 oldmatch = installmatchfn(overridematch)
696 696 try:
697 697 orig(ui, repo, *pats, **opts)
698 698 finally:
699 699 restorematchfn()
700 700
701 701 newstandins = lfutil.getstandinsstate(repo)
702 702 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
703 703 # lfdirstate should be 'normallookup'-ed for updated files,
704 704 # because reverting doesn't touch dirstate for 'normal' files
705 705 # when target revision is explicitly specified: in such case,
706 706 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
707 707 # of target (standin) file.
708 708 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
709 709 normallookup=True)
710 710
711 711 finally:
712 712 wlock.release()
713 713
714 714 # When we rebase a repository with remotely changed largefiles, we need to
715 715 # take some extra care so that the largefiles are correctly updated in the
716 716 # working copy
717 717 def overridepull(orig, ui, repo, source=None, **opts):
718 718 revsprepull = len(repo)
719 719 if not source:
720 720 source = 'default'
721 721 repo.lfpullsource = source
722 722 if opts.get('rebase', False):
723 723 repo._isrebasing = True
724 724 try:
725 725 if opts.get('update'):
726 726 del opts['update']
727 727 ui.debug('--update and --rebase are not compatible, ignoring '
728 728 'the update flag\n')
729 729 del opts['rebase']
730 730 origpostincoming = commands.postincoming
731 731 def _dummy(*args, **kwargs):
732 732 pass
733 733 commands.postincoming = _dummy
734 734 try:
735 735 result = commands.pull(ui, repo, source, **opts)
736 736 finally:
737 737 commands.postincoming = origpostincoming
738 738 revspostpull = len(repo)
739 739 if revspostpull > revsprepull:
740 740 result = result or rebase.rebase(ui, repo)
741 741 finally:
742 742 repo._isrebasing = False
743 743 else:
744 744 result = orig(ui, repo, source, **opts)
745 745 revspostpull = len(repo)
746 746 lfrevs = opts.get('lfrev', [])
747 747 if opts.get('all_largefiles'):
748 748 lfrevs.append('pulled()')
749 749 if lfrevs and revspostpull > revsprepull:
750 750 numcached = 0
751 751 repo.firstpulled = revsprepull # for pulled() revset expression
752 752 try:
753 753 for rev in scmutil.revrange(repo, lfrevs):
754 754 ui.note(_('pulling largefiles for revision %s\n') % rev)
755 755 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
756 756 numcached += len(cached)
757 757 finally:
758 758 del repo.firstpulled
759 759 ui.status(_("%d largefiles cached\n") % numcached)
760 760 return result
761 761
762 762 def pulledrevsetsymbol(repo, subset, x):
763 763 """``pulled()``
764 764 Changesets that just has been pulled.
765 765
766 766 Only available with largefiles from pull --lfrev expressions.
767 767
768 768 .. container:: verbose
769 769
770 770 Some examples:
771 771
772 772 - pull largefiles for all new changesets::
773 773
774 774 hg pull -lfrev "pulled()"
775 775
776 776 - pull largefiles for all new branch heads::
777 777
778 778 hg pull -lfrev "head(pulled()) and not closed()"
779 779
780 780 """
781 781
782 782 try:
783 783 firstpulled = repo.firstpulled
784 784 except AttributeError:
785 785 raise util.Abort(_("pulled() only available in --lfrev"))
786 786 return revset.baseset([r for r in subset if r >= firstpulled])
787 787
788 788 def overrideclone(orig, ui, source, dest=None, **opts):
789 789 d = dest
790 790 if d is None:
791 791 d = hg.defaultdest(source)
792 792 if opts.get('all_largefiles') and not hg.islocal(d):
793 793 raise util.Abort(_(
794 794 '--all-largefiles is incompatible with non-local destination %s') %
795 795 d)
796 796
797 797 return orig(ui, source, dest, **opts)
798 798
799 799 def hgclone(orig, ui, opts, *args, **kwargs):
800 800 result = orig(ui, opts, *args, **kwargs)
801 801
802 802 if result is not None:
803 803 sourcerepo, destrepo = result
804 804 repo = destrepo.local()
805 805
806 806 # Caching is implicitly limited to 'rev' option, since the dest repo was
807 807 # truncated at that point. The user may expect a download count with
808 808 # this option, so attempt whether or not this is a largefile repo.
809 809 if opts.get('all_largefiles'):
810 810 success, missing = lfcommands.downloadlfiles(ui, repo, None)
811 811
812 812 if missing != 0:
813 813 return None
814 814
815 815 return result
816 816
817 817 def overriderebase(orig, ui, repo, **opts):
818 818 repo._isrebasing = True
819 819 try:
820 820 return orig(ui, repo, **opts)
821 821 finally:
822 822 repo._isrebasing = False
823 823
824 824 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
825 825 prefix=None, mtime=None, subrepos=None):
826 826 # No need to lock because we are only reading history and
827 827 # largefile caches, neither of which are modified.
828 828 lfcommands.cachelfiles(repo.ui, repo, node)
829 829
830 830 if kind not in archival.archivers:
831 831 raise util.Abort(_("unknown archive type '%s'") % kind)
832 832
833 833 ctx = repo[node]
834 834
835 835 if kind == 'files':
836 836 if prefix:
837 837 raise util.Abort(
838 838 _('cannot give prefix when archiving to files'))
839 839 else:
840 840 prefix = archival.tidyprefix(dest, kind, prefix)
841 841
842 842 def write(name, mode, islink, getdata):
843 843 if matchfn and not matchfn(name):
844 844 return
845 845 data = getdata()
846 846 if decode:
847 847 data = repo.wwritedata(name, data)
848 848 archiver.addfile(prefix + name, mode, islink, data)
849 849
850 850 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
851 851
852 852 if repo.ui.configbool("ui", "archivemeta", True):
853 853 def metadata():
854 854 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
855 855 hex(repo.changelog.node(0)), hex(node), ctx.branch())
856 856
857 857 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
858 858 if repo.tagtype(t) == 'global')
859 859 if not tags:
860 860 repo.ui.pushbuffer()
861 861 opts = {'template': '{latesttag}\n{latesttagdistance}',
862 862 'style': '', 'patch': None, 'git': None}
863 863 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
864 864 ltags, dist = repo.ui.popbuffer().split('\n')
865 865 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
866 866 tags += 'latesttagdistance: %s\n' % dist
867 867
868 868 return base + tags
869 869
870 870 write('.hg_archival.txt', 0644, False, metadata)
871 871
872 872 for f in ctx:
873 873 ff = ctx.flags(f)
874 874 getdata = ctx[f].data
875 875 if lfutil.isstandin(f):
876 876 path = lfutil.findfile(repo, getdata().strip())
877 877 if path is None:
878 878 raise util.Abort(
879 879 _('largefile %s not found in repo store or system cache')
880 880 % lfutil.splitstandin(f))
881 881 f = lfutil.splitstandin(f)
882 882
883 883 def getdatafn():
884 884 fd = None
885 885 try:
886 886 fd = open(path, 'rb')
887 887 return fd.read()
888 888 finally:
889 889 if fd:
890 890 fd.close()
891 891
892 892 getdata = getdatafn
893 893 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
894 894
895 895 if subrepos:
896 896 for subpath in sorted(ctx.substate):
897 897 sub = ctx.sub(subpath)
898 898 submatch = match_.narrowmatcher(subpath, matchfn)
899 899 sub.archive(repo.ui, archiver, prefix, submatch)
900 900
901 901 archiver.done()
902 902
903 903 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
904 904 repo._get(repo._state + ('hg',))
905 905 rev = repo._state[1]
906 906 ctx = repo._repo[rev]
907 907
908 908 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
909 909
910 910 def write(name, mode, islink, getdata):
911 911 # At this point, the standin has been replaced with the largefile name,
912 912 # so the normal matcher works here without the lfutil variants.
913 913 if match and not match(f):
914 914 return
915 915 data = getdata()
916 916
917 917 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
918 918
919 919 for f in ctx:
920 920 ff = ctx.flags(f)
921 921 getdata = ctx[f].data
922 922 if lfutil.isstandin(f):
923 923 path = lfutil.findfile(repo._repo, getdata().strip())
924 924 if path is None:
925 925 raise util.Abort(
926 926 _('largefile %s not found in repo store or system cache')
927 927 % lfutil.splitstandin(f))
928 928 f = lfutil.splitstandin(f)
929 929
930 930 def getdatafn():
931 931 fd = None
932 932 try:
933 933 fd = open(os.path.join(prefix, path), 'rb')
934 934 return fd.read()
935 935 finally:
936 936 if fd:
937 937 fd.close()
938 938
939 939 getdata = getdatafn
940 940
941 941 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
942 942
943 943 for subpath in sorted(ctx.substate):
944 944 sub = ctx.sub(subpath)
945 945 submatch = match_.narrowmatcher(subpath, match)
946 946 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
947 947 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 and backout.
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 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
962 962 def overridefetch(orig, ui, repo, *pats, **opts):
963 963 repo.lfstatus = True
964 964 s = repo.status()
965 965 repo.lfstatus = False
966 966 if s.modified or s.added or s.removed or s.deleted:
967 967 raise util.Abort(_('uncommitted changes'))
968 968 return orig(ui, repo, *pats, **opts)
969 969
970 970 def overrideforget(orig, ui, repo, *pats, **opts):
971 971 installnormalfilesmatchfn(repo[None].manifest())
972 972 result = orig(ui, repo, *pats, **opts)
973 973 restorematchfn()
974 974 m = scmutil.match(repo[None], pats, opts)
975 975
976 976 try:
977 977 repo.lfstatus = True
978 978 s = repo.status(match=m, clean=True)
979 979 finally:
980 980 repo.lfstatus = False
981 981 forget = sorted(s.modified + s.added + s.deleted + s.clean)
982 982 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
983 983
984 984 for f in forget:
985 985 if lfutil.standin(f) not in repo.dirstate and not \
986 986 os.path.isdir(m.rel(lfutil.standin(f))):
987 987 ui.warn(_('not removing %s: file is already untracked\n')
988 988 % m.rel(f))
989 989 result = 1
990 990
991 991 for f in forget:
992 992 if ui.verbose or not m.exact(f):
993 993 ui.status(_('removing %s\n') % m.rel(f))
994 994
995 995 # Need to lock because standin files are deleted then removed from the
996 996 # repository and we could race in-between.
997 997 wlock = repo.wlock()
998 998 try:
999 999 lfdirstate = lfutil.openlfdirstate(ui, repo)
1000 1000 for f in forget:
1001 1001 if lfdirstate[f] == 'a':
1002 1002 lfdirstate.drop(f)
1003 1003 else:
1004 1004 lfdirstate.remove(f)
1005 1005 lfdirstate.write()
1006 1006 standins = [lfutil.standin(f) for f in forget]
1007 1007 for f in standins:
1008 1008 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1009 1009 repo[None].forget(standins)
1010 1010 finally:
1011 1011 wlock.release()
1012 1012
1013 1013 return result
1014 1014
1015 1015 def _getoutgoings(repo, other, missing, addfunc):
1016 1016 """get pairs of filename and largefile hash in outgoing revisions
1017 1017 in 'missing'.
1018 1018
1019 1019 largefiles already existing on 'other' repository are ignored.
1020 1020
1021 1021 'addfunc' is invoked with each unique pairs of filename and
1022 1022 largefile hash value.
1023 1023 """
1024 1024 knowns = set()
1025 1025 lfhashes = set()
1026 1026 def dedup(fn, lfhash):
1027 1027 k = (fn, lfhash)
1028 1028 if k not in knowns:
1029 1029 knowns.add(k)
1030 1030 lfhashes.add(lfhash)
1031 1031 lfutil.getlfilestoupload(repo, missing, dedup)
1032 1032 if lfhashes:
1033 1033 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1034 1034 for fn, lfhash in knowns:
1035 1035 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1036 1036 addfunc(fn, lfhash)
1037 1037
1038 1038 def outgoinghook(ui, repo, other, opts, missing):
1039 1039 if opts.pop('large', None):
1040 1040 lfhashes = set()
1041 1041 if ui.debugflag:
1042 1042 toupload = {}
1043 1043 def addfunc(fn, lfhash):
1044 1044 if fn not in toupload:
1045 1045 toupload[fn] = []
1046 1046 toupload[fn].append(lfhash)
1047 1047 lfhashes.add(lfhash)
1048 1048 def showhashes(fn):
1049 1049 for lfhash in sorted(toupload[fn]):
1050 1050 ui.debug(' %s\n' % (lfhash))
1051 1051 else:
1052 1052 toupload = set()
1053 1053 def addfunc(fn, lfhash):
1054 1054 toupload.add(fn)
1055 1055 lfhashes.add(lfhash)
1056 1056 def showhashes(fn):
1057 1057 pass
1058 1058 _getoutgoings(repo, other, missing, addfunc)
1059 1059
1060 1060 if not toupload:
1061 1061 ui.status(_('largefiles: no files to upload\n'))
1062 1062 else:
1063 1063 ui.status(_('largefiles to upload (%d entities):\n')
1064 1064 % (len(lfhashes)))
1065 1065 for file in sorted(toupload):
1066 1066 ui.status(lfutil.splitstandin(file) + '\n')
1067 1067 showhashes(file)
1068 1068 ui.status('\n')
1069 1069
1070 1070 def summaryremotehook(ui, repo, opts, changes):
1071 1071 largeopt = opts.get('large', False)
1072 1072 if changes is None:
1073 1073 if largeopt:
1074 1074 return (False, True) # only outgoing check is needed
1075 1075 else:
1076 1076 return (False, False)
1077 1077 elif largeopt:
1078 1078 url, branch, peer, outgoing = changes[1]
1079 1079 if peer is None:
1080 1080 # i18n: column positioning for "hg summary"
1081 1081 ui.status(_('largefiles: (no remote repo)\n'))
1082 1082 return
1083 1083
1084 1084 toupload = set()
1085 1085 lfhashes = set()
1086 1086 def addfunc(fn, lfhash):
1087 1087 toupload.add(fn)
1088 1088 lfhashes.add(lfhash)
1089 1089 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1090 1090
1091 1091 if not toupload:
1092 1092 # i18n: column positioning for "hg summary"
1093 1093 ui.status(_('largefiles: (no files to upload)\n'))
1094 1094 else:
1095 1095 # i18n: column positioning for "hg summary"
1096 1096 ui.status(_('largefiles: %d entities for %d files to upload\n')
1097 1097 % (len(lfhashes), len(toupload)))
1098 1098
1099 1099 def overridesummary(orig, ui, repo, *pats, **opts):
1100 1100 try:
1101 1101 repo.lfstatus = True
1102 1102 orig(ui, repo, *pats, **opts)
1103 1103 finally:
1104 1104 repo.lfstatus = False
1105 1105
1106 1106 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1107 1107 similarity=None):
1108 1108 if not lfutil.islfilesrepo(repo):
1109 1109 return orig(repo, pats, opts, dry_run, similarity)
1110 1110 # Get the list of missing largefiles so we can remove them
1111 1111 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1112 1112 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1113 1113 False, False, False)
1114 1114
1115 1115 # Call into the normal remove code, but the removing of the standin, we want
1116 1116 # to have handled by original addremove. Monkey patching here makes sure
1117 1117 # we don't remove the standin in the largefiles code, preventing a very
1118 1118 # confused state later.
1119 1119 if s.deleted:
1120 1120 m = [repo.wjoin(f) for f in s.deleted]
1121 repo._isaddremove = True
1122 removelargefiles(repo.ui, repo, *m, **opts)
1123 repo._isaddremove = False
1121 removelargefiles(repo.ui, repo, True, *m, **opts)
1124 1122 # Call into the normal add code, and any files that *should* be added as
1125 1123 # largefiles will be
1126 1124 addlargefiles(repo.ui, repo, *pats, **opts)
1127 1125 # Now that we've handled largefiles, hand off to the original addremove
1128 1126 # function to take care of the rest. Make sure it doesn't do anything with
1129 1127 # largefiles by installing a matcher that will ignore them.
1130 1128 installnormalfilesmatchfn(repo[None].manifest())
1131 1129 result = orig(repo, pats, opts, dry_run, similarity)
1132 1130 restorematchfn()
1133 1131 return result
1134 1132
1135 1133 # Calling purge with --all will cause the largefiles to be deleted.
1136 1134 # Override repo.status to prevent this from happening.
1137 1135 def overridepurge(orig, ui, repo, *dirs, **opts):
1138 1136 # XXX large file status is buggy when used on repo proxy.
1139 1137 # XXX this needs to be investigate.
1140 1138 repo = repo.unfiltered()
1141 1139 oldstatus = repo.status
1142 1140 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1143 1141 clean=False, unknown=False, listsubrepos=False):
1144 1142 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1145 1143 listsubrepos)
1146 1144 lfdirstate = lfutil.openlfdirstate(ui, repo)
1147 1145 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1148 1146 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1149 1147 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1150 1148 unknown, ignored, r.clean)
1151 1149 repo.status = overridestatus
1152 1150 orig(ui, repo, *dirs, **opts)
1153 1151 repo.status = oldstatus
1154 1152 def overriderollback(orig, ui, repo, **opts):
1155 1153 wlock = repo.wlock()
1156 1154 try:
1157 1155 before = repo.dirstate.parents()
1158 1156 orphans = set(f for f in repo.dirstate
1159 1157 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1160 1158 result = orig(ui, repo, **opts)
1161 1159 after = repo.dirstate.parents()
1162 1160 if before == after:
1163 1161 return result # no need to restore standins
1164 1162
1165 1163 pctx = repo['.']
1166 1164 for f in repo.dirstate:
1167 1165 if lfutil.isstandin(f):
1168 1166 orphans.discard(f)
1169 1167 if repo.dirstate[f] == 'r':
1170 1168 repo.wvfs.unlinkpath(f, ignoremissing=True)
1171 1169 elif f in pctx:
1172 1170 fctx = pctx[f]
1173 1171 repo.wwrite(f, fctx.data(), fctx.flags())
1174 1172 else:
1175 1173 # content of standin is not so important in 'a',
1176 1174 # 'm' or 'n' (coming from the 2nd parent) cases
1177 1175 lfutil.writestandin(repo, f, '', False)
1178 1176 for standin in orphans:
1179 1177 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1180 1178
1181 1179 lfdirstate = lfutil.openlfdirstate(ui, repo)
1182 1180 orphans = set(lfdirstate)
1183 1181 lfiles = lfutil.listlfiles(repo)
1184 1182 for file in lfiles:
1185 1183 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1186 1184 orphans.discard(file)
1187 1185 for lfile in orphans:
1188 1186 lfdirstate.drop(lfile)
1189 1187 lfdirstate.write()
1190 1188 finally:
1191 1189 wlock.release()
1192 1190 return result
1193 1191
1194 1192 def overridetransplant(orig, ui, repo, *revs, **opts):
1195 1193 try:
1196 1194 oldstandins = lfutil.getstandinsstate(repo)
1197 1195 repo._istransplanting = True
1198 1196 result = orig(ui, repo, *revs, **opts)
1199 1197 newstandins = lfutil.getstandinsstate(repo)
1200 1198 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1201 1199 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1202 1200 printmessage=True)
1203 1201 finally:
1204 1202 repo._istransplanting = False
1205 1203 return result
1206 1204
1207 1205 def overridecat(orig, ui, repo, file1, *pats, **opts):
1208 1206 ctx = scmutil.revsingle(repo, opts.get('rev'))
1209 1207 err = 1
1210 1208 notbad = set()
1211 1209 m = scmutil.match(ctx, (file1,) + pats, opts)
1212 1210 origmatchfn = m.matchfn
1213 1211 def lfmatchfn(f):
1214 1212 if origmatchfn(f):
1215 1213 return True
1216 1214 lf = lfutil.splitstandin(f)
1217 1215 if lf is None:
1218 1216 return False
1219 1217 notbad.add(lf)
1220 1218 return origmatchfn(lf)
1221 1219 m.matchfn = lfmatchfn
1222 1220 origbadfn = m.bad
1223 1221 def lfbadfn(f, msg):
1224 1222 if not f in notbad:
1225 1223 origbadfn(f, msg)
1226 1224 m.bad = lfbadfn
1227 1225 for f in ctx.walk(m):
1228 1226 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1229 1227 pathname=f)
1230 1228 lf = lfutil.splitstandin(f)
1231 1229 if lf is None or origmatchfn(f):
1232 1230 # duplicating unreachable code from commands.cat
1233 1231 data = ctx[f].data()
1234 1232 if opts.get('decode'):
1235 1233 data = repo.wwritedata(f, data)
1236 1234 fp.write(data)
1237 1235 else:
1238 1236 hash = lfutil.readstandin(repo, lf, ctx.rev())
1239 1237 if not lfutil.inusercache(repo.ui, hash):
1240 1238 store = basestore._openstore(repo)
1241 1239 success, missing = store.get([(lf, hash)])
1242 1240 if len(success) != 1:
1243 1241 raise util.Abort(
1244 1242 _('largefile %s is not in cache and could not be '
1245 1243 'downloaded') % lf)
1246 1244 path = lfutil.usercachepath(repo.ui, hash)
1247 1245 fpin = open(path, "rb")
1248 1246 for chunk in util.filechunkiter(fpin, 128 * 1024):
1249 1247 fp.write(chunk)
1250 1248 fpin.close()
1251 1249 fp.close()
1252 1250 err = 0
1253 1251 return err
1254 1252
1255 1253 def mercurialsinkbefore(orig, sink):
1256 1254 sink.repo._isconverting = True
1257 1255 orig(sink)
1258 1256
1259 1257 def mercurialsinkafter(orig, sink):
1260 1258 sink.repo._isconverting = False
1261 1259 orig(sink)
1262 1260
1263 1261 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1264 1262 *args, **kwargs):
1265 1263 wlock = repo.wlock()
1266 1264 try:
1267 1265 # branch | | |
1268 1266 # merge | force | partial | action
1269 1267 # -------+-------+---------+--------------
1270 1268 # x | x | x | linear-merge
1271 1269 # o | x | x | branch-merge
1272 1270 # x | o | x | overwrite (as clean update)
1273 1271 # o | o | x | force-branch-merge (*1)
1274 1272 # x | x | o | (*)
1275 1273 # o | x | o | (*)
1276 1274 # x | o | o | overwrite (as revert)
1277 1275 # o | o | o | (*)
1278 1276 #
1279 1277 # (*) don't care
1280 1278 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1281 1279
1282 1280 linearmerge = not branchmerge and not force and not partial
1283 1281
1284 1282 if linearmerge or (branchmerge and force and not partial):
1285 1283 # update standins for linear-merge or force-branch-merge,
1286 1284 # because largefiles in the working directory may be modified
1287 1285 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1288 1286 unsure, s = lfdirstate.status(match_.always(repo.root,
1289 1287 repo.getcwd()),
1290 1288 [], False, False, False)
1291 1289 for lfile in unsure + s.modified + s.added:
1292 1290 lfutil.updatestandin(repo, lfutil.standin(lfile))
1293 1291
1294 1292 if linearmerge:
1295 1293 # Only call updatelfiles on the standins that have changed
1296 1294 # to save time
1297 1295 oldstandins = lfutil.getstandinsstate(repo)
1298 1296
1299 1297 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1300 1298
1301 1299 filelist = None
1302 1300 if linearmerge:
1303 1301 newstandins = lfutil.getstandinsstate(repo)
1304 1302 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1305 1303
1306 1304 # suppress status message while automated committing
1307 1305 printmessage = not (getattr(repo, "_isrebasing", False) or
1308 1306 getattr(repo, "_istransplanting", False))
1309 1307 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1310 1308 printmessage=printmessage,
1311 1309 normallookup=partial)
1312 1310
1313 1311 return result
1314 1312 finally:
1315 1313 wlock.release()
1316 1314
1317 1315 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1318 1316 result = orig(repo, files, *args, **kwargs)
1319 1317
1320 1318 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1321 1319 if filelist:
1322 1320 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1323 1321 printmessage=False, normallookup=True)
1324 1322
1325 1323 return result
General Comments 0
You need to be logged in to leave comments. Login now