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