##// END OF EJS Templates
largefiles: fix confusion upon removal of added largefile (issue3176)...
Na'Tosha Bard -
r15786:aca0f2b3 stable
parent child Browse files
Show More
@@ -1,912 +1,910 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 node, archival, error, merge
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
23 23 def installnormalfilesmatchfn(manifest):
24 24 '''overrides scmutil.match so that the matcher it returns will ignore all
25 25 largefiles'''
26 26 oldmatch = None # for the closure
27 27 def override_match(ctx, pats=[], opts={}, globbed=False,
28 28 default='relpath'):
29 29 match = oldmatch(ctx, pats, opts, globbed, default)
30 30 m = copy.copy(match)
31 31 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
32 32 manifest)
33 33 m._files = filter(notlfile, m._files)
34 34 m._fmap = set(m._files)
35 35 orig_matchfn = m.matchfn
36 36 m.matchfn = lambda f: notlfile(f) and orig_matchfn(f) or None
37 37 return m
38 38 oldmatch = installmatchfn(override_match)
39 39
40 40 def installmatchfn(f):
41 41 oldmatch = scmutil.match
42 42 setattr(f, 'oldmatch', oldmatch)
43 43 scmutil.match = f
44 44 return oldmatch
45 45
46 46 def restorematchfn():
47 47 '''restores scmutil.match to what it was before installnormalfilesmatchfn
48 48 was called. no-op if scmutil.match is its original function.
49 49
50 50 Note that n calls to installnormalfilesmatchfn will require n calls to
51 51 restore matchfn to reverse'''
52 52 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
53 53
54 54 # -- Wrappers: modify existing commands --------------------------------
55 55
56 56 # Add works by going through the files that the user wanted to add and
57 57 # checking if they should be added as largefiles. Then it makes a new
58 58 # matcher which matches only the normal files and runs the original
59 59 # version of add.
60 60 def override_add(orig, ui, repo, *pats, **opts):
61 61 large = opts.pop('large', None)
62 62 lfsize = lfutil.getminsize(
63 63 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
64 64
65 65 lfmatcher = None
66 66 if os.path.exists(repo.wjoin(lfutil.shortname)):
67 67 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
68 68 if lfpats:
69 69 lfmatcher = match_.match(repo.root, '', list(lfpats))
70 70
71 71 lfnames = []
72 72 m = scmutil.match(repo[None], pats, opts)
73 73 m.bad = lambda x, y: None
74 74 wctx = repo[None]
75 75 for f in repo.walk(m):
76 76 exact = m.exact(f)
77 77 lfile = lfutil.standin(f) in wctx
78 78 nfile = f in wctx
79 79 exists = lfile or nfile
80 80
81 81 # Don't warn the user when they attempt to add a normal tracked file.
82 82 # The normal add code will do that for us.
83 83 if exact and exists:
84 84 if lfile:
85 85 ui.warn(_('%s already a largefile\n') % f)
86 86 continue
87 87
88 88 if exact or not exists:
89 89 abovemin = (lfsize and
90 90 os.lstat(repo.wjoin(f)).st_size >= lfsize * 1024 * 1024)
91 91 if large or abovemin or (lfmatcher and lfmatcher(f)):
92 92 lfnames.append(f)
93 93 if ui.verbose or not exact:
94 94 ui.status(_('adding %s as a largefile\n') % m.rel(f))
95 95
96 96 bad = []
97 97 standins = []
98 98
99 99 # Need to lock, otherwise there could be a race condition between
100 100 # when standins are created and added to the repo.
101 101 wlock = repo.wlock()
102 102 try:
103 103 if not opts.get('dry_run'):
104 104 lfdirstate = lfutil.openlfdirstate(ui, repo)
105 105 for f in lfnames:
106 106 standinname = lfutil.standin(f)
107 107 lfutil.writestandin(repo, standinname, hash='',
108 108 executable=lfutil.getexecutable(repo.wjoin(f)))
109 109 standins.append(standinname)
110 110 if lfdirstate[f] == 'r':
111 111 lfdirstate.normallookup(f)
112 112 else:
113 113 lfdirstate.add(f)
114 114 lfdirstate.write()
115 115 bad += [lfutil.splitstandin(f)
116 116 for f in lfutil.repo_add(repo, standins)
117 117 if f in m.files()]
118 118 finally:
119 119 wlock.release()
120 120
121 121 installnormalfilesmatchfn(repo[None].manifest())
122 122 result = orig(ui, repo, *pats, **opts)
123 123 restorematchfn()
124 124
125 125 return (result == 1 or bad) and 1 or 0
126 126
127 127 def override_remove(orig, ui, repo, *pats, **opts):
128 128 manifest = repo[None].manifest()
129 129 installnormalfilesmatchfn(manifest)
130 130 orig(ui, repo, *pats, **opts)
131 131 restorematchfn()
132 132
133 after, force = opts.get('after'), opts.get('force')
133 after = opts.get('after')
134 134 if not pats and not after:
135 135 raise util.Abort(_('no files specified'))
136 136 m = scmutil.match(repo[None], pats, opts)
137 137 try:
138 138 repo.lfstatus = True
139 139 s = repo.status(match=m, clean=True)
140 140 finally:
141 141 repo.lfstatus = False
142 142 modified, added, deleted, clean = [[f for f in list
143 143 if lfutil.standin(f) in manifest]
144 144 for list in [s[0], s[1], s[3], s[6]]]
145 145
146 146 def warn(files, reason):
147 147 for f in files:
148 ui.warn(_('not removing %s: %s (use -f to force removal)\n')
148 ui.warn(_('not removing %s: %s (use forget to undo)\n')
149 149 % (m.rel(f), reason))
150 150
151 if force:
152 remove, forget = modified + deleted + clean, added
153 elif after:
151 if after:
154 152 remove, forget = deleted, []
155 153 warn(modified + added + clean, _('file still exists'))
156 154 else:
157 155 remove, forget = deleted + clean, []
158 156 warn(modified, _('file is modified'))
159 157 warn(added, _('file has been marked for add'))
160 158
161 159 for f in sorted(remove + forget):
162 160 if ui.verbose or not m.exact(f):
163 161 ui.status(_('removing %s\n') % m.rel(f))
164 162
165 163 # Need to lock because standin files are deleted then removed from the
166 164 # repository and we could race inbetween.
167 165 wlock = repo.wlock()
168 166 try:
169 167 lfdirstate = lfutil.openlfdirstate(ui, repo)
170 168 for f in remove:
171 169 if not after:
172 170 os.unlink(repo.wjoin(f))
173 171 currentdir = os.path.split(f)[0]
174 172 while currentdir and not os.listdir(repo.wjoin(currentdir)):
175 173 os.rmdir(repo.wjoin(currentdir))
176 174 currentdir = os.path.split(currentdir)[0]
177 175 lfdirstate.remove(f)
178 176 lfdirstate.write()
179 177
180 178 forget = [lfutil.standin(f) for f in forget]
181 179 remove = [lfutil.standin(f) for f in remove]
182 180 lfutil.repo_forget(repo, forget)
183 181 lfutil.repo_remove(repo, remove, unlink=True)
184 182 finally:
185 183 wlock.release()
186 184
187 185 def override_status(orig, ui, repo, *pats, **opts):
188 186 try:
189 187 repo.lfstatus = True
190 188 return orig(ui, repo, *pats, **opts)
191 189 finally:
192 190 repo.lfstatus = False
193 191
194 192 def override_log(orig, ui, repo, *pats, **opts):
195 193 try:
196 194 repo.lfstatus = True
197 195 orig(ui, repo, *pats, **opts)
198 196 finally:
199 197 repo.lfstatus = False
200 198
201 199 def override_verify(orig, ui, repo, *pats, **opts):
202 200 large = opts.pop('large', False)
203 201 all = opts.pop('lfa', False)
204 202 contents = opts.pop('lfc', False)
205 203
206 204 result = orig(ui, repo, *pats, **opts)
207 205 if large:
208 206 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
209 207 return result
210 208
211 209 # Override needs to refresh standins so that update's normal merge
212 210 # will go through properly. Then the other update hook (overriding repo.update)
213 211 # will get the new files. Filemerge is also overriden so that the merge
214 212 # will merge standins correctly.
215 213 def override_update(orig, ui, repo, *pats, **opts):
216 214 lfdirstate = lfutil.openlfdirstate(ui, repo)
217 215 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
218 216 False, False)
219 217 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
220 218
221 219 # Need to lock between the standins getting updated and their
222 220 # largefiles getting updated
223 221 wlock = repo.wlock()
224 222 try:
225 223 if opts['check']:
226 224 mod = len(modified) > 0
227 225 for lfile in unsure:
228 226 standin = lfutil.standin(lfile)
229 227 if repo['.'][standin].data().strip() != \
230 228 lfutil.hashfile(repo.wjoin(lfile)):
231 229 mod = True
232 230 else:
233 231 lfdirstate.normal(lfile)
234 232 lfdirstate.write()
235 233 if mod:
236 234 raise util.Abort(_('uncommitted local changes'))
237 235 # XXX handle removed differently
238 236 if not opts['clean']:
239 237 for lfile in unsure + modified + added:
240 238 lfutil.updatestandin(repo, lfutil.standin(lfile))
241 239 finally:
242 240 wlock.release()
243 241 return orig(ui, repo, *pats, **opts)
244 242
245 243 # Before starting the manifest merge, merge.updates will call
246 244 # _checkunknown to check if there are any files in the merged-in
247 245 # changeset that collide with unknown files in the working copy.
248 246 #
249 247 # The largefiles are seen as unknown, so this prevents us from merging
250 248 # in a file 'foo' if we already have a largefile with the same name.
251 249 #
252 250 # The overridden function filters the unknown files by removing any
253 251 # largefiles. This makes the merge proceed and we can then handle this
254 252 # case further in the overridden manifestmerge function below.
255 253 def override_checkunknown(origfn, wctx, mctx, folding):
256 254 origunknown = wctx.unknown()
257 255 wctx._unknown = filter(lambda f: lfutil.standin(f) not in wctx, origunknown)
258 256 try:
259 257 return origfn(wctx, mctx, folding)
260 258 finally:
261 259 wctx._unknown = origunknown
262 260
263 261 # The manifest merge handles conflicts on the manifest level. We want
264 262 # to handle changes in largefile-ness of files at this level too.
265 263 #
266 264 # The strategy is to run the original manifestmerge and then process
267 265 # the action list it outputs. There are two cases we need to deal with:
268 266 #
269 267 # 1. Normal file in p1, largefile in p2. Here the largefile is
270 268 # detected via its standin file, which will enter the working copy
271 269 # with a "get" action. It is not "merge" since the standin is all
272 270 # Mercurial is concerned with at this level -- the link to the
273 271 # existing normal file is not relevant here.
274 272 #
275 273 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
276 274 # since the largefile will be present in the working copy and
277 275 # different from the normal file in p2. Mercurial therefore
278 276 # triggers a merge action.
279 277 #
280 278 # In both cases, we prompt the user and emit new actions to either
281 279 # remove the standin (if the normal file was kept) or to remove the
282 280 # normal file and get the standin (if the largefile was kept). The
283 281 # default prompt answer is to use the largefile version since it was
284 282 # presumably changed on purpose.
285 283 #
286 284 # Finally, the merge.applyupdates function will then take care of
287 285 # writing the files into the working copy and lfcommands.updatelfiles
288 286 # will update the largefiles.
289 287 def override_manifestmerge(origfn, repo, p1, p2, pa, overwrite, partial):
290 288 actions = origfn(repo, p1, p2, pa, overwrite, partial)
291 289 processed = []
292 290
293 291 for action in actions:
294 292 if overwrite:
295 293 processed.append(action)
296 294 continue
297 295 f, m = action[:2]
298 296
299 297 choices = (_('&Largefile'), _('&Normal file'))
300 298 if m == "g" and lfutil.splitstandin(f) in p1 and f in p2:
301 299 # Case 1: normal file in the working copy, largefile in
302 300 # the second parent
303 301 lfile = lfutil.splitstandin(f)
304 302 standin = f
305 303 msg = _('%s has been turned into a largefile\n'
306 304 'use (l)argefile or keep as (n)ormal file?') % lfile
307 305 if repo.ui.promptchoice(msg, choices, 0) == 0:
308 306 processed.append((lfile, "r"))
309 307 processed.append((standin, "g", p2.flags(standin)))
310 308 else:
311 309 processed.append((standin, "r"))
312 310 elif m == "m" and lfutil.standin(f) in p1 and f in p2:
313 311 # Case 2: largefile in the working copy, normal file in
314 312 # the second parent
315 313 standin = lfutil.standin(f)
316 314 lfile = f
317 315 msg = _('%s has been turned into a normal file\n'
318 316 'keep as (l)argefile or use (n)ormal file?') % lfile
319 317 if repo.ui.promptchoice(msg, choices, 0) == 0:
320 318 processed.append((lfile, "r"))
321 319 else:
322 320 processed.append((standin, "r"))
323 321 processed.append((lfile, "g", p2.flags(lfile)))
324 322 else:
325 323 processed.append(action)
326 324
327 325 return processed
328 326
329 327 # Override filemerge to prompt the user about how they wish to merge
330 328 # largefiles. This will handle identical edits, and copy/rename +
331 329 # edit without prompting the user.
332 330 def override_filemerge(origfn, repo, mynode, orig, fcd, fco, fca):
333 331 # Use better variable names here. Because this is a wrapper we cannot
334 332 # change the variable names in the function declaration.
335 333 fcdest, fcother, fcancestor = fcd, fco, fca
336 334 if not lfutil.isstandin(orig):
337 335 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
338 336 else:
339 337 if not fcother.cmp(fcdest): # files identical?
340 338 return None
341 339
342 340 # backwards, use working dir parent as ancestor
343 341 if fcancestor == fcother:
344 342 fcancestor = fcdest.parents()[0]
345 343
346 344 if orig != fcother.path():
347 345 repo.ui.status(_('merging %s and %s to %s\n')
348 346 % (lfutil.splitstandin(orig),
349 347 lfutil.splitstandin(fcother.path()),
350 348 lfutil.splitstandin(fcdest.path())))
351 349 else:
352 350 repo.ui.status(_('merging %s\n')
353 351 % lfutil.splitstandin(fcdest.path()))
354 352
355 353 if fcancestor.path() != fcother.path() and fcother.data() == \
356 354 fcancestor.data():
357 355 return 0
358 356 if fcancestor.path() != fcdest.path() and fcdest.data() == \
359 357 fcancestor.data():
360 358 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
361 359 return 0
362 360
363 361 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
364 362 'keep (l)ocal or take (o)ther?') %
365 363 lfutil.splitstandin(orig),
366 364 (_('&Local'), _('&Other')), 0) == 0:
367 365 return 0
368 366 else:
369 367 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
370 368 return 0
371 369
372 370 # Copy first changes the matchers to match standins instead of
373 371 # largefiles. Then it overrides util.copyfile in that function it
374 372 # checks if the destination largefile already exists. It also keeps a
375 373 # list of copied files so that the largefiles can be copied and the
376 374 # dirstate updated.
377 375 def override_copy(orig, ui, repo, pats, opts, rename=False):
378 376 # doesn't remove largefile on rename
379 377 if len(pats) < 2:
380 378 # this isn't legal, let the original function deal with it
381 379 return orig(ui, repo, pats, opts, rename)
382 380
383 381 def makestandin(relpath):
384 382 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
385 383 return os.path.join(repo.wjoin(lfutil.standin(path)))
386 384
387 385 fullpats = scmutil.expandpats(pats)
388 386 dest = fullpats[-1]
389 387
390 388 if os.path.isdir(dest):
391 389 if not os.path.isdir(makestandin(dest)):
392 390 os.makedirs(makestandin(dest))
393 391 # This could copy both lfiles and normal files in one command,
394 392 # but we don't want to do that. First replace their matcher to
395 393 # only match normal files and run it, then replace it to just
396 394 # match largefiles and run it again.
397 395 nonormalfiles = False
398 396 nolfiles = False
399 397 try:
400 398 try:
401 399 installnormalfilesmatchfn(repo[None].manifest())
402 400 result = orig(ui, repo, pats, opts, rename)
403 401 except util.Abort, e:
404 402 if str(e) != 'no files to copy':
405 403 raise e
406 404 else:
407 405 nonormalfiles = True
408 406 result = 0
409 407 finally:
410 408 restorematchfn()
411 409
412 410 # The first rename can cause our current working directory to be removed.
413 411 # In that case there is nothing left to copy/rename so just quit.
414 412 try:
415 413 repo.getcwd()
416 414 except OSError:
417 415 return result
418 416
419 417 try:
420 418 try:
421 419 # When we call orig below it creates the standins but we don't add them
422 420 # to the dir state until later so lock during that time.
423 421 wlock = repo.wlock()
424 422
425 423 manifest = repo[None].manifest()
426 424 oldmatch = None # for the closure
427 425 def override_match(ctx, pats=[], opts={}, globbed=False,
428 426 default='relpath'):
429 427 newpats = []
430 428 # The patterns were previously mangled to add the standin
431 429 # directory; we need to remove that now
432 430 for pat in pats:
433 431 if match_.patkind(pat) is None and lfutil.shortname in pat:
434 432 newpats.append(pat.replace(lfutil.shortname, ''))
435 433 else:
436 434 newpats.append(pat)
437 435 match = oldmatch(ctx, newpats, opts, globbed, default)
438 436 m = copy.copy(match)
439 437 lfile = lambda f: lfutil.standin(f) in manifest
440 438 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
441 439 m._fmap = set(m._files)
442 440 orig_matchfn = m.matchfn
443 441 m.matchfn = lambda f: (lfutil.isstandin(f) and
444 442 lfile(lfutil.splitstandin(f)) and
445 443 orig_matchfn(lfutil.splitstandin(f)) or
446 444 None)
447 445 return m
448 446 oldmatch = installmatchfn(override_match)
449 447 listpats = []
450 448 for pat in pats:
451 449 if match_.patkind(pat) is not None:
452 450 listpats.append(pat)
453 451 else:
454 452 listpats.append(makestandin(pat))
455 453
456 454 try:
457 455 origcopyfile = util.copyfile
458 456 copiedfiles = []
459 457 def override_copyfile(src, dest):
460 458 if (lfutil.shortname in src and
461 459 dest.startswith(repo.wjoin(lfutil.shortname))):
462 460 destlfile = dest.replace(lfutil.shortname, '')
463 461 if not opts['force'] and os.path.exists(destlfile):
464 462 raise IOError('',
465 463 _('destination largefile already exists'))
466 464 copiedfiles.append((src, dest))
467 465 origcopyfile(src, dest)
468 466
469 467 util.copyfile = override_copyfile
470 468 result += orig(ui, repo, listpats, opts, rename)
471 469 finally:
472 470 util.copyfile = origcopyfile
473 471
474 472 lfdirstate = lfutil.openlfdirstate(ui, repo)
475 473 for (src, dest) in copiedfiles:
476 474 if (lfutil.shortname in src and
477 475 dest.startswith(repo.wjoin(lfutil.shortname))):
478 476 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
479 477 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
480 478 destlfiledir = os.path.dirname(destlfile) or '.'
481 479 if not os.path.isdir(destlfiledir):
482 480 os.makedirs(destlfiledir)
483 481 if rename:
484 482 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
485 483 lfdirstate.remove(srclfile)
486 484 else:
487 485 util.copyfile(srclfile, destlfile)
488 486 lfdirstate.add(destlfile)
489 487 lfdirstate.write()
490 488 except util.Abort, e:
491 489 if str(e) != 'no files to copy':
492 490 raise e
493 491 else:
494 492 nolfiles = True
495 493 finally:
496 494 restorematchfn()
497 495 wlock.release()
498 496
499 497 if nolfiles and nonormalfiles:
500 498 raise util.Abort(_('no files to copy'))
501 499
502 500 return result
503 501
504 502 # When the user calls revert, we have to be careful to not revert any
505 503 # changes to other largefiles accidentally. This means we have to keep
506 504 # track of the largefiles that are being reverted so we only pull down
507 505 # the necessary largefiles.
508 506 #
509 507 # Standins are only updated (to match the hash of largefiles) before
510 508 # commits. Update the standins then run the original revert, changing
511 509 # the matcher to hit standins instead of largefiles. Based on the
512 510 # resulting standins update the largefiles. Then return the standins
513 511 # to their proper state
514 512 def override_revert(orig, ui, repo, *pats, **opts):
515 513 # Because we put the standins in a bad state (by updating them)
516 514 # and then return them to a correct state we need to lock to
517 515 # prevent others from changing them in their incorrect state.
518 516 wlock = repo.wlock()
519 517 try:
520 518 lfdirstate = lfutil.openlfdirstate(ui, repo)
521 519 (modified, added, removed, missing, unknown, ignored, clean) = \
522 520 lfutil.lfdirstate_status(lfdirstate, repo, repo['.'].rev())
523 521 for lfile in modified:
524 522 lfutil.updatestandin(repo, lfutil.standin(lfile))
525 523
526 524 try:
527 525 ctx = repo[opts.get('rev')]
528 526 oldmatch = None # for the closure
529 527 def override_match(ctx, pats=[], opts={}, globbed=False,
530 528 default='relpath'):
531 529 match = oldmatch(ctx, pats, opts, globbed, default)
532 530 m = copy.copy(match)
533 531 def tostandin(f):
534 532 if lfutil.standin(f) in ctx or lfutil.standin(f) in ctx:
535 533 return lfutil.standin(f)
536 534 elif lfutil.standin(f) in repo[None]:
537 535 return None
538 536 return f
539 537 m._files = [tostandin(f) for f in m._files]
540 538 m._files = [f for f in m._files if f is not None]
541 539 m._fmap = set(m._files)
542 540 orig_matchfn = m.matchfn
543 541 def matchfn(f):
544 542 if lfutil.isstandin(f):
545 543 # We need to keep track of what largefiles are being
546 544 # matched so we know which ones to update later --
547 545 # otherwise we accidentally revert changes to other
548 546 # largefiles. This is repo-specific, so duckpunch the
549 547 # repo object to keep the list of largefiles for us
550 548 # later.
551 549 if orig_matchfn(lfutil.splitstandin(f)) and \
552 550 (f in repo[None] or f in ctx):
553 551 lfileslist = getattr(repo, '_lfilestoupdate', [])
554 552 lfileslist.append(lfutil.splitstandin(f))
555 553 repo._lfilestoupdate = lfileslist
556 554 return True
557 555 else:
558 556 return False
559 557 return orig_matchfn(f)
560 558 m.matchfn = matchfn
561 559 return m
562 560 oldmatch = installmatchfn(override_match)
563 561 scmutil.match
564 562 matches = override_match(repo[None], pats, opts)
565 563 orig(ui, repo, *pats, **opts)
566 564 finally:
567 565 restorematchfn()
568 566 lfileslist = getattr(repo, '_lfilestoupdate', [])
569 567 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
570 568 printmessage=False)
571 569
572 570 # empty out the largefiles list so we start fresh next time
573 571 repo._lfilestoupdate = []
574 572 for lfile in modified:
575 573 if lfile in lfileslist:
576 574 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
577 575 in repo['.']:
578 576 lfutil.writestandin(repo, lfutil.standin(lfile),
579 577 repo['.'][lfile].data().strip(),
580 578 'x' in repo['.'][lfile].flags())
581 579 lfdirstate = lfutil.openlfdirstate(ui, repo)
582 580 for lfile in added:
583 581 standin = lfutil.standin(lfile)
584 582 if standin not in ctx and (standin in matches or opts.get('all')):
585 583 if lfile in lfdirstate:
586 584 lfdirstate.drop(lfile)
587 585 util.unlinkpath(repo.wjoin(standin))
588 586 lfdirstate.write()
589 587 finally:
590 588 wlock.release()
591 589
592 590 def hg_update(orig, repo, node):
593 591 result = orig(repo, node)
594 592 # XXX check if it worked first
595 593 lfcommands.updatelfiles(repo.ui, repo)
596 594 return result
597 595
598 596 def hg_clean(orig, repo, node, show_stats=True):
599 597 result = orig(repo, node, show_stats)
600 598 lfcommands.updatelfiles(repo.ui, repo)
601 599 return result
602 600
603 601 def hg_merge(orig, repo, node, force=None, remind=True):
604 602 result = orig(repo, node, force, remind)
605 603 lfcommands.updatelfiles(repo.ui, repo)
606 604 return result
607 605
608 606 # When we rebase a repository with remotely changed largefiles, we need to
609 607 # take some extra care so that the largefiles are correctly updated in the
610 608 # working copy
611 609 def override_pull(orig, ui, repo, source=None, **opts):
612 610 if opts.get('rebase', False):
613 611 repo._isrebasing = True
614 612 try:
615 613 if opts.get('update'):
616 614 del opts['update']
617 615 ui.debug('--update and --rebase are not compatible, ignoring '
618 616 'the update flag\n')
619 617 del opts['rebase']
620 618 cmdutil.bailifchanged(repo)
621 619 revsprepull = len(repo)
622 620 origpostincoming = commands.postincoming
623 621 def _dummy(*args, **kwargs):
624 622 pass
625 623 commands.postincoming = _dummy
626 624 repo.lfpullsource = source
627 625 if not source:
628 626 source = 'default'
629 627 try:
630 628 result = commands.pull(ui, repo, source, **opts)
631 629 finally:
632 630 commands.postincoming = origpostincoming
633 631 revspostpull = len(repo)
634 632 if revspostpull > revsprepull:
635 633 result = result or rebase.rebase(ui, repo)
636 634 finally:
637 635 repo._isrebasing = False
638 636 else:
639 637 repo.lfpullsource = source
640 638 if not source:
641 639 source = 'default'
642 640 result = orig(ui, repo, source, **opts)
643 641 return result
644 642
645 643 def override_rebase(orig, ui, repo, **opts):
646 644 repo._isrebasing = True
647 645 try:
648 646 orig(ui, repo, **opts)
649 647 finally:
650 648 repo._isrebasing = False
651 649
652 650 def override_archive(orig, repo, dest, node, kind, decode=True, matchfn=None,
653 651 prefix=None, mtime=None, subrepos=None):
654 652 # No need to lock because we are only reading history and
655 653 # largefile caches, neither of which are modified.
656 654 lfcommands.cachelfiles(repo.ui, repo, node)
657 655
658 656 if kind not in archival.archivers:
659 657 raise util.Abort(_("unknown archive type '%s'") % kind)
660 658
661 659 ctx = repo[node]
662 660
663 661 if kind == 'files':
664 662 if prefix:
665 663 raise util.Abort(
666 664 _('cannot give prefix when archiving to files'))
667 665 else:
668 666 prefix = archival.tidyprefix(dest, kind, prefix)
669 667
670 668 def write(name, mode, islink, getdata):
671 669 if matchfn and not matchfn(name):
672 670 return
673 671 data = getdata()
674 672 if decode:
675 673 data = repo.wwritedata(name, data)
676 674 archiver.addfile(prefix + name, mode, islink, data)
677 675
678 676 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
679 677
680 678 if repo.ui.configbool("ui", "archivemeta", True):
681 679 def metadata():
682 680 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
683 681 hex(repo.changelog.node(0)), hex(node), ctx.branch())
684 682
685 683 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
686 684 if repo.tagtype(t) == 'global')
687 685 if not tags:
688 686 repo.ui.pushbuffer()
689 687 opts = {'template': '{latesttag}\n{latesttagdistance}',
690 688 'style': '', 'patch': None, 'git': None}
691 689 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
692 690 ltags, dist = repo.ui.popbuffer().split('\n')
693 691 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
694 692 tags += 'latesttagdistance: %s\n' % dist
695 693
696 694 return base + tags
697 695
698 696 write('.hg_archival.txt', 0644, False, metadata)
699 697
700 698 for f in ctx:
701 699 ff = ctx.flags(f)
702 700 getdata = ctx[f].data
703 701 if lfutil.isstandin(f):
704 702 path = lfutil.findfile(repo, getdata().strip())
705 703 f = lfutil.splitstandin(f)
706 704
707 705 def getdatafn():
708 706 fd = None
709 707 try:
710 708 fd = open(path, 'rb')
711 709 return fd.read()
712 710 finally:
713 711 if fd:
714 712 fd.close()
715 713
716 714 getdata = getdatafn
717 715 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
718 716
719 717 if subrepos:
720 718 for subpath in ctx.substate:
721 719 sub = ctx.sub(subpath)
722 720 try:
723 721 sub.archive(repo.ui, archiver, prefix)
724 722 except TypeError:
725 723 sub.archive(archiver, prefix)
726 724
727 725 archiver.done()
728 726
729 727 # If a largefile is modified, the change is not reflected in its
730 728 # standin until a commit. cmdutil.bailifchanged() raises an exception
731 729 # if the repo has uncommitted changes. Wrap it to also check if
732 730 # largefiles were changed. This is used by bisect and backout.
733 731 def override_bailifchanged(orig, repo):
734 732 orig(repo)
735 733 repo.lfstatus = True
736 734 modified, added, removed, deleted = repo.status()[:4]
737 735 repo.lfstatus = False
738 736 if modified or added or removed or deleted:
739 737 raise util.Abort(_('outstanding uncommitted changes'))
740 738
741 739 # Fetch doesn't use cmdutil.bail_if_changed so override it to add the check
742 740 def override_fetch(orig, ui, repo, *pats, **opts):
743 741 repo.lfstatus = True
744 742 modified, added, removed, deleted = repo.status()[:4]
745 743 repo.lfstatus = False
746 744 if modified or added or removed or deleted:
747 745 raise util.Abort(_('outstanding uncommitted changes'))
748 746 return orig(ui, repo, *pats, **opts)
749 747
750 748 def override_forget(orig, ui, repo, *pats, **opts):
751 749 installnormalfilesmatchfn(repo[None].manifest())
752 750 orig(ui, repo, *pats, **opts)
753 751 restorematchfn()
754 752 m = scmutil.match(repo[None], pats, opts)
755 753
756 754 try:
757 755 repo.lfstatus = True
758 756 s = repo.status(match=m, clean=True)
759 757 finally:
760 758 repo.lfstatus = False
761 759 forget = sorted(s[0] + s[1] + s[3] + s[6])
762 760 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
763 761
764 762 for f in forget:
765 763 if lfutil.standin(f) not in repo.dirstate and not \
766 764 os.path.isdir(m.rel(lfutil.standin(f))):
767 765 ui.warn(_('not removing %s: file is already untracked\n')
768 766 % m.rel(f))
769 767
770 768 for f in forget:
771 769 if ui.verbose or not m.exact(f):
772 770 ui.status(_('removing %s\n') % m.rel(f))
773 771
774 772 # Need to lock because standin files are deleted then removed from the
775 773 # repository and we could race inbetween.
776 774 wlock = repo.wlock()
777 775 try:
778 776 lfdirstate = lfutil.openlfdirstate(ui, repo)
779 777 for f in forget:
780 778 if lfdirstate[f] == 'a':
781 779 lfdirstate.drop(f)
782 780 else:
783 781 lfdirstate.remove(f)
784 782 lfdirstate.write()
785 783 lfutil.repo_remove(repo, [lfutil.standin(f) for f in forget],
786 784 unlink=True)
787 785 finally:
788 786 wlock.release()
789 787
790 788 def getoutgoinglfiles(ui, repo, dest=None, **opts):
791 789 dest = ui.expandpath(dest or 'default-push', dest or 'default')
792 790 dest, branches = hg.parseurl(dest, opts.get('branch'))
793 791 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
794 792 if revs:
795 793 revs = [repo.lookup(rev) for rev in revs]
796 794
797 795 remoteui = hg.remoteui
798 796
799 797 try:
800 798 remote = hg.repository(remoteui(repo, opts), dest)
801 799 except error.RepoError:
802 800 return None
803 801 o = lfutil.findoutgoing(repo, remote, False)
804 802 if not o:
805 803 return None
806 804 o = repo.changelog.nodesbetween(o, revs)[0]
807 805 if opts.get('newest_first'):
808 806 o.reverse()
809 807
810 808 toupload = set()
811 809 for n in o:
812 810 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
813 811 ctx = repo[n]
814 812 files = set(ctx.files())
815 813 if len(parents) == 2:
816 814 mc = ctx.manifest()
817 815 mp1 = ctx.parents()[0].manifest()
818 816 mp2 = ctx.parents()[1].manifest()
819 817 for f in mp1:
820 818 if f not in mc:
821 819 files.add(f)
822 820 for f in mp2:
823 821 if f not in mc:
824 822 files.add(f)
825 823 for f in mc:
826 824 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
827 825 files.add(f)
828 826 toupload = toupload.union(
829 827 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
830 828 return toupload
831 829
832 830 def override_outgoing(orig, ui, repo, dest=None, **opts):
833 831 orig(ui, repo, dest, **opts)
834 832
835 833 if opts.pop('large', None):
836 834 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
837 835 if toupload is None:
838 836 ui.status(_('largefiles: No remote repo\n'))
839 837 else:
840 838 ui.status(_('largefiles to upload:\n'))
841 839 for file in toupload:
842 840 ui.status(lfutil.splitstandin(file) + '\n')
843 841 ui.status('\n')
844 842
845 843 def override_summary(orig, ui, repo, *pats, **opts):
846 844 orig(ui, repo, *pats, **opts)
847 845
848 846 if opts.pop('large', None):
849 847 toupload = getoutgoinglfiles(ui, repo, None, **opts)
850 848 if toupload is None:
851 849 ui.status(_('largefiles: No remote repo\n'))
852 850 else:
853 851 ui.status(_('largefiles: %d to upload\n') % len(toupload))
854 852
855 853 def override_addremove(orig, ui, repo, *pats, **opts):
856 854 # Check if the parent or child has largefiles; if so, disallow
857 855 # addremove. If there is a symlink in the manifest then getting
858 856 # the manifest throws an exception: catch it and let addremove
859 857 # deal with it.
860 858 try:
861 859 manifesttip = set(repo['tip'].manifest())
862 860 except util.Abort:
863 861 manifesttip = set()
864 862 try:
865 863 manifestworking = set(repo[None].manifest())
866 864 except util.Abort:
867 865 manifestworking = set()
868 866
869 867 # Manifests are only iterable so turn them into sets then union
870 868 for file in manifesttip.union(manifestworking):
871 869 if file.startswith(lfutil.shortname):
872 870 raise util.Abort(
873 871 _('addremove cannot be run on a repo with largefiles'))
874 872
875 873 return orig(ui, repo, *pats, **opts)
876 874
877 875 # Calling purge with --all will cause the largefiles to be deleted.
878 876 # Override repo.status to prevent this from happening.
879 877 def override_purge(orig, ui, repo, *dirs, **opts):
880 878 oldstatus = repo.status
881 879 def override_status(node1='.', node2=None, match=None, ignored=False,
882 880 clean=False, unknown=False, listsubrepos=False):
883 881 r = oldstatus(node1, node2, match, ignored, clean, unknown,
884 882 listsubrepos)
885 883 lfdirstate = lfutil.openlfdirstate(ui, repo)
886 884 modified, added, removed, deleted, unknown, ignored, clean = r
887 885 unknown = [f for f in unknown if lfdirstate[f] == '?']
888 886 ignored = [f for f in ignored if lfdirstate[f] == '?']
889 887 return modified, added, removed, deleted, unknown, ignored, clean
890 888 repo.status = override_status
891 889 orig(ui, repo, *dirs, **opts)
892 890 repo.status = oldstatus
893 891
894 892 def override_rollback(orig, ui, repo, **opts):
895 893 result = orig(ui, repo, **opts)
896 894 merge.update(repo, node=None, branchmerge=False, force=True,
897 895 partial=lfutil.isstandin)
898 896 lfdirstate = lfutil.openlfdirstate(ui, repo)
899 897 lfiles = lfutil.listlfiles(repo)
900 898 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
901 899 for file in lfiles:
902 900 if file in oldlfiles:
903 901 lfdirstate.normallookup(file)
904 902 else:
905 903 lfdirstate.add(file)
906 904 lfdirstate.write()
907 905 return result
908 906
909 907 def override_transplant(orig, ui, repo, *revs, **opts):
910 908 result = orig(ui, repo, *revs, **opts)
911 909 lfcommands.updatelfiles(repo.ui, repo)
912 910 return result
@@ -1,874 +1,886 b''
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extensions]
3 3 > largefiles=
4 4 > purge=
5 5 > rebase=
6 6 > transplant=
7 7 > [largefiles]
8 8 > minsize=2
9 9 > patterns=glob:**.dat
10 10 > EOF
11 11
12 12 Create the repo with a couple of revisions of both large and normal
13 13 files, testing that status correctly shows largefiles.
14 14
15 15 $ hg init a
16 16 $ cd a
17 17 $ mkdir sub
18 18 $ echo normal1 > normal1
19 19 $ echo normal2 > sub/normal2
20 20 $ echo large1 > large1
21 21 $ echo large2 > sub/large2
22 22 $ hg add normal1 sub/normal2
23 23 $ hg add --large large1 sub/large2
24 24 $ hg commit -m "add files"
25 25 $ echo normal11 > normal1
26 26 $ echo normal22 > sub/normal2
27 27 $ echo large11 > large1
28 28 $ echo large22 > sub/large2
29 29 $ hg st
30 30 M large1
31 31 M normal1
32 32 M sub/large2
33 33 M sub/normal2
34 34 $ hg commit -m "edit files"
35 35
36 36 Commit preserved largefile contents.
37 37
38 38 $ cat normal1
39 39 normal11
40 40 $ cat large1
41 41 large11
42 42 $ cat sub/normal2
43 43 normal22
44 44 $ cat sub/large2
45 45 large22
46 46
47 47 Remove both largefiles and normal files.
48 48
49 49 $ hg remove normal1 large1
50 50 $ hg commit -m "remove files"
51 51 $ ls
52 52 sub
53 $ echo "testlargefile" > large1-test
54 $ hg add --large large1-test
55 $ hg st
56 A large1-test
57 $ hg rm large1-test
58 not removing large1-test: file has been marked for add (use forget to undo)
59 $ hg st
60 A large1-test
61 $ hg forget large1-test
62 $ hg st
63 ? large1-test
64 $ rm large1-test
53 65
54 66 Copy both largefiles and normal files (testing that status output is correct).
55 67
56 68 $ hg cp sub/normal2 normal1
57 69 $ hg cp sub/large2 large1
58 70 $ hg st
59 71 A large1
60 72 A normal1
61 73 $ hg commit -m "copy files"
62 74 $ cat normal1
63 75 normal22
64 76 $ cat large1
65 77 large22
66 78
67 79 Test moving largefiles and verify that normal files are also unaffected.
68 80
69 81 $ hg mv normal1 normal3
70 82 $ hg mv large1 large3
71 83 $ hg mv sub/normal2 sub/normal4
72 84 $ hg mv sub/large2 sub/large4
73 85 $ hg commit -m "move files"
74 86 $ cat normal3
75 87 normal22
76 88 $ cat large3
77 89 large22
78 90 $ cat sub/normal4
79 91 normal22
80 92 $ cat sub/large4
81 93 large22
82 94
83 95 Test archiving the various revisions. These hit corner cases known with
84 96 archiving.
85 97
86 98 $ hg archive -r 0 ../archive0
87 99 $ hg archive -r 1 ../archive1
88 100 $ hg archive -r 2 ../archive2
89 101 $ hg archive -r 3 ../archive3
90 102 $ hg archive -r 4 ../archive4
91 103 $ cd ../archive0
92 104 $ cat normal1
93 105 normal1
94 106 $ cat large1
95 107 large1
96 108 $ cat sub/normal2
97 109 normal2
98 110 $ cat sub/large2
99 111 large2
100 112 $ cd ../archive1
101 113 $ cat normal1
102 114 normal11
103 115 $ cat large1
104 116 large11
105 117 $ cat sub/normal2
106 118 normal22
107 119 $ cat sub/large2
108 120 large22
109 121 $ cd ../archive2
110 122 $ ls
111 123 sub
112 124 $ cat sub/normal2
113 125 normal22
114 126 $ cat sub/large2
115 127 large22
116 128 $ cd ../archive3
117 129 $ cat normal1
118 130 normal22
119 131 $ cat large1
120 132 large22
121 133 $ cat sub/normal2
122 134 normal22
123 135 $ cat sub/large2
124 136 large22
125 137 $ cd ../archive4
126 138 $ cat normal3
127 139 normal22
128 140 $ cat large3
129 141 large22
130 142 $ cat sub/normal4
131 143 normal22
132 144 $ cat sub/large4
133 145 large22
134 146
135 147 Commit corner case: specify files to commit.
136 148
137 149 $ cd ../a
138 150 $ echo normal3 > normal3
139 151 $ echo large3 > large3
140 152 $ echo normal4 > sub/normal4
141 153 $ echo large4 > sub/large4
142 154 $ hg commit normal3 large3 sub/normal4 sub/large4 -m "edit files again"
143 155 $ cat normal3
144 156 normal3
145 157 $ cat large3
146 158 large3
147 159 $ cat sub/normal4
148 160 normal4
149 161 $ cat sub/large4
150 162 large4
151 163
152 164 One more commit corner case: commit from a subdirectory.
153 165
154 166 $ cd ../a
155 167 $ echo normal33 > normal3
156 168 $ echo large33 > large3
157 169 $ echo normal44 > sub/normal4
158 170 $ echo large44 > sub/large4
159 171 $ cd sub
160 172 $ hg commit -m "edit files yet again"
161 173 $ cat ../normal3
162 174 normal33
163 175 $ cat ../large3
164 176 large33
165 177 $ cat normal4
166 178 normal44
167 179 $ cat large4
168 180 large44
169 181
170 182 Committing standins is not allowed.
171 183
172 184 $ cd ..
173 185 $ echo large3 > large3
174 186 $ hg commit .hglf/large3 -m "try to commit standin"
175 187 abort: file ".hglf/large3" is a largefile standin
176 188 (commit the largefile itself instead)
177 189 [255]
178 190
179 191 Corner cases for adding largefiles.
180 192
181 193 $ echo large5 > large5
182 194 $ hg add --large large5
183 195 $ hg add --large large5
184 196 large5 already a largefile
185 197 $ mkdir sub2
186 198 $ echo large6 > sub2/large6
187 199 $ echo large7 > sub2/large7
188 200 $ hg add --large sub2
189 201 adding sub2/large6 as a largefile
190 202 adding sub2/large7 as a largefile
191 203 $ hg st
192 204 M large3
193 205 A large5
194 206 A sub2/large6
195 207 A sub2/large7
196 208
197 209 Config settings (pattern **.dat, minsize 2 MB) are respected.
198 210
199 211 $ echo testdata > test.dat
200 212 $ dd bs=1k count=2k if=/dev/zero of=reallylarge > /dev/null 2> /dev/null
201 213 $ hg add
202 214 adding reallylarge as a largefile
203 215 adding test.dat as a largefile
204 216
205 217 Test that minsize and --lfsize handle float values;
206 218 also tests that --lfsize overrides largefiles.minsize.
207 219 (0.250 MB = 256 kB = 262144 B)
208 220
209 221 $ dd if=/dev/zero of=ratherlarge bs=1024 count=256 > /dev/null 2> /dev/null
210 222 $ dd if=/dev/zero of=medium bs=1024 count=128 > /dev/null 2> /dev/null
211 223 $ hg --config largefiles.minsize=.25 add
212 224 adding ratherlarge as a largefile
213 225 adding medium
214 226 $ hg forget medium
215 227 $ hg --config largefiles.minsize=.25 add --lfsize=.125
216 228 adding medium as a largefile
217 229 $ dd if=/dev/zero of=notlarge bs=1024 count=127 > /dev/null 2> /dev/null
218 230 $ hg --config largefiles.minsize=.25 add --lfsize=.125
219 231 adding notlarge
220 232 $ hg forget notlarge
221 233
222 234 Test forget on largefiles.
223 235
224 236 $ hg forget large3 large5 test.dat reallylarge ratherlarge medium
225 237 $ hg st
226 238 A sub2/large6
227 239 A sub2/large7
228 240 R large3
229 241 ? large5
230 242 ? medium
231 243 ? notlarge
232 244 ? ratherlarge
233 245 ? reallylarge
234 246 ? test.dat
235 247 $ hg commit -m "add/edit more largefiles"
236 248 $ hg st
237 249 ? large3
238 250 ? large5
239 251 ? medium
240 252 ? notlarge
241 253 ? ratherlarge
242 254 ? reallylarge
243 255 ? test.dat
244 256
245 257 Purge with largefiles: verify that largefiles are still in the working
246 258 dir after a purge.
247 259
248 260 $ hg purge --all
249 261 $ cat sub/large4
250 262 large44
251 263 $ cat sub2/large6
252 264 large6
253 265 $ cat sub2/large7
254 266 large7
255 267
256 268 Clone a largefiles repo.
257 269
258 270 $ hg clone . ../b
259 271 updating to branch default
260 272 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
261 273 getting changed largefiles
262 274 3 largefiles updated, 0 removed
263 275 $ cd ../b
264 276 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
265 277 7:daea875e9014 add/edit more largefiles
266 278 6:4355d653f84f edit files yet again
267 279 5:9d5af5072dbd edit files again
268 280 4:74c02385b94c move files
269 281 3:9e8fbc4bce62 copy files
270 282 2:51a0ae4d5864 remove files
271 283 1:ce8896473775 edit files
272 284 0:30d30fe6a5be add files
273 285 $ cat normal3
274 286 normal33
275 287 $ cat sub/normal4
276 288 normal44
277 289 $ cat sub/large4
278 290 large44
279 291 $ cat sub2/large6
280 292 large6
281 293 $ cat sub2/large7
282 294 large7
283 295 $ cd ..
284 296 $ hg clone a -r 3 c
285 297 adding changesets
286 298 adding manifests
287 299 adding file changes
288 300 added 4 changesets with 10 changes to 4 files
289 301 updating to branch default
290 302 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
291 303 getting changed largefiles
292 304 2 largefiles updated, 0 removed
293 305 $ cd c
294 306 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
295 307 3:9e8fbc4bce62 copy files
296 308 2:51a0ae4d5864 remove files
297 309 1:ce8896473775 edit files
298 310 0:30d30fe6a5be add files
299 311 $ cat normal1
300 312 normal22
301 313 $ cat large1
302 314 large22
303 315 $ cat sub/normal2
304 316 normal22
305 317 $ cat sub/large2
306 318 large22
307 319
308 320 Old revisions of a clone have correct largefiles content (this also
309 321 tests update).
310 322
311 323 $ hg update -r 1
312 324 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
313 325 getting changed largefiles
314 326 1 largefiles updated, 0 removed
315 327 $ cat large1
316 328 large11
317 329 $ cat sub/large2
318 330 large22
319 331
320 332 Rebasing between two repositories does not revert largefiles to old
321 333 revisions (this was a very bad bug that took a lot of work to fix).
322 334
323 335 $ cd ..
324 336 $ hg clone a d
325 337 updating to branch default
326 338 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
327 339 getting changed largefiles
328 340 3 largefiles updated, 0 removed
329 341 $ cd b
330 342 $ echo large4-modified > sub/large4
331 343 $ echo normal3-modified > normal3
332 344 $ hg commit -m "modify normal file and largefile in repo b"
333 345 $ cd ../d
334 346 $ echo large6-modified > sub2/large6
335 347 $ echo normal4-modified > sub/normal4
336 348 $ hg commit -m "modify normal file largefile in repo d"
337 349 $ cd ..
338 350 $ hg clone d e
339 351 updating to branch default
340 352 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
341 353 getting changed largefiles
342 354 3 largefiles updated, 0 removed
343 355 $ cd d
344 356 $ hg pull --rebase ../b
345 357 pulling from ../b
346 358 searching for changes
347 359 adding changesets
348 360 adding manifests
349 361 adding file changes
350 362 added 1 changesets with 2 changes to 2 files (+1 heads)
351 363 getting changed largefiles
352 364 1 largefiles updated, 0 removed
353 365 saved backup bundle to $TESTTMP/d/.hg/strip-backup/f574fb32bb45-backup.hg
354 366 nothing to rebase
355 367 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
356 368 9:598410d3eb9a modify normal file largefile in repo d
357 369 8:a381d2c8c80e modify normal file and largefile in repo b
358 370 7:daea875e9014 add/edit more largefiles
359 371 6:4355d653f84f edit files yet again
360 372 5:9d5af5072dbd edit files again
361 373 4:74c02385b94c move files
362 374 3:9e8fbc4bce62 copy files
363 375 2:51a0ae4d5864 remove files
364 376 1:ce8896473775 edit files
365 377 0:30d30fe6a5be add files
366 378 $ cat normal3
367 379 normal3-modified
368 380 $ cat sub/normal4
369 381 normal4-modified
370 382 $ cat sub/large4
371 383 large4-modified
372 384 $ cat sub2/large6
373 385 large6-modified
374 386 $ cat sub2/large7
375 387 large7
376 388 $ cd ../e
377 389 $ hg pull ../b
378 390 pulling from ../b
379 391 searching for changes
380 392 adding changesets
381 393 adding manifests
382 394 adding file changes
383 395 added 1 changesets with 2 changes to 2 files (+1 heads)
384 396 (run 'hg heads' to see heads, 'hg merge' to merge)
385 397 $ hg rebase
386 398 getting changed largefiles
387 399 1 largefiles updated, 0 removed
388 400 saved backup bundle to $TESTTMP/e/.hg/strip-backup/f574fb32bb45-backup.hg
389 401 $ hg log
390 402 changeset: 9:598410d3eb9a
391 403 tag: tip
392 404 user: test
393 405 date: Thu Jan 01 00:00:00 1970 +0000
394 406 summary: modify normal file largefile in repo d
395 407
396 408 changeset: 8:a381d2c8c80e
397 409 user: test
398 410 date: Thu Jan 01 00:00:00 1970 +0000
399 411 summary: modify normal file and largefile in repo b
400 412
401 413 changeset: 7:daea875e9014
402 414 user: test
403 415 date: Thu Jan 01 00:00:00 1970 +0000
404 416 summary: add/edit more largefiles
405 417
406 418 changeset: 6:4355d653f84f
407 419 user: test
408 420 date: Thu Jan 01 00:00:00 1970 +0000
409 421 summary: edit files yet again
410 422
411 423 changeset: 5:9d5af5072dbd
412 424 user: test
413 425 date: Thu Jan 01 00:00:00 1970 +0000
414 426 summary: edit files again
415 427
416 428 changeset: 4:74c02385b94c
417 429 user: test
418 430 date: Thu Jan 01 00:00:00 1970 +0000
419 431 summary: move files
420 432
421 433 changeset: 3:9e8fbc4bce62
422 434 user: test
423 435 date: Thu Jan 01 00:00:00 1970 +0000
424 436 summary: copy files
425 437
426 438 changeset: 2:51a0ae4d5864
427 439 user: test
428 440 date: Thu Jan 01 00:00:00 1970 +0000
429 441 summary: remove files
430 442
431 443 changeset: 1:ce8896473775
432 444 user: test
433 445 date: Thu Jan 01 00:00:00 1970 +0000
434 446 summary: edit files
435 447
436 448 changeset: 0:30d30fe6a5be
437 449 user: test
438 450 date: Thu Jan 01 00:00:00 1970 +0000
439 451 summary: add files
440 452
441 453 $ cat normal3
442 454 normal3-modified
443 455 $ cat sub/normal4
444 456 normal4-modified
445 457 $ cat sub/large4
446 458 large4-modified
447 459 $ cat sub2/large6
448 460 large6-modified
449 461 $ cat sub2/large7
450 462 large7
451 463
452 464 Rollback on largefiles.
453 465
454 466 $ echo large4-modified-again > sub/large4
455 467 $ hg commit -m "Modify large4 again"
456 468 $ hg rollback
457 469 repository tip rolled back to revision 9 (undo commit)
458 470 working directory now based on revision 9
459 471 $ hg st
460 472 M sub/large4
461 473 $ hg log
462 474 changeset: 9:598410d3eb9a
463 475 tag: tip
464 476 user: test
465 477 date: Thu Jan 01 00:00:00 1970 +0000
466 478 summary: modify normal file largefile in repo d
467 479
468 480 changeset: 8:a381d2c8c80e
469 481 user: test
470 482 date: Thu Jan 01 00:00:00 1970 +0000
471 483 summary: modify normal file and largefile in repo b
472 484
473 485 changeset: 7:daea875e9014
474 486 user: test
475 487 date: Thu Jan 01 00:00:00 1970 +0000
476 488 summary: add/edit more largefiles
477 489
478 490 changeset: 6:4355d653f84f
479 491 user: test
480 492 date: Thu Jan 01 00:00:00 1970 +0000
481 493 summary: edit files yet again
482 494
483 495 changeset: 5:9d5af5072dbd
484 496 user: test
485 497 date: Thu Jan 01 00:00:00 1970 +0000
486 498 summary: edit files again
487 499
488 500 changeset: 4:74c02385b94c
489 501 user: test
490 502 date: Thu Jan 01 00:00:00 1970 +0000
491 503 summary: move files
492 504
493 505 changeset: 3:9e8fbc4bce62
494 506 user: test
495 507 date: Thu Jan 01 00:00:00 1970 +0000
496 508 summary: copy files
497 509
498 510 changeset: 2:51a0ae4d5864
499 511 user: test
500 512 date: Thu Jan 01 00:00:00 1970 +0000
501 513 summary: remove files
502 514
503 515 changeset: 1:ce8896473775
504 516 user: test
505 517 date: Thu Jan 01 00:00:00 1970 +0000
506 518 summary: edit files
507 519
508 520 changeset: 0:30d30fe6a5be
509 521 user: test
510 522 date: Thu Jan 01 00:00:00 1970 +0000
511 523 summary: add files
512 524
513 525 $ cat sub/large4
514 526 large4-modified-again
515 527
516 528 "update --check" refuses to update with uncommitted changes.
517 529 $ hg update --check 8
518 530 abort: uncommitted local changes
519 531 [255]
520 532
521 533 "update --clean" leaves correct largefiles in working copy.
522 534
523 535 $ hg update --clean
524 536 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
525 537 getting changed largefiles
526 538 1 largefiles updated, 0 removed
527 539 $ cat normal3
528 540 normal3-modified
529 541 $ cat sub/normal4
530 542 normal4-modified
531 543 $ cat sub/large4
532 544 large4-modified
533 545 $ cat sub2/large6
534 546 large6-modified
535 547 $ cat sub2/large7
536 548 large7
537 549
538 550 Now "update check" is happy.
539 551 $ hg update --check 8
540 552 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
541 553 getting changed largefiles
542 554 1 largefiles updated, 0 removed
543 555 $ hg update --check
544 556 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
545 557 getting changed largefiles
546 558 1 largefiles updated, 0 removed
547 559
548 560 "revert" works on largefiles (and normal files too).
549 561 $ echo hack3 >> normal3
550 562 $ echo hack4 >> sub/normal4
551 563 $ echo hack4 >> sub/large4
552 564 $ hg rm sub2/large6
553 565 $ echo new >> sub2/large8
554 566 $ hg add --large sub2/large8
555 567 # XXX we don't really want to report that we're reverting the standin;
556 568 # that's just an implementation detail. But I don't see an obvious fix. ;-(
557 569 $ hg revert sub
558 570 reverting .hglf/sub/large4
559 571 reverting sub/normal4
560 572 $ hg status
561 573 M normal3
562 574 A sub2/large8
563 575 R sub2/large6
564 576 ? sub/large4.orig
565 577 ? sub/normal4.orig
566 578 $ cat sub/normal4
567 579 normal4-modified
568 580 $ cat sub/large4
569 581 large4-modified
570 582 $ hg revert -a --no-backup
571 583 undeleting .hglf/sub2/large6
572 584 forgetting .hglf/sub2/large8
573 585 reverting normal3
574 586 $ hg status
575 587 ? sub/large4.orig
576 588 ? sub/normal4.orig
577 589 ? sub2/large8
578 590 $ cat normal3
579 591 normal3-modified
580 592 $ cat sub2/large6
581 593 large6-modified
582 594 $ rm sub/*.orig sub2/large8
583 595
584 596 revert some files to an older revision
585 597 $ hg revert --no-backup -r 8 sub2
586 598 reverting .hglf/sub2/large6
587 599 $ cat sub2/large6
588 600 large6
589 601 $ hg revert --no-backup sub2
590 602 reverting .hglf/sub2/large6
591 603 $ hg status
592 604
593 605 "verify --large" actually verifies largefiles
594 606
595 607 $ hg verify --large
596 608 checking changesets
597 609 checking manifests
598 610 crosschecking files in changesets and manifests
599 611 checking files
600 612 10 files, 10 changesets, 28 total revisions
601 613 searching 1 changesets for largefiles
602 614 verified existence of 3 revisions of 3 largefiles
603 615
604 616 Merging does not revert to old versions of largefiles (this has also
605 617 been very problematic).
606 618
607 619 $ cd ..
608 620 $ hg clone -r 7 e f
609 621 adding changesets
610 622 adding manifests
611 623 adding file changes
612 624 added 8 changesets with 24 changes to 10 files
613 625 updating to branch default
614 626 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
615 627 getting changed largefiles
616 628 3 largefiles updated, 0 removed
617 629 $ cd f
618 630 $ echo "large4-merge-test" > sub/large4
619 631 $ hg commit -m "Modify large4 to test merge"
620 632 $ hg pull ../e
621 633 pulling from ../e
622 634 searching for changes
623 635 adding changesets
624 636 adding manifests
625 637 adding file changes
626 638 added 2 changesets with 4 changes to 4 files (+1 heads)
627 639 (run 'hg heads' to see heads, 'hg merge' to merge)
628 640 $ hg merge
629 641 merging sub/large4
630 642 largefile sub/large4 has a merge conflict
631 643 keep (l)ocal or take (o)ther? l
632 644 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
633 645 (branch merge, don't forget to commit)
634 646 getting changed largefiles
635 647 1 largefiles updated, 0 removed
636 648 $ hg commit -m "Merge repos e and f"
637 649 $ cat normal3
638 650 normal3-modified
639 651 $ cat sub/normal4
640 652 normal4-modified
641 653 $ cat sub/large4
642 654 large4-merge-test
643 655 $ cat sub2/large6
644 656 large6-modified
645 657 $ cat sub2/large7
646 658 large7
647 659
648 660 Test status after merging with a branch that introduces a new largefile:
649 661
650 662 $ echo large > large
651 663 $ hg add --large large
652 664 $ hg commit -m 'add largefile'
653 665 $ hg update -q ".^"
654 666 $ echo change >> normal3
655 667 $ hg commit -m 'some change'
656 668 created new head
657 669 $ hg merge
658 670 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
659 671 (branch merge, don't forget to commit)
660 672 getting changed largefiles
661 673 1 largefiles updated, 0 removed
662 674 $ hg status
663 675 M large
664 676
665 677 Test that a normal file and a largefile with the same name and path cannot
666 678 coexist.
667 679
668 680 $ rm sub2/large7
669 681 $ echo "largeasnormal" > sub2/large7
670 682 $ hg add sub2/large7
671 683 sub2/large7 already a largefile
672 684
673 685 Test that transplanting a largefile change works correctly.
674 686
675 687 $ cd ..
676 688 $ hg clone -r 8 d g
677 689 adding changesets
678 690 adding manifests
679 691 adding file changes
680 692 added 9 changesets with 26 changes to 10 files
681 693 updating to branch default
682 694 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
683 695 getting changed largefiles
684 696 3 largefiles updated, 0 removed
685 697 $ cd g
686 698 $ hg transplant -s ../d 598410d3eb9a
687 699 searching for changes
688 700 searching for changes
689 701 adding changesets
690 702 adding manifests
691 703 adding file changes
692 704 added 1 changesets with 2 changes to 2 files
693 705 getting changed largefiles
694 706 1 largefiles updated, 0 removed
695 707 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
696 708 9:598410d3eb9a modify normal file largefile in repo d
697 709 8:a381d2c8c80e modify normal file and largefile in repo b
698 710 7:daea875e9014 add/edit more largefiles
699 711 6:4355d653f84f edit files yet again
700 712 5:9d5af5072dbd edit files again
701 713 4:74c02385b94c move files
702 714 3:9e8fbc4bce62 copy files
703 715 2:51a0ae4d5864 remove files
704 716 1:ce8896473775 edit files
705 717 0:30d30fe6a5be add files
706 718 $ cat normal3
707 719 normal3-modified
708 720 $ cat sub/normal4
709 721 normal4-modified
710 722 $ cat sub/large4
711 723 large4-modified
712 724 $ cat sub2/large6
713 725 large6-modified
714 726 $ cat sub2/large7
715 727 large7
716 728
717 729 Test that renaming a largefile results in correct output for status
718 730
719 731 $ hg rename sub/large4 large4-renamed
720 732 $ hg st
721 733 A large4-renamed
722 734 R sub/large4
723 735 $ hg commit -m "test rename output"
724 736 $ cat large4-renamed
725 737 large4-modified
726 738 $ cd sub2
727 739 $ hg rename large6 large6-renamed
728 740 $ hg st
729 741 A sub2/large6-renamed
730 742 R sub2/large6
731 743 $ cd ../..
732 744
733 745 vanilla clients not locked out from largefiles servers on vanilla repos
734 746 $ mkdir r1
735 747 $ cd r1
736 748 $ hg init
737 749 $ echo c1 > f1
738 750 $ hg add f1
739 751 $ hg com -m "m1"
740 752 $ cd ..
741 753 $ hg serve -R r1 -d -p $HGPORT --pid-file hg.pid
742 754 $ cat hg.pid >> $DAEMON_PIDS
743 755 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT r2
744 756 requesting all changes
745 757 adding changesets
746 758 adding manifests
747 759 adding file changes
748 760 added 1 changesets with 1 changes to 1 files
749 761 updating to branch default
750 762 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
751 763
752 764 largefiles clients still work with vanilla servers
753 765 $ hg --config extensions.largefiles=! serve -R r1 -d -p $HGPORT1 --pid-file hg.pid
754 766 $ cat hg.pid >> $DAEMON_PIDS
755 767 $ hg clone http://localhost:$HGPORT1 r3
756 768 requesting all changes
757 769 adding changesets
758 770 adding manifests
759 771 adding file changes
760 772 added 1 changesets with 1 changes to 1 files
761 773 updating to branch default
762 774 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
763 775
764 776 vanilla clients locked out from largefiles http repos
765 777 $ mkdir r4
766 778 $ cd r4
767 779 $ hg init
768 780 $ echo c1 > f1
769 781 $ hg add --large f1
770 782 $ hg com -m "m1"
771 783 $ cd ..
772 784 $ hg serve -R r4 -d -p $HGPORT2 --pid-file hg.pid
773 785 $ cat hg.pid >> $DAEMON_PIDS
774 786 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT2 r5
775 787 abort: remote error:
776 788
777 789 This repository uses the largefiles extension.
778 790
779 791 Please enable it in your Mercurial config file.
780 792 [255]
781 793
782 794 used all HGPORTs, kill all daemons
783 795 $ "$TESTDIR/killdaemons.py"
784 796
785 797 vanilla clients locked out from largefiles ssh repos
786 798 $ hg --config extensions.largefiles=! clone -e "python $TESTDIR/dummyssh" ssh://user@dummy/r4 r5
787 799 abort: remote error:
788 800
789 801 This repository uses the largefiles extension.
790 802
791 803 Please enable it in your Mercurial config file.
792 804 [255]
793 805
794 806 largefiles clients refuse to push largefiles repos to vanilla servers
795 807 $ mkdir r6
796 808 $ cd r6
797 809 $ hg init
798 810 $ echo c1 > f1
799 811 $ hg add f1
800 812 $ hg com -m "m1"
801 813 $ cat >> .hg/hgrc <<!
802 814 > [web]
803 815 > push_ssl = false
804 816 > allow_push = *
805 817 > !
806 818 $ cd ..
807 819 $ hg clone r6 r7
808 820 updating to branch default
809 821 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
810 822 $ cd r7
811 823 $ echo c2 > f2
812 824 $ hg add --large f2
813 825 $ hg com -m "m2"
814 826 $ hg --config extensions.largefiles=! -R ../r6 serve -d -p $HGPORT --pid-file ../hg.pid
815 827 $ cat ../hg.pid >> $DAEMON_PIDS
816 828 $ hg push http://localhost:$HGPORT
817 829 pushing to http://localhost:$HGPORT/
818 830 searching for changes
819 831 abort: http://localhost:$HGPORT/ does not appear to be a largefile store
820 832 [255]
821 833 $ cd ..
822 834
823 835 Clone a local repository owned by another user
824 836 We have to simulate that here by setting $HOME and removing write permissions
825 837 $ ORIGHOME="$HOME"
826 838 $ mkdir alice
827 839 $ HOME="`pwd`/alice"
828 840 $ cd alice
829 841 $ hg init pubrepo
830 842 $ cd pubrepo
831 843 $ dd if=/dev/urandom bs=1k count=11k > a-large-file 2> /dev/null
832 844 $ hg add --large a-large-file
833 845 $ hg commit -m "Add a large file"
834 846 $ cd ..
835 847 $ chmod -R a-w pubrepo
836 848 $ cd ..
837 849 $ mkdir bob
838 850 $ HOME="`pwd`/bob"
839 851 $ cd bob
840 852 $ hg clone --pull ../alice/pubrepo pubrepo
841 853 requesting all changes
842 854 adding changesets
843 855 adding manifests
844 856 adding file changes
845 857 added 1 changesets with 1 changes to 1 files
846 858 updating to branch default
847 859 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
848 860 getting changed largefiles
849 861 1 largefiles updated, 0 removed
850 862 $ cd ..
851 863 $ HOME="$ORIGHOME"
852 864
853 865 Symlink to a large largefile should behave the same as a symlink to a normal file
854 866 $ hg init largesymlink
855 867 $ cd largesymlink
856 868 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
857 869 $ hg add --large largefile
858 870 $ hg commit -m "commit a large file"
859 871 $ ln -s largefile largelink
860 872 $ hg add largelink
861 873 $ hg commit -m "commit a large symlink"
862 874 $ rm -f largelink
863 875 $ hg up >/dev/null
864 876 $ test -f largelink
865 877 [1]
866 878 $ test -L largelink
867 879 [1]
868 880 $ rm -f largelink # make next part of the test independent of the previous
869 881 $ hg up -C >/dev/null
870 882 $ test -f largelink
871 883 $ test -L largelink
872 884 $ cd ..
873 885
874 886
General Comments 0
You need to be logged in to leave comments. Login now