##// END OF EJS Templates
largefiles revert: update lfdirstate with result from first cleanliness check...
Mads Kiilerich -
r18140:e388273f stable
parent child Browse files
Show More
@@ -1,1126 +1,1127 b''
1 1 # Copyright 2009-2010 Gregory P. Ward
2 2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 3 # Copyright 2010-2011 Fog Creek Software
4 4 # Copyright 2010-2011 Unity Technologies
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 '''Overridden Mercurial commands and functions for the largefiles extension'''
10 10
11 11 import os
12 12 import copy
13 13
14 14 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
15 15 node, archival, error, merge
16 16 from mercurial.i18n import _
17 17 from mercurial.node import hex
18 18 from hgext import rebase
19 19
20 20 import lfutil
21 21 import lfcommands
22 22
23 23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
24 24
25 25 def installnormalfilesmatchfn(manifest):
26 26 '''overrides scmutil.match so that the matcher it returns will ignore all
27 27 largefiles'''
28 28 oldmatch = None # for the closure
29 29 def overridematch(ctx, pats=[], opts={}, globbed=False,
30 30 default='relpath'):
31 31 match = oldmatch(ctx, pats, opts, globbed, default)
32 32 m = copy.copy(match)
33 33 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
34 34 manifest)
35 35 m._files = filter(notlfile, m._files)
36 36 m._fmap = set(m._files)
37 37 origmatchfn = m.matchfn
38 38 m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None
39 39 return m
40 40 oldmatch = installmatchfn(overridematch)
41 41
42 42 def installmatchfn(f):
43 43 oldmatch = scmutil.match
44 44 setattr(f, 'oldmatch', oldmatch)
45 45 scmutil.match = f
46 46 return oldmatch
47 47
48 48 def restorematchfn():
49 49 '''restores scmutil.match to what it was before installnormalfilesmatchfn
50 50 was called. no-op if scmutil.match is its original function.
51 51
52 52 Note that n calls to installnormalfilesmatchfn will require n calls to
53 53 restore matchfn to reverse'''
54 54 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
55 55
56 56 def addlargefiles(ui, repo, *pats, **opts):
57 57 large = opts.pop('large', None)
58 58 lfsize = lfutil.getminsize(
59 59 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
60 60
61 61 lfmatcher = None
62 62 if lfutil.islfilesrepo(repo):
63 63 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
64 64 if lfpats:
65 65 lfmatcher = match_.match(repo.root, '', list(lfpats))
66 66
67 67 lfnames = []
68 68 m = scmutil.match(repo[None], pats, opts)
69 69 m.bad = lambda x, y: None
70 70 wctx = repo[None]
71 71 for f in repo.walk(m):
72 72 exact = m.exact(f)
73 73 lfile = lfutil.standin(f) in wctx
74 74 nfile = f in wctx
75 75 exists = lfile or nfile
76 76
77 77 # Don't warn the user when they attempt to add a normal tracked file.
78 78 # The normal add code will do that for us.
79 79 if exact and exists:
80 80 if lfile:
81 81 ui.warn(_('%s already a largefile\n') % f)
82 82 continue
83 83
84 84 if (exact or not exists) and not lfutil.isstandin(f):
85 85 wfile = repo.wjoin(f)
86 86
87 87 # In case the file was removed previously, but not committed
88 88 # (issue3507)
89 89 if not os.path.exists(wfile):
90 90 continue
91 91
92 92 abovemin = (lfsize and
93 93 os.lstat(wfile).st_size >= lfsize * 1024 * 1024)
94 94 if large or abovemin or (lfmatcher and lfmatcher(f)):
95 95 lfnames.append(f)
96 96 if ui.verbose or not exact:
97 97 ui.status(_('adding %s as a largefile\n') % m.rel(f))
98 98
99 99 bad = []
100 100 standins = []
101 101
102 102 # Need to lock, otherwise there could be a race condition between
103 103 # when standins are created and added to the repo.
104 104 wlock = repo.wlock()
105 105 try:
106 106 if not opts.get('dry_run'):
107 107 lfdirstate = lfutil.openlfdirstate(ui, repo)
108 108 for f in lfnames:
109 109 standinname = lfutil.standin(f)
110 110 lfutil.writestandin(repo, standinname, hash='',
111 111 executable=lfutil.getexecutable(repo.wjoin(f)))
112 112 standins.append(standinname)
113 113 if lfdirstate[f] == 'r':
114 114 lfdirstate.normallookup(f)
115 115 else:
116 116 lfdirstate.add(f)
117 117 lfdirstate.write()
118 118 bad += [lfutil.splitstandin(f)
119 119 for f in lfutil.repoadd(repo, standins)
120 120 if f in m.files()]
121 121 finally:
122 122 wlock.release()
123 123 return bad
124 124
125 125 def removelargefiles(ui, repo, *pats, **opts):
126 126 after = opts.get('after')
127 127 if not pats and not after:
128 128 raise util.Abort(_('no files specified'))
129 129 m = scmutil.match(repo[None], pats, opts)
130 130 try:
131 131 repo.lfstatus = True
132 132 s = repo.status(match=m, clean=True)
133 133 finally:
134 134 repo.lfstatus = False
135 135 manifest = repo[None].manifest()
136 136 modified, added, deleted, clean = [[f for f in list
137 137 if lfutil.standin(f) in manifest]
138 138 for list in [s[0], s[1], s[3], s[6]]]
139 139
140 140 def warn(files, reason):
141 141 for f in files:
142 142 ui.warn(_('not removing %s: %s (use forget to undo)\n')
143 143 % (m.rel(f), reason))
144 144 return int(len(files) > 0)
145 145
146 146 result = 0
147 147
148 148 if after:
149 149 remove, forget = deleted, []
150 150 result = warn(modified + added + clean, _('file still exists'))
151 151 else:
152 152 remove, forget = deleted + clean, []
153 153 result = warn(modified, _('file is modified'))
154 154 result = warn(added, _('file has been marked for add')) or result
155 155
156 156 for f in sorted(remove + forget):
157 157 if ui.verbose or not m.exact(f):
158 158 ui.status(_('removing %s\n') % m.rel(f))
159 159
160 160 # Need to lock because standin files are deleted then removed from the
161 161 # repository and we could race in-between.
162 162 wlock = repo.wlock()
163 163 try:
164 164 lfdirstate = lfutil.openlfdirstate(ui, repo)
165 165 for f in remove:
166 166 if not after:
167 167 # If this is being called by addremove, notify the user that we
168 168 # are removing the file.
169 169 if getattr(repo, "_isaddremove", False):
170 170 ui.status(_('removing %s\n') % f)
171 171 if os.path.exists(repo.wjoin(f)):
172 172 util.unlinkpath(repo.wjoin(f))
173 173 lfdirstate.remove(f)
174 174 lfdirstate.write()
175 175 forget = [lfutil.standin(f) for f in forget]
176 176 remove = [lfutil.standin(f) for f in remove]
177 177 lfutil.repoforget(repo, forget)
178 178 # If this is being called by addremove, let the original addremove
179 179 # function handle this.
180 180 if not getattr(repo, "_isaddremove", False):
181 181 lfutil.reporemove(repo, remove, unlink=True)
182 182 else:
183 183 lfutil.reporemove(repo, remove, unlink=False)
184 184 finally:
185 185 wlock.release()
186 186
187 187 return result
188 188
189 189 # For overriding mercurial.hgweb.webcommands so that largefiles will
190 190 # appear at their right place in the manifests.
191 191 def decodepath(orig, path):
192 192 return lfutil.splitstandin(path) or path
193 193
194 194 # -- Wrappers: modify existing commands --------------------------------
195 195
196 196 # Add works by going through the files that the user wanted to add and
197 197 # checking if they should be added as largefiles. Then it makes a new
198 198 # matcher which matches only the normal files and runs the original
199 199 # version of add.
200 200 def overrideadd(orig, ui, repo, *pats, **opts):
201 201 normal = opts.pop('normal')
202 202 if normal:
203 203 if opts.get('large'):
204 204 raise util.Abort(_('--normal cannot be used with --large'))
205 205 return orig(ui, repo, *pats, **opts)
206 206 bad = addlargefiles(ui, repo, *pats, **opts)
207 207 installnormalfilesmatchfn(repo[None].manifest())
208 208 result = orig(ui, repo, *pats, **opts)
209 209 restorematchfn()
210 210
211 211 return (result == 1 or bad) and 1 or 0
212 212
213 213 def overrideremove(orig, ui, repo, *pats, **opts):
214 214 installnormalfilesmatchfn(repo[None].manifest())
215 215 result = orig(ui, repo, *pats, **opts)
216 216 restorematchfn()
217 217 return removelargefiles(ui, repo, *pats, **opts) or result
218 218
219 219 def overridestatusfn(orig, repo, rev2, **opts):
220 220 try:
221 221 repo._repo.lfstatus = True
222 222 return orig(repo, rev2, **opts)
223 223 finally:
224 224 repo._repo.lfstatus = False
225 225
226 226 def overridestatus(orig, ui, repo, *pats, **opts):
227 227 try:
228 228 repo.lfstatus = True
229 229 return orig(ui, repo, *pats, **opts)
230 230 finally:
231 231 repo.lfstatus = False
232 232
233 233 def overridedirty(orig, repo, ignoreupdate=False):
234 234 try:
235 235 repo._repo.lfstatus = True
236 236 return orig(repo, ignoreupdate)
237 237 finally:
238 238 repo._repo.lfstatus = False
239 239
240 240 def overridelog(orig, ui, repo, *pats, **opts):
241 241 try:
242 242 repo.lfstatus = True
243 243 return orig(ui, repo, *pats, **opts)
244 244 finally:
245 245 repo.lfstatus = False
246 246
247 247 def overrideverify(orig, ui, repo, *pats, **opts):
248 248 large = opts.pop('large', False)
249 249 all = opts.pop('lfa', False)
250 250 contents = opts.pop('lfc', False)
251 251
252 252 result = orig(ui, repo, *pats, **opts)
253 253 if large:
254 254 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
255 255 return result
256 256
257 257 # Override needs to refresh standins so that update's normal merge
258 258 # will go through properly. Then the other update hook (overriding repo.update)
259 259 # will get the new files. Filemerge is also overridden so that the merge
260 260 # will merge standins correctly.
261 261 def overrideupdate(orig, ui, repo, *pats, **opts):
262 262 lfdirstate = lfutil.openlfdirstate(ui, repo)
263 263 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
264 264 False, False)
265 265 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
266 266
267 267 # Need to lock between the standins getting updated and their
268 268 # largefiles getting updated
269 269 wlock = repo.wlock()
270 270 try:
271 271 if opts['check']:
272 272 mod = len(modified) > 0
273 273 for lfile in unsure:
274 274 standin = lfutil.standin(lfile)
275 275 if repo['.'][standin].data().strip() != \
276 276 lfutil.hashfile(repo.wjoin(lfile)):
277 277 mod = True
278 278 else:
279 279 lfdirstate.normal(lfile)
280 280 lfdirstate.write()
281 281 if mod:
282 282 raise util.Abort(_('uncommitted local changes'))
283 283 # XXX handle removed differently
284 284 if not opts['clean']:
285 285 for lfile in unsure + modified + added:
286 286 lfutil.updatestandin(repo, lfutil.standin(lfile))
287 287 finally:
288 288 wlock.release()
289 289 return orig(ui, repo, *pats, **opts)
290 290
291 291 # Before starting the manifest merge, merge.updates will call
292 292 # _checkunknown to check if there are any files in the merged-in
293 293 # changeset that collide with unknown files in the working copy.
294 294 #
295 295 # The largefiles are seen as unknown, so this prevents us from merging
296 296 # in a file 'foo' if we already have a largefile with the same name.
297 297 #
298 298 # The overridden function filters the unknown files by removing any
299 299 # largefiles. This makes the merge proceed and we can then handle this
300 300 # case further in the overridden manifestmerge function below.
301 301 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
302 302 if lfutil.standin(f) in wctx:
303 303 return False
304 304 return origfn(repo, wctx, mctx, f)
305 305
306 306 # The manifest merge handles conflicts on the manifest level. We want
307 307 # to handle changes in largefile-ness of files at this level too.
308 308 #
309 309 # The strategy is to run the original manifestmerge and then process
310 310 # the action list it outputs. There are two cases we need to deal with:
311 311 #
312 312 # 1. Normal file in p1, largefile in p2. Here the largefile is
313 313 # detected via its standin file, which will enter the working copy
314 314 # with a "get" action. It is not "merge" since the standin is all
315 315 # Mercurial is concerned with at this level -- the link to the
316 316 # existing normal file is not relevant here.
317 317 #
318 318 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
319 319 # since the largefile will be present in the working copy and
320 320 # different from the normal file in p2. Mercurial therefore
321 321 # triggers a merge action.
322 322 #
323 323 # In both cases, we prompt the user and emit new actions to either
324 324 # remove the standin (if the normal file was kept) or to remove the
325 325 # normal file and get the standin (if the largefile was kept). The
326 326 # default prompt answer is to use the largefile version since it was
327 327 # presumably changed on purpose.
328 328 #
329 329 # Finally, the merge.applyupdates function will then take care of
330 330 # writing the files into the working copy and lfcommands.updatelfiles
331 331 # will update the largefiles.
332 332 def overridemanifestmerge(origfn, repo, p1, p2, pa, overwrite, partial):
333 333 actions = origfn(repo, p1, p2, pa, overwrite, partial)
334 334 processed = []
335 335
336 336 for action in actions:
337 337 if overwrite:
338 338 processed.append(action)
339 339 continue
340 340 f, m = action[:2]
341 341
342 342 choices = (_('&Largefile'), _('&Normal file'))
343 343 if m == "g" and lfutil.splitstandin(f) in p1 and f in p2:
344 344 # Case 1: normal file in the working copy, largefile in
345 345 # the second parent
346 346 lfile = lfutil.splitstandin(f)
347 347 standin = f
348 348 msg = _('%s has been turned into a largefile\n'
349 349 'use (l)argefile or keep as (n)ormal file?') % lfile
350 350 if repo.ui.promptchoice(msg, choices, 0) == 0:
351 351 processed.append((lfile, "r"))
352 352 processed.append((standin, "g", p2.flags(standin)))
353 353 else:
354 354 processed.append((standin, "r"))
355 355 elif m == "g" and lfutil.standin(f) in p1 and f in p2:
356 356 # Case 2: largefile in the working copy, normal file in
357 357 # the second parent
358 358 standin = lfutil.standin(f)
359 359 lfile = f
360 360 msg = _('%s has been turned into a normal file\n'
361 361 'keep as (l)argefile or use (n)ormal file?') % lfile
362 362 if repo.ui.promptchoice(msg, choices, 0) == 0:
363 363 processed.append((lfile, "r"))
364 364 else:
365 365 processed.append((standin, "r"))
366 366 processed.append((lfile, "g", p2.flags(lfile)))
367 367 else:
368 368 processed.append(action)
369 369
370 370 return processed
371 371
372 372 # Override filemerge to prompt the user about how they wish to merge
373 373 # largefiles. This will handle identical edits, and copy/rename +
374 374 # edit without prompting the user.
375 375 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca):
376 376 # Use better variable names here. Because this is a wrapper we cannot
377 377 # change the variable names in the function declaration.
378 378 fcdest, fcother, fcancestor = fcd, fco, fca
379 379 if not lfutil.isstandin(orig):
380 380 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
381 381 else:
382 382 if not fcother.cmp(fcdest): # files identical?
383 383 return None
384 384
385 385 # backwards, use working dir parent as ancestor
386 386 if fcancestor == fcother:
387 387 fcancestor = fcdest.parents()[0]
388 388
389 389 if orig != fcother.path():
390 390 repo.ui.status(_('merging %s and %s to %s\n')
391 391 % (lfutil.splitstandin(orig),
392 392 lfutil.splitstandin(fcother.path()),
393 393 lfutil.splitstandin(fcdest.path())))
394 394 else:
395 395 repo.ui.status(_('merging %s\n')
396 396 % lfutil.splitstandin(fcdest.path()))
397 397
398 398 if fcancestor.path() != fcother.path() and fcother.data() == \
399 399 fcancestor.data():
400 400 return 0
401 401 if fcancestor.path() != fcdest.path() and fcdest.data() == \
402 402 fcancestor.data():
403 403 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
404 404 return 0
405 405
406 406 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
407 407 'keep (l)ocal or take (o)ther?') %
408 408 lfutil.splitstandin(orig),
409 409 (_('&Local'), _('&Other')), 0) == 0:
410 410 return 0
411 411 else:
412 412 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
413 413 return 0
414 414
415 415 # Copy first changes the matchers to match standins instead of
416 416 # largefiles. Then it overrides util.copyfile in that function it
417 417 # checks if the destination largefile already exists. It also keeps a
418 418 # list of copied files so that the largefiles can be copied and the
419 419 # dirstate updated.
420 420 def overridecopy(orig, ui, repo, pats, opts, rename=False):
421 421 # doesn't remove largefile on rename
422 422 if len(pats) < 2:
423 423 # this isn't legal, let the original function deal with it
424 424 return orig(ui, repo, pats, opts, rename)
425 425
426 426 def makestandin(relpath):
427 427 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
428 428 return os.path.join(repo.wjoin(lfutil.standin(path)))
429 429
430 430 fullpats = scmutil.expandpats(pats)
431 431 dest = fullpats[-1]
432 432
433 433 if os.path.isdir(dest):
434 434 if not os.path.isdir(makestandin(dest)):
435 435 os.makedirs(makestandin(dest))
436 436 # This could copy both lfiles and normal files in one command,
437 437 # but we don't want to do that. First replace their matcher to
438 438 # only match normal files and run it, then replace it to just
439 439 # match largefiles and run it again.
440 440 nonormalfiles = False
441 441 nolfiles = False
442 442 try:
443 443 try:
444 444 installnormalfilesmatchfn(repo[None].manifest())
445 445 result = orig(ui, repo, pats, opts, rename)
446 446 except util.Abort, e:
447 447 if str(e) != _('no files to copy'):
448 448 raise e
449 449 else:
450 450 nonormalfiles = True
451 451 result = 0
452 452 finally:
453 453 restorematchfn()
454 454
455 455 # The first rename can cause our current working directory to be removed.
456 456 # In that case there is nothing left to copy/rename so just quit.
457 457 try:
458 458 repo.getcwd()
459 459 except OSError:
460 460 return result
461 461
462 462 try:
463 463 try:
464 464 # When we call orig below it creates the standins but we don't add
465 465 # them to the dir state until later so lock during that time.
466 466 wlock = repo.wlock()
467 467
468 468 manifest = repo[None].manifest()
469 469 oldmatch = None # for the closure
470 470 def overridematch(ctx, pats=[], opts={}, globbed=False,
471 471 default='relpath'):
472 472 newpats = []
473 473 # The patterns were previously mangled to add the standin
474 474 # directory; we need to remove that now
475 475 for pat in pats:
476 476 if match_.patkind(pat) is None and lfutil.shortname in pat:
477 477 newpats.append(pat.replace(lfutil.shortname, ''))
478 478 else:
479 479 newpats.append(pat)
480 480 match = oldmatch(ctx, newpats, opts, globbed, default)
481 481 m = copy.copy(match)
482 482 lfile = lambda f: lfutil.standin(f) in manifest
483 483 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
484 484 m._fmap = set(m._files)
485 485 origmatchfn = m.matchfn
486 486 m.matchfn = lambda f: (lfutil.isstandin(f) and
487 487 (f in manifest) and
488 488 origmatchfn(lfutil.splitstandin(f)) or
489 489 None)
490 490 return m
491 491 oldmatch = installmatchfn(overridematch)
492 492 listpats = []
493 493 for pat in pats:
494 494 if match_.patkind(pat) is not None:
495 495 listpats.append(pat)
496 496 else:
497 497 listpats.append(makestandin(pat))
498 498
499 499 try:
500 500 origcopyfile = util.copyfile
501 501 copiedfiles = []
502 502 def overridecopyfile(src, dest):
503 503 if (lfutil.shortname in src and
504 504 dest.startswith(repo.wjoin(lfutil.shortname))):
505 505 destlfile = dest.replace(lfutil.shortname, '')
506 506 if not opts['force'] and os.path.exists(destlfile):
507 507 raise IOError('',
508 508 _('destination largefile already exists'))
509 509 copiedfiles.append((src, dest))
510 510 origcopyfile(src, dest)
511 511
512 512 util.copyfile = overridecopyfile
513 513 result += orig(ui, repo, listpats, opts, rename)
514 514 finally:
515 515 util.copyfile = origcopyfile
516 516
517 517 lfdirstate = lfutil.openlfdirstate(ui, repo)
518 518 for (src, dest) in copiedfiles:
519 519 if (lfutil.shortname in src and
520 520 dest.startswith(repo.wjoin(lfutil.shortname))):
521 521 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
522 522 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
523 523 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
524 524 if not os.path.isdir(destlfiledir):
525 525 os.makedirs(destlfiledir)
526 526 if rename:
527 527 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
528 528 lfdirstate.remove(srclfile)
529 529 else:
530 530 util.copyfile(repo.wjoin(srclfile),
531 531 repo.wjoin(destlfile))
532 532
533 533 lfdirstate.add(destlfile)
534 534 lfdirstate.write()
535 535 except util.Abort, e:
536 536 if str(e) != _('no files to copy'):
537 537 raise e
538 538 else:
539 539 nolfiles = True
540 540 finally:
541 541 restorematchfn()
542 542 wlock.release()
543 543
544 544 if nolfiles and nonormalfiles:
545 545 raise util.Abort(_('no files to copy'))
546 546
547 547 return result
548 548
549 549 # When the user calls revert, we have to be careful to not revert any
550 550 # changes to other largefiles accidentally. This means we have to keep
551 551 # track of the largefiles that are being reverted so we only pull down
552 552 # the necessary largefiles.
553 553 #
554 554 # Standins are only updated (to match the hash of largefiles) before
555 555 # commits. Update the standins then run the original revert, changing
556 556 # the matcher to hit standins instead of largefiles. Based on the
557 557 # resulting standins update the largefiles. Then return the standins
558 558 # to their proper state
559 559 def overriderevert(orig, ui, repo, *pats, **opts):
560 560 # Because we put the standins in a bad state (by updating them)
561 561 # and then return them to a correct state we need to lock to
562 562 # prevent others from changing them in their incorrect state.
563 563 wlock = repo.wlock()
564 564 try:
565 565 lfdirstate = lfutil.openlfdirstate(ui, repo)
566 566 (modified, added, removed, missing, unknown, ignored, clean) = \
567 567 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
568 lfdirstate.write()
568 569 for lfile in modified:
569 570 lfutil.updatestandin(repo, lfutil.standin(lfile))
570 571 for lfile in missing:
571 572 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
572 573 os.unlink(repo.wjoin(lfutil.standin(lfile)))
573 574
574 575 try:
575 576 ctx = scmutil.revsingle(repo, opts.get('rev'))
576 577 oldmatch = None # for the closure
577 578 def overridematch(ctx, pats=[], opts={}, globbed=False,
578 579 default='relpath'):
579 580 match = oldmatch(ctx, pats, opts, globbed, default)
580 581 m = copy.copy(match)
581 582 def tostandin(f):
582 583 if lfutil.standin(f) in ctx:
583 584 return lfutil.standin(f)
584 585 elif lfutil.standin(f) in repo[None]:
585 586 return None
586 587 return f
587 588 m._files = [tostandin(f) for f in m._files]
588 589 m._files = [f for f in m._files if f is not None]
589 590 m._fmap = set(m._files)
590 591 origmatchfn = m.matchfn
591 592 def matchfn(f):
592 593 if lfutil.isstandin(f):
593 594 # We need to keep track of what largefiles are being
594 595 # matched so we know which ones to update later --
595 596 # otherwise we accidentally revert changes to other
596 597 # largefiles. This is repo-specific, so duckpunch the
597 598 # repo object to keep the list of largefiles for us
598 599 # later.
599 600 if origmatchfn(lfutil.splitstandin(f)) and \
600 601 (f in repo[None] or f in ctx):
601 602 lfileslist = getattr(repo, '_lfilestoupdate', [])
602 603 lfileslist.append(lfutil.splitstandin(f))
603 604 repo._lfilestoupdate = lfileslist
604 605 return True
605 606 else:
606 607 return False
607 608 return origmatchfn(f)
608 609 m.matchfn = matchfn
609 610 return m
610 611 oldmatch = installmatchfn(overridematch)
611 612 scmutil.match
612 613 matches = overridematch(repo[None], pats, opts)
613 614 orig(ui, repo, *pats, **opts)
614 615 finally:
615 616 restorematchfn()
616 617 lfileslist = getattr(repo, '_lfilestoupdate', [])
617 618 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
618 619 printmessage=False)
619 620
620 621 # empty out the largefiles list so we start fresh next time
621 622 repo._lfilestoupdate = []
622 623 for lfile in modified:
623 624 if lfile in lfileslist:
624 625 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
625 626 in repo['.']:
626 627 lfutil.writestandin(repo, lfutil.standin(lfile),
627 628 repo['.'][lfile].data().strip(),
628 629 'x' in repo['.'][lfile].flags())
629 630 lfdirstate = lfutil.openlfdirstate(ui, repo)
630 631 for lfile in added:
631 632 standin = lfutil.standin(lfile)
632 633 if standin not in ctx and (standin in matches or opts.get('all')):
633 634 if lfile in lfdirstate:
634 635 lfdirstate.drop(lfile)
635 636 util.unlinkpath(repo.wjoin(standin))
636 637 lfdirstate.write()
637 638 finally:
638 639 wlock.release()
639 640
640 641 def hgupdate(orig, repo, node):
641 642 # Only call updatelfiles the standins that have changed to save time
642 643 oldstandins = lfutil.getstandinsstate(repo)
643 644 result = orig(repo, node)
644 645 newstandins = lfutil.getstandinsstate(repo)
645 646 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
646 647 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist, printmessage=True)
647 648 return result
648 649
649 650 def hgclean(orig, repo, node, show_stats=True):
650 651 result = orig(repo, node, show_stats)
651 652 lfcommands.updatelfiles(repo.ui, repo)
652 653 return result
653 654
654 655 def hgmerge(orig, repo, node, force=None, remind=True):
655 656 # Mark the repo as being in the middle of a merge, so that
656 657 # updatelfiles() will know that it needs to trust the standins in
657 658 # the working copy, not in the standins in the current node
658 659 repo._ismerging = True
659 660 try:
660 661 result = orig(repo, node, force, remind)
661 662 lfcommands.updatelfiles(repo.ui, repo)
662 663 finally:
663 664 repo._ismerging = False
664 665 return result
665 666
666 667 # When we rebase a repository with remotely changed largefiles, we need to
667 668 # take some extra care so that the largefiles are correctly updated in the
668 669 # working copy
669 670 def overridepull(orig, ui, repo, source=None, **opts):
670 671 revsprepull = len(repo)
671 672 if opts.get('rebase', False):
672 673 repo._isrebasing = True
673 674 try:
674 675 if opts.get('update'):
675 676 del opts['update']
676 677 ui.debug('--update and --rebase are not compatible, ignoring '
677 678 'the update flag\n')
678 679 del opts['rebase']
679 680 cmdutil.bailifchanged(repo)
680 681 origpostincoming = commands.postincoming
681 682 def _dummy(*args, **kwargs):
682 683 pass
683 684 commands.postincoming = _dummy
684 685 if not source:
685 686 source = 'default'
686 687 repo.lfpullsource = source
687 688 try:
688 689 result = commands.pull(ui, repo, source, **opts)
689 690 finally:
690 691 commands.postincoming = origpostincoming
691 692 revspostpull = len(repo)
692 693 if revspostpull > revsprepull:
693 694 result = result or rebase.rebase(ui, repo)
694 695 finally:
695 696 repo._isrebasing = False
696 697 else:
697 698 if not source:
698 699 source = 'default'
699 700 repo.lfpullsource = source
700 701 oldheads = lfutil.getcurrentheads(repo)
701 702 result = orig(ui, repo, source, **opts)
702 703 # If we do not have the new largefiles for any new heads we pulled, we
703 704 # will run into a problem later if we try to merge or rebase with one of
704 705 # these heads, so cache the largefiles now directly into the system
705 706 # cache.
706 707 ui.status(_("caching new largefiles\n"))
707 708 numcached = 0
708 709 heads = lfutil.getcurrentheads(repo)
709 710 newheads = set(heads).difference(set(oldheads))
710 711 for head in newheads:
711 712 (cached, missing) = lfcommands.cachelfiles(ui, repo, head)
712 713 numcached += len(cached)
713 714 ui.status(_("%d largefiles cached\n") % numcached)
714 715 if opts.get('all_largefiles'):
715 716 revspostpull = len(repo)
716 717 revs = []
717 718 for rev in xrange(revsprepull + 1, revspostpull):
718 719 revs.append(repo[rev].rev())
719 720 lfcommands.downloadlfiles(ui, repo, revs)
720 721 return result
721 722
722 723 def overrideclone(orig, ui, source, dest=None, **opts):
723 724 d = dest
724 725 if d is None:
725 726 d = hg.defaultdest(source)
726 727 if opts.get('all_largefiles') and not hg.islocal(d):
727 728 raise util.Abort(_(
728 729 '--all-largefiles is incompatible with non-local destination %s' %
729 730 d))
730 731
731 732 return orig(ui, source, dest, **opts)
732 733
733 734 def hgclone(orig, ui, opts, *args, **kwargs):
734 735 result = orig(ui, opts, *args, **kwargs)
735 736
736 737 if result is not None:
737 738 sourcerepo, destrepo = result
738 739 repo = destrepo.local()
739 740
740 741 # The .hglf directory must exist for the standin matcher to match
741 742 # anything (which listlfiles uses for each rev), and .hg/largefiles is
742 743 # assumed to exist by the code that caches the downloaded file. These
743 744 # directories exist if clone updated to any rev. (If the repo does not
744 745 # have largefiles, download never gets to the point of needing
745 746 # .hg/largefiles, and the standin matcher won't match anything anyway.)
746 747 if 'largefiles' in repo.requirements:
747 748 if opts.get('noupdate'):
748 749 util.makedirs(repo.pathto(lfutil.shortname))
749 750 util.makedirs(repo.join(lfutil.longname))
750 751
751 752 # Caching is implicitly limited to 'rev' option, since the dest repo was
752 753 # truncated at that point. The user may expect a download count with
753 754 # this option, so attempt whether or not this is a largefile repo.
754 755 if opts.get('all_largefiles'):
755 756 success, missing = lfcommands.downloadlfiles(ui, repo, None)
756 757
757 758 if missing != 0:
758 759 return None
759 760
760 761 return result
761 762
762 763 def overriderebase(orig, ui, repo, **opts):
763 764 repo._isrebasing = True
764 765 try:
765 766 return orig(ui, repo, **opts)
766 767 finally:
767 768 repo._isrebasing = False
768 769
769 770 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
770 771 prefix=None, mtime=None, subrepos=None):
771 772 # No need to lock because we are only reading history and
772 773 # largefile caches, neither of which are modified.
773 774 lfcommands.cachelfiles(repo.ui, repo, node)
774 775
775 776 if kind not in archival.archivers:
776 777 raise util.Abort(_("unknown archive type '%s'") % kind)
777 778
778 779 ctx = repo[node]
779 780
780 781 if kind == 'files':
781 782 if prefix:
782 783 raise util.Abort(
783 784 _('cannot give prefix when archiving to files'))
784 785 else:
785 786 prefix = archival.tidyprefix(dest, kind, prefix)
786 787
787 788 def write(name, mode, islink, getdata):
788 789 if matchfn and not matchfn(name):
789 790 return
790 791 data = getdata()
791 792 if decode:
792 793 data = repo.wwritedata(name, data)
793 794 archiver.addfile(prefix + name, mode, islink, data)
794 795
795 796 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
796 797
797 798 if repo.ui.configbool("ui", "archivemeta", True):
798 799 def metadata():
799 800 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
800 801 hex(repo.changelog.node(0)), hex(node), ctx.branch())
801 802
802 803 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
803 804 if repo.tagtype(t) == 'global')
804 805 if not tags:
805 806 repo.ui.pushbuffer()
806 807 opts = {'template': '{latesttag}\n{latesttagdistance}',
807 808 'style': '', 'patch': None, 'git': None}
808 809 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
809 810 ltags, dist = repo.ui.popbuffer().split('\n')
810 811 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
811 812 tags += 'latesttagdistance: %s\n' % dist
812 813
813 814 return base + tags
814 815
815 816 write('.hg_archival.txt', 0644, False, metadata)
816 817
817 818 for f in ctx:
818 819 ff = ctx.flags(f)
819 820 getdata = ctx[f].data
820 821 if lfutil.isstandin(f):
821 822 path = lfutil.findfile(repo, getdata().strip())
822 823 if path is None:
823 824 raise util.Abort(
824 825 _('largefile %s not found in repo store or system cache')
825 826 % lfutil.splitstandin(f))
826 827 f = lfutil.splitstandin(f)
827 828
828 829 def getdatafn():
829 830 fd = None
830 831 try:
831 832 fd = open(path, 'rb')
832 833 return fd.read()
833 834 finally:
834 835 if fd:
835 836 fd.close()
836 837
837 838 getdata = getdatafn
838 839 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
839 840
840 841 if subrepos:
841 842 for subpath in ctx.substate:
842 843 sub = ctx.sub(subpath)
843 844 submatch = match_.narrowmatcher(subpath, matchfn)
844 845 sub.archive(repo.ui, archiver, prefix, submatch)
845 846
846 847 archiver.done()
847 848
848 849 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
849 850 repo._get(repo._state + ('hg',))
850 851 rev = repo._state[1]
851 852 ctx = repo._repo[rev]
852 853
853 854 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
854 855
855 856 def write(name, mode, islink, getdata):
856 857 # At this point, the standin has been replaced with the largefile name,
857 858 # so the normal matcher works here without the lfutil variants.
858 859 if match and not match(f):
859 860 return
860 861 data = getdata()
861 862
862 863 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
863 864
864 865 for f in ctx:
865 866 ff = ctx.flags(f)
866 867 getdata = ctx[f].data
867 868 if lfutil.isstandin(f):
868 869 path = lfutil.findfile(repo._repo, getdata().strip())
869 870 if path is None:
870 871 raise util.Abort(
871 872 _('largefile %s not found in repo store or system cache')
872 873 % lfutil.splitstandin(f))
873 874 f = lfutil.splitstandin(f)
874 875
875 876 def getdatafn():
876 877 fd = None
877 878 try:
878 879 fd = open(os.path.join(prefix, path), 'rb')
879 880 return fd.read()
880 881 finally:
881 882 if fd:
882 883 fd.close()
883 884
884 885 getdata = getdatafn
885 886
886 887 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
887 888
888 889 for subpath in ctx.substate:
889 890 sub = ctx.sub(subpath)
890 891 submatch = match_.narrowmatcher(subpath, match)
891 892 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
892 893 submatch)
893 894
894 895 # If a largefile is modified, the change is not reflected in its
895 896 # standin until a commit. cmdutil.bailifchanged() raises an exception
896 897 # if the repo has uncommitted changes. Wrap it to also check if
897 898 # largefiles were changed. This is used by bisect and backout.
898 899 def overridebailifchanged(orig, repo):
899 900 orig(repo)
900 901 repo.lfstatus = True
901 902 modified, added, removed, deleted = repo.status()[:4]
902 903 repo.lfstatus = False
903 904 if modified or added or removed or deleted:
904 905 raise util.Abort(_('outstanding uncommitted changes'))
905 906
906 907 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
907 908 def overridefetch(orig, ui, repo, *pats, **opts):
908 909 repo.lfstatus = True
909 910 modified, added, removed, deleted = repo.status()[:4]
910 911 repo.lfstatus = False
911 912 if modified or added or removed or deleted:
912 913 raise util.Abort(_('outstanding uncommitted changes'))
913 914 return orig(ui, repo, *pats, **opts)
914 915
915 916 def overrideforget(orig, ui, repo, *pats, **opts):
916 917 installnormalfilesmatchfn(repo[None].manifest())
917 918 result = orig(ui, repo, *pats, **opts)
918 919 restorematchfn()
919 920 m = scmutil.match(repo[None], pats, opts)
920 921
921 922 try:
922 923 repo.lfstatus = True
923 924 s = repo.status(match=m, clean=True)
924 925 finally:
925 926 repo.lfstatus = False
926 927 forget = sorted(s[0] + s[1] + s[3] + s[6])
927 928 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
928 929
929 930 for f in forget:
930 931 if lfutil.standin(f) not in repo.dirstate and not \
931 932 os.path.isdir(m.rel(lfutil.standin(f))):
932 933 ui.warn(_('not removing %s: file is already untracked\n')
933 934 % m.rel(f))
934 935 result = 1
935 936
936 937 for f in forget:
937 938 if ui.verbose or not m.exact(f):
938 939 ui.status(_('removing %s\n') % m.rel(f))
939 940
940 941 # Need to lock because standin files are deleted then removed from the
941 942 # repository and we could race in-between.
942 943 wlock = repo.wlock()
943 944 try:
944 945 lfdirstate = lfutil.openlfdirstate(ui, repo)
945 946 for f in forget:
946 947 if lfdirstate[f] == 'a':
947 948 lfdirstate.drop(f)
948 949 else:
949 950 lfdirstate.remove(f)
950 951 lfdirstate.write()
951 952 lfutil.reporemove(repo, [lfutil.standin(f) for f in forget],
952 953 unlink=True)
953 954 finally:
954 955 wlock.release()
955 956
956 957 return result
957 958
958 959 def getoutgoinglfiles(ui, repo, dest=None, **opts):
959 960 dest = ui.expandpath(dest or 'default-push', dest or 'default')
960 961 dest, branches = hg.parseurl(dest, opts.get('branch'))
961 962 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
962 963 if revs:
963 964 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
964 965
965 966 try:
966 967 remote = hg.peer(repo, opts, dest)
967 968 except error.RepoError:
968 969 return None
969 970 o = lfutil.findoutgoing(repo, remote, False)
970 971 if not o:
971 972 return o
972 973 o = repo.changelog.nodesbetween(o, revs)[0]
973 974 if opts.get('newest_first'):
974 975 o.reverse()
975 976
976 977 toupload = set()
977 978 for n in o:
978 979 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
979 980 ctx = repo[n]
980 981 files = set(ctx.files())
981 982 if len(parents) == 2:
982 983 mc = ctx.manifest()
983 984 mp1 = ctx.parents()[0].manifest()
984 985 mp2 = ctx.parents()[1].manifest()
985 986 for f in mp1:
986 987 if f not in mc:
987 988 files.add(f)
988 989 for f in mp2:
989 990 if f not in mc:
990 991 files.add(f)
991 992 for f in mc:
992 993 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
993 994 files.add(f)
994 995 toupload = toupload.union(
995 996 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
996 997 return toupload
997 998
998 999 def overrideoutgoing(orig, ui, repo, dest=None, **opts):
999 1000 result = orig(ui, repo, dest, **opts)
1000 1001
1001 1002 if opts.pop('large', None):
1002 1003 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
1003 1004 if toupload is None:
1004 1005 ui.status(_('largefiles: No remote repo\n'))
1005 1006 elif not toupload:
1006 1007 ui.status(_('largefiles: no files to upload\n'))
1007 1008 else:
1008 1009 ui.status(_('largefiles to upload:\n'))
1009 1010 for file in toupload:
1010 1011 ui.status(lfutil.splitstandin(file) + '\n')
1011 1012 ui.status('\n')
1012 1013
1013 1014 return result
1014 1015
1015 1016 def overridesummary(orig, ui, repo, *pats, **opts):
1016 1017 try:
1017 1018 repo.lfstatus = True
1018 1019 orig(ui, repo, *pats, **opts)
1019 1020 finally:
1020 1021 repo.lfstatus = False
1021 1022
1022 1023 if opts.pop('large', None):
1023 1024 toupload = getoutgoinglfiles(ui, repo, None, **opts)
1024 1025 if toupload is None:
1025 1026 # i18n: column positioning for "hg summary"
1026 1027 ui.status(_('largefiles: (no remote repo)\n'))
1027 1028 elif not toupload:
1028 1029 # i18n: column positioning for "hg summary"
1029 1030 ui.status(_('largefiles: (no files to upload)\n'))
1030 1031 else:
1031 1032 # i18n: column positioning for "hg summary"
1032 1033 ui.status(_('largefiles: %d to upload\n') % len(toupload))
1033 1034
1034 1035 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1035 1036 similarity=None):
1036 1037 if not lfutil.islfilesrepo(repo):
1037 1038 return orig(repo, pats, opts, dry_run, similarity)
1038 1039 # Get the list of missing largefiles so we can remove them
1039 1040 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1040 1041 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
1041 1042 False, False)
1042 1043 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
1043 1044
1044 1045 # Call into the normal remove code, but the removing of the standin, we want
1045 1046 # to have handled by original addremove. Monkey patching here makes sure
1046 1047 # we don't remove the standin in the largefiles code, preventing a very
1047 1048 # confused state later.
1048 1049 if missing:
1049 1050 m = [repo.wjoin(f) for f in missing]
1050 1051 repo._isaddremove = True
1051 1052 removelargefiles(repo.ui, repo, *m, **opts)
1052 1053 repo._isaddremove = False
1053 1054 # Call into the normal add code, and any files that *should* be added as
1054 1055 # largefiles will be
1055 1056 addlargefiles(repo.ui, repo, *pats, **opts)
1056 1057 # Now that we've handled largefiles, hand off to the original addremove
1057 1058 # function to take care of the rest. Make sure it doesn't do anything with
1058 1059 # largefiles by installing a matcher that will ignore them.
1059 1060 installnormalfilesmatchfn(repo[None].manifest())
1060 1061 result = orig(repo, pats, opts, dry_run, similarity)
1061 1062 restorematchfn()
1062 1063 return result
1063 1064
1064 1065 # Calling purge with --all will cause the largefiles to be deleted.
1065 1066 # Override repo.status to prevent this from happening.
1066 1067 def overridepurge(orig, ui, repo, *dirs, **opts):
1067 1068 oldstatus = repo.status
1068 1069 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1069 1070 clean=False, unknown=False, listsubrepos=False):
1070 1071 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1071 1072 listsubrepos)
1072 1073 lfdirstate = lfutil.openlfdirstate(ui, repo)
1073 1074 modified, added, removed, deleted, unknown, ignored, clean = r
1074 1075 unknown = [f for f in unknown if lfdirstate[f] == '?']
1075 1076 ignored = [f for f in ignored if lfdirstate[f] == '?']
1076 1077 return modified, added, removed, deleted, unknown, ignored, clean
1077 1078 repo.status = overridestatus
1078 1079 orig(ui, repo, *dirs, **opts)
1079 1080 repo.status = oldstatus
1080 1081
1081 1082 def overriderollback(orig, ui, repo, **opts):
1082 1083 result = orig(ui, repo, **opts)
1083 1084 merge.update(repo, node=None, branchmerge=False, force=True,
1084 1085 partial=lfutil.isstandin)
1085 1086 wlock = repo.wlock()
1086 1087 try:
1087 1088 lfdirstate = lfutil.openlfdirstate(ui, repo)
1088 1089 lfiles = lfutil.listlfiles(repo)
1089 1090 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
1090 1091 for file in lfiles:
1091 1092 if file in oldlfiles:
1092 1093 lfdirstate.normallookup(file)
1093 1094 else:
1094 1095 lfdirstate.add(file)
1095 1096 lfdirstate.write()
1096 1097 finally:
1097 1098 wlock.release()
1098 1099 return result
1099 1100
1100 1101 def overridetransplant(orig, ui, repo, *revs, **opts):
1101 1102 try:
1102 1103 oldstandins = lfutil.getstandinsstate(repo)
1103 1104 repo._istransplanting = True
1104 1105 result = orig(ui, repo, *revs, **opts)
1105 1106 newstandins = lfutil.getstandinsstate(repo)
1106 1107 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1107 1108 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1108 1109 printmessage=True)
1109 1110 finally:
1110 1111 repo._istransplanting = False
1111 1112 return result
1112 1113
1113 1114 def overridecat(orig, ui, repo, file1, *pats, **opts):
1114 1115 ctx = scmutil.revsingle(repo, opts.get('rev'))
1115 1116 if not lfutil.standin(file1) in ctx:
1116 1117 result = orig(ui, repo, file1, *pats, **opts)
1117 1118 return result
1118 1119 return lfcommands.catlfile(repo, file1, ctx.rev(), opts.get('output'))
1119 1120
1120 1121 def mercurialsinkbefore(orig, sink):
1121 1122 sink.repo._isconverting = True
1122 1123 orig(sink)
1123 1124
1124 1125 def mercurialsinkafter(orig, sink):
1125 1126 sink.repo._isconverting = False
1126 1127 orig(sink)
General Comments 0
You need to be logged in to leave comments. Login now