##// END OF EJS Templates
largefiles: make archive -S store largefiles instead of standins...
Matt Harbison -
r16578:43fb170a default
parent child Browse files
Show More
@@ -1,994 +1,1035 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:
85 85 abovemin = (lfsize and
86 86 os.lstat(repo.wjoin(f)).st_size >= lfsize * 1024 * 1024)
87 87 if large or abovemin or (lfmatcher and lfmatcher(f)):
88 88 lfnames.append(f)
89 89 if ui.verbose or not exact:
90 90 ui.status(_('adding %s as a largefile\n') % m.rel(f))
91 91
92 92 bad = []
93 93 standins = []
94 94
95 95 # Need to lock, otherwise there could be a race condition between
96 96 # when standins are created and added to the repo.
97 97 wlock = repo.wlock()
98 98 try:
99 99 if not opts.get('dry_run'):
100 100 lfdirstate = lfutil.openlfdirstate(ui, repo)
101 101 for f in lfnames:
102 102 standinname = lfutil.standin(f)
103 103 lfutil.writestandin(repo, standinname, hash='',
104 104 executable=lfutil.getexecutable(repo.wjoin(f)))
105 105 standins.append(standinname)
106 106 if lfdirstate[f] == 'r':
107 107 lfdirstate.normallookup(f)
108 108 else:
109 109 lfdirstate.add(f)
110 110 lfdirstate.write()
111 111 bad += [lfutil.splitstandin(f)
112 112 for f in lfutil.repoadd(repo, standins)
113 113 if f in m.files()]
114 114 finally:
115 115 wlock.release()
116 116 return bad
117 117
118 118 def removelargefiles(ui, repo, *pats, **opts):
119 119 after = opts.get('after')
120 120 if not pats and not after:
121 121 raise util.Abort(_('no files specified'))
122 122 m = scmutil.match(repo[None], pats, opts)
123 123 try:
124 124 repo.lfstatus = True
125 125 s = repo.status(match=m, clean=True)
126 126 finally:
127 127 repo.lfstatus = False
128 128 manifest = repo[None].manifest()
129 129 modified, added, deleted, clean = [[f for f in list
130 130 if lfutil.standin(f) in manifest]
131 131 for list in [s[0], s[1], s[3], s[6]]]
132 132
133 133 def warn(files, reason):
134 134 for f in files:
135 135 ui.warn(_('not removing %s: %s (use forget to undo)\n')
136 136 % (m.rel(f), reason))
137 137
138 138 if after:
139 139 remove, forget = deleted, []
140 140 warn(modified + added + clean, _('file still exists'))
141 141 else:
142 142 remove, forget = deleted + clean, []
143 143 warn(modified, _('file is modified'))
144 144 warn(added, _('file has been marked for add'))
145 145
146 146 for f in sorted(remove + forget):
147 147 if ui.verbose or not m.exact(f):
148 148 ui.status(_('removing %s\n') % m.rel(f))
149 149
150 150 # Need to lock because standin files are deleted then removed from the
151 151 # repository and we could race inbetween.
152 152 wlock = repo.wlock()
153 153 try:
154 154 lfdirstate = lfutil.openlfdirstate(ui, repo)
155 155 for f in remove:
156 156 if not after:
157 157 # If this is being called by addremove, notify the user that we
158 158 # are removing the file.
159 159 if getattr(repo, "_isaddremove", False):
160 160 ui.status(_('removing %s\n') % f)
161 161 if os.path.exists(repo.wjoin(f)):
162 162 util.unlinkpath(repo.wjoin(f))
163 163 lfdirstate.remove(f)
164 164 lfdirstate.write()
165 165 forget = [lfutil.standin(f) for f in forget]
166 166 remove = [lfutil.standin(f) for f in remove]
167 167 lfutil.repoforget(repo, forget)
168 168 # If this is being called by addremove, let the original addremove
169 169 # function handle this.
170 170 if not getattr(repo, "_isaddremove", False):
171 171 lfutil.reporemove(repo, remove, unlink=True)
172 172 finally:
173 173 wlock.release()
174 174
175 175 # For overriding mercurial.hgweb.webcommands so that largefiles will
176 176 # appear at their right place in the manifests.
177 177 def decodepath(orig, path):
178 178 return lfutil.splitstandin(path) or path
179 179
180 180 # -- Wrappers: modify existing commands --------------------------------
181 181
182 182 # Add works by going through the files that the user wanted to add and
183 183 # checking if they should be added as largefiles. Then it makes a new
184 184 # matcher which matches only the normal files and runs the original
185 185 # version of add.
186 186 def overrideadd(orig, ui, repo, *pats, **opts):
187 187 normal = opts.pop('normal')
188 188 if normal:
189 189 if opts.get('large'):
190 190 raise util.Abort(_('--normal cannot be used with --large'))
191 191 return orig(ui, repo, *pats, **opts)
192 192 bad = addlargefiles(ui, repo, *pats, **opts)
193 193 installnormalfilesmatchfn(repo[None].manifest())
194 194 result = orig(ui, repo, *pats, **opts)
195 195 restorematchfn()
196 196
197 197 return (result == 1 or bad) and 1 or 0
198 198
199 199 def overrideremove(orig, ui, repo, *pats, **opts):
200 200 installnormalfilesmatchfn(repo[None].manifest())
201 201 orig(ui, repo, *pats, **opts)
202 202 restorematchfn()
203 203 removelargefiles(ui, repo, *pats, **opts)
204 204
205 205 def overridestatusfn(orig, repo, rev2, **opts):
206 206 try:
207 207 repo._repo.lfstatus = True
208 208 return orig(repo, rev2, **opts)
209 209 finally:
210 210 repo._repo.lfstatus = False
211 211
212 212 def overridestatus(orig, ui, repo, *pats, **opts):
213 213 try:
214 214 repo.lfstatus = True
215 215 return orig(ui, repo, *pats, **opts)
216 216 finally:
217 217 repo.lfstatus = False
218 218
219 219 def overridedirty(orig, repo, ignoreupdate=False):
220 220 try:
221 221 repo._repo.lfstatus = True
222 222 return orig(repo, ignoreupdate)
223 223 finally:
224 224 repo._repo.lfstatus = False
225 225
226 226 def overridelog(orig, ui, repo, *pats, **opts):
227 227 try:
228 228 repo.lfstatus = True
229 229 orig(ui, repo, *pats, **opts)
230 230 finally:
231 231 repo.lfstatus = False
232 232
233 233 def overrideverify(orig, ui, repo, *pats, **opts):
234 234 large = opts.pop('large', False)
235 235 all = opts.pop('lfa', False)
236 236 contents = opts.pop('lfc', False)
237 237
238 238 result = orig(ui, repo, *pats, **opts)
239 239 if large:
240 240 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
241 241 return result
242 242
243 243 # Override needs to refresh standins so that update's normal merge
244 244 # will go through properly. Then the other update hook (overriding repo.update)
245 245 # will get the new files. Filemerge is also overriden so that the merge
246 246 # will merge standins correctly.
247 247 def overrideupdate(orig, ui, repo, *pats, **opts):
248 248 lfdirstate = lfutil.openlfdirstate(ui, repo)
249 249 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
250 250 False, False)
251 251 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
252 252
253 253 # Need to lock between the standins getting updated and their
254 254 # largefiles getting updated
255 255 wlock = repo.wlock()
256 256 try:
257 257 if opts['check']:
258 258 mod = len(modified) > 0
259 259 for lfile in unsure:
260 260 standin = lfutil.standin(lfile)
261 261 if repo['.'][standin].data().strip() != \
262 262 lfutil.hashfile(repo.wjoin(lfile)):
263 263 mod = True
264 264 else:
265 265 lfdirstate.normal(lfile)
266 266 lfdirstate.write()
267 267 if mod:
268 268 raise util.Abort(_('uncommitted local changes'))
269 269 # XXX handle removed differently
270 270 if not opts['clean']:
271 271 for lfile in unsure + modified + added:
272 272 lfutil.updatestandin(repo, lfutil.standin(lfile))
273 273 finally:
274 274 wlock.release()
275 275 return orig(ui, repo, *pats, **opts)
276 276
277 277 # Before starting the manifest merge, merge.updates will call
278 278 # _checkunknown to check if there are any files in the merged-in
279 279 # changeset that collide with unknown files in the working copy.
280 280 #
281 281 # The largefiles are seen as unknown, so this prevents us from merging
282 282 # in a file 'foo' if we already have a largefile with the same name.
283 283 #
284 284 # The overridden function filters the unknown files by removing any
285 285 # largefiles. This makes the merge proceed and we can then handle this
286 286 # case further in the overridden manifestmerge function below.
287 287 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
288 288 if lfutil.standin(f) in wctx:
289 289 return False
290 290 return origfn(repo, wctx, mctx, f)
291 291
292 292 # The manifest merge handles conflicts on the manifest level. We want
293 293 # to handle changes in largefile-ness of files at this level too.
294 294 #
295 295 # The strategy is to run the original manifestmerge and then process
296 296 # the action list it outputs. There are two cases we need to deal with:
297 297 #
298 298 # 1. Normal file in p1, largefile in p2. Here the largefile is
299 299 # detected via its standin file, which will enter the working copy
300 300 # with a "get" action. It is not "merge" since the standin is all
301 301 # Mercurial is concerned with at this level -- the link to the
302 302 # existing normal file is not relevant here.
303 303 #
304 304 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
305 305 # since the largefile will be present in the working copy and
306 306 # different from the normal file in p2. Mercurial therefore
307 307 # triggers a merge action.
308 308 #
309 309 # In both cases, we prompt the user and emit new actions to either
310 310 # remove the standin (if the normal file was kept) or to remove the
311 311 # normal file and get the standin (if the largefile was kept). The
312 312 # default prompt answer is to use the largefile version since it was
313 313 # presumably changed on purpose.
314 314 #
315 315 # Finally, the merge.applyupdates function will then take care of
316 316 # writing the files into the working copy and lfcommands.updatelfiles
317 317 # will update the largefiles.
318 318 def overridemanifestmerge(origfn, repo, p1, p2, pa, overwrite, partial):
319 319 actions = origfn(repo, p1, p2, pa, overwrite, partial)
320 320 processed = []
321 321
322 322 for action in actions:
323 323 if overwrite:
324 324 processed.append(action)
325 325 continue
326 326 f, m = action[:2]
327 327
328 328 choices = (_('&Largefile'), _('&Normal file'))
329 329 if m == "g" and lfutil.splitstandin(f) in p1 and f in p2:
330 330 # Case 1: normal file in the working copy, largefile in
331 331 # the second parent
332 332 lfile = lfutil.splitstandin(f)
333 333 standin = f
334 334 msg = _('%s has been turned into a largefile\n'
335 335 'use (l)argefile or keep as (n)ormal file?') % lfile
336 336 if repo.ui.promptchoice(msg, choices, 0) == 0:
337 337 processed.append((lfile, "r"))
338 338 processed.append((standin, "g", p2.flags(standin)))
339 339 else:
340 340 processed.append((standin, "r"))
341 341 elif m == "g" and lfutil.standin(f) in p1 and f in p2:
342 342 # Case 2: largefile in the working copy, normal file in
343 343 # the second parent
344 344 standin = lfutil.standin(f)
345 345 lfile = f
346 346 msg = _('%s has been turned into a normal file\n'
347 347 'keep as (l)argefile or use (n)ormal file?') % lfile
348 348 if repo.ui.promptchoice(msg, choices, 0) == 0:
349 349 processed.append((lfile, "r"))
350 350 else:
351 351 processed.append((standin, "r"))
352 352 processed.append((lfile, "g", p2.flags(lfile)))
353 353 else:
354 354 processed.append(action)
355 355
356 356 return processed
357 357
358 358 # Override filemerge to prompt the user about how they wish to merge
359 359 # largefiles. This will handle identical edits, and copy/rename +
360 360 # edit without prompting the user.
361 361 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca):
362 362 # Use better variable names here. Because this is a wrapper we cannot
363 363 # change the variable names in the function declaration.
364 364 fcdest, fcother, fcancestor = fcd, fco, fca
365 365 if not lfutil.isstandin(orig):
366 366 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
367 367 else:
368 368 if not fcother.cmp(fcdest): # files identical?
369 369 return None
370 370
371 371 # backwards, use working dir parent as ancestor
372 372 if fcancestor == fcother:
373 373 fcancestor = fcdest.parents()[0]
374 374
375 375 if orig != fcother.path():
376 376 repo.ui.status(_('merging %s and %s to %s\n')
377 377 % (lfutil.splitstandin(orig),
378 378 lfutil.splitstandin(fcother.path()),
379 379 lfutil.splitstandin(fcdest.path())))
380 380 else:
381 381 repo.ui.status(_('merging %s\n')
382 382 % lfutil.splitstandin(fcdest.path()))
383 383
384 384 if fcancestor.path() != fcother.path() and fcother.data() == \
385 385 fcancestor.data():
386 386 return 0
387 387 if fcancestor.path() != fcdest.path() and fcdest.data() == \
388 388 fcancestor.data():
389 389 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
390 390 return 0
391 391
392 392 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
393 393 'keep (l)ocal or take (o)ther?') %
394 394 lfutil.splitstandin(orig),
395 395 (_('&Local'), _('&Other')), 0) == 0:
396 396 return 0
397 397 else:
398 398 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
399 399 return 0
400 400
401 401 # Copy first changes the matchers to match standins instead of
402 402 # largefiles. Then it overrides util.copyfile in that function it
403 403 # checks if the destination largefile already exists. It also keeps a
404 404 # list of copied files so that the largefiles can be copied and the
405 405 # dirstate updated.
406 406 def overridecopy(orig, ui, repo, pats, opts, rename=False):
407 407 # doesn't remove largefile on rename
408 408 if len(pats) < 2:
409 409 # this isn't legal, let the original function deal with it
410 410 return orig(ui, repo, pats, opts, rename)
411 411
412 412 def makestandin(relpath):
413 413 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
414 414 return os.path.join(repo.wjoin(lfutil.standin(path)))
415 415
416 416 fullpats = scmutil.expandpats(pats)
417 417 dest = fullpats[-1]
418 418
419 419 if os.path.isdir(dest):
420 420 if not os.path.isdir(makestandin(dest)):
421 421 os.makedirs(makestandin(dest))
422 422 # This could copy both lfiles and normal files in one command,
423 423 # but we don't want to do that. First replace their matcher to
424 424 # only match normal files and run it, then replace it to just
425 425 # match largefiles and run it again.
426 426 nonormalfiles = False
427 427 nolfiles = False
428 428 try:
429 429 try:
430 430 installnormalfilesmatchfn(repo[None].manifest())
431 431 result = orig(ui, repo, pats, opts, rename)
432 432 except util.Abort, e:
433 433 if str(e) != 'no files to copy':
434 434 raise e
435 435 else:
436 436 nonormalfiles = True
437 437 result = 0
438 438 finally:
439 439 restorematchfn()
440 440
441 441 # The first rename can cause our current working directory to be removed.
442 442 # In that case there is nothing left to copy/rename so just quit.
443 443 try:
444 444 repo.getcwd()
445 445 except OSError:
446 446 return result
447 447
448 448 try:
449 449 try:
450 450 # When we call orig below it creates the standins but we don't add
451 451 # them to the dir state until later so lock during that time.
452 452 wlock = repo.wlock()
453 453
454 454 manifest = repo[None].manifest()
455 455 oldmatch = None # for the closure
456 456 def overridematch(ctx, pats=[], opts={}, globbed=False,
457 457 default='relpath'):
458 458 newpats = []
459 459 # The patterns were previously mangled to add the standin
460 460 # directory; we need to remove that now
461 461 for pat in pats:
462 462 if match_.patkind(pat) is None and lfutil.shortname in pat:
463 463 newpats.append(pat.replace(lfutil.shortname, ''))
464 464 else:
465 465 newpats.append(pat)
466 466 match = oldmatch(ctx, newpats, opts, globbed, default)
467 467 m = copy.copy(match)
468 468 lfile = lambda f: lfutil.standin(f) in manifest
469 469 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
470 470 m._fmap = set(m._files)
471 471 origmatchfn = m.matchfn
472 472 m.matchfn = lambda f: (lfutil.isstandin(f) and
473 473 (f in manifest) and
474 474 origmatchfn(lfutil.splitstandin(f)) or
475 475 None)
476 476 return m
477 477 oldmatch = installmatchfn(overridematch)
478 478 listpats = []
479 479 for pat in pats:
480 480 if match_.patkind(pat) is not None:
481 481 listpats.append(pat)
482 482 else:
483 483 listpats.append(makestandin(pat))
484 484
485 485 try:
486 486 origcopyfile = util.copyfile
487 487 copiedfiles = []
488 488 def overridecopyfile(src, dest):
489 489 if (lfutil.shortname in src and
490 490 dest.startswith(repo.wjoin(lfutil.shortname))):
491 491 destlfile = dest.replace(lfutil.shortname, '')
492 492 if not opts['force'] and os.path.exists(destlfile):
493 493 raise IOError('',
494 494 _('destination largefile already exists'))
495 495 copiedfiles.append((src, dest))
496 496 origcopyfile(src, dest)
497 497
498 498 util.copyfile = overridecopyfile
499 499 result += orig(ui, repo, listpats, opts, rename)
500 500 finally:
501 501 util.copyfile = origcopyfile
502 502
503 503 lfdirstate = lfutil.openlfdirstate(ui, repo)
504 504 for (src, dest) in copiedfiles:
505 505 if (lfutil.shortname in src and
506 506 dest.startswith(repo.wjoin(lfutil.shortname))):
507 507 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
508 508 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
509 509 destlfiledir = os.path.dirname(destlfile) or '.'
510 510 if not os.path.isdir(destlfiledir):
511 511 os.makedirs(destlfiledir)
512 512 if rename:
513 513 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
514 514 lfdirstate.remove(srclfile)
515 515 else:
516 516 util.copyfile(srclfile, destlfile)
517 517 lfdirstate.add(destlfile)
518 518 lfdirstate.write()
519 519 except util.Abort, e:
520 520 if str(e) != 'no files to copy':
521 521 raise e
522 522 else:
523 523 nolfiles = True
524 524 finally:
525 525 restorematchfn()
526 526 wlock.release()
527 527
528 528 if nolfiles and nonormalfiles:
529 529 raise util.Abort(_('no files to copy'))
530 530
531 531 return result
532 532
533 533 # When the user calls revert, we have to be careful to not revert any
534 534 # changes to other largefiles accidentally. This means we have to keep
535 535 # track of the largefiles that are being reverted so we only pull down
536 536 # the necessary largefiles.
537 537 #
538 538 # Standins are only updated (to match the hash of largefiles) before
539 539 # commits. Update the standins then run the original revert, changing
540 540 # the matcher to hit standins instead of largefiles. Based on the
541 541 # resulting standins update the largefiles. Then return the standins
542 542 # to their proper state
543 543 def overriderevert(orig, ui, repo, *pats, **opts):
544 544 # Because we put the standins in a bad state (by updating them)
545 545 # and then return them to a correct state we need to lock to
546 546 # prevent others from changing them in their incorrect state.
547 547 wlock = repo.wlock()
548 548 try:
549 549 lfdirstate = lfutil.openlfdirstate(ui, repo)
550 550 (modified, added, removed, missing, unknown, ignored, clean) = \
551 551 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
552 552 for lfile in modified:
553 553 lfutil.updatestandin(repo, lfutil.standin(lfile))
554 554 for lfile in missing:
555 555 os.unlink(repo.wjoin(lfutil.standin(lfile)))
556 556
557 557 try:
558 558 ctx = repo[opts.get('rev')]
559 559 oldmatch = None # for the closure
560 560 def overridematch(ctx, pats=[], opts={}, globbed=False,
561 561 default='relpath'):
562 562 match = oldmatch(ctx, pats, opts, globbed, default)
563 563 m = copy.copy(match)
564 564 def tostandin(f):
565 565 if lfutil.standin(f) in ctx:
566 566 return lfutil.standin(f)
567 567 elif lfutil.standin(f) in repo[None]:
568 568 return None
569 569 return f
570 570 m._files = [tostandin(f) for f in m._files]
571 571 m._files = [f for f in m._files if f is not None]
572 572 m._fmap = set(m._files)
573 573 origmatchfn = m.matchfn
574 574 def matchfn(f):
575 575 if lfutil.isstandin(f):
576 576 # We need to keep track of what largefiles are being
577 577 # matched so we know which ones to update later --
578 578 # otherwise we accidentally revert changes to other
579 579 # largefiles. This is repo-specific, so duckpunch the
580 580 # repo object to keep the list of largefiles for us
581 581 # later.
582 582 if origmatchfn(lfutil.splitstandin(f)) and \
583 583 (f in repo[None] or f in ctx):
584 584 lfileslist = getattr(repo, '_lfilestoupdate', [])
585 585 lfileslist.append(lfutil.splitstandin(f))
586 586 repo._lfilestoupdate = lfileslist
587 587 return True
588 588 else:
589 589 return False
590 590 return origmatchfn(f)
591 591 m.matchfn = matchfn
592 592 return m
593 593 oldmatch = installmatchfn(overridematch)
594 594 scmutil.match
595 595 matches = overridematch(repo[None], pats, opts)
596 596 orig(ui, repo, *pats, **opts)
597 597 finally:
598 598 restorematchfn()
599 599 lfileslist = getattr(repo, '_lfilestoupdate', [])
600 600 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
601 601 printmessage=False)
602 602
603 603 # empty out the largefiles list so we start fresh next time
604 604 repo._lfilestoupdate = []
605 605 for lfile in modified:
606 606 if lfile in lfileslist:
607 607 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
608 608 in repo['.']:
609 609 lfutil.writestandin(repo, lfutil.standin(lfile),
610 610 repo['.'][lfile].data().strip(),
611 611 'x' in repo['.'][lfile].flags())
612 612 lfdirstate = lfutil.openlfdirstate(ui, repo)
613 613 for lfile in added:
614 614 standin = lfutil.standin(lfile)
615 615 if standin not in ctx and (standin in matches or opts.get('all')):
616 616 if lfile in lfdirstate:
617 617 lfdirstate.drop(lfile)
618 618 util.unlinkpath(repo.wjoin(standin))
619 619 lfdirstate.write()
620 620 finally:
621 621 wlock.release()
622 622
623 623 def hgupdate(orig, repo, node):
624 624 # Only call updatelfiles the standins that have changed to save time
625 625 oldstandins = lfutil.getstandinsstate(repo)
626 626 result = orig(repo, node)
627 627 newstandins = lfutil.getstandinsstate(repo)
628 628 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
629 629 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist, printmessage=True)
630 630 return result
631 631
632 632 def hgclean(orig, repo, node, show_stats=True):
633 633 result = orig(repo, node, show_stats)
634 634 lfcommands.updatelfiles(repo.ui, repo)
635 635 return result
636 636
637 637 def hgmerge(orig, repo, node, force=None, remind=True):
638 638 # Mark the repo as being in the middle of a merge, so that
639 639 # updatelfiles() will know that it needs to trust the standins in
640 640 # the working copy, not in the standins in the current node
641 641 repo._ismerging = True
642 642 try:
643 643 result = orig(repo, node, force, remind)
644 644 lfcommands.updatelfiles(repo.ui, repo)
645 645 finally:
646 646 repo._ismerging = False
647 647 return result
648 648
649 649 # When we rebase a repository with remotely changed largefiles, we need to
650 650 # take some extra care so that the largefiles are correctly updated in the
651 651 # working copy
652 652 def overridepull(orig, ui, repo, source=None, **opts):
653 653 if opts.get('rebase', False):
654 654 repo._isrebasing = True
655 655 try:
656 656 if opts.get('update'):
657 657 del opts['update']
658 658 ui.debug('--update and --rebase are not compatible, ignoring '
659 659 'the update flag\n')
660 660 del opts['rebase']
661 661 cmdutil.bailifchanged(repo)
662 662 revsprepull = len(repo)
663 663 origpostincoming = commands.postincoming
664 664 def _dummy(*args, **kwargs):
665 665 pass
666 666 commands.postincoming = _dummy
667 667 repo.lfpullsource = source
668 668 if not source:
669 669 source = 'default'
670 670 try:
671 671 result = commands.pull(ui, repo, source, **opts)
672 672 finally:
673 673 commands.postincoming = origpostincoming
674 674 revspostpull = len(repo)
675 675 if revspostpull > revsprepull:
676 676 result = result or rebase.rebase(ui, repo)
677 677 finally:
678 678 repo._isrebasing = False
679 679 else:
680 680 repo.lfpullsource = source
681 681 if not source:
682 682 source = 'default'
683 683 oldheads = lfutil.getcurrentheads(repo)
684 684 result = orig(ui, repo, source, **opts)
685 685 # If we do not have the new largefiles for any new heads we pulled, we
686 686 # will run into a problem later if we try to merge or rebase with one of
687 687 # these heads, so cache the largefiles now direclty into the system
688 688 # cache.
689 689 ui.status(_("caching new largefiles\n"))
690 690 numcached = 0
691 691 heads = lfutil.getcurrentheads(repo)
692 692 newheads = set(heads).difference(set(oldheads))
693 693 for head in newheads:
694 694 (cached, missing) = lfcommands.cachelfiles(ui, repo, head)
695 695 numcached += len(cached)
696 696 ui.status(_("%d largefiles cached\n") % numcached)
697 697 return result
698 698
699 699 def overriderebase(orig, ui, repo, **opts):
700 700 repo._isrebasing = True
701 701 try:
702 702 orig(ui, repo, **opts)
703 703 finally:
704 704 repo._isrebasing = False
705 705
706 706 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
707 707 prefix=None, mtime=None, subrepos=None):
708 708 # No need to lock because we are only reading history and
709 709 # largefile caches, neither of which are modified.
710 710 lfcommands.cachelfiles(repo.ui, repo, node)
711 711
712 712 if kind not in archival.archivers:
713 713 raise util.Abort(_("unknown archive type '%s'") % kind)
714 714
715 715 ctx = repo[node]
716 716
717 717 if kind == 'files':
718 718 if prefix:
719 719 raise util.Abort(
720 720 _('cannot give prefix when archiving to files'))
721 721 else:
722 722 prefix = archival.tidyprefix(dest, kind, prefix)
723 723
724 724 def write(name, mode, islink, getdata):
725 725 if matchfn and not matchfn(name):
726 726 return
727 727 data = getdata()
728 728 if decode:
729 729 data = repo.wwritedata(name, data)
730 730 archiver.addfile(prefix + name, mode, islink, data)
731 731
732 732 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
733 733
734 734 if repo.ui.configbool("ui", "archivemeta", True):
735 735 def metadata():
736 736 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
737 737 hex(repo.changelog.node(0)), hex(node), ctx.branch())
738 738
739 739 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
740 740 if repo.tagtype(t) == 'global')
741 741 if not tags:
742 742 repo.ui.pushbuffer()
743 743 opts = {'template': '{latesttag}\n{latesttagdistance}',
744 744 'style': '', 'patch': None, 'git': None}
745 745 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
746 746 ltags, dist = repo.ui.popbuffer().split('\n')
747 747 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
748 748 tags += 'latesttagdistance: %s\n' % dist
749 749
750 750 return base + tags
751 751
752 752 write('.hg_archival.txt', 0644, False, metadata)
753 753
754 754 for f in ctx:
755 755 ff = ctx.flags(f)
756 756 getdata = ctx[f].data
757 757 if lfutil.isstandin(f):
758 758 path = lfutil.findfile(repo, getdata().strip())
759 759 if path is None:
760 760 raise util.Abort(
761 761 _('largefile %s not found in repo store or system cache')
762 762 % lfutil.splitstandin(f))
763 763 f = lfutil.splitstandin(f)
764 764
765 765 def getdatafn():
766 766 fd = None
767 767 try:
768 768 fd = open(path, 'rb')
769 769 return fd.read()
770 770 finally:
771 771 if fd:
772 772 fd.close()
773 773
774 774 getdata = getdatafn
775 775 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
776 776
777 777 if subrepos:
778 778 for subpath in ctx.substate:
779 779 sub = ctx.sub(subpath)
780 780 sub.archive(repo.ui, archiver, prefix)
781 781
782 782 archiver.done()
783 783
784 def hgsubrepoarchive(orig, repo, ui, archiver, prefix):
785 rev = repo._state[1]
786 ctx = repo._repo[rev]
787
788 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
789
790 def write(name, mode, islink, getdata):
791 if lfutil.isstandin(name):
792 return
793 data = getdata()
794
795 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
796
797 for f in ctx:
798 ff = ctx.flags(f)
799 getdata = ctx[f].data
800 if lfutil.isstandin(f):
801 path = lfutil.findfile(repo._repo, getdata().strip())
802 if path is None:
803 raise util.Abort(
804 _('largefile %s not found in repo store or system cache')
805 % lfutil.splitstandin(f))
806 f = lfutil.splitstandin(f)
807
808 def getdatafn():
809 fd = None
810 try:
811 fd = open(os.path.join(prefix, path), 'rb')
812 return fd.read()
813 finally:
814 if fd:
815 fd.close()
816
817 getdata = getdatafn
818
819 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
820
821 for subpath in ctx.substate:
822 sub = ctx.sub(subpath)
823 sub.archive(repo.ui, archiver, prefix)
824
784 825 # If a largefile is modified, the change is not reflected in its
785 826 # standin until a commit. cmdutil.bailifchanged() raises an exception
786 827 # if the repo has uncommitted changes. Wrap it to also check if
787 828 # largefiles were changed. This is used by bisect and backout.
788 829 def overridebailifchanged(orig, repo):
789 830 orig(repo)
790 831 repo.lfstatus = True
791 832 modified, added, removed, deleted = repo.status()[:4]
792 833 repo.lfstatus = False
793 834 if modified or added or removed or deleted:
794 835 raise util.Abort(_('outstanding uncommitted changes'))
795 836
796 837 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
797 838 def overridefetch(orig, ui, repo, *pats, **opts):
798 839 repo.lfstatus = True
799 840 modified, added, removed, deleted = repo.status()[:4]
800 841 repo.lfstatus = False
801 842 if modified or added or removed or deleted:
802 843 raise util.Abort(_('outstanding uncommitted changes'))
803 844 return orig(ui, repo, *pats, **opts)
804 845
805 846 def overrideforget(orig, ui, repo, *pats, **opts):
806 847 installnormalfilesmatchfn(repo[None].manifest())
807 848 orig(ui, repo, *pats, **opts)
808 849 restorematchfn()
809 850 m = scmutil.match(repo[None], pats, opts)
810 851
811 852 try:
812 853 repo.lfstatus = True
813 854 s = repo.status(match=m, clean=True)
814 855 finally:
815 856 repo.lfstatus = False
816 857 forget = sorted(s[0] + s[1] + s[3] + s[6])
817 858 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
818 859
819 860 for f in forget:
820 861 if lfutil.standin(f) not in repo.dirstate and not \
821 862 os.path.isdir(m.rel(lfutil.standin(f))):
822 863 ui.warn(_('not removing %s: file is already untracked\n')
823 864 % m.rel(f))
824 865
825 866 for f in forget:
826 867 if ui.verbose or not m.exact(f):
827 868 ui.status(_('removing %s\n') % m.rel(f))
828 869
829 870 # Need to lock because standin files are deleted then removed from the
830 871 # repository and we could race inbetween.
831 872 wlock = repo.wlock()
832 873 try:
833 874 lfdirstate = lfutil.openlfdirstate(ui, repo)
834 875 for f in forget:
835 876 if lfdirstate[f] == 'a':
836 877 lfdirstate.drop(f)
837 878 else:
838 879 lfdirstate.remove(f)
839 880 lfdirstate.write()
840 881 lfutil.reporemove(repo, [lfutil.standin(f) for f in forget],
841 882 unlink=True)
842 883 finally:
843 884 wlock.release()
844 885
845 886 def getoutgoinglfiles(ui, repo, dest=None, **opts):
846 887 dest = ui.expandpath(dest or 'default-push', dest or 'default')
847 888 dest, branches = hg.parseurl(dest, opts.get('branch'))
848 889 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
849 890 if revs:
850 891 revs = [repo.lookup(rev) for rev in revs]
851 892
852 893 remoteui = hg.remoteui
853 894
854 895 try:
855 896 remote = hg.repository(remoteui(repo, opts), dest)
856 897 except error.RepoError:
857 898 return None
858 899 o = lfutil.findoutgoing(repo, remote, False)
859 900 if not o:
860 901 return None
861 902 o = repo.changelog.nodesbetween(o, revs)[0]
862 903 if opts.get('newest_first'):
863 904 o.reverse()
864 905
865 906 toupload = set()
866 907 for n in o:
867 908 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
868 909 ctx = repo[n]
869 910 files = set(ctx.files())
870 911 if len(parents) == 2:
871 912 mc = ctx.manifest()
872 913 mp1 = ctx.parents()[0].manifest()
873 914 mp2 = ctx.parents()[1].manifest()
874 915 for f in mp1:
875 916 if f not in mc:
876 917 files.add(f)
877 918 for f in mp2:
878 919 if f not in mc:
879 920 files.add(f)
880 921 for f in mc:
881 922 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
882 923 files.add(f)
883 924 toupload = toupload.union(
884 925 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
885 926 return toupload
886 927
887 928 def overrideoutgoing(orig, ui, repo, dest=None, **opts):
888 929 orig(ui, repo, dest, **opts)
889 930
890 931 if opts.pop('large', None):
891 932 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
892 933 if toupload is None:
893 934 ui.status(_('largefiles: No remote repo\n'))
894 935 else:
895 936 ui.status(_('largefiles to upload:\n'))
896 937 for file in toupload:
897 938 ui.status(lfutil.splitstandin(file) + '\n')
898 939 ui.status('\n')
899 940
900 941 def overridesummary(orig, ui, repo, *pats, **opts):
901 942 try:
902 943 repo.lfstatus = True
903 944 orig(ui, repo, *pats, **opts)
904 945 finally:
905 946 repo.lfstatus = False
906 947
907 948 if opts.pop('large', None):
908 949 toupload = getoutgoinglfiles(ui, repo, None, **opts)
909 950 if toupload is None:
910 951 ui.status(_('largefiles: No remote repo\n'))
911 952 else:
912 953 ui.status(_('largefiles: %d to upload\n') % len(toupload))
913 954
914 955 def overrideaddremove(orig, ui, repo, *pats, **opts):
915 956 # Get the list of missing largefiles so we can remove them
916 957 lfdirstate = lfutil.openlfdirstate(ui, repo)
917 958 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
918 959 False, False)
919 960 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
920 961
921 962 # Call into the normal remove code, but the removing of the standin, we want
922 963 # to have handled by original addremove. Monkey patching here makes sure
923 964 # we don't remove the standin in the largefiles code, preventing a very
924 965 # confused state later.
925 966 if missing:
926 967 repo._isaddremove = True
927 968 removelargefiles(ui, repo, *missing, **opts)
928 969 repo._isaddremove = False
929 970 # Call into the normal add code, and any files that *should* be added as
930 971 # largefiles will be
931 972 addlargefiles(ui, repo, *pats, **opts)
932 973 # Now that we've handled largefiles, hand off to the original addremove
933 974 # function to take care of the rest. Make sure it doesn't do anything with
934 975 # largefiles by installing a matcher that will ignore them.
935 976 installnormalfilesmatchfn(repo[None].manifest())
936 977 result = orig(ui, repo, *pats, **opts)
937 978 restorematchfn()
938 979 return result
939 980
940 981 # Calling purge with --all will cause the largefiles to be deleted.
941 982 # Override repo.status to prevent this from happening.
942 983 def overridepurge(orig, ui, repo, *dirs, **opts):
943 984 oldstatus = repo.status
944 985 def overridestatus(node1='.', node2=None, match=None, ignored=False,
945 986 clean=False, unknown=False, listsubrepos=False):
946 987 r = oldstatus(node1, node2, match, ignored, clean, unknown,
947 988 listsubrepos)
948 989 lfdirstate = lfutil.openlfdirstate(ui, repo)
949 990 modified, added, removed, deleted, unknown, ignored, clean = r
950 991 unknown = [f for f in unknown if lfdirstate[f] == '?']
951 992 ignored = [f for f in ignored if lfdirstate[f] == '?']
952 993 return modified, added, removed, deleted, unknown, ignored, clean
953 994 repo.status = overridestatus
954 995 orig(ui, repo, *dirs, **opts)
955 996 repo.status = oldstatus
956 997
957 998 def overriderollback(orig, ui, repo, **opts):
958 999 result = orig(ui, repo, **opts)
959 1000 merge.update(repo, node=None, branchmerge=False, force=True,
960 1001 partial=lfutil.isstandin)
961 1002 wlock = repo.wlock()
962 1003 try:
963 1004 lfdirstate = lfutil.openlfdirstate(ui, repo)
964 1005 lfiles = lfutil.listlfiles(repo)
965 1006 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
966 1007 for file in lfiles:
967 1008 if file in oldlfiles:
968 1009 lfdirstate.normallookup(file)
969 1010 else:
970 1011 lfdirstate.add(file)
971 1012 lfdirstate.write()
972 1013 finally:
973 1014 wlock.release()
974 1015 return result
975 1016
976 1017 def overridetransplant(orig, ui, repo, *revs, **opts):
977 1018 try:
978 1019 oldstandins = lfutil.getstandinsstate(repo)
979 1020 repo._istransplanting = True
980 1021 result = orig(ui, repo, *revs, **opts)
981 1022 newstandins = lfutil.getstandinsstate(repo)
982 1023 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
983 1024 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
984 1025 printmessage=True)
985 1026 finally:
986 1027 repo._istransplanting = False
987 1028 return result
988 1029
989 1030 def overridecat(orig, ui, repo, file1, *pats, **opts):
990 1031 rev = opts.get('rev')
991 1032 if not lfutil.standin(file1) in repo[rev]:
992 1033 result = orig(ui, repo, file1, *pats, **opts)
993 1034 return result
994 1035 return lfcommands.catlfile(repo, file1, opts.get('rev'), opts.get('output'))
@@ -1,157 +1,158 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 '''setup for largefiles extension: uisetup'''
10 10
11 11 from mercurial import archival, cmdutil, commands, extensions, filemerge, hg, \
12 12 httprepo, localrepo, merge, sshrepo, sshserver, wireproto
13 13 from mercurial.i18n import _
14 14 from mercurial.hgweb import hgweb_mod, protocol, webcommands
15 15 from mercurial.subrepo import hgsubrepo
16 16
17 17 import overrides
18 18 import proto
19 19
20 20 def uisetup(ui):
21 21 # Disable auto-status for some commands which assume that all
22 22 # files in the result are under Mercurial's control
23 23
24 24 entry = extensions.wrapcommand(commands.table, 'add',
25 25 overrides.overrideadd)
26 26 addopt = [('', 'large', None, _('add as largefile')),
27 27 ('', 'normal', None, _('add as normal file')),
28 28 ('', 'lfsize', '', _('add all files above this size '
29 29 '(in megabytes) as largefiles '
30 30 '(default: 10)'))]
31 31 entry[1].extend(addopt)
32 32
33 33 entry = extensions.wrapcommand(commands.table, 'addremove',
34 34 overrides.overrideaddremove)
35 35 entry = extensions.wrapcommand(commands.table, 'remove',
36 36 overrides.overrideremove)
37 37 entry = extensions.wrapcommand(commands.table, 'forget',
38 38 overrides.overrideforget)
39 39
40 40 # Subrepos call status function
41 41 entry = extensions.wrapcommand(commands.table, 'status',
42 42 overrides.overridestatus)
43 43 entry = extensions.wrapfunction(hgsubrepo, 'status',
44 44 overrides.overridestatusfn)
45 45
46 46 entry = extensions.wrapcommand(commands.table, 'log',
47 47 overrides.overridelog)
48 48 entry = extensions.wrapcommand(commands.table, 'rollback',
49 49 overrides.overriderollback)
50 50 entry = extensions.wrapcommand(commands.table, 'verify',
51 51 overrides.overrideverify)
52 52
53 53 verifyopt = [('', 'large', None, _('verify largefiles')),
54 54 ('', 'lfa', None,
55 55 _('verify all revisions of largefiles not just current')),
56 56 ('', 'lfc', None,
57 57 _('verify largefile contents not just existence'))]
58 58 entry[1].extend(verifyopt)
59 59
60 60 entry = extensions.wrapcommand(commands.table, 'outgoing',
61 61 overrides.overrideoutgoing)
62 62 outgoingopt = [('', 'large', None, _('display outgoing largefiles'))]
63 63 entry[1].extend(outgoingopt)
64 64 entry = extensions.wrapcommand(commands.table, 'summary',
65 65 overrides.overridesummary)
66 66 summaryopt = [('', 'large', None, _('display outgoing largefiles'))]
67 67 entry[1].extend(summaryopt)
68 68
69 69 entry = extensions.wrapcommand(commands.table, 'update',
70 70 overrides.overrideupdate)
71 71 entry = extensions.wrapcommand(commands.table, 'pull',
72 72 overrides.overridepull)
73 73 entry = extensions.wrapcommand(commands.table, 'cat',
74 74 overrides.overridecat)
75 75 entry = extensions.wrapfunction(merge, '_checkunknownfile',
76 76 overrides.overridecheckunknownfile)
77 77 entry = extensions.wrapfunction(merge, 'manifestmerge',
78 78 overrides.overridemanifestmerge)
79 79 entry = extensions.wrapfunction(filemerge, 'filemerge',
80 80 overrides.overridefilemerge)
81 81 entry = extensions.wrapfunction(cmdutil, 'copy',
82 82 overrides.overridecopy)
83 83
84 84 # Summary calls dirty on the subrepos
85 85 entry = extensions.wrapfunction(hgsubrepo, 'dirty',
86 86 overrides.overridedirty)
87 87
88 88 # Backout calls revert so we need to override both the command and the
89 89 # function
90 90 entry = extensions.wrapcommand(commands.table, 'revert',
91 91 overrides.overriderevert)
92 92 entry = extensions.wrapfunction(commands, 'revert',
93 93 overrides.overriderevert)
94 94
95 95 # clone uses hg._update instead of hg.update even though they are the
96 96 # same function... so wrap both of them)
97 97 extensions.wrapfunction(hg, 'update', overrides.hgupdate)
98 98 extensions.wrapfunction(hg, '_update', overrides.hgupdate)
99 99 extensions.wrapfunction(hg, 'clean', overrides.hgclean)
100 100 extensions.wrapfunction(hg, 'merge', overrides.hgmerge)
101 101
102 102 extensions.wrapfunction(archival, 'archive', overrides.overridearchive)
103 extensions.wrapfunction(hgsubrepo, 'archive', overrides.hgsubrepoarchive)
103 104 extensions.wrapfunction(cmdutil, 'bailifchanged',
104 105 overrides.overridebailifchanged)
105 106
106 107 # create the new wireproto commands ...
107 108 wireproto.commands['putlfile'] = (proto.putlfile, 'sha')
108 109 wireproto.commands['getlfile'] = (proto.getlfile, 'sha')
109 110 wireproto.commands['statlfile'] = (proto.statlfile, 'sha')
110 111
111 112 # ... and wrap some existing ones
112 113 wireproto.commands['capabilities'] = (proto.capabilities, '')
113 114 wireproto.commands['heads'] = (proto.heads, '')
114 115 wireproto.commands['lheads'] = (wireproto.heads, '')
115 116
116 117 # make putlfile behave the same as push and {get,stat}lfile behave
117 118 # the same as pull w.r.t. permissions checks
118 119 hgweb_mod.perms['putlfile'] = 'push'
119 120 hgweb_mod.perms['getlfile'] = 'pull'
120 121 hgweb_mod.perms['statlfile'] = 'pull'
121 122
122 123 extensions.wrapfunction(webcommands, 'decodepath', overrides.decodepath)
123 124
124 125 # the hello wireproto command uses wireproto.capabilities, so it won't see
125 126 # our largefiles capability unless we replace the actual function as well.
126 127 proto.capabilitiesorig = wireproto.capabilities
127 128 wireproto.capabilities = proto.capabilities
128 129
129 130 # these let us reject non-largefiles clients and make them display
130 131 # our error messages
131 132 protocol.webproto.refuseclient = proto.webprotorefuseclient
132 133 sshserver.sshserver.refuseclient = proto.sshprotorefuseclient
133 134
134 135 # can't do this in reposetup because it needs to have happened before
135 136 # wirerepo.__init__ is called
136 137 proto.ssholdcallstream = sshrepo.sshrepository._callstream
137 138 proto.httpoldcallstream = httprepo.httprepository._callstream
138 139 sshrepo.sshrepository._callstream = proto.sshrepocallstream
139 140 httprepo.httprepository._callstream = proto.httprepocallstream
140 141
141 142 # don't die on seeing a repo with the largefiles requirement
142 143 localrepo.localrepository.supported |= set(['largefiles'])
143 144
144 145 # override some extensions' stuff as well
145 146 for name, module in extensions.extensions():
146 147 if name == 'fetch':
147 148 extensions.wrapcommand(getattr(module, 'cmdtable'), 'fetch',
148 149 overrides.overridefetch)
149 150 if name == 'purge':
150 151 extensions.wrapcommand(getattr(module, 'cmdtable'), 'purge',
151 152 overrides.overridepurge)
152 153 if name == 'rebase':
153 154 extensions.wrapcommand(getattr(module, 'cmdtable'), 'rebase',
154 155 overrides.overriderebase)
155 156 if name == 'transplant':
156 157 extensions.wrapcommand(getattr(module, 'cmdtable'), 'transplant',
157 158 overrides.overridetransplant)
@@ -1,1099 +1,1129 b''
1 1 $ "$TESTDIR/hghave" symlink unix-permissions serve || exit 80
2 2 $ USERCACHE=`pwd`/cache; export USERCACHE
3 3 $ mkdir -p ${USERCACHE}
4 4 $ cat >> $HGRCPATH <<EOF
5 5 > [extensions]
6 6 > largefiles=
7 7 > purge=
8 8 > rebase=
9 9 > transplant=
10 10 > [phases]
11 11 > publish=False
12 12 > [largefiles]
13 13 > minsize=2
14 14 > patterns=glob:**.dat
15 15 > usercache=${USERCACHE}
16 16 > [hooks]
17 17 > precommit=echo "Invoking status precommit hook"; hg status
18 18 > EOF
19 19
20 20 Create the repo with a couple of revisions of both large and normal
21 21 files, testing that status correctly shows largefiles and that summary output
22 22 is correct.
23 23
24 24 $ hg init a
25 25 $ cd a
26 26 $ mkdir sub
27 27 $ echo normal1 > normal1
28 28 $ echo normal2 > sub/normal2
29 29 $ echo large1 > large1
30 30 $ echo large2 > sub/large2
31 31 $ hg add normal1 sub/normal2
32 32 $ hg add --large large1 sub/large2
33 33 $ hg commit -m "add files"
34 34 Invoking status precommit hook
35 35 A large1
36 36 A normal1
37 37 A sub/large2
38 38 A sub/normal2
39 39 $ echo normal11 > normal1
40 40 $ echo normal22 > sub/normal2
41 41 $ echo large11 > large1
42 42 $ echo large22 > sub/large2
43 43 $ hg commit -m "edit files"
44 44 Invoking status precommit hook
45 45 M large1
46 46 M normal1
47 47 M sub/large2
48 48 M sub/normal2
49 49 $ hg sum --large
50 50 parent: 1:ce8896473775 tip
51 51 edit files
52 52 branch: default
53 53 commit: (clean)
54 54 update: (current)
55 55 largefiles: No remote repo
56 56
57 57 Commit preserved largefile contents.
58 58
59 59 $ cat normal1
60 60 normal11
61 61 $ cat large1
62 62 large11
63 63 $ cat sub/normal2
64 64 normal22
65 65 $ cat sub/large2
66 66 large22
67 67
68 68 Remove both largefiles and normal files.
69 69
70 70 $ hg remove normal1 large1
71 71 $ hg status large1
72 72 R large1
73 73 $ hg commit -m "remove files"
74 74 Invoking status precommit hook
75 75 R large1
76 76 R normal1
77 77 $ ls
78 78 sub
79 79 $ echo "testlargefile" > large1-test
80 80 $ hg add --large large1-test
81 81 $ hg st
82 82 A large1-test
83 83 $ hg rm large1-test
84 84 not removing large1-test: file has been marked for add (use forget to undo)
85 85 $ hg st
86 86 A large1-test
87 87 $ hg forget large1-test
88 88 $ hg st
89 89 ? large1-test
90 90 $ rm large1-test
91 91
92 92 Copy both largefiles and normal files (testing that status output is correct).
93 93
94 94 $ hg cp sub/normal2 normal1
95 95 $ hg cp sub/large2 large1
96 96 $ hg commit -m "copy files"
97 97 Invoking status precommit hook
98 98 A large1
99 99 A normal1
100 100 $ cat normal1
101 101 normal22
102 102 $ cat large1
103 103 large22
104 104
105 105 Test moving largefiles and verify that normal files are also unaffected.
106 106
107 107 $ hg mv normal1 normal3
108 108 $ hg mv large1 large3
109 109 $ hg mv sub/normal2 sub/normal4
110 110 $ hg mv sub/large2 sub/large4
111 111 $ hg commit -m "move files"
112 112 Invoking status precommit hook
113 113 A large3
114 114 A normal3
115 115 A sub/large4
116 116 A sub/normal4
117 117 R large1
118 118 R normal1
119 119 R sub/large2
120 120 R sub/normal2
121 121 $ cat normal3
122 122 normal22
123 123 $ cat large3
124 124 large22
125 125 $ cat sub/normal4
126 126 normal22
127 127 $ cat sub/large4
128 128 large22
129 129
130 130 Test display of largefiles in hgweb
131 131
132 132 $ hg serve -d -p $HGPORT --pid-file ../hg.pid
133 133 $ cat ../hg.pid >> $DAEMON_PIDS
134 134 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/tip/?style=raw'
135 135 200 Script output follows
136 136
137 137
138 138 drwxr-xr-x sub
139 139 -rw-r--r-- 41 large3
140 140 -rw-r--r-- 9 normal3
141 141
142 142
143 143 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/tip/sub/?style=raw'
144 144 200 Script output follows
145 145
146 146
147 147 -rw-r--r-- 41 large4
148 148 -rw-r--r-- 9 normal4
149 149
150 150
151 151 $ "$TESTDIR/killdaemons.py"
152 152
153 153 Test archiving the various revisions. These hit corner cases known with
154 154 archiving.
155 155
156 156 $ hg archive -r 0 ../archive0
157 157 $ hg archive -r 1 ../archive1
158 158 $ hg archive -r 2 ../archive2
159 159 $ hg archive -r 3 ../archive3
160 160 $ hg archive -r 4 ../archive4
161 161 $ cd ../archive0
162 162 $ cat normal1
163 163 normal1
164 164 $ cat large1
165 165 large1
166 166 $ cat sub/normal2
167 167 normal2
168 168 $ cat sub/large2
169 169 large2
170 170 $ cd ../archive1
171 171 $ cat normal1
172 172 normal11
173 173 $ cat large1
174 174 large11
175 175 $ cat sub/normal2
176 176 normal22
177 177 $ cat sub/large2
178 178 large22
179 179 $ cd ../archive2
180 180 $ ls
181 181 sub
182 182 $ cat sub/normal2
183 183 normal22
184 184 $ cat sub/large2
185 185 large22
186 186 $ cd ../archive3
187 187 $ cat normal1
188 188 normal22
189 189 $ cat large1
190 190 large22
191 191 $ cat sub/normal2
192 192 normal22
193 193 $ cat sub/large2
194 194 large22
195 195 $ cd ../archive4
196 196 $ cat normal3
197 197 normal22
198 198 $ cat large3
199 199 large22
200 200 $ cat sub/normal4
201 201 normal22
202 202 $ cat sub/large4
203 203 large22
204 204
205 205 Commit corner case: specify files to commit.
206 206
207 207 $ cd ../a
208 208 $ echo normal3 > normal3
209 209 $ echo large3 > large3
210 210 $ echo normal4 > sub/normal4
211 211 $ echo large4 > sub/large4
212 212 $ hg commit normal3 large3 sub/normal4 sub/large4 -m "edit files again"
213 213 Invoking status precommit hook
214 214 M large3
215 215 M normal3
216 216 M sub/large4
217 217 M sub/normal4
218 218 $ cat normal3
219 219 normal3
220 220 $ cat large3
221 221 large3
222 222 $ cat sub/normal4
223 223 normal4
224 224 $ cat sub/large4
225 225 large4
226 226
227 227 One more commit corner case: commit from a subdirectory.
228 228
229 229 $ cd ../a
230 230 $ echo normal33 > normal3
231 231 $ echo large33 > large3
232 232 $ echo normal44 > sub/normal4
233 233 $ echo large44 > sub/large4
234 234 $ cd sub
235 235 $ hg commit -m "edit files yet again"
236 236 Invoking status precommit hook
237 237 M large3
238 238 M normal3
239 239 M sub/large4
240 240 M sub/normal4
241 241 $ cat ../normal3
242 242 normal33
243 243 $ cat ../large3
244 244 large33
245 245 $ cat normal4
246 246 normal44
247 247 $ cat large4
248 248 large44
249 249
250 250 Committing standins is not allowed.
251 251
252 252 $ cd ..
253 253 $ echo large3 > large3
254 254 $ hg commit .hglf/large3 -m "try to commit standin"
255 255 abort: file ".hglf/large3" is a largefile standin
256 256 (commit the largefile itself instead)
257 257 [255]
258 258
259 259 Corner cases for adding largefiles.
260 260
261 261 $ echo large5 > large5
262 262 $ hg add --large large5
263 263 $ hg add --large large5
264 264 large5 already a largefile
265 265 $ mkdir sub2
266 266 $ echo large6 > sub2/large6
267 267 $ echo large7 > sub2/large7
268 268 $ hg add --large sub2
269 269 adding sub2/large6 as a largefile (glob)
270 270 adding sub2/large7 as a largefile (glob)
271 271 $ hg st
272 272 M large3
273 273 A large5
274 274 A sub2/large6
275 275 A sub2/large7
276 276
277 277 Test "hg status" with combination of 'file pattern' and 'directory
278 278 pattern' for largefiles:
279 279
280 280 $ hg status sub2/large6 sub2
281 281 A sub2/large6
282 282 A sub2/large7
283 283
284 284 Config settings (pattern **.dat, minsize 2 MB) are respected.
285 285
286 286 $ echo testdata > test.dat
287 287 $ dd bs=1k count=2k if=/dev/zero of=reallylarge > /dev/null 2> /dev/null
288 288 $ hg add
289 289 adding reallylarge as a largefile
290 290 adding test.dat as a largefile
291 291
292 292 Test that minsize and --lfsize handle float values;
293 293 also tests that --lfsize overrides largefiles.minsize.
294 294 (0.250 MB = 256 kB = 262144 B)
295 295
296 296 $ dd if=/dev/zero of=ratherlarge bs=1024 count=256 > /dev/null 2> /dev/null
297 297 $ dd if=/dev/zero of=medium bs=1024 count=128 > /dev/null 2> /dev/null
298 298 $ hg --config largefiles.minsize=.25 add
299 299 adding ratherlarge as a largefile
300 300 adding medium
301 301 $ hg forget medium
302 302 $ hg --config largefiles.minsize=.25 add --lfsize=.125
303 303 adding medium as a largefile
304 304 $ dd if=/dev/zero of=notlarge bs=1024 count=127 > /dev/null 2> /dev/null
305 305 $ hg --config largefiles.minsize=.25 add --lfsize=.125
306 306 adding notlarge
307 307 $ hg forget notlarge
308 308
309 309 Test forget on largefiles.
310 310
311 311 $ hg forget large3 large5 test.dat reallylarge ratherlarge medium
312 312 $ hg commit -m "add/edit more largefiles"
313 313 Invoking status precommit hook
314 314 A sub2/large6
315 315 A sub2/large7
316 316 R large3
317 317 ? large5
318 318 ? medium
319 319 ? notlarge
320 320 ? ratherlarge
321 321 ? reallylarge
322 322 ? test.dat
323 323 $ hg st
324 324 ? large3
325 325 ? large5
326 326 ? medium
327 327 ? notlarge
328 328 ? ratherlarge
329 329 ? reallylarge
330 330 ? test.dat
331 331
332 332 Purge with largefiles: verify that largefiles are still in the working
333 333 dir after a purge.
334 334
335 335 $ hg purge --all
336 336 $ cat sub/large4
337 337 large44
338 338 $ cat sub2/large6
339 339 large6
340 340 $ cat sub2/large7
341 341 large7
342 342
343 343 Test addremove: verify that files that should be added as largfiles are added as
344 344 such and that already-existing largfiles are not added as normal files by
345 345 accident.
346 346
347 347 $ rm normal3
348 348 $ rm sub/large4
349 349 $ echo "testing addremove with patterns" > testaddremove.dat
350 350 $ echo "normaladdremove" > normaladdremove
351 351 $ hg addremove
352 352 removing sub/large4
353 353 adding testaddremove.dat as a largefile
354 354 removing normal3
355 355 adding normaladdremove
356 356
357 357 Clone a largefiles repo.
358 358
359 359 $ hg clone . ../b
360 360 updating to branch default
361 361 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
362 362 getting changed largefiles
363 363 3 largefiles updated, 0 removed
364 364 $ cd ../b
365 365 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
366 366 7:daea875e9014 add/edit more largefiles
367 367 6:4355d653f84f edit files yet again
368 368 5:9d5af5072dbd edit files again
369 369 4:74c02385b94c move files
370 370 3:9e8fbc4bce62 copy files
371 371 2:51a0ae4d5864 remove files
372 372 1:ce8896473775 edit files
373 373 0:30d30fe6a5be add files
374 374 $ cat normal3
375 375 normal33
376 376 $ cat sub/normal4
377 377 normal44
378 378 $ cat sub/large4
379 379 large44
380 380 $ cat sub2/large6
381 381 large6
382 382 $ cat sub2/large7
383 383 large7
384 384 $ cd ..
385 385 $ hg clone a -r 3 c
386 386 adding changesets
387 387 adding manifests
388 388 adding file changes
389 389 added 4 changesets with 10 changes to 4 files
390 390 updating to branch default
391 391 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
392 392 getting changed largefiles
393 393 2 largefiles updated, 0 removed
394 394 $ cd c
395 395 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
396 396 3:9e8fbc4bce62 copy files
397 397 2:51a0ae4d5864 remove files
398 398 1:ce8896473775 edit files
399 399 0:30d30fe6a5be add files
400 400 $ cat normal1
401 401 normal22
402 402 $ cat large1
403 403 large22
404 404 $ cat sub/normal2
405 405 normal22
406 406 $ cat sub/large2
407 407 large22
408 408
409 409 Old revisions of a clone have correct largefiles content (this also
410 410 tests update).
411 411
412 412 $ hg update -r 1
413 413 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
414 414 getting changed largefiles
415 415 1 largefiles updated, 0 removed
416 416 $ cat large1
417 417 large11
418 418 $ cat sub/large2
419 419 large22
420 420
421 421 Rebasing between two repositories does not revert largefiles to old
422 422 revisions (this was a very bad bug that took a lot of work to fix).
423 423
424 424 $ cd ..
425 425 $ hg clone a d
426 426 updating to branch default
427 427 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
428 428 getting changed largefiles
429 429 3 largefiles updated, 0 removed
430 430 $ cd b
431 431 $ echo large4-modified > sub/large4
432 432 $ echo normal3-modified > normal3
433 433 $ hg commit -m "modify normal file and largefile in repo b"
434 434 Invoking status precommit hook
435 435 M normal3
436 436 M sub/large4
437 437 $ cd ../d
438 438 $ echo large6-modified > sub2/large6
439 439 $ echo normal4-modified > sub/normal4
440 440 $ hg commit -m "modify normal file largefile in repo d"
441 441 Invoking status precommit hook
442 442 M sub/normal4
443 443 M sub2/large6
444 444 $ cd ..
445 445 $ hg clone d e
446 446 updating to branch default
447 447 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
448 448 getting changed largefiles
449 449 3 largefiles updated, 0 removed
450 450 $ cd d
451 451 $ hg pull --rebase ../b
452 452 pulling from ../b
453 453 searching for changes
454 454 adding changesets
455 455 adding manifests
456 456 adding file changes
457 457 added 1 changesets with 2 changes to 2 files (+1 heads)
458 458 Invoking status precommit hook
459 459 M sub/normal4
460 460 M sub2/large6
461 461 saved backup bundle to $TESTTMP/d/.hg/strip-backup/f574fb32bb45-backup.hg
462 462 nothing to rebase
463 463 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
464 464 9:598410d3eb9a modify normal file largefile in repo d
465 465 8:a381d2c8c80e modify normal file and largefile in repo b
466 466 7:daea875e9014 add/edit more largefiles
467 467 6:4355d653f84f edit files yet again
468 468 5:9d5af5072dbd edit files again
469 469 4:74c02385b94c move files
470 470 3:9e8fbc4bce62 copy files
471 471 2:51a0ae4d5864 remove files
472 472 1:ce8896473775 edit files
473 473 0:30d30fe6a5be add files
474 474 $ cat normal3
475 475 normal3-modified
476 476 $ cat sub/normal4
477 477 normal4-modified
478 478 $ cat sub/large4
479 479 large4-modified
480 480 $ cat sub2/large6
481 481 large6-modified
482 482 $ cat sub2/large7
483 483 large7
484 484 $ cd ../e
485 485 $ hg pull ../b
486 486 pulling from ../b
487 487 searching for changes
488 488 adding changesets
489 489 adding manifests
490 490 adding file changes
491 491 added 1 changesets with 2 changes to 2 files (+1 heads)
492 492 (run 'hg heads' to see heads, 'hg merge' to merge)
493 493 caching new largefiles
494 494 0 largefiles cached
495 495 $ hg rebase
496 496 Invoking status precommit hook
497 497 M sub/normal4
498 498 M sub2/large6
499 499 saved backup bundle to $TESTTMP/e/.hg/strip-backup/f574fb32bb45-backup.hg
500 500 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
501 501 9:598410d3eb9a modify normal file largefile in repo d
502 502 8:a381d2c8c80e modify normal file and largefile in repo b
503 503 7:daea875e9014 add/edit more largefiles
504 504 6:4355d653f84f edit files yet again
505 505 5:9d5af5072dbd edit files again
506 506 4:74c02385b94c move files
507 507 3:9e8fbc4bce62 copy files
508 508 2:51a0ae4d5864 remove files
509 509 1:ce8896473775 edit files
510 510 0:30d30fe6a5be add files
511 511 $ cat normal3
512 512 normal3-modified
513 513 $ cat sub/normal4
514 514 normal4-modified
515 515 $ cat sub/large4
516 516 large4-modified
517 517 $ cat sub2/large6
518 518 large6-modified
519 519 $ cat sub2/large7
520 520 large7
521 521
522 522 Rollback on largefiles.
523 523
524 524 $ echo large4-modified-again > sub/large4
525 525 $ hg commit -m "Modify large4 again"
526 526 Invoking status precommit hook
527 527 M sub/large4
528 528 $ hg rollback
529 529 repository tip rolled back to revision 9 (undo commit)
530 530 working directory now based on revision 9
531 531 $ hg st
532 532 M sub/large4
533 533 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
534 534 9:598410d3eb9a modify normal file largefile in repo d
535 535 8:a381d2c8c80e modify normal file and largefile in repo b
536 536 7:daea875e9014 add/edit more largefiles
537 537 6:4355d653f84f edit files yet again
538 538 5:9d5af5072dbd edit files again
539 539 4:74c02385b94c move files
540 540 3:9e8fbc4bce62 copy files
541 541 2:51a0ae4d5864 remove files
542 542 1:ce8896473775 edit files
543 543 0:30d30fe6a5be add files
544 544 $ cat sub/large4
545 545 large4-modified-again
546 546
547 547 "update --check" refuses to update with uncommitted changes.
548 548 $ hg update --check 8
549 549 abort: uncommitted local changes
550 550 [255]
551 551
552 552 "update --clean" leaves correct largefiles in working copy.
553 553
554 554 $ hg update --clean
555 555 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
556 556 getting changed largefiles
557 557 1 largefiles updated, 0 removed
558 558 $ cat normal3
559 559 normal3-modified
560 560 $ cat sub/normal4
561 561 normal4-modified
562 562 $ cat sub/large4
563 563 large4-modified
564 564 $ cat sub2/large6
565 565 large6-modified
566 566 $ cat sub2/large7
567 567 large7
568 568
569 569 Now "update check" is happy.
570 570 $ hg update --check 8
571 571 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
572 572 getting changed largefiles
573 573 1 largefiles updated, 0 removed
574 574 $ hg update --check
575 575 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
576 576 getting changed largefiles
577 577 1 largefiles updated, 0 removed
578 578
579 579 Test removing empty largefiles directories on update
580 580 $ test -d sub2 && echo "sub2 exists"
581 581 sub2 exists
582 582 $ hg update -q null
583 583 $ test -d sub2 && echo "error: sub2 should not exist anymore"
584 584 [1]
585 585 $ hg update -q
586 586
587 587 Test hg remove removes empty largefiles directories
588 588 $ test -d sub2 && echo "sub2 exists"
589 589 sub2 exists
590 590 $ hg remove sub2/*
591 591 $ test -d sub2 && echo "error: sub2 should not exist anymore"
592 592 [1]
593 593 $ hg revert sub2/large6 sub2/large7
594 594
595 595 "revert" works on largefiles (and normal files too).
596 596 $ echo hack3 >> normal3
597 597 $ echo hack4 >> sub/normal4
598 598 $ echo hack4 >> sub/large4
599 599 $ rm sub2/large6
600 600 $ hg revert sub2/large6
601 601 $ hg rm sub2/large6
602 602 $ echo new >> sub2/large8
603 603 $ hg add --large sub2/large8
604 604 # XXX we don't really want to report that we're reverting the standin;
605 605 # that's just an implementation detail. But I don't see an obvious fix. ;-(
606 606 $ hg revert sub
607 607 reverting .hglf/sub/large4 (glob)
608 608 reverting sub/normal4 (glob)
609 609 $ hg status
610 610 M normal3
611 611 A sub2/large8
612 612 R sub2/large6
613 613 ? sub/large4.orig
614 614 ? sub/normal4.orig
615 615 $ cat sub/normal4
616 616 normal4-modified
617 617 $ cat sub/large4
618 618 large4-modified
619 619 $ hg revert -a --no-backup
620 620 undeleting .hglf/sub2/large6 (glob)
621 621 forgetting .hglf/sub2/large8 (glob)
622 622 reverting normal3
623 623 $ hg status
624 624 ? sub/large4.orig
625 625 ? sub/normal4.orig
626 626 ? sub2/large8
627 627 $ cat normal3
628 628 normal3-modified
629 629 $ cat sub2/large6
630 630 large6-modified
631 631 $ rm sub/*.orig sub2/large8
632 632
633 633 revert some files to an older revision
634 634 $ hg revert --no-backup -r 8 sub2
635 635 reverting .hglf/sub2/large6 (glob)
636 636 $ cat sub2/large6
637 637 large6
638 638 $ hg revert --no-backup sub2
639 639 reverting .hglf/sub2/large6 (glob)
640 640 $ hg status
641 641
642 642 "verify --large" actually verifies largefiles
643 643
644 644 $ hg verify --large
645 645 checking changesets
646 646 checking manifests
647 647 crosschecking files in changesets and manifests
648 648 checking files
649 649 10 files, 10 changesets, 28 total revisions
650 650 searching 1 changesets for largefiles
651 651 verified existence of 3 revisions of 3 largefiles
652 652
653 653 Merging does not revert to old versions of largefiles and also check
654 654 that merging after having pulled from a non-default remote works
655 655 correctly.
656 656
657 657 $ cd ..
658 658 $ hg clone -r 7 e temp
659 659 adding changesets
660 660 adding manifests
661 661 adding file changes
662 662 added 8 changesets with 24 changes to 10 files
663 663 updating to branch default
664 664 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
665 665 getting changed largefiles
666 666 3 largefiles updated, 0 removed
667 667 $ hg clone temp f
668 668 updating to branch default
669 669 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
670 670 getting changed largefiles
671 671 3 largefiles updated, 0 removed
672 672 # Delete the largefiles in the largefiles system cache so that we have an
673 673 # opportunity to test that caching after a pull works.
674 674 $ rm ${USERCACHE}/*
675 675 $ cd f
676 676 $ echo "large4-merge-test" > sub/large4
677 677 $ hg commit -m "Modify large4 to test merge"
678 678 Invoking status precommit hook
679 679 M sub/large4
680 680 $ hg pull ../e
681 681 pulling from ../e
682 682 searching for changes
683 683 adding changesets
684 684 adding manifests
685 685 adding file changes
686 686 added 2 changesets with 4 changes to 4 files (+1 heads)
687 687 (run 'hg heads' to see heads, 'hg merge' to merge)
688 688 caching new largefiles
689 689 2 largefiles cached
690 690 $ hg merge
691 691 merging sub/large4
692 692 largefile sub/large4 has a merge conflict
693 693 keep (l)ocal or take (o)ther? l
694 694 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
695 695 (branch merge, don't forget to commit)
696 696 getting changed largefiles
697 697 1 largefiles updated, 0 removed
698 698 $ hg commit -m "Merge repos e and f"
699 699 Invoking status precommit hook
700 700 M normal3
701 701 M sub/normal4
702 702 M sub2/large6
703 703 $ cat normal3
704 704 normal3-modified
705 705 $ cat sub/normal4
706 706 normal4-modified
707 707 $ cat sub/large4
708 708 large4-merge-test
709 709 $ cat sub2/large6
710 710 large6-modified
711 711 $ cat sub2/large7
712 712 large7
713 713
714 714 Test status after merging with a branch that introduces a new largefile:
715 715
716 716 $ echo large > large
717 717 $ hg add --large large
718 718 $ hg commit -m 'add largefile'
719 719 Invoking status precommit hook
720 720 A large
721 721 $ hg update -q ".^"
722 722 $ echo change >> normal3
723 723 $ hg commit -m 'some change'
724 724 Invoking status precommit hook
725 725 M normal3
726 726 created new head
727 727 $ hg merge
728 728 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
729 729 (branch merge, don't forget to commit)
730 730 getting changed largefiles
731 731 1 largefiles updated, 0 removed
732 732 $ hg status
733 733 M large
734 734
735 735 Test that a normal file and a largefile with the same name and path cannot
736 736 coexist.
737 737
738 738 $ rm sub2/large7
739 739 $ echo "largeasnormal" > sub2/large7
740 740 $ hg add sub2/large7
741 741 sub2/large7 already a largefile
742 742
743 743 Test that transplanting a largefile change works correctly.
744 744
745 745 $ cd ..
746 746 $ hg clone -r 8 d g
747 747 adding changesets
748 748 adding manifests
749 749 adding file changes
750 750 added 9 changesets with 26 changes to 10 files
751 751 updating to branch default
752 752 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
753 753 getting changed largefiles
754 754 3 largefiles updated, 0 removed
755 755 $ cd g
756 756 $ hg transplant -s ../d 598410d3eb9a
757 757 searching for changes
758 758 searching for changes
759 759 adding changesets
760 760 adding manifests
761 761 adding file changes
762 762 added 1 changesets with 2 changes to 2 files
763 763 getting changed largefiles
764 764 1 largefiles updated, 0 removed
765 765 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
766 766 9:598410d3eb9a modify normal file largefile in repo d
767 767 8:a381d2c8c80e modify normal file and largefile in repo b
768 768 7:daea875e9014 add/edit more largefiles
769 769 6:4355d653f84f edit files yet again
770 770 5:9d5af5072dbd edit files again
771 771 4:74c02385b94c move files
772 772 3:9e8fbc4bce62 copy files
773 773 2:51a0ae4d5864 remove files
774 774 1:ce8896473775 edit files
775 775 0:30d30fe6a5be add files
776 776 $ cat normal3
777 777 normal3-modified
778 778 $ cat sub/normal4
779 779 normal4-modified
780 780 $ cat sub/large4
781 781 large4-modified
782 782 $ cat sub2/large6
783 783 large6-modified
784 784 $ cat sub2/large7
785 785 large7
786 786
787 787 Cat a largefile
788 788 $ hg cat normal3
789 789 normal3-modified
790 790 $ hg cat sub/large4
791 791 large4-modified
792 792 $ rm ${USERCACHE}/*
793 793 $ hg cat -r a381d2c8c80e -o cat.out sub/large4
794 794 $ cat cat.out
795 795 large4-modified
796 796 $ rm cat.out
797 797 $ hg cat -r a381d2c8c80e normal3
798 798 normal3-modified
799 799
800 800 Test that renaming a largefile results in correct output for status
801 801
802 802 $ hg rename sub/large4 large4-renamed
803 803 $ hg commit -m "test rename output"
804 804 Invoking status precommit hook
805 805 A large4-renamed
806 806 R sub/large4
807 807 $ cat large4-renamed
808 808 large4-modified
809 809 $ cd sub2
810 810 $ hg rename large6 large6-renamed
811 811 $ hg st
812 812 A sub2/large6-renamed
813 813 R sub2/large6
814 814 $ cd ..
815 815
816 816 Test --normal flag
817 817
818 818 $ dd if=/dev/zero bs=2k count=11k > new-largefile 2> /dev/null
819 819 $ hg add --normal --large new-largefile
820 820 abort: --normal cannot be used with --large
821 821 [255]
822 822 $ hg add --normal new-largefile
823 823 new-largefile: up to 69 MB of RAM may be required to manage this file
824 824 (use 'hg revert new-largefile' to cancel the pending addition)
825 825 $ cd ..
826 826
827 827 vanilla clients not locked out from largefiles servers on vanilla repos
828 828 $ mkdir r1
829 829 $ cd r1
830 830 $ hg init
831 831 $ echo c1 > f1
832 832 $ hg add f1
833 833 $ hg commit -m "m1"
834 834 Invoking status precommit hook
835 835 A f1
836 836 $ cd ..
837 837 $ hg serve -R r1 -d -p $HGPORT --pid-file hg.pid
838 838 $ cat hg.pid >> $DAEMON_PIDS
839 839 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT r2
840 840 requesting all changes
841 841 adding changesets
842 842 adding manifests
843 843 adding file changes
844 844 added 1 changesets with 1 changes to 1 files
845 845 updating to branch default
846 846 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
847 847
848 848 largefiles clients still work with vanilla servers
849 849 $ hg --config extensions.largefiles=! serve -R r1 -d -p $HGPORT1 --pid-file hg.pid
850 850 $ cat hg.pid >> $DAEMON_PIDS
851 851 $ hg clone http://localhost:$HGPORT1 r3
852 852 requesting all changes
853 853 adding changesets
854 854 adding manifests
855 855 adding file changes
856 856 added 1 changesets with 1 changes to 1 files
857 857 updating to branch default
858 858 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
859 859
860 860 vanilla clients locked out from largefiles http repos
861 861 $ mkdir r4
862 862 $ cd r4
863 863 $ hg init
864 864 $ echo c1 > f1
865 865 $ hg add --large f1
866 866 $ hg commit -m "m1"
867 867 Invoking status precommit hook
868 868 A f1
869 869 $ cd ..
870 870 $ hg serve -R r4 -d -p $HGPORT2 --pid-file hg.pid
871 871 $ cat hg.pid >> $DAEMON_PIDS
872 872 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT2 r5
873 873 abort: remote error:
874 874
875 875 This repository uses the largefiles extension.
876 876
877 877 Please enable it in your Mercurial config file.
878 878 [255]
879 879
880 880 used all HGPORTs, kill all daemons
881 881 $ "$TESTDIR/killdaemons.py"
882 882
883 883 vanilla clients locked out from largefiles ssh repos
884 884 $ hg --config extensions.largefiles=! clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/r4 r5
885 885 abort: remote error:
886 886
887 887 This repository uses the largefiles extension.
888 888
889 889 Please enable it in your Mercurial config file.
890 890 [255]
891 891
892 892 largefiles clients refuse to push largefiles repos to vanilla servers
893 893 $ mkdir r6
894 894 $ cd r6
895 895 $ hg init
896 896 $ echo c1 > f1
897 897 $ hg add f1
898 898 $ hg commit -m "m1"
899 899 Invoking status precommit hook
900 900 A f1
901 901 $ cat >> .hg/hgrc <<!
902 902 > [web]
903 903 > push_ssl = false
904 904 > allow_push = *
905 905 > !
906 906 $ cd ..
907 907 $ hg clone r6 r7
908 908 updating to branch default
909 909 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
910 910 $ cd r7
911 911 $ echo c2 > f2
912 912 $ hg add --large f2
913 913 $ hg commit -m "m2"
914 914 Invoking status precommit hook
915 915 A f2
916 916 $ hg --config extensions.largefiles=! -R ../r6 serve -d -p $HGPORT --pid-file ../hg.pid
917 917 $ cat ../hg.pid >> $DAEMON_PIDS
918 918 $ hg push http://localhost:$HGPORT
919 919 pushing to http://localhost:$HGPORT/
920 920 searching for changes
921 921 abort: http://localhost:$HGPORT/ does not appear to be a largefile store
922 922 [255]
923 923 $ cd ..
924 924
925 925 putlfile errors are shown (issue3123)
926 926 Corrupt the cached largefile in r7
927 927 $ echo corruption > $USERCACHE/4cdac4d8b084d0b599525cf732437fb337d422a8
928 928 $ hg init empty
929 929 $ hg serve -R empty -d -p $HGPORT1 --pid-file hg.pid \
930 930 > --config 'web.allow_push=*' --config web.push_ssl=False
931 931 $ cat hg.pid >> $DAEMON_PIDS
932 932 $ hg push -R r7 http://localhost:$HGPORT1
933 933 pushing to http://localhost:$HGPORT1/
934 934 searching for changes
935 935 remote: largefiles: failed to put 4cdac4d8b084d0b599525cf732437fb337d422a8 into store: largefile contents do not match hash
936 936 abort: remotestore: could not put $TESTTMP/r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 to remote store http://localhost:$HGPORT1/
937 937 [255]
938 938 $ rm -rf empty
939 939
940 940 Clone a local repository owned by another user
941 941 We have to simulate that here by setting $HOME and removing write permissions
942 942 $ ORIGHOME="$HOME"
943 943 $ mkdir alice
944 944 $ HOME="`pwd`/alice"
945 945 $ cd alice
946 946 $ hg init pubrepo
947 947 $ cd pubrepo
948 948 $ dd if=/dev/zero bs=1k count=11k > a-large-file 2> /dev/null
949 949 $ hg add --large a-large-file
950 950 $ hg commit -m "Add a large file"
951 951 Invoking status precommit hook
952 952 A a-large-file
953 953 $ cd ..
954 954 $ chmod -R a-w pubrepo
955 955 $ cd ..
956 956 $ mkdir bob
957 957 $ HOME="`pwd`/bob"
958 958 $ cd bob
959 959 $ hg clone --pull ../alice/pubrepo pubrepo
960 960 requesting all changes
961 961 adding changesets
962 962 adding manifests
963 963 adding file changes
964 964 added 1 changesets with 1 changes to 1 files
965 965 updating to branch default
966 966 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
967 967 getting changed largefiles
968 968 1 largefiles updated, 0 removed
969 969 $ cd ..
970 970 $ chmod -R u+w alice/pubrepo
971 971 $ HOME="$ORIGHOME"
972 972
973 973 Symlink to a large largefile should behave the same as a symlink to a normal file
974 974 $ hg init largesymlink
975 975 $ cd largesymlink
976 976 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
977 977 $ hg add --large largefile
978 978 $ hg commit -m "commit a large file"
979 979 Invoking status precommit hook
980 980 A largefile
981 981 $ ln -s largefile largelink
982 982 $ hg add largelink
983 983 $ hg commit -m "commit a large symlink"
984 984 Invoking status precommit hook
985 985 A largelink
986 986 $ rm -f largelink
987 987 $ hg up >/dev/null
988 988 $ test -f largelink
989 989 [1]
990 990 $ test -L largelink
991 991 [1]
992 992 $ rm -f largelink # make next part of the test independent of the previous
993 993 $ hg up -C >/dev/null
994 994 $ test -f largelink
995 995 $ test -L largelink
996 996 $ cd ..
997 997
998 998 test for pattern matching on 'hg status':
999 999 to boost performance, largefiles checks whether specified patterns are
1000 1000 related to largefiles in working directory (NOT to STANDIN) or not.
1001 1001
1002 1002 $ hg init statusmatch
1003 1003 $ cd statusmatch
1004 1004
1005 1005 $ mkdir -p a/b/c/d
1006 1006 $ echo normal > a/b/c/d/e.normal.txt
1007 1007 $ hg add a/b/c/d/e.normal.txt
1008 1008 $ echo large > a/b/c/d/e.large.txt
1009 1009 $ hg add --large a/b/c/d/e.large.txt
1010 1010 $ mkdir -p a/b/c/x
1011 1011 $ echo normal > a/b/c/x/y.normal.txt
1012 1012 $ hg add a/b/c/x/y.normal.txt
1013 1013 $ hg commit -m 'add files'
1014 1014 Invoking status precommit hook
1015 1015 A a/b/c/d/e.large.txt
1016 1016 A a/b/c/d/e.normal.txt
1017 1017 A a/b/c/x/y.normal.txt
1018 1018
1019 1019 (1) no pattern: no performance boost
1020 1020 $ hg status -A
1021 1021 C a/b/c/d/e.large.txt
1022 1022 C a/b/c/d/e.normal.txt
1023 1023 C a/b/c/x/y.normal.txt
1024 1024
1025 1025 (2) pattern not related to largefiles: performance boost
1026 1026 $ hg status -A a/b/c/x
1027 1027 C a/b/c/x/y.normal.txt
1028 1028
1029 1029 (3) pattern related to largefiles: no performance boost
1030 1030 $ hg status -A a/b/c/d
1031 1031 C a/b/c/d/e.large.txt
1032 1032 C a/b/c/d/e.normal.txt
1033 1033
1034 1034 (4) pattern related to STANDIN (not to largefiles): performance boost
1035 1035 $ hg status -A .hglf/a
1036 1036 C .hglf/a/b/c/d/e.large.txt
1037 1037
1038 1038 (5) mixed case: no performance boost
1039 1039 $ hg status -A a/b/c/x a/b/c/d
1040 1040 C a/b/c/d/e.large.txt
1041 1041 C a/b/c/d/e.normal.txt
1042 1042 C a/b/c/x/y.normal.txt
1043 1043
1044 1044 verify that largefiles doesn't break filesets
1045 1045
1046 1046 $ hg log --rev . --exclude "set:binary()"
1047 1047 changeset: 0:41bd42f10efa
1048 1048 tag: tip
1049 1049 user: test
1050 1050 date: Thu Jan 01 00:00:00 1970 +0000
1051 1051 summary: add files
1052 1052
1053 1053 verify that large files in subrepos handled properly
1054 1054 $ hg init subrepo
1055 1055 $ echo "subrepo = subrepo" > .hgsub
1056 1056 $ hg add .hgsub
1057 1057 $ hg ci -m "add subrepo"
1058 1058 Invoking status precommit hook
1059 1059 A .hgsub
1060 1060 ? .hgsubstate
1061 1061 $ echo "rev 1" > subrepo/large.txt
1062 1062 $ hg -R subrepo add --large subrepo/large.txt
1063 1063 $ hg sum
1064 1064 parent: 1:8ee150ea2e9c tip
1065 1065 add subrepo
1066 1066 branch: default
1067 1067 commit: 1 subrepos
1068 1068 update: (current)
1069 1069 $ hg st
1070 1070 $ hg st -S
1071 1071 A subrepo/large.txt
1072 1072 $ hg ci -S -m "commit top repo"
1073 1073 committing subrepository subrepo
1074 1074 Invoking status precommit hook
1075 1075 A large.txt
1076 1076 Invoking status precommit hook
1077 1077 M .hgsubstate
1078 1078 # No differences
1079 1079 $ hg st -S
1080 1080 $ hg sum
1081 1081 parent: 2:ce4cd0c527a6 tip
1082 1082 commit top repo
1083 1083 branch: default
1084 1084 commit: (clean)
1085 1085 update: (current)
1086 1086 $ echo "rev 2" > subrepo/large.txt
1087 1087 $ hg st -S
1088 1088 M subrepo/large.txt
1089 1089 $ hg sum
1090 1090 parent: 2:ce4cd0c527a6 tip
1091 1091 commit top repo
1092 1092 branch: default
1093 1093 commit: 1 subrepos
1094 1094 update: (current)
1095 1095 $ hg ci -m "this commit should fail without -S"
1096 1096 abort: uncommitted changes in subrepo subrepo
1097 1097 (use --subrepos for recursive commit)
1098 1098 [255]
1099
1100 # Add a normal file to the subrepo, then test archiving
1101 $ echo 'normal file' > subrepo/normal.txt
1102 $ hg -R subrepo add subrepo/normal.txt
1103 # Lock in subrepo, otherwise the change isn't archived
1104 $ hg ci -S -m "add normal file to top level"
1105 committing subrepository subrepo
1106 Invoking status precommit hook
1107 M large.txt
1108 A normal.txt
1109 Invoking status precommit hook
1110 M .hgsubstate
1111 $ hg archive -S lf_subrepo_archive
1112 $ find lf_subrepo_archive -print
1113 lf_subrepo_archive
1114 lf_subrepo_archive/.hg_archival.txt
1115 lf_subrepo_archive/.hgsubstate
1116 lf_subrepo_archive/subrepo
1117 lf_subrepo_archive/subrepo/large.txt
1118 lf_subrepo_archive/subrepo/normal.txt
1119 lf_subrepo_archive/a
1120 lf_subrepo_archive/a/b
1121 lf_subrepo_archive/a/b/c
1122 lf_subrepo_archive/a/b/c/d
1123 lf_subrepo_archive/a/b/c/d/e.normal.txt
1124 lf_subrepo_archive/a/b/c/d/e.large.txt
1125 lf_subrepo_archive/a/b/c/x
1126 lf_subrepo_archive/a/b/c/x/y.normal.txt
1127 lf_subrepo_archive/.hgsub
1128
1099 1129 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now