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