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