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