##// END OF EJS Templates
largefiles: enable subrepo support for remove...
Matt Harbison -
r23782:304e69cb default
parent child Browse files
Show More
@@ -1,1329 +1,1328 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, util, cmdutil, scmutil, match as match_, \
15 15 archival, pathutil, revset
16 16 from mercurial.i18n import _
17 17 from mercurial.node import hex
18 18
19 19 import lfutil
20 20 import lfcommands
21 21 import basestore
22 22
23 23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
24 24
25 25 def composelargefilematcher(match, manifest):
26 26 '''create a matcher that matches only the largefiles in the original
27 27 matcher'''
28 28 m = copy.copy(match)
29 29 lfile = lambda f: lfutil.standin(f) in manifest
30 30 m._files = filter(lfile, m._files)
31 31 m._fmap = set(m._files)
32 32 m._always = False
33 33 origmatchfn = m.matchfn
34 34 m.matchfn = lambda f: lfile(f) and origmatchfn(f)
35 35 return m
36 36
37 37 def composenormalfilematcher(match, manifest, exclude=None):
38 38 excluded = set()
39 39 if exclude is not None:
40 40 excluded.update(exclude)
41 41
42 42 m = copy.copy(match)
43 43 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
44 44 manifest or f in excluded)
45 45 m._files = filter(notlfile, m._files)
46 46 m._fmap = set(m._files)
47 47 m._always = False
48 48 origmatchfn = m.matchfn
49 49 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
50 50 return m
51 51
52 52 def installnormalfilesmatchfn(manifest):
53 53 '''installmatchfn with a matchfn that ignores all largefiles'''
54 54 def overridematch(ctx, pats=[], opts={}, globbed=False,
55 55 default='relpath'):
56 56 match = oldmatch(ctx, pats, opts, globbed, default)
57 57 return composenormalfilematcher(match, manifest)
58 58 oldmatch = installmatchfn(overridematch)
59 59
60 60 def installmatchfn(f):
61 61 '''monkey patch the scmutil module with a custom match function.
62 62 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
63 63 oldmatch = scmutil.match
64 64 setattr(f, 'oldmatch', oldmatch)
65 65 scmutil.match = f
66 66 return oldmatch
67 67
68 68 def restorematchfn():
69 69 '''restores scmutil.match to what it was before installmatchfn
70 70 was called. no-op if scmutil.match is its original function.
71 71
72 72 Note that n calls to installmatchfn will require n calls to
73 73 restore the original matchfn.'''
74 74 scmutil.match = getattr(scmutil.match, 'oldmatch')
75 75
76 76 def installmatchandpatsfn(f):
77 77 oldmatchandpats = scmutil.matchandpats
78 78 setattr(f, 'oldmatchandpats', oldmatchandpats)
79 79 scmutil.matchandpats = f
80 80 return oldmatchandpats
81 81
82 82 def restorematchandpatsfn():
83 83 '''restores scmutil.matchandpats to what it was before
84 84 installmatchandpatsfn was called. No-op if scmutil.matchandpats
85 85 is its original function.
86 86
87 87 Note that n calls to installmatchandpatsfn will require n calls
88 88 to restore the original matchfn.'''
89 89 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
90 90 scmutil.matchandpats)
91 91
92 92 def addlargefiles(ui, repo, isaddremove, matcher, **opts):
93 93 large = opts.pop('large', None)
94 94 lfsize = lfutil.getminsize(
95 95 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
96 96
97 97 lfmatcher = None
98 98 if lfutil.islfilesrepo(repo):
99 99 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
100 100 if lfpats:
101 101 lfmatcher = match_.match(repo.root, '', list(lfpats))
102 102
103 103 lfnames = []
104 104 m = copy.copy(matcher)
105 105 m.bad = lambda x, y: None
106 106 wctx = repo[None]
107 107 for f in repo.walk(m):
108 108 exact = m.exact(f)
109 109 lfile = lfutil.standin(f) in wctx
110 110 nfile = f in wctx
111 111 exists = lfile or nfile
112 112
113 113 # addremove in core gets fancy with the name, add doesn't
114 114 if isaddremove:
115 115 name = m.uipath(f)
116 116 else:
117 117 name = m.rel(f)
118 118
119 119 # Don't warn the user when they attempt to add a normal tracked file.
120 120 # The normal add code will do that for us.
121 121 if exact and exists:
122 122 if lfile:
123 123 ui.warn(_('%s already a largefile\n') % name)
124 124 continue
125 125
126 126 if (exact or not exists) and not lfutil.isstandin(f):
127 127 # In case the file was removed previously, but not committed
128 128 # (issue3507)
129 129 if not repo.wvfs.exists(f):
130 130 continue
131 131
132 132 abovemin = (lfsize and
133 133 repo.wvfs.lstat(f).st_size >= lfsize * 1024 * 1024)
134 134 if large or abovemin or (lfmatcher and lfmatcher(f)):
135 135 lfnames.append(f)
136 136 if ui.verbose or not exact:
137 137 ui.status(_('adding %s as a largefile\n') % name)
138 138
139 139 bad = []
140 140
141 141 # Need to lock, otherwise there could be a race condition between
142 142 # when standins are created and added to the repo.
143 143 wlock = repo.wlock()
144 144 try:
145 145 if not opts.get('dry_run'):
146 146 standins = []
147 147 lfdirstate = lfutil.openlfdirstate(ui, repo)
148 148 for f in lfnames:
149 149 standinname = lfutil.standin(f)
150 150 lfutil.writestandin(repo, standinname, hash='',
151 151 executable=lfutil.getexecutable(repo.wjoin(f)))
152 152 standins.append(standinname)
153 153 if lfdirstate[f] == 'r':
154 154 lfdirstate.normallookup(f)
155 155 else:
156 156 lfdirstate.add(f)
157 157 lfdirstate.write()
158 158 bad += [lfutil.splitstandin(f)
159 159 for f in repo[None].add(standins)
160 160 if f in m.files()]
161 161
162 162 added = [f for f in lfnames if f not in bad]
163 163 finally:
164 164 wlock.release()
165 165 return added, bad
166 166
167 167 def removelargefiles(ui, repo, isaddremove, matcher, **opts):
168 168 after = opts.get('after')
169 169 m = composelargefilematcher(matcher, repo[None].manifest())
170 170 try:
171 171 repo.lfstatus = True
172 172 s = repo.status(match=m, clean=not isaddremove)
173 173 finally:
174 174 repo.lfstatus = False
175 175 manifest = repo[None].manifest()
176 176 modified, added, deleted, clean = [[f for f in list
177 177 if lfutil.standin(f) in manifest]
178 178 for list in (s.modified, s.added,
179 179 s.deleted, s.clean)]
180 180
181 181 def warn(files, msg):
182 182 for f in files:
183 183 ui.warn(msg % m.rel(f))
184 184 return int(len(files) > 0)
185 185
186 186 result = 0
187 187
188 188 if after:
189 189 remove = deleted
190 190 result = warn(modified + added + clean,
191 191 _('not removing %s: file still exists\n'))
192 192 else:
193 193 remove = deleted + clean
194 194 result = warn(modified, _('not removing %s: file is modified (use -f'
195 195 ' to force removal)\n'))
196 196 result = warn(added, _('not removing %s: file has been marked for add'
197 197 ' (use forget to undo)\n')) or result
198 198
199 199 # Need to lock because standin files are deleted then removed from the
200 200 # repository and we could race in-between.
201 201 wlock = repo.wlock()
202 202 try:
203 203 lfdirstate = lfutil.openlfdirstate(ui, repo)
204 204 for f in sorted(remove):
205 205 if ui.verbose or not m.exact(f):
206 206 # addremove in core gets fancy with the name, remove doesn't
207 207 if isaddremove:
208 208 name = m.uipath(f)
209 209 else:
210 210 name = m.rel(f)
211 211 ui.status(_('removing %s\n') % name)
212 212
213 213 if not opts.get('dry_run'):
214 214 if not after:
215 215 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
216 216
217 217 if opts.get('dry_run'):
218 218 return result
219 219
220 220 remove = [lfutil.standin(f) for f in remove]
221 221 # If this is being called by addremove, let the original addremove
222 222 # function handle this.
223 223 if not isaddremove:
224 224 for f in remove:
225 225 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
226 226 repo[None].forget(remove)
227 227
228 228 for f in remove:
229 229 lfutil.synclfdirstate(repo, lfdirstate, lfutil.splitstandin(f),
230 230 False)
231 231
232 232 lfdirstate.write()
233 233 finally:
234 234 wlock.release()
235 235
236 236 return result
237 237
238 238 # For overriding mercurial.hgweb.webcommands so that largefiles will
239 239 # appear at their right place in the manifests.
240 240 def decodepath(orig, path):
241 241 return lfutil.splitstandin(path) or path
242 242
243 243 # -- Wrappers: modify existing commands --------------------------------
244 244
245 245 # Add works by going through the files that the user wanted to add and
246 246 # checking if they should be added as largefiles. Then it makes a new
247 247 # matcher which matches only the normal files and runs the original
248 248 # version of add.
249 249 def overrideadd(orig, ui, repo, *pats, **opts):
250 250 normal = opts.pop('normal')
251 251 if normal:
252 252 if opts.get('large'):
253 253 raise util.Abort(_('--normal cannot be used with --large'))
254 254 return orig(ui, repo, *pats, **opts)
255 255 matcher = scmutil.match(repo[None], pats, opts)
256 256 added, bad = addlargefiles(ui, repo, False, matcher, **opts)
257 257 installnormalfilesmatchfn(repo[None].manifest())
258 258 result = orig(ui, repo, *pats, **opts)
259 259 restorematchfn()
260 260
261 261 return (result == 1 or bad) and 1 or 0
262 262
263 def overrideremove(orig, ui, repo, *pats, **opts):
264 installnormalfilesmatchfn(repo[None].manifest())
265 result = orig(ui, repo, *pats, **opts)
266 restorematchfn()
267 matcher = scmutil.match(repo[None], pats, opts)
268 return removelargefiles(ui, repo, False, matcher, **opts) or result
263 def cmdutilremove(orig, ui, repo, matcher, prefix, after, force, subrepos):
264 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest())
265 result = orig(ui, repo, normalmatcher, prefix, after, force, subrepos)
266 return removelargefiles(ui, repo, False, matcher, after=after,
267 force=force) or result
269 268
270 269 def overridestatusfn(orig, repo, rev2, **opts):
271 270 try:
272 271 repo._repo.lfstatus = True
273 272 return orig(repo, rev2, **opts)
274 273 finally:
275 274 repo._repo.lfstatus = False
276 275
277 276 def overridestatus(orig, ui, repo, *pats, **opts):
278 277 try:
279 278 repo.lfstatus = True
280 279 return orig(ui, repo, *pats, **opts)
281 280 finally:
282 281 repo.lfstatus = False
283 282
284 283 def overridedirty(orig, repo, ignoreupdate=False):
285 284 try:
286 285 repo._repo.lfstatus = True
287 286 return orig(repo, ignoreupdate)
288 287 finally:
289 288 repo._repo.lfstatus = False
290 289
291 290 def overridelog(orig, ui, repo, *pats, **opts):
292 291 def overridematchandpats(ctx, pats=[], opts={}, globbed=False,
293 292 default='relpath'):
294 293 """Matcher that merges root directory with .hglf, suitable for log.
295 294 It is still possible to match .hglf directly.
296 295 For any listed files run log on the standin too.
297 296 matchfn tries both the given filename and with .hglf stripped.
298 297 """
299 298 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default)
300 299 m, p = copy.copy(matchandpats)
301 300
302 301 if m.always():
303 302 # We want to match everything anyway, so there's no benefit trying
304 303 # to add standins.
305 304 return matchandpats
306 305
307 306 pats = set(p)
308 307 # TODO: handling of patterns in both cases below
309 308 if m._cwd:
310 309 if os.path.isabs(m._cwd):
311 310 # TODO: handle largefile magic when invoked from other cwd
312 311 return matchandpats
313 312 back = (m._cwd.count('/') + 1) * '../'
314 313 pats.update(back + lfutil.standin(m._cwd + '/' + f) for f in p)
315 314 else:
316 315 pats.update(lfutil.standin(f) for f in p)
317 316
318 317 for i in range(0, len(m._files)):
319 318 standin = lfutil.standin(m._files[i])
320 319 if standin in repo[ctx.node()]:
321 320 m._files[i] = standin
322 321 elif m._files[i] not in repo[ctx.node()]:
323 322 m._files.append(standin)
324 323 pats.add(standin)
325 324
326 325 m._fmap = set(m._files)
327 326 m._always = False
328 327 origmatchfn = m.matchfn
329 328 def lfmatchfn(f):
330 329 lf = lfutil.splitstandin(f)
331 330 if lf is not None and origmatchfn(lf):
332 331 return True
333 332 r = origmatchfn(f)
334 333 return r
335 334 m.matchfn = lfmatchfn
336 335
337 336 return m, pats
338 337
339 338 # For hg log --patch, the match object is used in two different senses:
340 339 # (1) to determine what revisions should be printed out, and
341 340 # (2) to determine what files to print out diffs for.
342 341 # The magic matchandpats override should be used for case (1) but not for
343 342 # case (2).
344 343 def overridemakelogfilematcher(repo, pats, opts):
345 344 pctx = repo[None]
346 345 match, pats = oldmatchandpats(pctx, pats, opts)
347 346 return lambda rev: match
348 347
349 348 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
350 349 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
351 350 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
352 351
353 352 try:
354 353 return orig(ui, repo, *pats, **opts)
355 354 finally:
356 355 restorematchandpatsfn()
357 356 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
358 357
359 358 def overrideverify(orig, ui, repo, *pats, **opts):
360 359 large = opts.pop('large', False)
361 360 all = opts.pop('lfa', False)
362 361 contents = opts.pop('lfc', False)
363 362
364 363 result = orig(ui, repo, *pats, **opts)
365 364 if large or all or contents:
366 365 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
367 366 return result
368 367
369 368 def overridedebugstate(orig, ui, repo, *pats, **opts):
370 369 large = opts.pop('large', False)
371 370 if large:
372 371 class fakerepo(object):
373 372 dirstate = lfutil.openlfdirstate(ui, repo)
374 373 orig(ui, fakerepo, *pats, **opts)
375 374 else:
376 375 orig(ui, repo, *pats, **opts)
377 376
378 377 # Override needs to refresh standins so that update's normal merge
379 378 # will go through properly. Then the other update hook (overriding repo.update)
380 379 # will get the new files. Filemerge is also overridden so that the merge
381 380 # will merge standins correctly.
382 381 def overrideupdate(orig, ui, repo, *pats, **opts):
383 382 # Need to lock between the standins getting updated and their
384 383 # largefiles getting updated
385 384 wlock = repo.wlock()
386 385 try:
387 386 if opts['check']:
388 387 lfdirstate = lfutil.openlfdirstate(ui, repo)
389 388 unsure, s = lfdirstate.status(
390 389 match_.always(repo.root, repo.getcwd()),
391 390 [], False, False, False)
392 391
393 392 mod = len(s.modified) > 0
394 393 for lfile in unsure:
395 394 standin = lfutil.standin(lfile)
396 395 if repo['.'][standin].data().strip() != \
397 396 lfutil.hashfile(repo.wjoin(lfile)):
398 397 mod = True
399 398 else:
400 399 lfdirstate.normal(lfile)
401 400 lfdirstate.write()
402 401 if mod:
403 402 raise util.Abort(_('uncommitted changes'))
404 403 return orig(ui, repo, *pats, **opts)
405 404 finally:
406 405 wlock.release()
407 406
408 407 # Before starting the manifest merge, merge.updates will call
409 408 # _checkunknownfile to check if there are any files in the merged-in
410 409 # changeset that collide with unknown files in the working copy.
411 410 #
412 411 # The largefiles are seen as unknown, so this prevents us from merging
413 412 # in a file 'foo' if we already have a largefile with the same name.
414 413 #
415 414 # The overridden function filters the unknown files by removing any
416 415 # largefiles. This makes the merge proceed and we can then handle this
417 416 # case further in the overridden calculateupdates function below.
418 417 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
419 418 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
420 419 return False
421 420 return origfn(repo, wctx, mctx, f, f2)
422 421
423 422 # The manifest merge handles conflicts on the manifest level. We want
424 423 # to handle changes in largefile-ness of files at this level too.
425 424 #
426 425 # The strategy is to run the original calculateupdates and then process
427 426 # the action list it outputs. There are two cases we need to deal with:
428 427 #
429 428 # 1. Normal file in p1, largefile in p2. Here the largefile is
430 429 # detected via its standin file, which will enter the working copy
431 430 # with a "get" action. It is not "merge" since the standin is all
432 431 # Mercurial is concerned with at this level -- the link to the
433 432 # existing normal file is not relevant here.
434 433 #
435 434 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
436 435 # since the largefile will be present in the working copy and
437 436 # different from the normal file in p2. Mercurial therefore
438 437 # triggers a merge action.
439 438 #
440 439 # In both cases, we prompt the user and emit new actions to either
441 440 # remove the standin (if the normal file was kept) or to remove the
442 441 # normal file and get the standin (if the largefile was kept). The
443 442 # default prompt answer is to use the largefile version since it was
444 443 # presumably changed on purpose.
445 444 #
446 445 # Finally, the merge.applyupdates function will then take care of
447 446 # writing the files into the working copy and lfcommands.updatelfiles
448 447 # will update the largefiles.
449 448 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
450 449 partial, acceptremote, followcopies):
451 450 overwrite = force and not branchmerge
452 451 actions, diverge, renamedelete = origfn(
453 452 repo, p1, p2, pas, branchmerge, force, partial, acceptremote,
454 453 followcopies)
455 454
456 455 if overwrite:
457 456 return actions, diverge, renamedelete
458 457
459 458 # Convert to dictionary with filename as key and action as value.
460 459 lfiles = set()
461 460 for f in actions:
462 461 splitstandin = f and lfutil.splitstandin(f)
463 462 if splitstandin in p1:
464 463 lfiles.add(splitstandin)
465 464 elif lfutil.standin(f) in p1:
466 465 lfiles.add(f)
467 466
468 467 for lfile in lfiles:
469 468 standin = lfutil.standin(lfile)
470 469 (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
471 470 (sm, sargs, smsg) = actions.get(standin, (None, None, None))
472 471 if sm in ('g', 'dc') and lm != 'r':
473 472 # Case 1: normal file in the working copy, largefile in
474 473 # the second parent
475 474 usermsg = _('remote turned local normal file %s into a largefile\n'
476 475 'use (l)argefile or keep (n)ormal file?'
477 476 '$$ &Largefile $$ &Normal file') % lfile
478 477 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
479 478 actions[lfile] = ('r', None, 'replaced by standin')
480 479 actions[standin] = ('g', sargs, 'replaces standin')
481 480 else: # keep local normal file
482 481 actions[lfile] = ('k', None, 'replaces standin')
483 482 if branchmerge:
484 483 actions[standin] = ('k', None, 'replaced by non-standin')
485 484 else:
486 485 actions[standin] = ('r', None, 'replaced by non-standin')
487 486 elif lm in ('g', 'dc') and sm != 'r':
488 487 # Case 2: largefile in the working copy, normal file in
489 488 # the second parent
490 489 usermsg = _('remote turned local largefile %s into a normal file\n'
491 490 'keep (l)argefile or use (n)ormal file?'
492 491 '$$ &Largefile $$ &Normal file') % lfile
493 492 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
494 493 if branchmerge:
495 494 # largefile can be restored from standin safely
496 495 actions[lfile] = ('k', None, 'replaced by standin')
497 496 actions[standin] = ('k', None, 'replaces standin')
498 497 else:
499 498 # "lfile" should be marked as "removed" without
500 499 # removal of itself
501 500 actions[lfile] = ('lfmr', None,
502 501 'forget non-standin largefile')
503 502
504 503 # linear-merge should treat this largefile as 're-added'
505 504 actions[standin] = ('a', None, 'keep standin')
506 505 else: # pick remote normal file
507 506 actions[lfile] = ('g', largs, 'replaces standin')
508 507 actions[standin] = ('r', None, 'replaced by non-standin')
509 508
510 509 return actions, diverge, renamedelete
511 510
512 511 def mergerecordupdates(orig, repo, actions, branchmerge):
513 512 if 'lfmr' in actions:
514 513 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
515 514 for lfile, args, msg in actions['lfmr']:
516 515 # this should be executed before 'orig', to execute 'remove'
517 516 # before all other actions
518 517 repo.dirstate.remove(lfile)
519 518 # make sure lfile doesn't get synclfdirstate'd as normal
520 519 lfdirstate.add(lfile)
521 520 lfdirstate.write()
522 521
523 522 return orig(repo, actions, branchmerge)
524 523
525 524
526 525 # Override filemerge to prompt the user about how they wish to merge
527 526 # largefiles. This will handle identical edits without prompting the user.
528 527 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
529 528 if not lfutil.isstandin(orig):
530 529 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
531 530
532 531 ahash = fca.data().strip().lower()
533 532 dhash = fcd.data().strip().lower()
534 533 ohash = fco.data().strip().lower()
535 534 if (ohash != ahash and
536 535 ohash != dhash and
537 536 (dhash == ahash or
538 537 repo.ui.promptchoice(
539 538 _('largefile %s has a merge conflict\nancestor was %s\n'
540 539 'keep (l)ocal %s or\ntake (o)ther %s?'
541 540 '$$ &Local $$ &Other') %
542 541 (lfutil.splitstandin(orig), ahash, dhash, ohash),
543 542 0) == 1)):
544 543 repo.wwrite(fcd.path(), fco.data(), fco.flags())
545 544 return 0
546 545
547 546 # Copy first changes the matchers to match standins instead of
548 547 # largefiles. Then it overrides util.copyfile in that function it
549 548 # checks if the destination largefile already exists. It also keeps a
550 549 # list of copied files so that the largefiles can be copied and the
551 550 # dirstate updated.
552 551 def overridecopy(orig, ui, repo, pats, opts, rename=False):
553 552 # doesn't remove largefile on rename
554 553 if len(pats) < 2:
555 554 # this isn't legal, let the original function deal with it
556 555 return orig(ui, repo, pats, opts, rename)
557 556
558 557 def makestandin(relpath):
559 558 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
560 559 return os.path.join(repo.wjoin(lfutil.standin(path)))
561 560
562 561 fullpats = scmutil.expandpats(pats)
563 562 dest = fullpats[-1]
564 563
565 564 if os.path.isdir(dest):
566 565 if not os.path.isdir(makestandin(dest)):
567 566 os.makedirs(makestandin(dest))
568 567 # This could copy both lfiles and normal files in one command,
569 568 # but we don't want to do that. First replace their matcher to
570 569 # only match normal files and run it, then replace it to just
571 570 # match largefiles and run it again.
572 571 nonormalfiles = False
573 572 nolfiles = False
574 573 installnormalfilesmatchfn(repo[None].manifest())
575 574 try:
576 575 try:
577 576 result = orig(ui, repo, pats, opts, rename)
578 577 except util.Abort, e:
579 578 if str(e) != _('no files to copy'):
580 579 raise e
581 580 else:
582 581 nonormalfiles = True
583 582 result = 0
584 583 finally:
585 584 restorematchfn()
586 585
587 586 # The first rename can cause our current working directory to be removed.
588 587 # In that case there is nothing left to copy/rename so just quit.
589 588 try:
590 589 repo.getcwd()
591 590 except OSError:
592 591 return result
593 592
594 593 try:
595 594 try:
596 595 # When we call orig below it creates the standins but we don't add
597 596 # them to the dir state until later so lock during that time.
598 597 wlock = repo.wlock()
599 598
600 599 manifest = repo[None].manifest()
601 600 def overridematch(ctx, pats=[], opts={}, globbed=False,
602 601 default='relpath'):
603 602 newpats = []
604 603 # The patterns were previously mangled to add the standin
605 604 # directory; we need to remove that now
606 605 for pat in pats:
607 606 if match_.patkind(pat) is None and lfutil.shortname in pat:
608 607 newpats.append(pat.replace(lfutil.shortname, ''))
609 608 else:
610 609 newpats.append(pat)
611 610 match = oldmatch(ctx, newpats, opts, globbed, default)
612 611 m = copy.copy(match)
613 612 lfile = lambda f: lfutil.standin(f) in manifest
614 613 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
615 614 m._fmap = set(m._files)
616 615 origmatchfn = m.matchfn
617 616 m.matchfn = lambda f: (lfutil.isstandin(f) and
618 617 (f in manifest) and
619 618 origmatchfn(lfutil.splitstandin(f)) or
620 619 None)
621 620 return m
622 621 oldmatch = installmatchfn(overridematch)
623 622 listpats = []
624 623 for pat in pats:
625 624 if match_.patkind(pat) is not None:
626 625 listpats.append(pat)
627 626 else:
628 627 listpats.append(makestandin(pat))
629 628
630 629 try:
631 630 origcopyfile = util.copyfile
632 631 copiedfiles = []
633 632 def overridecopyfile(src, dest):
634 633 if (lfutil.shortname in src and
635 634 dest.startswith(repo.wjoin(lfutil.shortname))):
636 635 destlfile = dest.replace(lfutil.shortname, '')
637 636 if not opts['force'] and os.path.exists(destlfile):
638 637 raise IOError('',
639 638 _('destination largefile already exists'))
640 639 copiedfiles.append((src, dest))
641 640 origcopyfile(src, dest)
642 641
643 642 util.copyfile = overridecopyfile
644 643 result += orig(ui, repo, listpats, opts, rename)
645 644 finally:
646 645 util.copyfile = origcopyfile
647 646
648 647 lfdirstate = lfutil.openlfdirstate(ui, repo)
649 648 for (src, dest) in copiedfiles:
650 649 if (lfutil.shortname in src and
651 650 dest.startswith(repo.wjoin(lfutil.shortname))):
652 651 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
653 652 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
654 653 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
655 654 if not os.path.isdir(destlfiledir):
656 655 os.makedirs(destlfiledir)
657 656 if rename:
658 657 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
659 658
660 659 # The file is gone, but this deletes any empty parent
661 660 # directories as a side-effect.
662 661 util.unlinkpath(repo.wjoin(srclfile), True)
663 662 lfdirstate.remove(srclfile)
664 663 else:
665 664 util.copyfile(repo.wjoin(srclfile),
666 665 repo.wjoin(destlfile))
667 666
668 667 lfdirstate.add(destlfile)
669 668 lfdirstate.write()
670 669 except util.Abort, e:
671 670 if str(e) != _('no files to copy'):
672 671 raise e
673 672 else:
674 673 nolfiles = True
675 674 finally:
676 675 restorematchfn()
677 676 wlock.release()
678 677
679 678 if nolfiles and nonormalfiles:
680 679 raise util.Abort(_('no files to copy'))
681 680
682 681 return result
683 682
684 683 # When the user calls revert, we have to be careful to not revert any
685 684 # changes to other largefiles accidentally. This means we have to keep
686 685 # track of the largefiles that are being reverted so we only pull down
687 686 # the necessary largefiles.
688 687 #
689 688 # Standins are only updated (to match the hash of largefiles) before
690 689 # commits. Update the standins then run the original revert, changing
691 690 # the matcher to hit standins instead of largefiles. Based on the
692 691 # resulting standins update the largefiles.
693 692 def overriderevert(orig, ui, repo, *pats, **opts):
694 693 # Because we put the standins in a bad state (by updating them)
695 694 # and then return them to a correct state we need to lock to
696 695 # prevent others from changing them in their incorrect state.
697 696 wlock = repo.wlock()
698 697 try:
699 698 lfdirstate = lfutil.openlfdirstate(ui, repo)
700 699 s = lfutil.lfdirstatestatus(lfdirstate, repo)
701 700 lfdirstate.write()
702 701 for lfile in s.modified:
703 702 lfutil.updatestandin(repo, lfutil.standin(lfile))
704 703 for lfile in s.deleted:
705 704 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
706 705 os.unlink(repo.wjoin(lfutil.standin(lfile)))
707 706
708 707 oldstandins = lfutil.getstandinsstate(repo)
709 708
710 709 def overridematch(ctx, pats=[], opts={}, globbed=False,
711 710 default='relpath'):
712 711 match = oldmatch(ctx, pats, opts, globbed, default)
713 712 m = copy.copy(match)
714 713 def tostandin(f):
715 714 if lfutil.standin(f) in ctx:
716 715 return lfutil.standin(f)
717 716 elif lfutil.standin(f) in repo[None]:
718 717 return None
719 718 return f
720 719 m._files = [tostandin(f) for f in m._files]
721 720 m._files = [f for f in m._files if f is not None]
722 721 m._fmap = set(m._files)
723 722 origmatchfn = m.matchfn
724 723 def matchfn(f):
725 724 if lfutil.isstandin(f):
726 725 return (origmatchfn(lfutil.splitstandin(f)) and
727 726 (f in repo[None] or f in ctx))
728 727 return origmatchfn(f)
729 728 m.matchfn = matchfn
730 729 return m
731 730 oldmatch = installmatchfn(overridematch)
732 731 try:
733 732 orig(ui, repo, *pats, **opts)
734 733 finally:
735 734 restorematchfn()
736 735
737 736 newstandins = lfutil.getstandinsstate(repo)
738 737 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
739 738 # lfdirstate should be 'normallookup'-ed for updated files,
740 739 # because reverting doesn't touch dirstate for 'normal' files
741 740 # when target revision is explicitly specified: in such case,
742 741 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
743 742 # of target (standin) file.
744 743 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
745 744 normallookup=True)
746 745
747 746 finally:
748 747 wlock.release()
749 748
750 749 # after pulling changesets, we need to take some extra care to get
751 750 # largefiles updated remotely
752 751 def overridepull(orig, ui, repo, source=None, **opts):
753 752 revsprepull = len(repo)
754 753 if not source:
755 754 source = 'default'
756 755 repo.lfpullsource = source
757 756 result = orig(ui, repo, source, **opts)
758 757 revspostpull = len(repo)
759 758 lfrevs = opts.get('lfrev', [])
760 759 if opts.get('all_largefiles'):
761 760 lfrevs.append('pulled()')
762 761 if lfrevs and revspostpull > revsprepull:
763 762 numcached = 0
764 763 repo.firstpulled = revsprepull # for pulled() revset expression
765 764 try:
766 765 for rev in scmutil.revrange(repo, lfrevs):
767 766 ui.note(_('pulling largefiles for revision %s\n') % rev)
768 767 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
769 768 numcached += len(cached)
770 769 finally:
771 770 del repo.firstpulled
772 771 ui.status(_("%d largefiles cached\n") % numcached)
773 772 return result
774 773
775 774 def pulledrevsetsymbol(repo, subset, x):
776 775 """``pulled()``
777 776 Changesets that just has been pulled.
778 777
779 778 Only available with largefiles from pull --lfrev expressions.
780 779
781 780 .. container:: verbose
782 781
783 782 Some examples:
784 783
785 784 - pull largefiles for all new changesets::
786 785
787 786 hg pull -lfrev "pulled()"
788 787
789 788 - pull largefiles for all new branch heads::
790 789
791 790 hg pull -lfrev "head(pulled()) and not closed()"
792 791
793 792 """
794 793
795 794 try:
796 795 firstpulled = repo.firstpulled
797 796 except AttributeError:
798 797 raise util.Abort(_("pulled() only available in --lfrev"))
799 798 return revset.baseset([r for r in subset if r >= firstpulled])
800 799
801 800 def overrideclone(orig, ui, source, dest=None, **opts):
802 801 d = dest
803 802 if d is None:
804 803 d = hg.defaultdest(source)
805 804 if opts.get('all_largefiles') and not hg.islocal(d):
806 805 raise util.Abort(_(
807 806 '--all-largefiles is incompatible with non-local destination %s') %
808 807 d)
809 808
810 809 return orig(ui, source, dest, **opts)
811 810
812 811 def hgclone(orig, ui, opts, *args, **kwargs):
813 812 result = orig(ui, opts, *args, **kwargs)
814 813
815 814 if result is not None:
816 815 sourcerepo, destrepo = result
817 816 repo = destrepo.local()
818 817
819 818 # Caching is implicitly limited to 'rev' option, since the dest repo was
820 819 # truncated at that point. The user may expect a download count with
821 820 # this option, so attempt whether or not this is a largefile repo.
822 821 if opts.get('all_largefiles'):
823 822 success, missing = lfcommands.downloadlfiles(ui, repo, None)
824 823
825 824 if missing != 0:
826 825 return None
827 826
828 827 return result
829 828
830 829 def overriderebase(orig, ui, repo, **opts):
831 830 resuming = opts.get('continue')
832 831 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
833 832 repo._lfstatuswriters.append(lambda *msg, **opts: None)
834 833 try:
835 834 return orig(ui, repo, **opts)
836 835 finally:
837 836 repo._lfstatuswriters.pop()
838 837 repo._lfcommithooks.pop()
839 838
840 839 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
841 840 prefix=None, mtime=None, subrepos=None):
842 841 # No need to lock because we are only reading history and
843 842 # largefile caches, neither of which are modified.
844 843 lfcommands.cachelfiles(repo.ui, repo, node)
845 844
846 845 if kind not in archival.archivers:
847 846 raise util.Abort(_("unknown archive type '%s'") % kind)
848 847
849 848 ctx = repo[node]
850 849
851 850 if kind == 'files':
852 851 if prefix:
853 852 raise util.Abort(
854 853 _('cannot give prefix when archiving to files'))
855 854 else:
856 855 prefix = archival.tidyprefix(dest, kind, prefix)
857 856
858 857 def write(name, mode, islink, getdata):
859 858 if matchfn and not matchfn(name):
860 859 return
861 860 data = getdata()
862 861 if decode:
863 862 data = repo.wwritedata(name, data)
864 863 archiver.addfile(prefix + name, mode, islink, data)
865 864
866 865 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
867 866
868 867 if repo.ui.configbool("ui", "archivemeta", True):
869 868 def metadata():
870 869 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
871 870 hex(repo.changelog.node(0)), hex(node), ctx.branch())
872 871
873 872 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
874 873 if repo.tagtype(t) == 'global')
875 874 if not tags:
876 875 repo.ui.pushbuffer()
877 876 opts = {'template': '{latesttag}\n{latesttagdistance}',
878 877 'style': '', 'patch': None, 'git': None}
879 878 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
880 879 ltags, dist = repo.ui.popbuffer().split('\n')
881 880 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
882 881 tags += 'latesttagdistance: %s\n' % dist
883 882
884 883 return base + tags
885 884
886 885 write('.hg_archival.txt', 0644, False, metadata)
887 886
888 887 for f in ctx:
889 888 ff = ctx.flags(f)
890 889 getdata = ctx[f].data
891 890 if lfutil.isstandin(f):
892 891 path = lfutil.findfile(repo, getdata().strip())
893 892 if path is None:
894 893 raise util.Abort(
895 894 _('largefile %s not found in repo store or system cache')
896 895 % lfutil.splitstandin(f))
897 896 f = lfutil.splitstandin(f)
898 897
899 898 def getdatafn():
900 899 fd = None
901 900 try:
902 901 fd = open(path, 'rb')
903 902 return fd.read()
904 903 finally:
905 904 if fd:
906 905 fd.close()
907 906
908 907 getdata = getdatafn
909 908 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
910 909
911 910 if subrepos:
912 911 for subpath in sorted(ctx.substate):
913 912 sub = ctx.sub(subpath)
914 913 submatch = match_.narrowmatcher(subpath, matchfn)
915 914 sub.archive(archiver, prefix, submatch)
916 915
917 916 archiver.done()
918 917
919 918 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None):
920 919 repo._get(repo._state + ('hg',))
921 920 rev = repo._state[1]
922 921 ctx = repo._repo[rev]
923 922
924 923 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
925 924
926 925 def write(name, mode, islink, getdata):
927 926 # At this point, the standin has been replaced with the largefile name,
928 927 # so the normal matcher works here without the lfutil variants.
929 928 if match and not match(f):
930 929 return
931 930 data = getdata()
932 931
933 932 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
934 933
935 934 for f in ctx:
936 935 ff = ctx.flags(f)
937 936 getdata = ctx[f].data
938 937 if lfutil.isstandin(f):
939 938 path = lfutil.findfile(repo._repo, getdata().strip())
940 939 if path is None:
941 940 raise util.Abort(
942 941 _('largefile %s not found in repo store or system cache')
943 942 % lfutil.splitstandin(f))
944 943 f = lfutil.splitstandin(f)
945 944
946 945 def getdatafn():
947 946 fd = None
948 947 try:
949 948 fd = open(os.path.join(prefix, path), 'rb')
950 949 return fd.read()
951 950 finally:
952 951 if fd:
953 952 fd.close()
954 953
955 954 getdata = getdatafn
956 955
957 956 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
958 957
959 958 for subpath in sorted(ctx.substate):
960 959 sub = ctx.sub(subpath)
961 960 submatch = match_.narrowmatcher(subpath, match)
962 961 sub.archive(archiver, os.path.join(prefix, repo._path) + '/', submatch)
963 962
964 963 # If a largefile is modified, the change is not reflected in its
965 964 # standin until a commit. cmdutil.bailifchanged() raises an exception
966 965 # if the repo has uncommitted changes. Wrap it to also check if
967 966 # largefiles were changed. This is used by bisect, backout and fetch.
968 967 def overridebailifchanged(orig, repo):
969 968 orig(repo)
970 969 repo.lfstatus = True
971 970 s = repo.status()
972 971 repo.lfstatus = False
973 972 if s.modified or s.added or s.removed or s.deleted:
974 973 raise util.Abort(_('uncommitted changes'))
975 974
976 975 def overrideforget(orig, ui, repo, *pats, **opts):
977 976 installnormalfilesmatchfn(repo[None].manifest())
978 977 result = orig(ui, repo, *pats, **opts)
979 978 restorematchfn()
980 979 m = composelargefilematcher(scmutil.match(repo[None], pats, opts),
981 980 repo[None].manifest())
982 981
983 982 try:
984 983 repo.lfstatus = True
985 984 s = repo.status(match=m, clean=True)
986 985 finally:
987 986 repo.lfstatus = False
988 987 forget = sorted(s.modified + s.added + s.deleted + s.clean)
989 988 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
990 989
991 990 for f in forget:
992 991 if lfutil.standin(f) not in repo.dirstate and not \
993 992 repo.wvfs.isdir(lfutil.standin(f)):
994 993 ui.warn(_('not removing %s: file is already untracked\n')
995 994 % m.rel(f))
996 995 result = 1
997 996
998 997 for f in forget:
999 998 if ui.verbose or not m.exact(f):
1000 999 ui.status(_('removing %s\n') % m.rel(f))
1001 1000
1002 1001 # Need to lock because standin files are deleted then removed from the
1003 1002 # repository and we could race in-between.
1004 1003 wlock = repo.wlock()
1005 1004 try:
1006 1005 lfdirstate = lfutil.openlfdirstate(ui, repo)
1007 1006 for f in forget:
1008 1007 if lfdirstate[f] == 'a':
1009 1008 lfdirstate.drop(f)
1010 1009 else:
1011 1010 lfdirstate.remove(f)
1012 1011 lfdirstate.write()
1013 1012 standins = [lfutil.standin(f) for f in forget]
1014 1013 for f in standins:
1015 1014 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1016 1015 repo[None].forget(standins)
1017 1016 finally:
1018 1017 wlock.release()
1019 1018
1020 1019 return result
1021 1020
1022 1021 def _getoutgoings(repo, other, missing, addfunc):
1023 1022 """get pairs of filename and largefile hash in outgoing revisions
1024 1023 in 'missing'.
1025 1024
1026 1025 largefiles already existing on 'other' repository are ignored.
1027 1026
1028 1027 'addfunc' is invoked with each unique pairs of filename and
1029 1028 largefile hash value.
1030 1029 """
1031 1030 knowns = set()
1032 1031 lfhashes = set()
1033 1032 def dedup(fn, lfhash):
1034 1033 k = (fn, lfhash)
1035 1034 if k not in knowns:
1036 1035 knowns.add(k)
1037 1036 lfhashes.add(lfhash)
1038 1037 lfutil.getlfilestoupload(repo, missing, dedup)
1039 1038 if lfhashes:
1040 1039 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1041 1040 for fn, lfhash in knowns:
1042 1041 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1043 1042 addfunc(fn, lfhash)
1044 1043
1045 1044 def outgoinghook(ui, repo, other, opts, missing):
1046 1045 if opts.pop('large', None):
1047 1046 lfhashes = set()
1048 1047 if ui.debugflag:
1049 1048 toupload = {}
1050 1049 def addfunc(fn, lfhash):
1051 1050 if fn not in toupload:
1052 1051 toupload[fn] = []
1053 1052 toupload[fn].append(lfhash)
1054 1053 lfhashes.add(lfhash)
1055 1054 def showhashes(fn):
1056 1055 for lfhash in sorted(toupload[fn]):
1057 1056 ui.debug(' %s\n' % (lfhash))
1058 1057 else:
1059 1058 toupload = set()
1060 1059 def addfunc(fn, lfhash):
1061 1060 toupload.add(fn)
1062 1061 lfhashes.add(lfhash)
1063 1062 def showhashes(fn):
1064 1063 pass
1065 1064 _getoutgoings(repo, other, missing, addfunc)
1066 1065
1067 1066 if not toupload:
1068 1067 ui.status(_('largefiles: no files to upload\n'))
1069 1068 else:
1070 1069 ui.status(_('largefiles to upload (%d entities):\n')
1071 1070 % (len(lfhashes)))
1072 1071 for file in sorted(toupload):
1073 1072 ui.status(lfutil.splitstandin(file) + '\n')
1074 1073 showhashes(file)
1075 1074 ui.status('\n')
1076 1075
1077 1076 def summaryremotehook(ui, repo, opts, changes):
1078 1077 largeopt = opts.get('large', False)
1079 1078 if changes is None:
1080 1079 if largeopt:
1081 1080 return (False, True) # only outgoing check is needed
1082 1081 else:
1083 1082 return (False, False)
1084 1083 elif largeopt:
1085 1084 url, branch, peer, outgoing = changes[1]
1086 1085 if peer is None:
1087 1086 # i18n: column positioning for "hg summary"
1088 1087 ui.status(_('largefiles: (no remote repo)\n'))
1089 1088 return
1090 1089
1091 1090 toupload = set()
1092 1091 lfhashes = set()
1093 1092 def addfunc(fn, lfhash):
1094 1093 toupload.add(fn)
1095 1094 lfhashes.add(lfhash)
1096 1095 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1097 1096
1098 1097 if not toupload:
1099 1098 # i18n: column positioning for "hg summary"
1100 1099 ui.status(_('largefiles: (no files to upload)\n'))
1101 1100 else:
1102 1101 # i18n: column positioning for "hg summary"
1103 1102 ui.status(_('largefiles: %d entities for %d files to upload\n')
1104 1103 % (len(lfhashes), len(toupload)))
1105 1104
1106 1105 def overridesummary(orig, ui, repo, *pats, **opts):
1107 1106 try:
1108 1107 repo.lfstatus = True
1109 1108 orig(ui, repo, *pats, **opts)
1110 1109 finally:
1111 1110 repo.lfstatus = False
1112 1111
1113 1112 def scmutiladdremove(orig, repo, matcher, prefix, opts={}, dry_run=None,
1114 1113 similarity=None):
1115 1114 if not lfutil.islfilesrepo(repo):
1116 1115 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1117 1116 # Get the list of missing largefiles so we can remove them
1118 1117 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1119 1118 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1120 1119 False, False, False)
1121 1120
1122 1121 # Call into the normal remove code, but the removing of the standin, we want
1123 1122 # to have handled by original addremove. Monkey patching here makes sure
1124 1123 # we don't remove the standin in the largefiles code, preventing a very
1125 1124 # confused state later.
1126 1125 if s.deleted:
1127 1126 m = copy.copy(matcher)
1128 1127
1129 1128 # The m._files and m._map attributes are not changed to the deleted list
1130 1129 # because that affects the m.exact() test, which in turn governs whether
1131 1130 # or not the file name is printed, and how. Simply limit the original
1132 1131 # matches to those in the deleted status list.
1133 1132 matchfn = m.matchfn
1134 1133 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1135 1134
1136 1135 removelargefiles(repo.ui, repo, True, m, **opts)
1137 1136 # Call into the normal add code, and any files that *should* be added as
1138 1137 # largefiles will be
1139 1138 added, bad = addlargefiles(repo.ui, repo, True, matcher, **opts)
1140 1139 # Now that we've handled largefiles, hand off to the original addremove
1141 1140 # function to take care of the rest. Make sure it doesn't do anything with
1142 1141 # largefiles by passing a matcher that will ignore them.
1143 1142 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1144 1143 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1145 1144
1146 1145 # Calling purge with --all will cause the largefiles to be deleted.
1147 1146 # Override repo.status to prevent this from happening.
1148 1147 def overridepurge(orig, ui, repo, *dirs, **opts):
1149 1148 # XXX Monkey patching a repoview will not work. The assigned attribute will
1150 1149 # be set on the unfiltered repo, but we will only lookup attributes in the
1151 1150 # unfiltered repo if the lookup in the repoview object itself fails. As the
1152 1151 # monkey patched method exists on the repoview class the lookup will not
1153 1152 # fail. As a result, the original version will shadow the monkey patched
1154 1153 # one, defeating the monkey patch.
1155 1154 #
1156 1155 # As a work around we use an unfiltered repo here. We should do something
1157 1156 # cleaner instead.
1158 1157 repo = repo.unfiltered()
1159 1158 oldstatus = repo.status
1160 1159 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1161 1160 clean=False, unknown=False, listsubrepos=False):
1162 1161 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1163 1162 listsubrepos)
1164 1163 lfdirstate = lfutil.openlfdirstate(ui, repo)
1165 1164 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1166 1165 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1167 1166 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1168 1167 unknown, ignored, r.clean)
1169 1168 repo.status = overridestatus
1170 1169 orig(ui, repo, *dirs, **opts)
1171 1170 repo.status = oldstatus
1172 1171 def overriderollback(orig, ui, repo, **opts):
1173 1172 wlock = repo.wlock()
1174 1173 try:
1175 1174 before = repo.dirstate.parents()
1176 1175 orphans = set(f for f in repo.dirstate
1177 1176 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1178 1177 result = orig(ui, repo, **opts)
1179 1178 after = repo.dirstate.parents()
1180 1179 if before == after:
1181 1180 return result # no need to restore standins
1182 1181
1183 1182 pctx = repo['.']
1184 1183 for f in repo.dirstate:
1185 1184 if lfutil.isstandin(f):
1186 1185 orphans.discard(f)
1187 1186 if repo.dirstate[f] == 'r':
1188 1187 repo.wvfs.unlinkpath(f, ignoremissing=True)
1189 1188 elif f in pctx:
1190 1189 fctx = pctx[f]
1191 1190 repo.wwrite(f, fctx.data(), fctx.flags())
1192 1191 else:
1193 1192 # content of standin is not so important in 'a',
1194 1193 # 'm' or 'n' (coming from the 2nd parent) cases
1195 1194 lfutil.writestandin(repo, f, '', False)
1196 1195 for standin in orphans:
1197 1196 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1198 1197
1199 1198 lfdirstate = lfutil.openlfdirstate(ui, repo)
1200 1199 orphans = set(lfdirstate)
1201 1200 lfiles = lfutil.listlfiles(repo)
1202 1201 for file in lfiles:
1203 1202 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1204 1203 orphans.discard(file)
1205 1204 for lfile in orphans:
1206 1205 lfdirstate.drop(lfile)
1207 1206 lfdirstate.write()
1208 1207 finally:
1209 1208 wlock.release()
1210 1209 return result
1211 1210
1212 1211 def overridetransplant(orig, ui, repo, *revs, **opts):
1213 1212 resuming = opts.get('continue')
1214 1213 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1215 1214 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1216 1215 try:
1217 1216 result = orig(ui, repo, *revs, **opts)
1218 1217 finally:
1219 1218 repo._lfstatuswriters.pop()
1220 1219 repo._lfcommithooks.pop()
1221 1220 return result
1222 1221
1223 1222 def overridecat(orig, ui, repo, file1, *pats, **opts):
1224 1223 ctx = scmutil.revsingle(repo, opts.get('rev'))
1225 1224 err = 1
1226 1225 notbad = set()
1227 1226 m = scmutil.match(ctx, (file1,) + pats, opts)
1228 1227 origmatchfn = m.matchfn
1229 1228 def lfmatchfn(f):
1230 1229 if origmatchfn(f):
1231 1230 return True
1232 1231 lf = lfutil.splitstandin(f)
1233 1232 if lf is None:
1234 1233 return False
1235 1234 notbad.add(lf)
1236 1235 return origmatchfn(lf)
1237 1236 m.matchfn = lfmatchfn
1238 1237 origbadfn = m.bad
1239 1238 def lfbadfn(f, msg):
1240 1239 if not f in notbad:
1241 1240 origbadfn(f, msg)
1242 1241 m.bad = lfbadfn
1243 1242 for f in ctx.walk(m):
1244 1243 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1245 1244 pathname=f)
1246 1245 lf = lfutil.splitstandin(f)
1247 1246 if lf is None or origmatchfn(f):
1248 1247 # duplicating unreachable code from commands.cat
1249 1248 data = ctx[f].data()
1250 1249 if opts.get('decode'):
1251 1250 data = repo.wwritedata(f, data)
1252 1251 fp.write(data)
1253 1252 else:
1254 1253 hash = lfutil.readstandin(repo, lf, ctx.rev())
1255 1254 if not lfutil.inusercache(repo.ui, hash):
1256 1255 store = basestore._openstore(repo)
1257 1256 success, missing = store.get([(lf, hash)])
1258 1257 if len(success) != 1:
1259 1258 raise util.Abort(
1260 1259 _('largefile %s is not in cache and could not be '
1261 1260 'downloaded') % lf)
1262 1261 path = lfutil.usercachepath(repo.ui, hash)
1263 1262 fpin = open(path, "rb")
1264 1263 for chunk in util.filechunkiter(fpin, 128 * 1024):
1265 1264 fp.write(chunk)
1266 1265 fpin.close()
1267 1266 fp.close()
1268 1267 err = 0
1269 1268 return err
1270 1269
1271 1270 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1272 1271 *args, **kwargs):
1273 1272 wlock = repo.wlock()
1274 1273 try:
1275 1274 # branch | | |
1276 1275 # merge | force | partial | action
1277 1276 # -------+-------+---------+--------------
1278 1277 # x | x | x | linear-merge
1279 1278 # o | x | x | branch-merge
1280 1279 # x | o | x | overwrite (as clean update)
1281 1280 # o | o | x | force-branch-merge (*1)
1282 1281 # x | x | o | (*)
1283 1282 # o | x | o | (*)
1284 1283 # x | o | o | overwrite (as revert)
1285 1284 # o | o | o | (*)
1286 1285 #
1287 1286 # (*) don't care
1288 1287 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1289 1288
1290 1289 linearmerge = not branchmerge and not force and not partial
1291 1290
1292 1291 if linearmerge or (branchmerge and force and not partial):
1293 1292 # update standins for linear-merge or force-branch-merge,
1294 1293 # because largefiles in the working directory may be modified
1295 1294 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1296 1295 unsure, s = lfdirstate.status(match_.always(repo.root,
1297 1296 repo.getcwd()),
1298 1297 [], False, False, False)
1299 1298 for lfile in unsure + s.modified + s.added:
1300 1299 lfutil.updatestandin(repo, lfutil.standin(lfile))
1301 1300
1302 1301 if linearmerge:
1303 1302 # Only call updatelfiles on the standins that have changed
1304 1303 # to save time
1305 1304 oldstandins = lfutil.getstandinsstate(repo)
1306 1305
1307 1306 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1308 1307
1309 1308 filelist = None
1310 1309 if linearmerge:
1311 1310 newstandins = lfutil.getstandinsstate(repo)
1312 1311 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1313 1312
1314 1313 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1315 1314 normallookup=partial)
1316 1315
1317 1316 return result
1318 1317 finally:
1319 1318 wlock.release()
1320 1319
1321 1320 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1322 1321 result = orig(repo, files, *args, **kwargs)
1323 1322
1324 1323 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1325 1324 if filelist:
1326 1325 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1327 1326 printmessage=False, normallookup=True)
1328 1327
1329 1328 return result
@@ -1,173 +1,172 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 httppeer, merge, scmutil, sshpeer, wireproto, revset, subrepo
13 13 from mercurial.i18n import _
14 14 from mercurial.hgweb import hgweb_mod, webcommands
15 15
16 16 import overrides
17 17 import proto
18 18
19 19 def uisetup(ui):
20 20 # Disable auto-status for some commands which assume that all
21 21 # files in the result are under Mercurial's control
22 22
23 23 entry = extensions.wrapcommand(commands.table, 'add',
24 24 overrides.overrideadd)
25 25 addopt = [('', 'large', None, _('add as largefile')),
26 26 ('', 'normal', None, _('add as normal file')),
27 27 ('', 'lfsize', '', _('add all files above this size '
28 28 '(in megabytes) as largefiles '
29 29 '(default: 10)'))]
30 30 entry[1].extend(addopt)
31 31
32 32 # The scmutil function is called both by the (trivial) addremove command,
33 33 # and in the process of handling commit -A (issue3542)
34 34 entry = extensions.wrapfunction(scmutil, 'addremove',
35 35 overrides.scmutiladdremove)
36 entry = extensions.wrapcommand(commands.table, 'remove',
37 overrides.overrideremove)
36 extensions.wrapfunction(cmdutil, 'remove', overrides.cmdutilremove)
38 37 entry = extensions.wrapcommand(commands.table, 'forget',
39 38 overrides.overrideforget)
40 39
41 40 # Subrepos call status function
42 41 entry = extensions.wrapcommand(commands.table, 'status',
43 42 overrides.overridestatus)
44 43 entry = extensions.wrapfunction(subrepo.hgsubrepo, 'status',
45 44 overrides.overridestatusfn)
46 45
47 46 entry = extensions.wrapcommand(commands.table, 'log',
48 47 overrides.overridelog)
49 48 entry = extensions.wrapcommand(commands.table, 'rollback',
50 49 overrides.overriderollback)
51 50 entry = extensions.wrapcommand(commands.table, 'verify',
52 51 overrides.overrideverify)
53 52
54 53 verifyopt = [('', 'large', None,
55 54 _('verify that all largefiles in current revision exists')),
56 55 ('', 'lfa', None,
57 56 _('verify largefiles in all revisions, not just current')),
58 57 ('', 'lfc', None,
59 58 _('verify local largefile contents, not just existence'))]
60 59 entry[1].extend(verifyopt)
61 60
62 61 entry = extensions.wrapcommand(commands.table, 'debugstate',
63 62 overrides.overridedebugstate)
64 63 debugstateopt = [('', 'large', None, _('display largefiles dirstate'))]
65 64 entry[1].extend(debugstateopt)
66 65
67 66 outgoing = lambda orgfunc, *arg, **kwargs: orgfunc(*arg, **kwargs)
68 67 entry = extensions.wrapcommand(commands.table, 'outgoing', outgoing)
69 68 outgoingopt = [('', 'large', None, _('display outgoing largefiles'))]
70 69 entry[1].extend(outgoingopt)
71 70 cmdutil.outgoinghooks.add('largefiles', overrides.outgoinghook)
72 71 entry = extensions.wrapcommand(commands.table, 'summary',
73 72 overrides.overridesummary)
74 73 summaryopt = [('', 'large', None, _('display outgoing largefiles'))]
75 74 entry[1].extend(summaryopt)
76 75 cmdutil.summaryremotehooks.add('largefiles', overrides.summaryremotehook)
77 76
78 77 entry = extensions.wrapcommand(commands.table, 'update',
79 78 overrides.overrideupdate)
80 79 entry = extensions.wrapcommand(commands.table, 'pull',
81 80 overrides.overridepull)
82 81 pullopt = [('', 'all-largefiles', None,
83 82 _('download all pulled versions of largefiles (DEPRECATED)')),
84 83 ('', 'lfrev', [],
85 84 _('download largefiles for these revisions'), _('REV'))]
86 85 entry[1].extend(pullopt)
87 86 revset.symbols['pulled'] = overrides.pulledrevsetsymbol
88 87
89 88 entry = extensions.wrapcommand(commands.table, 'clone',
90 89 overrides.overrideclone)
91 90 cloneopt = [('', 'all-largefiles', None,
92 91 _('download all versions of all largefiles'))]
93 92 entry[1].extend(cloneopt)
94 93 entry = extensions.wrapfunction(hg, 'clone', overrides.hgclone)
95 94
96 95 entry = extensions.wrapcommand(commands.table, 'cat',
97 96 overrides.overridecat)
98 97 entry = extensions.wrapfunction(merge, '_checkunknownfile',
99 98 overrides.overridecheckunknownfile)
100 99 entry = extensions.wrapfunction(merge, 'calculateupdates',
101 100 overrides.overridecalculateupdates)
102 101 entry = extensions.wrapfunction(merge, 'recordupdates',
103 102 overrides.mergerecordupdates)
104 103 entry = extensions.wrapfunction(merge, 'update',
105 104 overrides.mergeupdate)
106 105 entry = extensions.wrapfunction(filemerge, 'filemerge',
107 106 overrides.overridefilemerge)
108 107 entry = extensions.wrapfunction(cmdutil, 'copy',
109 108 overrides.overridecopy)
110 109
111 110 # Summary calls dirty on the subrepos
112 111 entry = extensions.wrapfunction(subrepo.hgsubrepo, 'dirty',
113 112 overrides.overridedirty)
114 113
115 114 # Backout calls revert so we need to override both the command and the
116 115 # function
117 116 entry = extensions.wrapcommand(commands.table, 'revert',
118 117 overrides.overriderevert)
119 118 entry = extensions.wrapfunction(commands, 'revert',
120 119 overrides.overriderevert)
121 120
122 121 extensions.wrapfunction(archival, 'archive', overrides.overridearchive)
123 122 extensions.wrapfunction(subrepo.hgsubrepo, 'archive',
124 123 overrides.hgsubrepoarchive)
125 124 extensions.wrapfunction(cmdutil, 'bailifchanged',
126 125 overrides.overridebailifchanged)
127 126
128 127 extensions.wrapfunction(scmutil, 'marktouched',
129 128 overrides.scmutilmarktouched)
130 129
131 130 # create the new wireproto commands ...
132 131 wireproto.commands['putlfile'] = (proto.putlfile, 'sha')
133 132 wireproto.commands['getlfile'] = (proto.getlfile, 'sha')
134 133 wireproto.commands['statlfile'] = (proto.statlfile, 'sha')
135 134
136 135 # ... and wrap some existing ones
137 136 wireproto.commands['capabilities'] = (proto.capabilities, '')
138 137 wireproto.commands['heads'] = (proto.heads, '')
139 138 wireproto.commands['lheads'] = (wireproto.heads, '')
140 139
141 140 # make putlfile behave the same as push and {get,stat}lfile behave
142 141 # the same as pull w.r.t. permissions checks
143 142 hgweb_mod.perms['putlfile'] = 'push'
144 143 hgweb_mod.perms['getlfile'] = 'pull'
145 144 hgweb_mod.perms['statlfile'] = 'pull'
146 145
147 146 extensions.wrapfunction(webcommands, 'decodepath', overrides.decodepath)
148 147
149 148 # the hello wireproto command uses wireproto.capabilities, so it won't see
150 149 # our largefiles capability unless we replace the actual function as well.
151 150 proto.capabilitiesorig = wireproto.capabilities
152 151 wireproto.capabilities = proto.capabilities
153 152
154 153 # can't do this in reposetup because it needs to have happened before
155 154 # wirerepo.__init__ is called
156 155 proto.ssholdcallstream = sshpeer.sshpeer._callstream
157 156 proto.httpoldcallstream = httppeer.httppeer._callstream
158 157 sshpeer.sshpeer._callstream = proto.sshrepocallstream
159 158 httppeer.httppeer._callstream = proto.httprepocallstream
160 159
161 160 # override some extensions' stuff as well
162 161 for name, module in extensions.extensions():
163 162 if name == 'purge':
164 163 extensions.wrapcommand(getattr(module, 'cmdtable'), 'purge',
165 164 overrides.overridepurge)
166 165 if name == 'rebase':
167 166 extensions.wrapcommand(getattr(module, 'cmdtable'), 'rebase',
168 167 overrides.overriderebase)
169 168 extensions.wrapfunction(module, 'rebase',
170 169 overrides.overriderebase)
171 170 if name == 'transplant':
172 171 extensions.wrapcommand(getattr(module, 'cmdtable'), 'transplant',
173 172 overrides.overridetransplant)
@@ -1,928 +1,931 b''
1 1 This file contains testcases that tend to be related to special cases or less
2 2 common commands affecting largefile.
3 3
4 4 Each sections should be independent of each others.
5 5
6 6 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
7 7 $ mkdir "${USERCACHE}"
8 8 $ cat >> $HGRCPATH <<EOF
9 9 > [extensions]
10 10 > largefiles=
11 11 > purge=
12 12 > rebase=
13 13 > transplant=
14 14 > [phases]
15 15 > publish=False
16 16 > [largefiles]
17 17 > minsize=2
18 18 > patterns=glob:**.dat
19 19 > usercache=${USERCACHE}
20 20 > [hooks]
21 21 > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status"
22 22 > EOF
23 23
24 24
25 25
26 26 Test copies and moves from a directory other than root (issue3516)
27 27 =========================================================================
28 28
29 29 $ hg init lf_cpmv
30 30 $ cd lf_cpmv
31 31 $ mkdir dira
32 32 $ mkdir dira/dirb
33 33 $ touch dira/dirb/largefile
34 34 $ hg add --large dira/dirb/largefile
35 35 $ hg commit -m "added"
36 36 Invoking status precommit hook
37 37 A dira/dirb/largefile
38 38 $ cd dira
39 39 $ hg cp dirb/largefile foo/largefile
40 40 $ hg ci -m "deep copy"
41 41 Invoking status precommit hook
42 42 A dira/foo/largefile
43 43 $ find . | sort
44 44 .
45 45 ./dirb
46 46 ./dirb/largefile
47 47 ./foo
48 48 ./foo/largefile
49 49 $ hg mv foo/largefile baz/largefile
50 50 $ hg ci -m "moved"
51 51 Invoking status precommit hook
52 52 A dira/baz/largefile
53 53 R dira/foo/largefile
54 54 $ find . | sort
55 55 .
56 56 ./baz
57 57 ./baz/largefile
58 58 ./dirb
59 59 ./dirb/largefile
60 60 $ cd ..
61 61 $ hg mv dira dirc
62 62 moving .hglf/dira/baz/largefile to .hglf/dirc/baz/largefile (glob)
63 63 moving .hglf/dira/dirb/largefile to .hglf/dirc/dirb/largefile (glob)
64 64 $ find * | sort
65 65 dirc
66 66 dirc/baz
67 67 dirc/baz/largefile
68 68 dirc/dirb
69 69 dirc/dirb/largefile
70 70
71 71 $ hg clone -q . ../fetch
72 72 $ hg --config extensions.fetch= fetch ../fetch
73 73 abort: uncommitted changes
74 74 [255]
75 75 $ hg up -qC
76 76 $ cd ..
77 77
78 78 Clone a local repository owned by another user
79 79 ===================================================
80 80
81 81 #if unix-permissions
82 82
83 83 We have to simulate that here by setting $HOME and removing write permissions
84 84 $ ORIGHOME="$HOME"
85 85 $ mkdir alice
86 86 $ HOME="`pwd`/alice"
87 87 $ cd alice
88 88 $ hg init pubrepo
89 89 $ cd pubrepo
90 90 $ dd if=/dev/zero bs=1k count=11k > a-large-file 2> /dev/null
91 91 $ hg add --large a-large-file
92 92 $ hg commit -m "Add a large file"
93 93 Invoking status precommit hook
94 94 A a-large-file
95 95 $ cd ..
96 96 $ chmod -R a-w pubrepo
97 97 $ cd ..
98 98 $ mkdir bob
99 99 $ HOME="`pwd`/bob"
100 100 $ cd bob
101 101 $ hg clone --pull ../alice/pubrepo pubrepo
102 102 requesting all changes
103 103 adding changesets
104 104 adding manifests
105 105 adding file changes
106 106 added 1 changesets with 1 changes to 1 files
107 107 updating to branch default
108 108 getting changed largefiles
109 109 1 largefiles updated, 0 removed
110 110 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
111 111 $ cd ..
112 112 $ chmod -R u+w alice/pubrepo
113 113 $ HOME="$ORIGHOME"
114 114
115 115 #endif
116 116
117 117
118 118 Symlink to a large largefile should behave the same as a symlink to a normal file
119 119 =====================================================================================
120 120
121 121 #if symlink
122 122
123 123 $ hg init largesymlink
124 124 $ cd largesymlink
125 125 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
126 126 $ hg add --large largefile
127 127 $ hg commit -m "commit a large file"
128 128 Invoking status precommit hook
129 129 A largefile
130 130 $ ln -s largefile largelink
131 131 $ hg add largelink
132 132 $ hg commit -m "commit a large symlink"
133 133 Invoking status precommit hook
134 134 A largelink
135 135 $ rm -f largelink
136 136 $ hg up >/dev/null
137 137 $ test -f largelink
138 138 [1]
139 139 $ test -L largelink
140 140 [1]
141 141 $ rm -f largelink # make next part of the test independent of the previous
142 142 $ hg up -C >/dev/null
143 143 $ test -f largelink
144 144 $ test -L largelink
145 145 $ cd ..
146 146
147 147 #endif
148 148
149 149
150 150 test for pattern matching on 'hg status':
151 151 ==============================================
152 152
153 153
154 154 to boost performance, largefiles checks whether specified patterns are
155 155 related to largefiles in working directory (NOT to STANDIN) or not.
156 156
157 157 $ hg init statusmatch
158 158 $ cd statusmatch
159 159
160 160 $ mkdir -p a/b/c/d
161 161 $ echo normal > a/b/c/d/e.normal.txt
162 162 $ hg add a/b/c/d/e.normal.txt
163 163 $ echo large > a/b/c/d/e.large.txt
164 164 $ hg add --large a/b/c/d/e.large.txt
165 165 $ mkdir -p a/b/c/x
166 166 $ echo normal > a/b/c/x/y.normal.txt
167 167 $ hg add a/b/c/x/y.normal.txt
168 168 $ hg commit -m 'add files'
169 169 Invoking status precommit hook
170 170 A a/b/c/d/e.large.txt
171 171 A a/b/c/d/e.normal.txt
172 172 A a/b/c/x/y.normal.txt
173 173
174 174 (1) no pattern: no performance boost
175 175 $ hg status -A
176 176 C a/b/c/d/e.large.txt
177 177 C a/b/c/d/e.normal.txt
178 178 C a/b/c/x/y.normal.txt
179 179
180 180 (2) pattern not related to largefiles: performance boost
181 181 $ hg status -A a/b/c/x
182 182 C a/b/c/x/y.normal.txt
183 183
184 184 (3) pattern related to largefiles: no performance boost
185 185 $ hg status -A a/b/c/d
186 186 C a/b/c/d/e.large.txt
187 187 C a/b/c/d/e.normal.txt
188 188
189 189 (4) pattern related to STANDIN (not to largefiles): performance boost
190 190 $ hg status -A .hglf/a
191 191 C .hglf/a/b/c/d/e.large.txt
192 192
193 193 (5) mixed case: no performance boost
194 194 $ hg status -A a/b/c/x a/b/c/d
195 195 C a/b/c/d/e.large.txt
196 196 C a/b/c/d/e.normal.txt
197 197 C a/b/c/x/y.normal.txt
198 198
199 199 verify that largefiles doesn't break filesets
200 200
201 201 $ hg log --rev . --exclude "set:binary()"
202 202 changeset: 0:41bd42f10efa
203 203 tag: tip
204 204 user: test
205 205 date: Thu Jan 01 00:00:00 1970 +0000
206 206 summary: add files
207 207
208 208 verify that large files in subrepos handled properly
209 209 $ hg init subrepo
210 210 $ echo "subrepo = subrepo" > .hgsub
211 211 $ hg add .hgsub
212 212 $ hg ci -m "add subrepo"
213 213 Invoking status precommit hook
214 214 A .hgsub
215 215 ? .hgsubstate
216 216 $ echo "rev 1" > subrepo/large.txt
217 217 $ hg -R subrepo add --large subrepo/large.txt
218 218 $ hg sum
219 219 parent: 1:8ee150ea2e9c tip
220 220 add subrepo
221 221 branch: default
222 222 commit: 1 subrepos
223 223 update: (current)
224 224 $ hg st
225 225 $ hg st -S
226 226 A subrepo/large.txt
227 227 $ hg ci -S -m "commit top repo"
228 228 committing subrepository subrepo
229 229 Invoking status precommit hook
230 230 A large.txt
231 231 Invoking status precommit hook
232 232 M .hgsubstate
233 233 # No differences
234 234 $ hg st -S
235 235 $ hg sum
236 236 parent: 2:ce4cd0c527a6 tip
237 237 commit top repo
238 238 branch: default
239 239 commit: (clean)
240 240 update: (current)
241 241 $ echo "rev 2" > subrepo/large.txt
242 242 $ hg st -S
243 243 M subrepo/large.txt
244 244 $ hg sum
245 245 parent: 2:ce4cd0c527a6 tip
246 246 commit top repo
247 247 branch: default
248 248 commit: 1 subrepos
249 249 update: (current)
250 250 $ hg ci -m "this commit should fail without -S"
251 251 abort: uncommitted changes in subrepo subrepo
252 252 (use --subrepos for recursive commit)
253 253 [255]
254 254
255 255 Add a normal file to the subrepo, then test archiving
256 256
257 257 $ echo 'normal file' > subrepo/normal.txt
258 258 $ touch large.dat
259 259 $ mv subrepo/large.txt subrepo/renamed-large.txt
260 260 $ hg addremove -S --dry-run
261 261 adding large.dat as a largefile
262 262 removing subrepo/large.txt
263 263 adding subrepo/normal.txt
264 264 adding subrepo/renamed-large.txt
265 265 $ hg status -S
266 266 ! subrepo/large.txt
267 267 ? large.dat
268 268 ? subrepo/normal.txt
269 269 ? subrepo/renamed-large.txt
270 270
271 271 $ hg addremove --dry-run subrepo
272 272 removing subrepo/large.txt (glob)
273 273 adding subrepo/normal.txt (glob)
274 274 adding subrepo/renamed-large.txt (glob)
275 275 $ hg status -S
276 276 ! subrepo/large.txt
277 277 ? large.dat
278 278 ? subrepo/normal.txt
279 279 ? subrepo/renamed-large.txt
280 280 $ cd ..
281 281
282 282 $ hg -R statusmatch addremove --dry-run statusmatch/subrepo
283 283 removing statusmatch/subrepo/large.txt (glob)
284 284 adding statusmatch/subrepo/normal.txt (glob)
285 285 adding statusmatch/subrepo/renamed-large.txt (glob)
286 286 $ hg -R statusmatch status -S
287 287 ! subrepo/large.txt
288 288 ? large.dat
289 289 ? subrepo/normal.txt
290 290 ? subrepo/renamed-large.txt
291 291
292 292 $ hg -R statusmatch addremove --dry-run -S
293 293 adding large.dat as a largefile
294 294 removing subrepo/large.txt
295 295 adding subrepo/normal.txt
296 296 adding subrepo/renamed-large.txt
297 297 $ cd statusmatch
298 298
299 299 $ mv subrepo/renamed-large.txt subrepo/large.txt
300 300 $ hg addremove subrepo
301 301 adding subrepo/normal.txt (glob)
302 302 $ hg forget subrepo/normal.txt
303 303
304 304 $ hg addremove -S
305 305 adding large.dat as a largefile
306 306 adding subrepo/normal.txt
307 307 $ rm large.dat
308 308
309 309 $ hg addremove subrepo
310 310 $ hg addremove -S
311 311 removing large.dat
312 312
313 313 Lock in subrepo, otherwise the change isn't archived
314 314
315 315 $ hg ci -S -m "add normal file to top level"
316 316 committing subrepository subrepo
317 317 Invoking status precommit hook
318 318 M large.txt
319 319 A normal.txt
320 320 Invoking status precommit hook
321 321 M .hgsubstate
322 322 $ hg archive -S ../lf_subrepo_archive
323 323 $ find ../lf_subrepo_archive | sort
324 324 ../lf_subrepo_archive
325 325 ../lf_subrepo_archive/.hg_archival.txt
326 326 ../lf_subrepo_archive/.hgsub
327 327 ../lf_subrepo_archive/.hgsubstate
328 328 ../lf_subrepo_archive/a
329 329 ../lf_subrepo_archive/a/b
330 330 ../lf_subrepo_archive/a/b/c
331 331 ../lf_subrepo_archive/a/b/c/d
332 332 ../lf_subrepo_archive/a/b/c/d/e.large.txt
333 333 ../lf_subrepo_archive/a/b/c/d/e.normal.txt
334 334 ../lf_subrepo_archive/a/b/c/x
335 335 ../lf_subrepo_archive/a/b/c/x/y.normal.txt
336 336 ../lf_subrepo_archive/subrepo
337 337 ../lf_subrepo_archive/subrepo/large.txt
338 338 ../lf_subrepo_archive/subrepo/normal.txt
339 339
340 340 Test update with subrepos.
341 341
342 342 $ hg update 0
343 343 getting changed largefiles
344 344 0 largefiles updated, 1 removed
345 345 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
346 346 $ hg status -S
347 347 $ hg update tip
348 348 getting changed largefiles
349 349 1 largefiles updated, 0 removed
350 350 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
351 351 $ hg status -S
352 352 # modify a large file
353 353 $ echo "modified" > subrepo/large.txt
354 354 $ hg st -S
355 355 M subrepo/large.txt
356 356 # update -C should revert the change.
357 357 $ hg update -C
358 358 getting changed largefiles
359 359 1 largefiles updated, 0 removed
360 360 getting changed largefiles
361 361 0 largefiles updated, 0 removed
362 362 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
363 363 $ hg status -S
364 364
365 $ hg rm -v subrepo/large.txt
366 removing subrepo/large.txt (glob)
367 $ hg revert -R subrepo subrepo/large.txt
365 368 $ rm subrepo/large.txt
366 369 $ hg addremove -S
367 370 removing subrepo/large.txt
368 371 $ hg st -S
369 372 R subrepo/large.txt
370 373
371 374 Test archiving a revision that references a subrepo that is not yet
372 375 cloned (see test-subrepo-recursion.t):
373 376
374 377 $ hg clone -U . ../empty
375 378 $ cd ../empty
376 379 $ hg archive --subrepos -r tip ../archive.tar.gz
377 380 cloning subrepo subrepo from $TESTTMP/statusmatch/subrepo
378 381 $ cd ..
379 382
380 383
381 384
382 385
383 386
384 387
385 388 Test addremove, forget and others
386 389 ==============================================
387 390
388 391 Test that addremove picks up largefiles prior to the initial commit (issue3541)
389 392
390 393 $ hg init addrm2
391 394 $ cd addrm2
392 395 $ touch large.dat
393 396 $ touch large2.dat
394 397 $ touch normal
395 398 $ hg add --large large.dat
396 399 $ hg addremove -v
397 400 adding large2.dat as a largefile
398 401 adding normal
399 402
400 403 Test that forgetting all largefiles reverts to islfilesrepo() == False
401 404 (addremove will add *.dat as normal files now)
402 405 $ hg forget large.dat
403 406 $ hg forget large2.dat
404 407 $ hg addremove -v
405 408 adding large.dat
406 409 adding large2.dat
407 410
408 411 Test commit's addremove option prior to the first commit
409 412 $ hg forget large.dat
410 413 $ hg forget large2.dat
411 414 $ hg add --large large.dat
412 415 $ hg ci -Am "commit"
413 416 adding large2.dat as a largefile
414 417 Invoking status precommit hook
415 418 A large.dat
416 419 A large2.dat
417 420 A normal
418 421 $ find .hglf | sort
419 422 .hglf
420 423 .hglf/large.dat
421 424 .hglf/large2.dat
422 425
423 426 Test actions on largefiles using relative paths from subdir
424 427
425 428 $ mkdir sub
426 429 $ cd sub
427 430 $ echo anotherlarge > anotherlarge
428 431 $ hg add --large anotherlarge
429 432 $ hg st
430 433 A sub/anotherlarge
431 434 $ hg st anotherlarge
432 435 A anotherlarge
433 436 $ hg commit -m anotherlarge anotherlarge
434 437 Invoking status precommit hook
435 438 A sub/anotherlarge
436 439 $ hg log anotherlarge
437 440 changeset: 1:9627a577c5e9
438 441 tag: tip
439 442 user: test
440 443 date: Thu Jan 01 00:00:00 1970 +0000
441 444 summary: anotherlarge
442 445
443 446 $ hg log -G anotherlarge
444 447 @ changeset: 1:9627a577c5e9
445 448 | tag: tip
446 449 | user: test
447 450 | date: Thu Jan 01 00:00:00 1970 +0000
448 451 | summary: anotherlarge
449 452 |
450 453 $ echo more >> anotherlarge
451 454 $ hg st .
452 455 M anotherlarge
453 456 $ hg cat anotherlarge
454 457 anotherlarge
455 458 $ hg revert anotherlarge
456 459 $ hg st
457 460 ? sub/anotherlarge.orig
458 461 $ cd ..
459 462
460 463 $ cd ..
461 464
462 465 Check error message while exchange
463 466 =========================================================
464 467
465 468 issue3651: summary/outgoing with largefiles shows "no remote repo"
466 469 unexpectedly
467 470
468 471 $ mkdir issue3651
469 472 $ cd issue3651
470 473
471 474 $ hg init src
472 475 $ echo a > src/a
473 476 $ hg -R src add --large src/a
474 477 $ hg -R src commit -m '#0'
475 478 Invoking status precommit hook
476 479 A a
477 480
478 481 check messages when no remote repository is specified:
479 482 "no remote repo" route for "hg outgoing --large" is not tested here,
480 483 because it can't be reproduced easily.
481 484
482 485 $ hg init clone1
483 486 $ hg -R clone1 -q pull src
484 487 $ hg -R clone1 -q update
485 488 $ hg -R clone1 paths | grep default
486 489 [1]
487 490
488 491 $ hg -R clone1 summary --large
489 492 parent: 0:fc0bd45326d3 tip
490 493 #0
491 494 branch: default
492 495 commit: (clean)
493 496 update: (current)
494 497 largefiles: (no remote repo)
495 498
496 499 check messages when there is no files to upload:
497 500
498 501 $ hg -q clone src clone2
499 502 $ hg -R clone2 paths | grep default
500 503 default = $TESTTMP/issue3651/src (glob)
501 504
502 505 $ hg -R clone2 summary --large
503 506 parent: 0:fc0bd45326d3 tip
504 507 #0
505 508 branch: default
506 509 commit: (clean)
507 510 update: (current)
508 511 largefiles: (no files to upload)
509 512 $ hg -R clone2 outgoing --large
510 513 comparing with $TESTTMP/issue3651/src (glob)
511 514 searching for changes
512 515 no changes found
513 516 largefiles: no files to upload
514 517 [1]
515 518
516 519 $ hg -R clone2 outgoing --large --graph --template "{rev}"
517 520 comparing with $TESTTMP/issue3651/src (glob)
518 521 searching for changes
519 522 no changes found
520 523 largefiles: no files to upload
521 524
522 525 check messages when there are files to upload:
523 526
524 527 $ echo b > clone2/b
525 528 $ hg -R clone2 add --large clone2/b
526 529 $ hg -R clone2 commit -m '#1'
527 530 Invoking status precommit hook
528 531 A b
529 532 $ hg -R clone2 summary --large
530 533 parent: 1:1acbe71ce432 tip
531 534 #1
532 535 branch: default
533 536 commit: (clean)
534 537 update: (current)
535 538 largefiles: 1 entities for 1 files to upload
536 539 $ hg -R clone2 outgoing --large
537 540 comparing with $TESTTMP/issue3651/src (glob)
538 541 searching for changes
539 542 changeset: 1:1acbe71ce432
540 543 tag: tip
541 544 user: test
542 545 date: Thu Jan 01 00:00:00 1970 +0000
543 546 summary: #1
544 547
545 548 largefiles to upload (1 entities):
546 549 b
547 550
548 551 $ hg -R clone2 outgoing --large --graph --template "{rev}"
549 552 comparing with $TESTTMP/issue3651/src (glob)
550 553 searching for changes
551 554 @ 1
552 555
553 556 largefiles to upload (1 entities):
554 557 b
555 558
556 559
557 560 $ cp clone2/b clone2/b1
558 561 $ cp clone2/b clone2/b2
559 562 $ hg -R clone2 add --large clone2/b1 clone2/b2
560 563 $ hg -R clone2 commit -m '#2: add largefiles referring same entity'
561 564 Invoking status precommit hook
562 565 A b1
563 566 A b2
564 567 $ hg -R clone2 summary --large
565 568 parent: 2:6095d0695d70 tip
566 569 #2: add largefiles referring same entity
567 570 branch: default
568 571 commit: (clean)
569 572 update: (current)
570 573 largefiles: 1 entities for 3 files to upload
571 574 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
572 575 comparing with $TESTTMP/issue3651/src (glob)
573 576 searching for changes
574 577 1:1acbe71ce432
575 578 2:6095d0695d70
576 579 largefiles to upload (1 entities):
577 580 b
578 581 b1
579 582 b2
580 583
581 584 $ hg -R clone2 cat -r 1 clone2/.hglf/b
582 585 89e6c98d92887913cadf06b2adb97f26cde4849b
583 586 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
584 587 comparing with $TESTTMP/issue3651/src (glob)
585 588 query 1; heads
586 589 searching for changes
587 590 all remote heads known locally
588 591 1:1acbe71ce432
589 592 2:6095d0695d70
590 593 largefiles to upload (1 entities):
591 594 b
592 595 89e6c98d92887913cadf06b2adb97f26cde4849b
593 596 b1
594 597 89e6c98d92887913cadf06b2adb97f26cde4849b
595 598 b2
596 599 89e6c98d92887913cadf06b2adb97f26cde4849b
597 600
598 601
599 602 $ echo bbb > clone2/b
600 603 $ hg -R clone2 commit -m '#3: add new largefile entity as existing file'
601 604 Invoking status precommit hook
602 605 M b
603 606 $ echo bbbb > clone2/b
604 607 $ hg -R clone2 commit -m '#4: add new largefile entity as existing file'
605 608 Invoking status precommit hook
606 609 M b
607 610 $ cp clone2/b1 clone2/b
608 611 $ hg -R clone2 commit -m '#5: refer existing largefile entity again'
609 612 Invoking status precommit hook
610 613 M b
611 614 $ hg -R clone2 summary --large
612 615 parent: 5:036794ea641c tip
613 616 #5: refer existing largefile entity again
614 617 branch: default
615 618 commit: (clean)
616 619 update: (current)
617 620 largefiles: 3 entities for 3 files to upload
618 621 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
619 622 comparing with $TESTTMP/issue3651/src (glob)
620 623 searching for changes
621 624 1:1acbe71ce432
622 625 2:6095d0695d70
623 626 3:7983dce246cc
624 627 4:233f12ada4ae
625 628 5:036794ea641c
626 629 largefiles to upload (3 entities):
627 630 b
628 631 b1
629 632 b2
630 633
631 634 $ hg -R clone2 cat -r 3 clone2/.hglf/b
632 635 c801c9cfe94400963fcb683246217d5db77f9a9a
633 636 $ hg -R clone2 cat -r 4 clone2/.hglf/b
634 637 13f9ed0898e315bf59dc2973fec52037b6f441a2
635 638 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
636 639 comparing with $TESTTMP/issue3651/src (glob)
637 640 query 1; heads
638 641 searching for changes
639 642 all remote heads known locally
640 643 1:1acbe71ce432
641 644 2:6095d0695d70
642 645 3:7983dce246cc
643 646 4:233f12ada4ae
644 647 5:036794ea641c
645 648 largefiles to upload (3 entities):
646 649 b
647 650 13f9ed0898e315bf59dc2973fec52037b6f441a2
648 651 89e6c98d92887913cadf06b2adb97f26cde4849b
649 652 c801c9cfe94400963fcb683246217d5db77f9a9a
650 653 b1
651 654 89e6c98d92887913cadf06b2adb97f26cde4849b
652 655 b2
653 656 89e6c98d92887913cadf06b2adb97f26cde4849b
654 657
655 658
656 659 Pushing revision #1 causes uploading entity 89e6c98d9288, which is
657 660 shared also by largefiles b1, b2 in revision #2 and b in revision #5.
658 661
659 662 Then, entity 89e6c98d9288 is not treated as "outgoing entity" at "hg
660 663 summary" and "hg outgoing", even though files in outgoing revision #2
661 664 and #5 refer it.
662 665
663 666 $ hg -R clone2 push -r 1 -q
664 667 $ hg -R clone2 summary --large
665 668 parent: 5:036794ea641c tip
666 669 #5: refer existing largefile entity again
667 670 branch: default
668 671 commit: (clean)
669 672 update: (current)
670 673 largefiles: 2 entities for 1 files to upload
671 674 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
672 675 comparing with $TESTTMP/issue3651/src (glob)
673 676 searching for changes
674 677 2:6095d0695d70
675 678 3:7983dce246cc
676 679 4:233f12ada4ae
677 680 5:036794ea641c
678 681 largefiles to upload (2 entities):
679 682 b
680 683
681 684 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
682 685 comparing with $TESTTMP/issue3651/src (glob)
683 686 query 1; heads
684 687 searching for changes
685 688 all remote heads known locally
686 689 2:6095d0695d70
687 690 3:7983dce246cc
688 691 4:233f12ada4ae
689 692 5:036794ea641c
690 693 largefiles to upload (2 entities):
691 694 b
692 695 13f9ed0898e315bf59dc2973fec52037b6f441a2
693 696 c801c9cfe94400963fcb683246217d5db77f9a9a
694 697
695 698
696 699 $ cd ..
697 700
698 701 merge action 'd' for 'local renamed directory to d2/g' which has no filename
699 702 ==================================================================================
700 703
701 704 $ hg init merge-action
702 705 $ cd merge-action
703 706 $ touch l
704 707 $ hg add --large l
705 708 $ mkdir d1
706 709 $ touch d1/f
707 710 $ hg ci -Aqm0
708 711 Invoking status precommit hook
709 712 A d1/f
710 713 A l
711 714 $ echo > d1/f
712 715 $ touch d1/g
713 716 $ hg ci -Aqm1
714 717 Invoking status precommit hook
715 718 M d1/f
716 719 A d1/g
717 720 $ hg up -qr0
718 721 $ hg mv d1 d2
719 722 moving d1/f to d2/f (glob)
720 723 $ hg ci -qm2
721 724 Invoking status precommit hook
722 725 A d2/f
723 726 R d1/f
724 727 $ hg merge
725 728 merging d2/f and d1/f to d2/f
726 729 getting changed largefiles
727 730 0 largefiles updated, 0 removed
728 731 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
729 732 (branch merge, don't forget to commit)
730 733 $ cd ..
731 734
732 735
733 736 Merge conflicts:
734 737 =====================
735 738
736 739 $ hg init merge
737 740 $ cd merge
738 741 $ echo 0 > f-different
739 742 $ echo 0 > f-same
740 743 $ echo 0 > f-unchanged-1
741 744 $ echo 0 > f-unchanged-2
742 745 $ hg add --large *
743 746 $ hg ci -m0
744 747 Invoking status precommit hook
745 748 A f-different
746 749 A f-same
747 750 A f-unchanged-1
748 751 A f-unchanged-2
749 752 $ echo tmp1 > f-unchanged-1
750 753 $ echo tmp1 > f-unchanged-2
751 754 $ echo tmp1 > f-same
752 755 $ hg ci -m1
753 756 Invoking status precommit hook
754 757 M f-same
755 758 M f-unchanged-1
756 759 M f-unchanged-2
757 760 $ echo 2 > f-different
758 761 $ echo 0 > f-unchanged-1
759 762 $ echo 1 > f-unchanged-2
760 763 $ echo 1 > f-same
761 764 $ hg ci -m2
762 765 Invoking status precommit hook
763 766 M f-different
764 767 M f-same
765 768 M f-unchanged-1
766 769 M f-unchanged-2
767 770 $ hg up -qr0
768 771 $ echo tmp2 > f-unchanged-1
769 772 $ echo tmp2 > f-unchanged-2
770 773 $ echo tmp2 > f-same
771 774 $ hg ci -m3
772 775 Invoking status precommit hook
773 776 M f-same
774 777 M f-unchanged-1
775 778 M f-unchanged-2
776 779 created new head
777 780 $ echo 1 > f-different
778 781 $ echo 1 > f-unchanged-1
779 782 $ echo 0 > f-unchanged-2
780 783 $ echo 1 > f-same
781 784 $ hg ci -m4
782 785 Invoking status precommit hook
783 786 M f-different
784 787 M f-same
785 788 M f-unchanged-1
786 789 M f-unchanged-2
787 790 $ hg merge
788 791 largefile f-different has a merge conflict
789 792 ancestor was 09d2af8dd22201dd8d48e5dcfcaed281ff9422c7
790 793 keep (l)ocal e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e or
791 794 take (o)ther 7448d8798a4380162d4b56f9b452e2f6f9e24e7a? l
792 795 getting changed largefiles
793 796 1 largefiles updated, 0 removed
794 797 0 files updated, 4 files merged, 0 files removed, 0 files unresolved
795 798 (branch merge, don't forget to commit)
796 799 $ cat f-different
797 800 1
798 801 $ cat f-same
799 802 1
800 803 $ cat f-unchanged-1
801 804 1
802 805 $ cat f-unchanged-2
803 806 1
804 807 $ cd ..
805 808
806 809 Test largefile insulation (do not enabled a side effect
807 810 ========================================================
808 811
809 812 Check whether "largefiles" feature is supported only in repositories
810 813 enabling largefiles extension.
811 814
812 815 $ mkdir individualenabling
813 816 $ cd individualenabling
814 817
815 818 $ hg init enabledlocally
816 819 $ echo large > enabledlocally/large
817 820 $ hg -R enabledlocally add --large enabledlocally/large
818 821 $ hg -R enabledlocally commit -m '#0'
819 822 Invoking status precommit hook
820 823 A large
821 824
822 825 $ hg init notenabledlocally
823 826 $ echo large > notenabledlocally/large
824 827 $ hg -R notenabledlocally add --large notenabledlocally/large
825 828 $ hg -R notenabledlocally commit -m '#0'
826 829 Invoking status precommit hook
827 830 A large
828 831
829 832 $ cat >> $HGRCPATH <<EOF
830 833 > [extensions]
831 834 > # disable globally
832 835 > largefiles=!
833 836 > EOF
834 837 $ cat >> enabledlocally/.hg/hgrc <<EOF
835 838 > [extensions]
836 839 > # enable locally
837 840 > largefiles=
838 841 > EOF
839 842 $ hg -R enabledlocally root
840 843 $TESTTMP/individualenabling/enabledlocally (glob)
841 844 $ hg -R notenabledlocally root
842 845 abort: repository requires features unknown to this Mercurial: largefiles!
843 846 (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
844 847 [255]
845 848
846 849 $ hg init push-dst
847 850 $ hg -R enabledlocally push push-dst
848 851 pushing to push-dst
849 852 abort: required features are not supported in the destination: largefiles
850 853 [255]
851 854
852 855 $ hg init pull-src
853 856 $ hg -R pull-src pull enabledlocally
854 857 pulling from enabledlocally
855 858 abort: required features are not supported in the destination: largefiles
856 859 [255]
857 860
858 861 $ hg clone enabledlocally clone-dst
859 862 abort: repository requires features unknown to this Mercurial: largefiles!
860 863 (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
861 864 [255]
862 865 $ test -d clone-dst
863 866 [1]
864 867 $ hg clone --pull enabledlocally clone-pull-dst
865 868 abort: required features are not supported in the destination: largefiles
866 869 [255]
867 870 $ test -d clone-pull-dst
868 871 [1]
869 872
870 873 #if serve
871 874
872 875 Test largefiles specific peer setup, when largefiles is enabled
873 876 locally (issue4109)
874 877
875 878 $ hg showconfig extensions | grep largefiles
876 879 extensions.largefiles=!
877 880 $ mkdir -p $TESTTMP/individualenabling/usercache
878 881
879 882 $ hg serve -R enabledlocally -d -p $HGPORT --pid-file hg.pid
880 883 $ cat hg.pid >> $DAEMON_PIDS
881 884
882 885 $ hg init pull-dst
883 886 $ cat > pull-dst/.hg/hgrc <<EOF
884 887 > [extensions]
885 888 > # enable locally
886 889 > largefiles=
887 890 > [largefiles]
888 891 > # ignore system cache to force largefiles specific wire proto access
889 892 > usercache=$TESTTMP/individualenabling/usercache
890 893 > EOF
891 894 $ hg -R pull-dst -q pull -u http://localhost:$HGPORT
892 895
893 896 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
894 897 #endif
895 898
896 899 $ cd ..
897 900
898 901
899 902 Test "pull --rebase" when rebase is enabled before largefiles (issue3861)
900 903 =========================================================================
901 904
902 905 $ hg showconfig extensions | grep largefiles
903 906 extensions.largefiles=!
904 907
905 908 $ mkdir issue3861
906 909 $ cd issue3861
907 910 $ hg init src
908 911 $ hg clone -q src dst
909 912 $ echo a > src/a
910 913 $ hg -R src commit -Aqm "#0"
911 914 Invoking status precommit hook
912 915 A a
913 916
914 917 $ cat >> dst/.hg/hgrc <<EOF
915 918 > [extensions]
916 919 > largefiles=
917 920 > EOF
918 921 $ hg -R dst pull --rebase
919 922 pulling from $TESTTMP/issue3861/src (glob)
920 923 requesting all changes
921 924 adding changesets
922 925 adding manifests
923 926 adding file changes
924 927 added 1 changesets with 1 changes to 1 files
925 928 nothing to rebase - working directory parent is already an ancestor of destination bf5e395ced2c
926 929 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
927 930
928 931 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now