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