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