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