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