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