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