##// END OF EJS Templates
largefiles: always create the cache and standin directories when cloning...
Matt Harbison -
r17824:221c9c31 default
parent child Browse files
Show More
@@ -1,1106 +1,1111 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 568 for lfile in modified:
569 569 lfutil.updatestandin(repo, lfutil.standin(lfile))
570 570 for lfile in missing:
571 571 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
572 572 os.unlink(repo.wjoin(lfutil.standin(lfile)))
573 573
574 574 try:
575 575 ctx = scmutil.revsingle(repo, opts.get('rev'))
576 576 oldmatch = None # for the closure
577 577 def overridematch(ctx, pats=[], opts={}, globbed=False,
578 578 default='relpath'):
579 579 match = oldmatch(ctx, pats, opts, globbed, default)
580 580 m = copy.copy(match)
581 581 def tostandin(f):
582 582 if lfutil.standin(f) in ctx:
583 583 return lfutil.standin(f)
584 584 elif lfutil.standin(f) in repo[None]:
585 585 return None
586 586 return f
587 587 m._files = [tostandin(f) for f in m._files]
588 588 m._files = [f for f in m._files if f is not None]
589 589 m._fmap = set(m._files)
590 590 origmatchfn = m.matchfn
591 591 def matchfn(f):
592 592 if lfutil.isstandin(f):
593 593 # We need to keep track of what largefiles are being
594 594 # matched so we know which ones to update later --
595 595 # otherwise we accidentally revert changes to other
596 596 # largefiles. This is repo-specific, so duckpunch the
597 597 # repo object to keep the list of largefiles for us
598 598 # later.
599 599 if origmatchfn(lfutil.splitstandin(f)) and \
600 600 (f in repo[None] or f in ctx):
601 601 lfileslist = getattr(repo, '_lfilestoupdate', [])
602 602 lfileslist.append(lfutil.splitstandin(f))
603 603 repo._lfilestoupdate = lfileslist
604 604 return True
605 605 else:
606 606 return False
607 607 return origmatchfn(f)
608 608 m.matchfn = matchfn
609 609 return m
610 610 oldmatch = installmatchfn(overridematch)
611 611 scmutil.match
612 612 matches = overridematch(repo[None], pats, opts)
613 613 orig(ui, repo, *pats, **opts)
614 614 finally:
615 615 restorematchfn()
616 616 lfileslist = getattr(repo, '_lfilestoupdate', [])
617 617 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
618 618 printmessage=False)
619 619
620 620 # empty out the largefiles list so we start fresh next time
621 621 repo._lfilestoupdate = []
622 622 for lfile in modified:
623 623 if lfile in lfileslist:
624 624 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
625 625 in repo['.']:
626 626 lfutil.writestandin(repo, lfutil.standin(lfile),
627 627 repo['.'][lfile].data().strip(),
628 628 'x' in repo['.'][lfile].flags())
629 629 lfdirstate = lfutil.openlfdirstate(ui, repo)
630 630 for lfile in added:
631 631 standin = lfutil.standin(lfile)
632 632 if standin not in ctx and (standin in matches or opts.get('all')):
633 633 if lfile in lfdirstate:
634 634 lfdirstate.drop(lfile)
635 635 util.unlinkpath(repo.wjoin(standin))
636 636 lfdirstate.write()
637 637 finally:
638 638 wlock.release()
639 639
640 640 def hgupdate(orig, repo, node):
641 641 # Only call updatelfiles the standins that have changed to save time
642 642 oldstandins = lfutil.getstandinsstate(repo)
643 643 result = orig(repo, node)
644 644 newstandins = lfutil.getstandinsstate(repo)
645 645 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
646 646 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist, printmessage=True)
647 647 return result
648 648
649 649 def hgclean(orig, repo, node, show_stats=True):
650 650 result = orig(repo, node, show_stats)
651 651 lfcommands.updatelfiles(repo.ui, repo)
652 652 return result
653 653
654 654 def hgmerge(orig, repo, node, force=None, remind=True):
655 655 # Mark the repo as being in the middle of a merge, so that
656 656 # updatelfiles() will know that it needs to trust the standins in
657 657 # the working copy, not in the standins in the current node
658 658 repo._ismerging = True
659 659 try:
660 660 result = orig(repo, node, force, remind)
661 661 lfcommands.updatelfiles(repo.ui, repo)
662 662 finally:
663 663 repo._ismerging = False
664 664 return result
665 665
666 666 # When we rebase a repository with remotely changed largefiles, we need to
667 667 # take some extra care so that the largefiles are correctly updated in the
668 668 # working copy
669 669 def overridepull(orig, ui, repo, source=None, **opts):
670 670 revsprepull = len(repo)
671 671 if opts.get('rebase', False):
672 672 repo._isrebasing = True
673 673 try:
674 674 if opts.get('update'):
675 675 del opts['update']
676 676 ui.debug('--update and --rebase are not compatible, ignoring '
677 677 'the update flag\n')
678 678 del opts['rebase']
679 679 cmdutil.bailifchanged(repo)
680 680 origpostincoming = commands.postincoming
681 681 def _dummy(*args, **kwargs):
682 682 pass
683 683 commands.postincoming = _dummy
684 684 repo.lfpullsource = source
685 685 if not source:
686 686 source = 'default'
687 687 try:
688 688 result = commands.pull(ui, repo, source, **opts)
689 689 finally:
690 690 commands.postincoming = origpostincoming
691 691 revspostpull = len(repo)
692 692 if revspostpull > revsprepull:
693 693 result = result or rebase.rebase(ui, repo)
694 694 finally:
695 695 repo._isrebasing = False
696 696 else:
697 697 repo.lfpullsource = source
698 698 if not source:
699 699 source = 'default'
700 700 oldheads = lfutil.getcurrentheads(repo)
701 701 result = orig(ui, repo, source, **opts)
702 702 # If we do not have the new largefiles for any new heads we pulled, we
703 703 # will run into a problem later if we try to merge or rebase with one of
704 704 # these heads, so cache the largefiles now directly into the system
705 705 # cache.
706 706 ui.status(_("caching new largefiles\n"))
707 707 numcached = 0
708 708 heads = lfutil.getcurrentheads(repo)
709 709 newheads = set(heads).difference(set(oldheads))
710 710 for head in newheads:
711 711 (cached, missing) = lfcommands.cachelfiles(ui, repo, head)
712 712 numcached += len(cached)
713 713 ui.status(_("%d largefiles cached\n") % numcached)
714 714 if opts.get('all_largefiles'):
715 715 revspostpull = len(repo)
716 716 revs = []
717 717 for rev in xrange(revsprepull + 1, revspostpull):
718 718 revs.append(repo[rev].rev())
719 719 lfcommands.downloadlfiles(ui, repo, revs)
720 720 return result
721 721
722 722 def overrideclone(orig, ui, source, dest=None, **opts):
723 723 d = dest
724 724 if d is None:
725 725 d = hg.defaultdest(source)
726 726 if opts.get('all_largefiles') and not hg.islocal(d):
727 727 raise util.Abort(_(
728 728 '--all-largefiles is incompatible with non-local destination %s' %
729 729 d))
730 730
731 731 return orig(ui, source, dest, **opts)
732 732
733 733 def hgclone(orig, ui, opts, *args, **kwargs):
734 734 result = orig(ui, opts, *args, **kwargs)
735 735
736 if result is not None and opts.get('all_largefiles'):
736 if result is not None:
737 737 sourcerepo, destrepo = result
738 738 repo = destrepo.local()
739 739
740 740 # The .hglf directory must exist for the standin matcher to match
741 741 # anything (which listlfiles uses for each rev), and .hg/largefiles is
742 742 # assumed to exist by the code that caches the downloaded file. These
743 # directories exist if clone updated to any rev.
744 if opts.get('noupdate'):
745 util.makedirs(repo.pathto(lfutil.shortname))
746 util.makedirs(repo.join(lfutil.longname))
743 # directories exist if clone updated to any rev. (If the repo does not
744 # have largefiles, download never gets to the point of needing
745 # .hg/largefiles, and the standin matcher won't match anything anyway.)
746 if 'largefiles' in repo.requirements:
747 if opts.get('noupdate'):
748 util.makedirs(repo.pathto(lfutil.shortname))
749 util.makedirs(repo.join(lfutil.longname))
747 750
748 751 # Caching is implicitly limited to 'rev' option, since the dest repo was
749 # truncated at that point.
750 success, missing = lfcommands.downloadlfiles(ui, repo, None)
752 # truncated at that point. The user may expect a download count with
753 # this option, so attempt whether or not this is a largefile repo.
754 if opts.get('all_largefiles'):
755 success, missing = lfcommands.downloadlfiles(ui, repo, None)
751 756
752 if missing != 0:
753 return None
757 if missing != 0:
758 return None
754 759
755 760 return result
756 761
757 762 def overriderebase(orig, ui, repo, **opts):
758 763 repo._isrebasing = True
759 764 try:
760 765 return orig(ui, repo, **opts)
761 766 finally:
762 767 repo._isrebasing = False
763 768
764 769 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
765 770 prefix=None, mtime=None, subrepos=None):
766 771 # No need to lock because we are only reading history and
767 772 # largefile caches, neither of which are modified.
768 773 lfcommands.cachelfiles(repo.ui, repo, node)
769 774
770 775 if kind not in archival.archivers:
771 776 raise util.Abort(_("unknown archive type '%s'") % kind)
772 777
773 778 ctx = repo[node]
774 779
775 780 if kind == 'files':
776 781 if prefix:
777 782 raise util.Abort(
778 783 _('cannot give prefix when archiving to files'))
779 784 else:
780 785 prefix = archival.tidyprefix(dest, kind, prefix)
781 786
782 787 def write(name, mode, islink, getdata):
783 788 if matchfn and not matchfn(name):
784 789 return
785 790 data = getdata()
786 791 if decode:
787 792 data = repo.wwritedata(name, data)
788 793 archiver.addfile(prefix + name, mode, islink, data)
789 794
790 795 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
791 796
792 797 if repo.ui.configbool("ui", "archivemeta", True):
793 798 def metadata():
794 799 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
795 800 hex(repo.changelog.node(0)), hex(node), ctx.branch())
796 801
797 802 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
798 803 if repo.tagtype(t) == 'global')
799 804 if not tags:
800 805 repo.ui.pushbuffer()
801 806 opts = {'template': '{latesttag}\n{latesttagdistance}',
802 807 'style': '', 'patch': None, 'git': None}
803 808 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
804 809 ltags, dist = repo.ui.popbuffer().split('\n')
805 810 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
806 811 tags += 'latesttagdistance: %s\n' % dist
807 812
808 813 return base + tags
809 814
810 815 write('.hg_archival.txt', 0644, False, metadata)
811 816
812 817 for f in ctx:
813 818 ff = ctx.flags(f)
814 819 getdata = ctx[f].data
815 820 if lfutil.isstandin(f):
816 821 path = lfutil.findfile(repo, getdata().strip())
817 822 if path is None:
818 823 raise util.Abort(
819 824 _('largefile %s not found in repo store or system cache')
820 825 % lfutil.splitstandin(f))
821 826 f = lfutil.splitstandin(f)
822 827
823 828 def getdatafn():
824 829 fd = None
825 830 try:
826 831 fd = open(path, 'rb')
827 832 return fd.read()
828 833 finally:
829 834 if fd:
830 835 fd.close()
831 836
832 837 getdata = getdatafn
833 838 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
834 839
835 840 if subrepos:
836 841 for subpath in ctx.substate:
837 842 sub = ctx.sub(subpath)
838 843 submatch = match_.narrowmatcher(subpath, matchfn)
839 844 sub.archive(repo.ui, archiver, prefix, submatch)
840 845
841 846 archiver.done()
842 847
843 848 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
844 849 repo._get(repo._state + ('hg',))
845 850 rev = repo._state[1]
846 851 ctx = repo._repo[rev]
847 852
848 853 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
849 854
850 855 def write(name, mode, islink, getdata):
851 856 # At this point, the standin has been replaced with the largefile name,
852 857 # so the normal matcher works here without the lfutil variants.
853 858 if match and not match(f):
854 859 return
855 860 data = getdata()
856 861
857 862 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
858 863
859 864 for f in ctx:
860 865 ff = ctx.flags(f)
861 866 getdata = ctx[f].data
862 867 if lfutil.isstandin(f):
863 868 path = lfutil.findfile(repo._repo, getdata().strip())
864 869 if path is None:
865 870 raise util.Abort(
866 871 _('largefile %s not found in repo store or system cache')
867 872 % lfutil.splitstandin(f))
868 873 f = lfutil.splitstandin(f)
869 874
870 875 def getdatafn():
871 876 fd = None
872 877 try:
873 878 fd = open(os.path.join(prefix, path), 'rb')
874 879 return fd.read()
875 880 finally:
876 881 if fd:
877 882 fd.close()
878 883
879 884 getdata = getdatafn
880 885
881 886 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
882 887
883 888 for subpath in ctx.substate:
884 889 sub = ctx.sub(subpath)
885 890 submatch = match_.narrowmatcher(subpath, match)
886 891 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
887 892 submatch)
888 893
889 894 # If a largefile is modified, the change is not reflected in its
890 895 # standin until a commit. cmdutil.bailifchanged() raises an exception
891 896 # if the repo has uncommitted changes. Wrap it to also check if
892 897 # largefiles were changed. This is used by bisect and backout.
893 898 def overridebailifchanged(orig, repo):
894 899 orig(repo)
895 900 repo.lfstatus = True
896 901 modified, added, removed, deleted = repo.status()[:4]
897 902 repo.lfstatus = False
898 903 if modified or added or removed or deleted:
899 904 raise util.Abort(_('outstanding uncommitted changes'))
900 905
901 906 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
902 907 def overridefetch(orig, ui, repo, *pats, **opts):
903 908 repo.lfstatus = True
904 909 modified, added, removed, deleted = repo.status()[:4]
905 910 repo.lfstatus = False
906 911 if modified or added or removed or deleted:
907 912 raise util.Abort(_('outstanding uncommitted changes'))
908 913 return orig(ui, repo, *pats, **opts)
909 914
910 915 def overrideforget(orig, ui, repo, *pats, **opts):
911 916 installnormalfilesmatchfn(repo[None].manifest())
912 917 result = orig(ui, repo, *pats, **opts)
913 918 restorematchfn()
914 919 m = scmutil.match(repo[None], pats, opts)
915 920
916 921 try:
917 922 repo.lfstatus = True
918 923 s = repo.status(match=m, clean=True)
919 924 finally:
920 925 repo.lfstatus = False
921 926 forget = sorted(s[0] + s[1] + s[3] + s[6])
922 927 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
923 928
924 929 for f in forget:
925 930 if lfutil.standin(f) not in repo.dirstate and not \
926 931 os.path.isdir(m.rel(lfutil.standin(f))):
927 932 ui.warn(_('not removing %s: file is already untracked\n')
928 933 % m.rel(f))
929 934 result = 1
930 935
931 936 for f in forget:
932 937 if ui.verbose or not m.exact(f):
933 938 ui.status(_('removing %s\n') % m.rel(f))
934 939
935 940 # Need to lock because standin files are deleted then removed from the
936 941 # repository and we could race in-between.
937 942 wlock = repo.wlock()
938 943 try:
939 944 lfdirstate = lfutil.openlfdirstate(ui, repo)
940 945 for f in forget:
941 946 if lfdirstate[f] == 'a':
942 947 lfdirstate.drop(f)
943 948 else:
944 949 lfdirstate.remove(f)
945 950 lfdirstate.write()
946 951 lfutil.reporemove(repo, [lfutil.standin(f) for f in forget],
947 952 unlink=True)
948 953 finally:
949 954 wlock.release()
950 955
951 956 return result
952 957
953 958 def getoutgoinglfiles(ui, repo, dest=None, **opts):
954 959 dest = ui.expandpath(dest or 'default-push', dest or 'default')
955 960 dest, branches = hg.parseurl(dest, opts.get('branch'))
956 961 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
957 962 if revs:
958 963 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
959 964
960 965 try:
961 966 remote = hg.peer(repo, opts, dest)
962 967 except error.RepoError:
963 968 return None
964 969 o = lfutil.findoutgoing(repo, remote, False)
965 970 if not o:
966 971 return None
967 972 o = repo.changelog.nodesbetween(o, revs)[0]
968 973 if opts.get('newest_first'):
969 974 o.reverse()
970 975
971 976 toupload = set()
972 977 for n in o:
973 978 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
974 979 ctx = repo[n]
975 980 files = set(ctx.files())
976 981 if len(parents) == 2:
977 982 mc = ctx.manifest()
978 983 mp1 = ctx.parents()[0].manifest()
979 984 mp2 = ctx.parents()[1].manifest()
980 985 for f in mp1:
981 986 if f not in mc:
982 987 files.add(f)
983 988 for f in mp2:
984 989 if f not in mc:
985 990 files.add(f)
986 991 for f in mc:
987 992 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
988 993 files.add(f)
989 994 toupload = toupload.union(
990 995 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
991 996 return toupload
992 997
993 998 def overrideoutgoing(orig, ui, repo, dest=None, **opts):
994 999 result = orig(ui, repo, dest, **opts)
995 1000
996 1001 if opts.pop('large', None):
997 1002 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
998 1003 if toupload is None:
999 1004 ui.status(_('largefiles: No remote repo\n'))
1000 1005 else:
1001 1006 ui.status(_('largefiles to upload:\n'))
1002 1007 for file in toupload:
1003 1008 ui.status(lfutil.splitstandin(file) + '\n')
1004 1009 ui.status('\n')
1005 1010
1006 1011 return result
1007 1012
1008 1013 def overridesummary(orig, ui, repo, *pats, **opts):
1009 1014 try:
1010 1015 repo.lfstatus = True
1011 1016 orig(ui, repo, *pats, **opts)
1012 1017 finally:
1013 1018 repo.lfstatus = False
1014 1019
1015 1020 if opts.pop('large', None):
1016 1021 toupload = getoutgoinglfiles(ui, repo, None, **opts)
1017 1022 if toupload is None:
1018 1023 ui.status(_('largefiles: No remote repo\n'))
1019 1024 else:
1020 1025 ui.status(_('largefiles: %d to upload\n') % len(toupload))
1021 1026
1022 1027 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1023 1028 similarity=None):
1024 1029 if not lfutil.islfilesrepo(repo):
1025 1030 return orig(repo, pats, opts, dry_run, similarity)
1026 1031 # Get the list of missing largefiles so we can remove them
1027 1032 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1028 1033 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
1029 1034 False, False)
1030 1035 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
1031 1036
1032 1037 # Call into the normal remove code, but the removing of the standin, we want
1033 1038 # to have handled by original addremove. Monkey patching here makes sure
1034 1039 # we don't remove the standin in the largefiles code, preventing a very
1035 1040 # confused state later.
1036 1041 if missing:
1037 1042 m = [repo.wjoin(f) for f in missing]
1038 1043 repo._isaddremove = True
1039 1044 removelargefiles(repo.ui, repo, *m, **opts)
1040 1045 repo._isaddremove = False
1041 1046 # Call into the normal add code, and any files that *should* be added as
1042 1047 # largefiles will be
1043 1048 addlargefiles(repo.ui, repo, *pats, **opts)
1044 1049 # Now that we've handled largefiles, hand off to the original addremove
1045 1050 # function to take care of the rest. Make sure it doesn't do anything with
1046 1051 # largefiles by installing a matcher that will ignore them.
1047 1052 installnormalfilesmatchfn(repo[None].manifest())
1048 1053 result = orig(repo, pats, opts, dry_run, similarity)
1049 1054 restorematchfn()
1050 1055 return result
1051 1056
1052 1057 # Calling purge with --all will cause the largefiles to be deleted.
1053 1058 # Override repo.status to prevent this from happening.
1054 1059 def overridepurge(orig, ui, repo, *dirs, **opts):
1055 1060 oldstatus = repo.status
1056 1061 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1057 1062 clean=False, unknown=False, listsubrepos=False):
1058 1063 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1059 1064 listsubrepos)
1060 1065 lfdirstate = lfutil.openlfdirstate(ui, repo)
1061 1066 modified, added, removed, deleted, unknown, ignored, clean = r
1062 1067 unknown = [f for f in unknown if lfdirstate[f] == '?']
1063 1068 ignored = [f for f in ignored if lfdirstate[f] == '?']
1064 1069 return modified, added, removed, deleted, unknown, ignored, clean
1065 1070 repo.status = overridestatus
1066 1071 orig(ui, repo, *dirs, **opts)
1067 1072 repo.status = oldstatus
1068 1073
1069 1074 def overriderollback(orig, ui, repo, **opts):
1070 1075 result = orig(ui, repo, **opts)
1071 1076 merge.update(repo, node=None, branchmerge=False, force=True,
1072 1077 partial=lfutil.isstandin)
1073 1078 wlock = repo.wlock()
1074 1079 try:
1075 1080 lfdirstate = lfutil.openlfdirstate(ui, repo)
1076 1081 lfiles = lfutil.listlfiles(repo)
1077 1082 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
1078 1083 for file in lfiles:
1079 1084 if file in oldlfiles:
1080 1085 lfdirstate.normallookup(file)
1081 1086 else:
1082 1087 lfdirstate.add(file)
1083 1088 lfdirstate.write()
1084 1089 finally:
1085 1090 wlock.release()
1086 1091 return result
1087 1092
1088 1093 def overridetransplant(orig, ui, repo, *revs, **opts):
1089 1094 try:
1090 1095 oldstandins = lfutil.getstandinsstate(repo)
1091 1096 repo._istransplanting = True
1092 1097 result = orig(ui, repo, *revs, **opts)
1093 1098 newstandins = lfutil.getstandinsstate(repo)
1094 1099 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1095 1100 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1096 1101 printmessage=True)
1097 1102 finally:
1098 1103 repo._istransplanting = False
1099 1104 return result
1100 1105
1101 1106 def overridecat(orig, ui, repo, file1, *pats, **opts):
1102 1107 ctx = scmutil.revsingle(repo, opts.get('rev'))
1103 1108 if not lfutil.standin(file1) in ctx:
1104 1109 result = orig(ui, repo, file1, *pats, **opts)
1105 1110 return result
1106 1111 return lfcommands.catlfile(repo, file1, ctx.rev(), opts.get('output'))
@@ -1,286 +1,297 b''
1 1 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
2 2 $ mkdir "${USERCACHE}"
3 3 $ cat >> $HGRCPATH <<EOF
4 4 > [extensions]
5 5 > largefiles =
6 6 > share =
7 7 > graphlog =
8 8 > mq =
9 9 > [largefiles]
10 10 > minsize = 0.5
11 11 > patterns = **.other
12 12 > **.dat
13 13 > usercache=${USERCACHE}
14 14 > EOF
15 15
16 16 "lfconvert" works
17 17 $ hg init bigfile-repo
18 18 $ cd bigfile-repo
19 19 $ cat >> .hg/hgrc <<EOF
20 20 > [extensions]
21 21 > largefiles = !
22 22 > EOF
23 23 $ mkdir sub
24 24 $ dd if=/dev/zero bs=1k count=256 > large 2> /dev/null
25 25 $ dd if=/dev/zero bs=1k count=256 > large2 2> /dev/null
26 26 $ echo normal > normal1
27 27 $ echo alsonormal > sub/normal2
28 28 $ dd if=/dev/zero bs=1k count=10 > sub/maybelarge.dat 2> /dev/null
29 29 $ hg addremove
30 30 adding large
31 31 adding large2
32 32 adding normal1
33 33 adding sub/maybelarge.dat
34 34 adding sub/normal2
35 35 $ hg commit -m"add large, normal1" large normal1
36 36 $ hg commit -m"add sub/*" sub
37 37
38 38 Test tag parsing
39 39 $ cat >> .hgtags <<EOF
40 40 > IncorrectlyFormattedTag!
41 41 > invalidhash sometag
42 42 > 0123456789abcdef anothertag
43 43 > EOF
44 44 $ hg add .hgtags
45 45 $ hg commit -m"add large2" large2 .hgtags
46 46
47 47 Test link+rename largefile codepath
48 48 $ [ -d .hg/largefiles ] && echo fail || echo pass
49 49 pass
50 50 $ cd ..
51 51 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo
52 52 initializing destination largefiles-repo
53 53 skipping incorrectly formatted tag IncorrectlyFormattedTag!
54 54 skipping incorrectly formatted id invalidhash
55 55 no mapping for id 0123456789abcdef
56 56 #if symlink
57 57 $ hg --cwd bigfile-repo rename large2 large3
58 58 $ ln -sf large bigfile-repo/large3
59 59 $ hg --cwd bigfile-repo commit -m"make large2 a symlink" large2 large3
60 60 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo-symlink
61 61 initializing destination largefiles-repo-symlink
62 62 skipping incorrectly formatted tag IncorrectlyFormattedTag!
63 63 skipping incorrectly formatted id invalidhash
64 64 no mapping for id 0123456789abcdef
65 65 abort: renamed/copied largefile large3 becomes symlink
66 66 [255]
67 67 #endif
68 68 $ cd bigfile-repo
69 69 $ hg strip --no-backup 2
70 70 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
71 71 $ cd ..
72 72 $ rm -rf largefiles-repo largefiles-repo-symlink
73 73
74 74 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo
75 75 initializing destination largefiles-repo
76 76
77 77 "lfconvert" converts content correctly
78 78 $ cd largefiles-repo
79 79 $ hg up
80 80 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
81 81 getting changed largefiles
82 82 2 largefiles updated, 0 removed
83 83 $ hg locate
84 84 .hglf/large
85 85 .hglf/sub/maybelarge.dat
86 86 normal1
87 87 sub/normal2
88 88 $ cat normal1
89 89 normal
90 90 $ cat sub/normal2
91 91 alsonormal
92 92 $ "$TESTDIR/md5sum.py" large sub/maybelarge.dat
93 93 ec87a838931d4d5d2e94a04644788a55 large
94 94 1276481102f218c981e0324180bafd9f sub/maybelarge.dat
95 95
96 96 "lfconvert" adds 'largefiles' to .hg/requires.
97 97 $ cat .hg/requires
98 98 largefiles
99 99 revlogv1
100 100 fncache
101 101 store
102 102 dotencode
103 103
104 104 "lfconvert" includes a newline at the end of the standin files.
105 105 $ cat .hglf/large .hglf/sub/maybelarge.dat
106 106 2e000fa7e85759c7f4c254d4d9c33ef481e459a7
107 107 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c
108 108 $ cd ..
109 109
110 110 add some changesets to rename/remove/merge
111 111 $ cd bigfile-repo
112 112 $ hg mv -q sub stuff
113 113 $ hg commit -m"rename sub/ to stuff/"
114 114 $ hg update -q 1
115 115 $ echo blah >> normal3
116 116 $ echo blah >> sub/normal2
117 117 $ echo blah >> sub/maybelarge.dat
118 118 $ "$TESTDIR/md5sum.py" sub/maybelarge.dat
119 119 1dd0b99ff80e19cff409702a1d3f5e15 sub/maybelarge.dat
120 120 $ hg commit -A -m"add normal3, modify sub/*"
121 121 adding normal3
122 122 created new head
123 123 $ hg rm large normal3
124 124 $ hg commit -q -m"remove large, normal3"
125 125 $ hg merge
126 126 merging sub/maybelarge.dat and stuff/maybelarge.dat to stuff/maybelarge.dat
127 127 warning: $TESTTMP/bigfile-repo/stuff/maybelarge.dat looks like a binary file. (glob)
128 128 merging stuff/maybelarge.dat incomplete! (edit conflicts, then use 'hg resolve --mark')
129 129 merging sub/normal2 and stuff/normal2 to stuff/normal2
130 130 0 files updated, 1 files merged, 0 files removed, 1 files unresolved
131 131 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
132 132 [1]
133 133 $ hg cat -r . sub/maybelarge.dat > stuff/maybelarge.dat
134 134 $ hg resolve -m stuff/maybelarge.dat
135 135 $ hg commit -m"merge"
136 136 $ hg glog --template "{rev}:{node|short} {desc|firstline}\n"
137 137 @ 5:4884f215abda merge
138 138 |\
139 139 | o 4:7285f817b77e remove large, normal3
140 140 | |
141 141 | o 3:67e3892e3534 add normal3, modify sub/*
142 142 | |
143 143 o | 2:c96c8beb5d56 rename sub/ to stuff/
144 144 |/
145 145 o 1:020c65d24e11 add sub/*
146 146 |
147 147 o 0:117b8328f97a add large, normal1
148 148
149 149 $ cd ..
150 150
151 151 lfconvert with rename, merge, and remove
152 152 $ rm -rf largefiles-repo
153 153 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo
154 154 initializing destination largefiles-repo
155 155 $ cd largefiles-repo
156 156 $ hg glog --template "{rev}:{node|short} {desc|firstline}\n"
157 157 o 5:8e05f5f2b77e merge
158 158 |\
159 159 | o 4:a5a02de7a8e4 remove large, normal3
160 160 | |
161 161 | o 3:55759520c76f add normal3, modify sub/*
162 162 | |
163 163 o | 2:261ad3f3f037 rename sub/ to stuff/
164 164 |/
165 165 o 1:334e5237836d add sub/*
166 166 |
167 167 o 0:d4892ec57ce2 add large, normal1
168 168
169 169 $ hg locate -r 2
170 170 .hglf/large
171 171 .hglf/stuff/maybelarge.dat
172 172 normal1
173 173 stuff/normal2
174 174 $ hg locate -r 3
175 175 .hglf/large
176 176 .hglf/sub/maybelarge.dat
177 177 normal1
178 178 normal3
179 179 sub/normal2
180 180 $ hg locate -r 4
181 181 .hglf/sub/maybelarge.dat
182 182 normal1
183 183 sub/normal2
184 184 $ hg locate -r 5
185 185 .hglf/stuff/maybelarge.dat
186 186 normal1
187 187 stuff/normal2
188 188 $ hg update
189 189 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
190 190 getting changed largefiles
191 191 1 largefiles updated, 0 removed
192 192 $ cat stuff/normal2
193 193 alsonormal
194 194 blah
195 195 $ "$TESTDIR/md5sum.py" stuff/maybelarge.dat
196 196 1dd0b99ff80e19cff409702a1d3f5e15 stuff/maybelarge.dat
197 197 $ cat .hglf/stuff/maybelarge.dat
198 198 76236b6a2c6102826c61af4297dd738fb3b1de38
199 199 $ cd ..
200 200
201 201 "lfconvert" error cases
202 202 $ hg lfconvert http://localhost/foo foo
203 203 abort: http://localhost/foo is not a local Mercurial repo
204 204 [255]
205 205 $ hg lfconvert foo ssh://localhost/foo
206 206 abort: ssh://localhost/foo is not a local Mercurial repo
207 207 [255]
208 208 $ hg lfconvert nosuchrepo foo
209 209 abort: repository nosuchrepo not found!
210 210 [255]
211 211 $ hg share -q -U bigfile-repo shared
212 212 $ printf 'bogus' > shared/.hg/sharedpath
213 213 $ hg lfconvert shared foo
214 214 abort: .hg/sharedpath points to nonexistent directory $TESTTMP/bogus! (glob)
215 215 [255]
216 216 $ hg lfconvert bigfile-repo largefiles-repo
217 217 initializing destination largefiles-repo
218 218 abort: repository largefiles-repo already exists!
219 219 [255]
220 220
221 221 add another largefile to the new largefiles repo
222 222 $ cd largefiles-repo
223 223 $ dd if=/dev/zero bs=1k count=1k > anotherlarge 2> /dev/null
224 224 $ hg add --lfsize=1 anotherlarge
225 225 $ hg commit -m "add anotherlarge (should be a largefile)"
226 226 $ cat .hglf/anotherlarge
227 227 3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3
228 228 $ cd ..
229 229
230 230 round-trip: converting back to a normal (non-largefiles) repo with
231 231 "lfconvert --to-normal" should give the same as ../bigfile-repo
232 232 $ cd largefiles-repo
233 233 $ hg lfconvert --to-normal . ../normal-repo
234 234 initializing destination ../normal-repo
235 235 $ cd ../normal-repo
236 236 $ cat >> .hg/hgrc <<EOF
237 237 > [extensions]
238 238 > largefiles = !
239 239 > EOF
240 240
241 241 # Hmmm: the changeset ID for rev 5 is different from the original
242 242 # normal repo (../bigfile-repo), because the changelog filelist
243 243 # differs between the two incarnations of rev 5: this repo includes
244 244 # 'large' in the list, but ../bigfile-repo does not. Since rev 5
245 245 # removes 'large' relative to the first parent in both repos, it seems
246 246 # to me that lfconvert is doing a *better* job than
247 247 # "hg remove" + "hg merge" + "hg commit".
248 248 # $ hg -R ../bigfile-repo debugdata -c 5
249 249 # $ hg debugdata -c 5
250 250 $ hg glog --template "{rev}:{node|short} {desc|firstline}\n"
251 251 o 6:1635824e6f59 add anotherlarge (should be a largefile)
252 252 |
253 253 o 5:7215f8deeaaf merge
254 254 |\
255 255 | o 4:7285f817b77e remove large, normal3
256 256 | |
257 257 | o 3:67e3892e3534 add normal3, modify sub/*
258 258 | |
259 259 o | 2:c96c8beb5d56 rename sub/ to stuff/
260 260 |/
261 261 o 1:020c65d24e11 add sub/*
262 262 |
263 263 o 0:117b8328f97a add large, normal1
264 264
265 265 $ hg update
266 266 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
267 267 $ hg locate
268 268 anotherlarge
269 269 normal1
270 270 stuff/maybelarge.dat
271 271 stuff/normal2
272 272 $ [ -d .hg/largefiles ] && echo fail || echo pass
273 273 pass
274 274
275 275 $ cd ..
276 276
277 277 Avoid a traceback if a largefile isn't available (issue3519)
278 278
279 Ensure the largefile can be cached in the source if necessary
279 280 $ hg clone -U largefiles-repo issue3519
280 281 $ rm "${USERCACHE}"/*
281 282 $ hg lfconvert --to-normal issue3519 normalized3519
282 283 initializing destination normalized3519
284
285 Ensure the abort message is useful if a largefile is entirely unavailable
286 $ rm -rf normalized3519
287 $ rm "${USERCACHE}"/*
288 $ rm issue3519/.hg/largefiles/*
289 $ rm largefiles-repo/.hg/largefiles/*
290 $ hg lfconvert --to-normal issue3519 normalized3519
291 initializing destination normalized3519
292 large: can't get file locally
293 (no default or default-push path set in hgrc)
283 294 abort: missing largefile 'large' from revision d4892ec57ce212905215fad1d9018f56b99202ad
284 295 [255]
285 296
286 297
General Comments 0
You need to be logged in to leave comments. Login now