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