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