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