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