##// END OF EJS Templates
largefiles: don't prompt for normal/largefile changes when doing plain updates...
Mads Kiilerich -
r19954:427ce563 stable
parent child Browse files
Show More
@@ -1,1216 +1,1218
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, discovery
16 16 from mercurial.i18n import _
17 17 from mercurial.node import hex
18 18 from hgext import rebase
19 19
20 20 import lfutil
21 21 import lfcommands
22 22 import basestore
23 23
24 24 # -- Utility functions: commonly/repeatedly needed functionality ---------------
25 25
26 26 def installnormalfilesmatchfn(manifest):
27 27 '''overrides scmutil.match so that the matcher it returns will ignore all
28 28 largefiles'''
29 29 oldmatch = None # for the closure
30 30 def overridematch(ctx, pats=[], opts={}, globbed=False,
31 31 default='relpath'):
32 32 match = oldmatch(ctx, pats, opts, globbed, default)
33 33 m = copy.copy(match)
34 34 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
35 35 manifest)
36 36 m._files = filter(notlfile, m._files)
37 37 m._fmap = set(m._files)
38 38 m._always = False
39 39 origmatchfn = m.matchfn
40 40 m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None
41 41 return m
42 42 oldmatch = installmatchfn(overridematch)
43 43
44 44 def installmatchfn(f):
45 45 oldmatch = scmutil.match
46 46 setattr(f, 'oldmatch', oldmatch)
47 47 scmutil.match = f
48 48 return oldmatch
49 49
50 50 def restorematchfn():
51 51 '''restores scmutil.match to what it was before installnormalfilesmatchfn
52 52 was called. no-op if scmutil.match is its original function.
53 53
54 54 Note that n calls to installnormalfilesmatchfn will require n calls to
55 55 restore matchfn to reverse'''
56 56 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
57 57
58 58 def addlargefiles(ui, repo, *pats, **opts):
59 59 large = opts.pop('large', None)
60 60 lfsize = lfutil.getminsize(
61 61 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
62 62
63 63 lfmatcher = None
64 64 if lfutil.islfilesrepo(repo):
65 65 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
66 66 if lfpats:
67 67 lfmatcher = match_.match(repo.root, '', list(lfpats))
68 68
69 69 lfnames = []
70 70 m = scmutil.match(repo[None], pats, opts)
71 71 m.bad = lambda x, y: None
72 72 wctx = repo[None]
73 73 for f in repo.walk(m):
74 74 exact = m.exact(f)
75 75 lfile = lfutil.standin(f) in wctx
76 76 nfile = f in wctx
77 77 exists = lfile or nfile
78 78
79 79 # Don't warn the user when they attempt to add a normal tracked file.
80 80 # The normal add code will do that for us.
81 81 if exact and exists:
82 82 if lfile:
83 83 ui.warn(_('%s already a largefile\n') % f)
84 84 continue
85 85
86 86 if (exact or not exists) and not lfutil.isstandin(f):
87 87 wfile = repo.wjoin(f)
88 88
89 89 # In case the file was removed previously, but not committed
90 90 # (issue3507)
91 91 if not os.path.exists(wfile):
92 92 continue
93 93
94 94 abovemin = (lfsize and
95 95 os.lstat(wfile).st_size >= lfsize * 1024 * 1024)
96 96 if large or abovemin or (lfmatcher and lfmatcher(f)):
97 97 lfnames.append(f)
98 98 if ui.verbose or not exact:
99 99 ui.status(_('adding %s as a largefile\n') % m.rel(f))
100 100
101 101 bad = []
102 102 standins = []
103 103
104 104 # Need to lock, otherwise there could be a race condition between
105 105 # when standins are created and added to the repo.
106 106 wlock = repo.wlock()
107 107 try:
108 108 if not opts.get('dry_run'):
109 109 lfdirstate = lfutil.openlfdirstate(ui, repo)
110 110 for f in lfnames:
111 111 standinname = lfutil.standin(f)
112 112 lfutil.writestandin(repo, standinname, hash='',
113 113 executable=lfutil.getexecutable(repo.wjoin(f)))
114 114 standins.append(standinname)
115 115 if lfdirstate[f] == 'r':
116 116 lfdirstate.normallookup(f)
117 117 else:
118 118 lfdirstate.add(f)
119 119 lfdirstate.write()
120 120 bad += [lfutil.splitstandin(f)
121 121 for f in repo[None].add(standins)
122 122 if f in m.files()]
123 123 finally:
124 124 wlock.release()
125 125 return bad
126 126
127 127 def removelargefiles(ui, repo, *pats, **opts):
128 128 after = opts.get('after')
129 129 if not pats and not after:
130 130 raise util.Abort(_('no files specified'))
131 131 m = scmutil.match(repo[None], pats, opts)
132 132 try:
133 133 repo.lfstatus = True
134 134 s = repo.status(match=m, clean=True)
135 135 finally:
136 136 repo.lfstatus = False
137 137 manifest = repo[None].manifest()
138 138 modified, added, deleted, clean = [[f for f in list
139 139 if lfutil.standin(f) in manifest]
140 140 for list in [s[0], s[1], s[3], s[6]]]
141 141
142 142 def warn(files, msg):
143 143 for f in files:
144 144 ui.warn(msg % m.rel(f))
145 145 return int(len(files) > 0)
146 146
147 147 result = 0
148 148
149 149 if after:
150 150 remove, forget = deleted, []
151 151 result = warn(modified + added + clean,
152 152 _('not removing %s: file still exists\n'))
153 153 else:
154 154 remove, forget = deleted + clean, []
155 155 result = warn(modified, _('not removing %s: file is modified (use -f'
156 156 ' to force removal)\n'))
157 157 result = warn(added, _('not removing %s: file has been marked for add'
158 158 ' (use forget to undo)\n')) or result
159 159
160 160 for f in sorted(remove + forget):
161 161 if ui.verbose or not m.exact(f):
162 162 ui.status(_('removing %s\n') % m.rel(f))
163 163
164 164 # Need to lock because standin files are deleted then removed from the
165 165 # repository and we could race in-between.
166 166 wlock = repo.wlock()
167 167 try:
168 168 lfdirstate = lfutil.openlfdirstate(ui, repo)
169 169 for f in remove:
170 170 if not after:
171 171 # If this is being called by addremove, notify the user that we
172 172 # are removing the file.
173 173 if getattr(repo, "_isaddremove", False):
174 174 ui.status(_('removing %s\n') % f)
175 175 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
176 176 lfdirstate.remove(f)
177 177 lfdirstate.write()
178 178 forget = [lfutil.standin(f) for f in forget]
179 179 remove = [lfutil.standin(f) for f in remove]
180 180 repo[None].forget(forget)
181 181 # If this is being called by addremove, let the original addremove
182 182 # function handle this.
183 183 if not getattr(repo, "_isaddremove", False):
184 184 for f in remove:
185 185 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
186 186 repo[None].forget(remove)
187 187 finally:
188 188 wlock.release()
189 189
190 190 return result
191 191
192 192 # For overriding mercurial.hgweb.webcommands so that largefiles will
193 193 # appear at their right place in the manifests.
194 194 def decodepath(orig, path):
195 195 return lfutil.splitstandin(path) or path
196 196
197 197 # -- Wrappers: modify existing commands --------------------------------
198 198
199 199 # Add works by going through the files that the user wanted to add and
200 200 # checking if they should be added as largefiles. Then it makes a new
201 201 # matcher which matches only the normal files and runs the original
202 202 # version of add.
203 203 def overrideadd(orig, ui, repo, *pats, **opts):
204 204 normal = opts.pop('normal')
205 205 if normal:
206 206 if opts.get('large'):
207 207 raise util.Abort(_('--normal cannot be used with --large'))
208 208 return orig(ui, repo, *pats, **opts)
209 209 bad = addlargefiles(ui, repo, *pats, **opts)
210 210 installnormalfilesmatchfn(repo[None].manifest())
211 211 result = orig(ui, repo, *pats, **opts)
212 212 restorematchfn()
213 213
214 214 return (result == 1 or bad) and 1 or 0
215 215
216 216 def overrideremove(orig, ui, repo, *pats, **opts):
217 217 installnormalfilesmatchfn(repo[None].manifest())
218 218 result = orig(ui, repo, *pats, **opts)
219 219 restorematchfn()
220 220 return removelargefiles(ui, repo, *pats, **opts) or result
221 221
222 222 def overridestatusfn(orig, repo, rev2, **opts):
223 223 try:
224 224 repo._repo.lfstatus = True
225 225 return orig(repo, rev2, **opts)
226 226 finally:
227 227 repo._repo.lfstatus = False
228 228
229 229 def overridestatus(orig, ui, repo, *pats, **opts):
230 230 try:
231 231 repo.lfstatus = True
232 232 return orig(ui, repo, *pats, **opts)
233 233 finally:
234 234 repo.lfstatus = False
235 235
236 236 def overridedirty(orig, repo, ignoreupdate=False):
237 237 try:
238 238 repo._repo.lfstatus = True
239 239 return orig(repo, ignoreupdate)
240 240 finally:
241 241 repo._repo.lfstatus = False
242 242
243 243 def overridelog(orig, ui, repo, *pats, **opts):
244 244 def overridematch(ctx, pats=[], opts={}, globbed=False,
245 245 default='relpath'):
246 246 """Matcher that merges root directory with .hglf, suitable for log.
247 247 It is still possible to match .hglf directly.
248 248 For any listed files run log on the standin too.
249 249 matchfn tries both the given filename and with .hglf stripped.
250 250 """
251 251 match = oldmatch(ctx, pats, opts, globbed, default)
252 252 m = copy.copy(match)
253 253 for i in range(0, len(m._files)):
254 254 standin = lfutil.standin(m._files[i])
255 255 if standin in repo[ctx.node()]:
256 256 m._files[i] = standin
257 257 m._fmap = set(m._files)
258 258 m._always = False
259 259 origmatchfn = m.matchfn
260 260 def lfmatchfn(f):
261 261 lf = lfutil.splitstandin(f)
262 262 if lf is not None and origmatchfn(lf):
263 263 return True
264 264 r = origmatchfn(f)
265 265 return r
266 266 m.matchfn = lfmatchfn
267 267 return m
268 268 oldmatch = installmatchfn(overridematch)
269 269 try:
270 270 repo.lfstatus = True
271 271 return orig(ui, repo, *pats, **opts)
272 272 finally:
273 273 repo.lfstatus = False
274 274 restorematchfn()
275 275
276 276 def overrideverify(orig, ui, repo, *pats, **opts):
277 277 large = opts.pop('large', False)
278 278 all = opts.pop('lfa', False)
279 279 contents = opts.pop('lfc', False)
280 280
281 281 result = orig(ui, repo, *pats, **opts)
282 282 if large or all or contents:
283 283 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
284 284 return result
285 285
286 286 def overridedebugstate(orig, ui, repo, *pats, **opts):
287 287 large = opts.pop('large', False)
288 288 if large:
289 289 lfcommands.debugdirstate(ui, repo)
290 290 else:
291 291 orig(ui, repo, *pats, **opts)
292 292
293 293 # Override needs to refresh standins so that update's normal merge
294 294 # will go through properly. Then the other update hook (overriding repo.update)
295 295 # will get the new files. Filemerge is also overridden so that the merge
296 296 # will merge standins correctly.
297 297 def overrideupdate(orig, ui, repo, *pats, **opts):
298 298 lfdirstate = lfutil.openlfdirstate(ui, repo)
299 299 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
300 300 False, False)
301 301 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
302 302
303 303 # Need to lock between the standins getting updated and their
304 304 # largefiles getting updated
305 305 wlock = repo.wlock()
306 306 try:
307 307 if opts['check']:
308 308 mod = len(modified) > 0
309 309 for lfile in unsure:
310 310 standin = lfutil.standin(lfile)
311 311 if repo['.'][standin].data().strip() != \
312 312 lfutil.hashfile(repo.wjoin(lfile)):
313 313 mod = True
314 314 else:
315 315 lfdirstate.normal(lfile)
316 316 lfdirstate.write()
317 317 if mod:
318 318 raise util.Abort(_('uncommitted changes'))
319 319 # XXX handle removed differently
320 320 if not opts['clean']:
321 321 for lfile in unsure + modified + added:
322 322 lfutil.updatestandin(repo, lfutil.standin(lfile))
323 323 finally:
324 324 wlock.release()
325 325 return orig(ui, repo, *pats, **opts)
326 326
327 327 # Before starting the manifest merge, merge.updates will call
328 328 # _checkunknown to check if there are any files in the merged-in
329 329 # changeset that collide with unknown files in the working copy.
330 330 #
331 331 # The largefiles are seen as unknown, so this prevents us from merging
332 332 # in a file 'foo' if we already have a largefile with the same name.
333 333 #
334 334 # The overridden function filters the unknown files by removing any
335 335 # largefiles. This makes the merge proceed and we can then handle this
336 336 # case further in the overridden manifestmerge function below.
337 337 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
338 338 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
339 339 return False
340 340 return origfn(repo, wctx, mctx, f)
341 341
342 342 # The manifest merge handles conflicts on the manifest level. We want
343 343 # to handle changes in largefile-ness of files at this level too.
344 344 #
345 345 # The strategy is to run the original manifestmerge and then process
346 346 # the action list it outputs. There are two cases we need to deal with:
347 347 #
348 348 # 1. Normal file in p1, largefile in p2. Here the largefile is
349 349 # detected via its standin file, which will enter the working copy
350 350 # with a "get" action. It is not "merge" since the standin is all
351 351 # Mercurial is concerned with at this level -- the link to the
352 352 # existing normal file is not relevant here.
353 353 #
354 354 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
355 355 # since the largefile will be present in the working copy and
356 356 # different from the normal file in p2. Mercurial therefore
357 357 # triggers a merge action.
358 358 #
359 359 # In both cases, we prompt the user and emit new actions to either
360 360 # remove the standin (if the normal file was kept) or to remove the
361 361 # normal file and get the standin (if the largefile was kept). The
362 362 # default prompt answer is to use the largefile version since it was
363 363 # presumably changed on purpose.
364 364 #
365 365 # Finally, the merge.applyupdates function will then take care of
366 366 # writing the files into the working copy and lfcommands.updatelfiles
367 367 # will update the largefiles.
368 368 def overridemanifestmerge(origfn, repo, p1, p2, pa, branchmerge, force,
369 369 partial, acceptremote=False):
370 370 overwrite = force and not branchmerge
371 371 actions = origfn(repo, p1, p2, pa, branchmerge, force, partial,
372 372 acceptremote)
373 373
374 374 if overwrite:
375 375 return actions
376 376
377 removes = set(a[0] for a in actions if a[1] == 'r')
377 378 processed = []
378 379
379 380 for action in actions:
380 381 f, m, args, msg = action
381 382
382 383 splitstandin = lfutil.splitstandin(f)
383 384 if (m == "g" and splitstandin is not None and
384 splitstandin in p1):
385 splitstandin in p1 and splitstandin not in removes):
385 386 # Case 1: normal file in the working copy, largefile in
386 387 # the second parent
387 388 lfile = splitstandin
388 389 standin = f
389 390 msg = _('%s has been turned into a largefile\n'
390 391 'use (l)argefile or keep as (n)ormal file?'
391 392 '$$ &Largefile $$ &Normal file') % lfile
392 393 if repo.ui.promptchoice(msg, 0) == 0:
393 394 processed.append((lfile, "r", None, msg))
394 395 processed.append((standin, "g", (p2.flags(standin),), msg))
395 396 else:
396 397 processed.append((standin, "r", None, msg))
397 elif m == "g" and lfutil.standin(f) in p1:
398 elif (m == "g" and
399 lfutil.standin(f) in p1 and lfutil.standin(f) not in removes):
398 400 # Case 2: largefile in the working copy, normal file in
399 401 # the second parent
400 402 standin = lfutil.standin(f)
401 403 lfile = f
402 404 msg = _('%s has been turned into a normal file\n'
403 405 'keep as (l)argefile or use (n)ormal file?'
404 406 '$$ &Largefile $$ &Normal file') % lfile
405 407 if repo.ui.promptchoice(msg, 0) == 0:
406 408 processed.append((lfile, "r", None, msg))
407 409 else:
408 410 processed.append((standin, "r", None, msg))
409 411 processed.append((lfile, "g", (p2.flags(lfile),), msg))
410 412 else:
411 413 processed.append(action)
412 414
413 415 return processed
414 416
415 417 # Override filemerge to prompt the user about how they wish to merge
416 418 # largefiles. This will handle identical edits, and copy/rename +
417 419 # edit without prompting the user.
418 420 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca):
419 421 # Use better variable names here. Because this is a wrapper we cannot
420 422 # change the variable names in the function declaration.
421 423 fcdest, fcother, fcancestor = fcd, fco, fca
422 424 if not lfutil.isstandin(orig):
423 425 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
424 426 else:
425 427 if not fcother.cmp(fcdest): # files identical?
426 428 return None
427 429
428 430 # backwards, use working dir parent as ancestor
429 431 if fcancestor == fcother:
430 432 fcancestor = fcdest.parents()[0]
431 433
432 434 if orig != fcother.path():
433 435 repo.ui.status(_('merging %s and %s to %s\n')
434 436 % (lfutil.splitstandin(orig),
435 437 lfutil.splitstandin(fcother.path()),
436 438 lfutil.splitstandin(fcdest.path())))
437 439 else:
438 440 repo.ui.status(_('merging %s\n')
439 441 % lfutil.splitstandin(fcdest.path()))
440 442
441 443 if fcancestor.path() != fcother.path() and fcother.data() == \
442 444 fcancestor.data():
443 445 return 0
444 446 if fcancestor.path() != fcdest.path() and fcdest.data() == \
445 447 fcancestor.data():
446 448 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
447 449 return 0
448 450
449 451 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
450 452 'keep (l)ocal or take (o)ther?'
451 453 '$$ &Local $$ &Other') %
452 454 lfutil.splitstandin(orig), 0) == 0:
453 455 return 0
454 456 else:
455 457 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
456 458 return 0
457 459
458 460 # Copy first changes the matchers to match standins instead of
459 461 # largefiles. Then it overrides util.copyfile in that function it
460 462 # checks if the destination largefile already exists. It also keeps a
461 463 # list of copied files so that the largefiles can be copied and the
462 464 # dirstate updated.
463 465 def overridecopy(orig, ui, repo, pats, opts, rename=False):
464 466 # doesn't remove largefile on rename
465 467 if len(pats) < 2:
466 468 # this isn't legal, let the original function deal with it
467 469 return orig(ui, repo, pats, opts, rename)
468 470
469 471 def makestandin(relpath):
470 472 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
471 473 return os.path.join(repo.wjoin(lfutil.standin(path)))
472 474
473 475 fullpats = scmutil.expandpats(pats)
474 476 dest = fullpats[-1]
475 477
476 478 if os.path.isdir(dest):
477 479 if not os.path.isdir(makestandin(dest)):
478 480 os.makedirs(makestandin(dest))
479 481 # This could copy both lfiles and normal files in one command,
480 482 # but we don't want to do that. First replace their matcher to
481 483 # only match normal files and run it, then replace it to just
482 484 # match largefiles and run it again.
483 485 nonormalfiles = False
484 486 nolfiles = False
485 487 try:
486 488 try:
487 489 installnormalfilesmatchfn(repo[None].manifest())
488 490 result = orig(ui, repo, pats, opts, rename)
489 491 except util.Abort, e:
490 492 if str(e) != _('no files to copy'):
491 493 raise e
492 494 else:
493 495 nonormalfiles = True
494 496 result = 0
495 497 finally:
496 498 restorematchfn()
497 499
498 500 # The first rename can cause our current working directory to be removed.
499 501 # In that case there is nothing left to copy/rename so just quit.
500 502 try:
501 503 repo.getcwd()
502 504 except OSError:
503 505 return result
504 506
505 507 try:
506 508 try:
507 509 # When we call orig below it creates the standins but we don't add
508 510 # them to the dir state until later so lock during that time.
509 511 wlock = repo.wlock()
510 512
511 513 manifest = repo[None].manifest()
512 514 oldmatch = None # for the closure
513 515 def overridematch(ctx, pats=[], opts={}, globbed=False,
514 516 default='relpath'):
515 517 newpats = []
516 518 # The patterns were previously mangled to add the standin
517 519 # directory; we need to remove that now
518 520 for pat in pats:
519 521 if match_.patkind(pat) is None and lfutil.shortname in pat:
520 522 newpats.append(pat.replace(lfutil.shortname, ''))
521 523 else:
522 524 newpats.append(pat)
523 525 match = oldmatch(ctx, newpats, opts, globbed, default)
524 526 m = copy.copy(match)
525 527 lfile = lambda f: lfutil.standin(f) in manifest
526 528 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
527 529 m._fmap = set(m._files)
528 530 m._always = False
529 531 origmatchfn = m.matchfn
530 532 m.matchfn = lambda f: (lfutil.isstandin(f) and
531 533 (f in manifest) and
532 534 origmatchfn(lfutil.splitstandin(f)) or
533 535 None)
534 536 return m
535 537 oldmatch = installmatchfn(overridematch)
536 538 listpats = []
537 539 for pat in pats:
538 540 if match_.patkind(pat) is not None:
539 541 listpats.append(pat)
540 542 else:
541 543 listpats.append(makestandin(pat))
542 544
543 545 try:
544 546 origcopyfile = util.copyfile
545 547 copiedfiles = []
546 548 def overridecopyfile(src, dest):
547 549 if (lfutil.shortname in src and
548 550 dest.startswith(repo.wjoin(lfutil.shortname))):
549 551 destlfile = dest.replace(lfutil.shortname, '')
550 552 if not opts['force'] and os.path.exists(destlfile):
551 553 raise IOError('',
552 554 _('destination largefile already exists'))
553 555 copiedfiles.append((src, dest))
554 556 origcopyfile(src, dest)
555 557
556 558 util.copyfile = overridecopyfile
557 559 result += orig(ui, repo, listpats, opts, rename)
558 560 finally:
559 561 util.copyfile = origcopyfile
560 562
561 563 lfdirstate = lfutil.openlfdirstate(ui, repo)
562 564 for (src, dest) in copiedfiles:
563 565 if (lfutil.shortname in src and
564 566 dest.startswith(repo.wjoin(lfutil.shortname))):
565 567 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
566 568 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
567 569 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
568 570 if not os.path.isdir(destlfiledir):
569 571 os.makedirs(destlfiledir)
570 572 if rename:
571 573 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
572 574 lfdirstate.remove(srclfile)
573 575 else:
574 576 util.copyfile(repo.wjoin(srclfile),
575 577 repo.wjoin(destlfile))
576 578
577 579 lfdirstate.add(destlfile)
578 580 lfdirstate.write()
579 581 except util.Abort, e:
580 582 if str(e) != _('no files to copy'):
581 583 raise e
582 584 else:
583 585 nolfiles = True
584 586 finally:
585 587 restorematchfn()
586 588 wlock.release()
587 589
588 590 if nolfiles and nonormalfiles:
589 591 raise util.Abort(_('no files to copy'))
590 592
591 593 return result
592 594
593 595 # When the user calls revert, we have to be careful to not revert any
594 596 # changes to other largefiles accidentally. This means we have to keep
595 597 # track of the largefiles that are being reverted so we only pull down
596 598 # the necessary largefiles.
597 599 #
598 600 # Standins are only updated (to match the hash of largefiles) before
599 601 # commits. Update the standins then run the original revert, changing
600 602 # the matcher to hit standins instead of largefiles. Based on the
601 603 # resulting standins update the largefiles. Then return the standins
602 604 # to their proper state
603 605 def overriderevert(orig, ui, repo, *pats, **opts):
604 606 # Because we put the standins in a bad state (by updating them)
605 607 # and then return them to a correct state we need to lock to
606 608 # prevent others from changing them in their incorrect state.
607 609 wlock = repo.wlock()
608 610 try:
609 611 lfdirstate = lfutil.openlfdirstate(ui, repo)
610 612 (modified, added, removed, missing, unknown, ignored, clean) = \
611 613 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
612 614 lfdirstate.write()
613 615 for lfile in modified:
614 616 lfutil.updatestandin(repo, lfutil.standin(lfile))
615 617 for lfile in missing:
616 618 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
617 619 os.unlink(repo.wjoin(lfutil.standin(lfile)))
618 620
619 621 try:
620 622 ctx = scmutil.revsingle(repo, opts.get('rev'))
621 623 oldmatch = None # for the closure
622 624 def overridematch(ctx, pats=[], opts={}, globbed=False,
623 625 default='relpath'):
624 626 match = oldmatch(ctx, pats, opts, globbed, default)
625 627 m = copy.copy(match)
626 628 def tostandin(f):
627 629 if lfutil.standin(f) in ctx:
628 630 return lfutil.standin(f)
629 631 elif lfutil.standin(f) in repo[None]:
630 632 return None
631 633 return f
632 634 m._files = [tostandin(f) for f in m._files]
633 635 m._files = [f for f in m._files if f is not None]
634 636 m._fmap = set(m._files)
635 637 m._always = False
636 638 origmatchfn = m.matchfn
637 639 def matchfn(f):
638 640 if lfutil.isstandin(f):
639 641 # We need to keep track of what largefiles are being
640 642 # matched so we know which ones to update later --
641 643 # otherwise we accidentally revert changes to other
642 644 # largefiles. This is repo-specific, so duckpunch the
643 645 # repo object to keep the list of largefiles for us
644 646 # later.
645 647 if origmatchfn(lfutil.splitstandin(f)) and \
646 648 (f in repo[None] or f in ctx):
647 649 lfileslist = getattr(repo, '_lfilestoupdate', [])
648 650 lfileslist.append(lfutil.splitstandin(f))
649 651 repo._lfilestoupdate = lfileslist
650 652 return True
651 653 else:
652 654 return False
653 655 return origmatchfn(f)
654 656 m.matchfn = matchfn
655 657 return m
656 658 oldmatch = installmatchfn(overridematch)
657 659 scmutil.match
658 660 matches = overridematch(repo[None], pats, opts)
659 661 orig(ui, repo, *pats, **opts)
660 662 finally:
661 663 restorematchfn()
662 664 lfileslist = getattr(repo, '_lfilestoupdate', [])
663 665 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
664 666 printmessage=False)
665 667
666 668 # empty out the largefiles list so we start fresh next time
667 669 repo._lfilestoupdate = []
668 670 for lfile in modified:
669 671 if lfile in lfileslist:
670 672 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
671 673 in repo['.']:
672 674 lfutil.writestandin(repo, lfutil.standin(lfile),
673 675 repo['.'][lfile].data().strip(),
674 676 'x' in repo['.'][lfile].flags())
675 677 lfdirstate = lfutil.openlfdirstate(ui, repo)
676 678 for lfile in added:
677 679 standin = lfutil.standin(lfile)
678 680 if standin not in ctx and (standin in matches or opts.get('all')):
679 681 if lfile in lfdirstate:
680 682 lfdirstate.drop(lfile)
681 683 util.unlinkpath(repo.wjoin(standin))
682 684 lfdirstate.write()
683 685 finally:
684 686 wlock.release()
685 687
686 688 def hgupdaterepo(orig, repo, node, overwrite):
687 689 if not overwrite:
688 690 # Only call updatelfiles on the standins that have changed to save time
689 691 oldstandins = lfutil.getstandinsstate(repo)
690 692
691 693 result = orig(repo, node, overwrite)
692 694
693 695 filelist = None
694 696 if not overwrite:
695 697 newstandins = lfutil.getstandinsstate(repo)
696 698 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
697 699 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist)
698 700 return result
699 701
700 702 def hgmerge(orig, repo, node, force=None, remind=True):
701 703 result = orig(repo, node, force, remind)
702 704 lfcommands.updatelfiles(repo.ui, repo)
703 705 return result
704 706
705 707 # When we rebase a repository with remotely changed largefiles, we need to
706 708 # take some extra care so that the largefiles are correctly updated in the
707 709 # working copy
708 710 def overridepull(orig, ui, repo, source=None, **opts):
709 711 revsprepull = len(repo)
710 712 if not source:
711 713 source = 'default'
712 714 repo.lfpullsource = source
713 715 if opts.get('rebase', False):
714 716 repo._isrebasing = True
715 717 try:
716 718 if opts.get('update'):
717 719 del opts['update']
718 720 ui.debug('--update and --rebase are not compatible, ignoring '
719 721 'the update flag\n')
720 722 del opts['rebase']
721 723 origpostincoming = commands.postincoming
722 724 def _dummy(*args, **kwargs):
723 725 pass
724 726 commands.postincoming = _dummy
725 727 try:
726 728 result = commands.pull(ui, repo, source, **opts)
727 729 finally:
728 730 commands.postincoming = origpostincoming
729 731 revspostpull = len(repo)
730 732 if revspostpull > revsprepull:
731 733 result = result or rebase.rebase(ui, repo)
732 734 finally:
733 735 repo._isrebasing = False
734 736 else:
735 737 result = orig(ui, repo, source, **opts)
736 738 revspostpull = len(repo)
737 739 lfrevs = opts.get('lfrev', [])
738 740 if opts.get('all_largefiles'):
739 741 lfrevs.append('pulled()')
740 742 if lfrevs and revspostpull > revsprepull:
741 743 numcached = 0
742 744 repo.firstpulled = revsprepull # for pulled() revset expression
743 745 try:
744 746 for rev in scmutil.revrange(repo, lfrevs):
745 747 ui.note(_('pulling largefiles for revision %s\n') % rev)
746 748 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
747 749 numcached += len(cached)
748 750 finally:
749 751 del repo.firstpulled
750 752 ui.status(_("%d largefiles cached\n") % numcached)
751 753 return result
752 754
753 755 def pulledrevsetsymbol(repo, subset, x):
754 756 """``pulled()``
755 757 Changesets that just has been pulled.
756 758
757 759 Only available with largefiles from pull --lfrev expressions.
758 760
759 761 .. container:: verbose
760 762
761 763 Some examples:
762 764
763 765 - pull largefiles for all new changesets::
764 766
765 767 hg pull -lfrev "pulled()"
766 768
767 769 - pull largefiles for all new branch heads::
768 770
769 771 hg pull -lfrev "head(pulled()) and not closed()"
770 772
771 773 """
772 774
773 775 try:
774 776 firstpulled = repo.firstpulled
775 777 except AttributeError:
776 778 raise util.Abort(_("pulled() only available in --lfrev"))
777 779 return [r for r in subset if r >= firstpulled]
778 780
779 781 def overrideclone(orig, ui, source, dest=None, **opts):
780 782 d = dest
781 783 if d is None:
782 784 d = hg.defaultdest(source)
783 785 if opts.get('all_largefiles') and not hg.islocal(d):
784 786 raise util.Abort(_(
785 787 '--all-largefiles is incompatible with non-local destination %s' %
786 788 d))
787 789
788 790 return orig(ui, source, dest, **opts)
789 791
790 792 def hgclone(orig, ui, opts, *args, **kwargs):
791 793 result = orig(ui, opts, *args, **kwargs)
792 794
793 795 if result is not None:
794 796 sourcerepo, destrepo = result
795 797 repo = destrepo.local()
796 798
797 799 # Caching is implicitly limited to 'rev' option, since the dest repo was
798 800 # truncated at that point. The user may expect a download count with
799 801 # this option, so attempt whether or not this is a largefile repo.
800 802 if opts.get('all_largefiles'):
801 803 success, missing = lfcommands.downloadlfiles(ui, repo, None)
802 804
803 805 if missing != 0:
804 806 return None
805 807
806 808 return result
807 809
808 810 def overriderebase(orig, ui, repo, **opts):
809 811 repo._isrebasing = True
810 812 try:
811 813 return orig(ui, repo, **opts)
812 814 finally:
813 815 repo._isrebasing = False
814 816
815 817 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
816 818 prefix=None, mtime=None, subrepos=None):
817 819 # No need to lock because we are only reading history and
818 820 # largefile caches, neither of which are modified.
819 821 lfcommands.cachelfiles(repo.ui, repo, node)
820 822
821 823 if kind not in archival.archivers:
822 824 raise util.Abort(_("unknown archive type '%s'") % kind)
823 825
824 826 ctx = repo[node]
825 827
826 828 if kind == 'files':
827 829 if prefix:
828 830 raise util.Abort(
829 831 _('cannot give prefix when archiving to files'))
830 832 else:
831 833 prefix = archival.tidyprefix(dest, kind, prefix)
832 834
833 835 def write(name, mode, islink, getdata):
834 836 if matchfn and not matchfn(name):
835 837 return
836 838 data = getdata()
837 839 if decode:
838 840 data = repo.wwritedata(name, data)
839 841 archiver.addfile(prefix + name, mode, islink, data)
840 842
841 843 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
842 844
843 845 if repo.ui.configbool("ui", "archivemeta", True):
844 846 def metadata():
845 847 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
846 848 hex(repo.changelog.node(0)), hex(node), ctx.branch())
847 849
848 850 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
849 851 if repo.tagtype(t) == 'global')
850 852 if not tags:
851 853 repo.ui.pushbuffer()
852 854 opts = {'template': '{latesttag}\n{latesttagdistance}',
853 855 'style': '', 'patch': None, 'git': None}
854 856 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
855 857 ltags, dist = repo.ui.popbuffer().split('\n')
856 858 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
857 859 tags += 'latesttagdistance: %s\n' % dist
858 860
859 861 return base + tags
860 862
861 863 write('.hg_archival.txt', 0644, False, metadata)
862 864
863 865 for f in ctx:
864 866 ff = ctx.flags(f)
865 867 getdata = ctx[f].data
866 868 if lfutil.isstandin(f):
867 869 path = lfutil.findfile(repo, getdata().strip())
868 870 if path is None:
869 871 raise util.Abort(
870 872 _('largefile %s not found in repo store or system cache')
871 873 % lfutil.splitstandin(f))
872 874 f = lfutil.splitstandin(f)
873 875
874 876 def getdatafn():
875 877 fd = None
876 878 try:
877 879 fd = open(path, 'rb')
878 880 return fd.read()
879 881 finally:
880 882 if fd:
881 883 fd.close()
882 884
883 885 getdata = getdatafn
884 886 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
885 887
886 888 if subrepos:
887 889 for subpath in sorted(ctx.substate):
888 890 sub = ctx.sub(subpath)
889 891 submatch = match_.narrowmatcher(subpath, matchfn)
890 892 sub.archive(repo.ui, archiver, prefix, submatch)
891 893
892 894 archiver.done()
893 895
894 896 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
895 897 repo._get(repo._state + ('hg',))
896 898 rev = repo._state[1]
897 899 ctx = repo._repo[rev]
898 900
899 901 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
900 902
901 903 def write(name, mode, islink, getdata):
902 904 # At this point, the standin has been replaced with the largefile name,
903 905 # so the normal matcher works here without the lfutil variants.
904 906 if match and not match(f):
905 907 return
906 908 data = getdata()
907 909
908 910 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
909 911
910 912 for f in ctx:
911 913 ff = ctx.flags(f)
912 914 getdata = ctx[f].data
913 915 if lfutil.isstandin(f):
914 916 path = lfutil.findfile(repo._repo, getdata().strip())
915 917 if path is None:
916 918 raise util.Abort(
917 919 _('largefile %s not found in repo store or system cache')
918 920 % lfutil.splitstandin(f))
919 921 f = lfutil.splitstandin(f)
920 922
921 923 def getdatafn():
922 924 fd = None
923 925 try:
924 926 fd = open(os.path.join(prefix, path), 'rb')
925 927 return fd.read()
926 928 finally:
927 929 if fd:
928 930 fd.close()
929 931
930 932 getdata = getdatafn
931 933
932 934 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
933 935
934 936 for subpath in sorted(ctx.substate):
935 937 sub = ctx.sub(subpath)
936 938 submatch = match_.narrowmatcher(subpath, match)
937 939 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
938 940 submatch)
939 941
940 942 # If a largefile is modified, the change is not reflected in its
941 943 # standin until a commit. cmdutil.bailifchanged() raises an exception
942 944 # if the repo has uncommitted changes. Wrap it to also check if
943 945 # largefiles were changed. This is used by bisect and backout.
944 946 def overridebailifchanged(orig, repo):
945 947 orig(repo)
946 948 repo.lfstatus = True
947 949 modified, added, removed, deleted = repo.status()[:4]
948 950 repo.lfstatus = False
949 951 if modified or added or removed or deleted:
950 952 raise util.Abort(_('uncommitted changes'))
951 953
952 954 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
953 955 def overridefetch(orig, ui, repo, *pats, **opts):
954 956 repo.lfstatus = True
955 957 modified, added, removed, deleted = repo.status()[:4]
956 958 repo.lfstatus = False
957 959 if modified or added or removed or deleted:
958 960 raise util.Abort(_('uncommitted changes'))
959 961 return orig(ui, repo, *pats, **opts)
960 962
961 963 def overrideforget(orig, ui, repo, *pats, **opts):
962 964 installnormalfilesmatchfn(repo[None].manifest())
963 965 result = orig(ui, repo, *pats, **opts)
964 966 restorematchfn()
965 967 m = scmutil.match(repo[None], pats, opts)
966 968
967 969 try:
968 970 repo.lfstatus = True
969 971 s = repo.status(match=m, clean=True)
970 972 finally:
971 973 repo.lfstatus = False
972 974 forget = sorted(s[0] + s[1] + s[3] + s[6])
973 975 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
974 976
975 977 for f in forget:
976 978 if lfutil.standin(f) not in repo.dirstate and not \
977 979 os.path.isdir(m.rel(lfutil.standin(f))):
978 980 ui.warn(_('not removing %s: file is already untracked\n')
979 981 % m.rel(f))
980 982 result = 1
981 983
982 984 for f in forget:
983 985 if ui.verbose or not m.exact(f):
984 986 ui.status(_('removing %s\n') % m.rel(f))
985 987
986 988 # Need to lock because standin files are deleted then removed from the
987 989 # repository and we could race in-between.
988 990 wlock = repo.wlock()
989 991 try:
990 992 lfdirstate = lfutil.openlfdirstate(ui, repo)
991 993 for f in forget:
992 994 if lfdirstate[f] == 'a':
993 995 lfdirstate.drop(f)
994 996 else:
995 997 lfdirstate.remove(f)
996 998 lfdirstate.write()
997 999 standins = [lfutil.standin(f) for f in forget]
998 1000 for f in standins:
999 1001 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1000 1002 repo[None].forget(standins)
1001 1003 finally:
1002 1004 wlock.release()
1003 1005
1004 1006 return result
1005 1007
1006 1008 def getoutgoinglfiles(ui, repo, dest=None, **opts):
1007 1009 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1008 1010 dest, branches = hg.parseurl(dest, opts.get('branch'))
1009 1011 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
1010 1012 if revs:
1011 1013 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
1012 1014
1013 1015 try:
1014 1016 remote = hg.peer(repo, opts, dest)
1015 1017 except error.RepoError:
1016 1018 return None
1017 1019 outgoing = discovery.findcommonoutgoing(repo, remote.peer(), force=False)
1018 1020 if not outgoing.missing:
1019 1021 return outgoing.missing
1020 1022 o = repo.changelog.nodesbetween(outgoing.missing, revs)[0]
1021 1023 if opts.get('newest_first'):
1022 1024 o.reverse()
1023 1025
1024 1026 toupload = set()
1025 1027 for n in o:
1026 1028 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
1027 1029 ctx = repo[n]
1028 1030 files = set(ctx.files())
1029 1031 if len(parents) == 2:
1030 1032 mc = ctx.manifest()
1031 1033 mp1 = ctx.parents()[0].manifest()
1032 1034 mp2 = ctx.parents()[1].manifest()
1033 1035 for f in mp1:
1034 1036 if f not in mc:
1035 1037 files.add(f)
1036 1038 for f in mp2:
1037 1039 if f not in mc:
1038 1040 files.add(f)
1039 1041 for f in mc:
1040 1042 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
1041 1043 files.add(f)
1042 1044 toupload = toupload.union(
1043 1045 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
1044 1046 return sorted(toupload)
1045 1047
1046 1048 def overrideoutgoing(orig, ui, repo, dest=None, **opts):
1047 1049 result = orig(ui, repo, dest, **opts)
1048 1050
1049 1051 if opts.pop('large', None):
1050 1052 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
1051 1053 if toupload is None:
1052 1054 ui.status(_('largefiles: No remote repo\n'))
1053 1055 elif not toupload:
1054 1056 ui.status(_('largefiles: no files to upload\n'))
1055 1057 else:
1056 1058 ui.status(_('largefiles to upload:\n'))
1057 1059 for file in toupload:
1058 1060 ui.status(lfutil.splitstandin(file) + '\n')
1059 1061 ui.status('\n')
1060 1062
1061 1063 return result
1062 1064
1063 1065 def overridesummary(orig, ui, repo, *pats, **opts):
1064 1066 try:
1065 1067 repo.lfstatus = True
1066 1068 orig(ui, repo, *pats, **opts)
1067 1069 finally:
1068 1070 repo.lfstatus = False
1069 1071
1070 1072 if opts.pop('large', None):
1071 1073 toupload = getoutgoinglfiles(ui, repo, None, **opts)
1072 1074 if toupload is None:
1073 1075 # i18n: column positioning for "hg summary"
1074 1076 ui.status(_('largefiles: (no remote repo)\n'))
1075 1077 elif not toupload:
1076 1078 # i18n: column positioning for "hg summary"
1077 1079 ui.status(_('largefiles: (no files to upload)\n'))
1078 1080 else:
1079 1081 # i18n: column positioning for "hg summary"
1080 1082 ui.status(_('largefiles: %d to upload\n') % len(toupload))
1081 1083
1082 1084 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1083 1085 similarity=None):
1084 1086 if not lfutil.islfilesrepo(repo):
1085 1087 return orig(repo, pats, opts, dry_run, similarity)
1086 1088 # Get the list of missing largefiles so we can remove them
1087 1089 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1088 1090 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
1089 1091 False, False)
1090 1092 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
1091 1093
1092 1094 # Call into the normal remove code, but the removing of the standin, we want
1093 1095 # to have handled by original addremove. Monkey patching here makes sure
1094 1096 # we don't remove the standin in the largefiles code, preventing a very
1095 1097 # confused state later.
1096 1098 if missing:
1097 1099 m = [repo.wjoin(f) for f in missing]
1098 1100 repo._isaddremove = True
1099 1101 removelargefiles(repo.ui, repo, *m, **opts)
1100 1102 repo._isaddremove = False
1101 1103 # Call into the normal add code, and any files that *should* be added as
1102 1104 # largefiles will be
1103 1105 addlargefiles(repo.ui, repo, *pats, **opts)
1104 1106 # Now that we've handled largefiles, hand off to the original addremove
1105 1107 # function to take care of the rest. Make sure it doesn't do anything with
1106 1108 # largefiles by installing a matcher that will ignore them.
1107 1109 installnormalfilesmatchfn(repo[None].manifest())
1108 1110 result = orig(repo, pats, opts, dry_run, similarity)
1109 1111 restorematchfn()
1110 1112 return result
1111 1113
1112 1114 # Calling purge with --all will cause the largefiles to be deleted.
1113 1115 # Override repo.status to prevent this from happening.
1114 1116 def overridepurge(orig, ui, repo, *dirs, **opts):
1115 1117 # XXX large file status is buggy when used on repo proxy.
1116 1118 # XXX this needs to be investigate.
1117 1119 repo = repo.unfiltered()
1118 1120 oldstatus = repo.status
1119 1121 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1120 1122 clean=False, unknown=False, listsubrepos=False):
1121 1123 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1122 1124 listsubrepos)
1123 1125 lfdirstate = lfutil.openlfdirstate(ui, repo)
1124 1126 modified, added, removed, deleted, unknown, ignored, clean = r
1125 1127 unknown = [f for f in unknown if lfdirstate[f] == '?']
1126 1128 ignored = [f for f in ignored if lfdirstate[f] == '?']
1127 1129 return modified, added, removed, deleted, unknown, ignored, clean
1128 1130 repo.status = overridestatus
1129 1131 orig(ui, repo, *dirs, **opts)
1130 1132 repo.status = oldstatus
1131 1133
1132 1134 def overriderollback(orig, ui, repo, **opts):
1133 1135 result = orig(ui, repo, **opts)
1134 1136 merge.update(repo, node=None, branchmerge=False, force=True,
1135 1137 partial=lfutil.isstandin)
1136 1138 wlock = repo.wlock()
1137 1139 try:
1138 1140 lfdirstate = lfutil.openlfdirstate(ui, repo)
1139 1141 lfiles = lfutil.listlfiles(repo)
1140 1142 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
1141 1143 for file in lfiles:
1142 1144 if file in oldlfiles:
1143 1145 lfdirstate.normallookup(file)
1144 1146 else:
1145 1147 lfdirstate.add(file)
1146 1148 lfdirstate.write()
1147 1149 finally:
1148 1150 wlock.release()
1149 1151 return result
1150 1152
1151 1153 def overridetransplant(orig, ui, repo, *revs, **opts):
1152 1154 try:
1153 1155 oldstandins = lfutil.getstandinsstate(repo)
1154 1156 repo._istransplanting = True
1155 1157 result = orig(ui, repo, *revs, **opts)
1156 1158 newstandins = lfutil.getstandinsstate(repo)
1157 1159 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1158 1160 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1159 1161 printmessage=True)
1160 1162 finally:
1161 1163 repo._istransplanting = False
1162 1164 return result
1163 1165
1164 1166 def overridecat(orig, ui, repo, file1, *pats, **opts):
1165 1167 ctx = scmutil.revsingle(repo, opts.get('rev'))
1166 1168 err = 1
1167 1169 notbad = set()
1168 1170 m = scmutil.match(ctx, (file1,) + pats, opts)
1169 1171 origmatchfn = m.matchfn
1170 1172 def lfmatchfn(f):
1171 1173 lf = lfutil.splitstandin(f)
1172 1174 if lf is None:
1173 1175 return origmatchfn(f)
1174 1176 notbad.add(lf)
1175 1177 return origmatchfn(lf)
1176 1178 m.matchfn = lfmatchfn
1177 1179 origbadfn = m.bad
1178 1180 def lfbadfn(f, msg):
1179 1181 if not f in notbad:
1180 1182 return origbadfn(f, msg)
1181 1183 m.bad = lfbadfn
1182 1184 for f in ctx.walk(m):
1183 1185 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1184 1186 pathname=f)
1185 1187 lf = lfutil.splitstandin(f)
1186 1188 if lf is None:
1187 1189 # duplicating unreachable code from commands.cat
1188 1190 data = ctx[f].data()
1189 1191 if opts.get('decode'):
1190 1192 data = repo.wwritedata(f, data)
1191 1193 fp.write(data)
1192 1194 else:
1193 1195 hash = lfutil.readstandin(repo, lf, ctx.rev())
1194 1196 if not lfutil.inusercache(repo.ui, hash):
1195 1197 store = basestore._openstore(repo)
1196 1198 success, missing = store.get([(lf, hash)])
1197 1199 if len(success) != 1:
1198 1200 raise util.Abort(
1199 1201 _('largefile %s is not in cache and could not be '
1200 1202 'downloaded') % lf)
1201 1203 path = lfutil.usercachepath(repo.ui, hash)
1202 1204 fpin = open(path, "rb")
1203 1205 for chunk in util.filechunkiter(fpin, 128 * 1024):
1204 1206 fp.write(chunk)
1205 1207 fpin.close()
1206 1208 fp.close()
1207 1209 err = 0
1208 1210 return err
1209 1211
1210 1212 def mercurialsinkbefore(orig, sink):
1211 1213 sink.repo._isconverting = True
1212 1214 orig(sink)
1213 1215
1214 1216 def mercurialsinkafter(orig, sink):
1215 1217 sink.repo._isconverting = False
1216 1218 orig(sink)
@@ -1,112 +1,139
1 1
2 2 $ echo "[extensions]" >> $HGRCPATH
3 3 $ echo "largefiles =" >> $HGRCPATH
4 4
5 5 Create the repository outside $HOME since largefiles write to
6 6 $HOME/.cache/largefiles.
7 7
8 8 $ hg init test
9 9 $ cd test
10 10 $ echo "root" > root
11 11 $ hg add root
12 12 $ hg commit -m "Root commit"
13 13
14 14 $ echo "large" > foo
15 15 $ hg add --large foo
16 16 $ hg commit -m "Add foo as a largefile"
17 17
18 18 $ hg update -r 0
19 19 getting changed largefiles
20 20 0 largefiles updated, 1 removed
21 21 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
22 22
23 23 $ echo "normal" > foo
24 24 $ hg add foo
25 25 $ hg commit -m "Add foo as normal file"
26 26 created new head
27 27
28 28 Normal file in the working copy, keeping the normal version:
29 29
30 30 $ echo "n" | hg merge --config ui.interactive=Yes
31 31 foo has been turned into a largefile
32 32 use (l)argefile or keep as (n)ormal file? 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
33 33 (branch merge, don't forget to commit)
34 34 getting changed largefiles
35 35 0 largefiles updated, 0 removed
36 36
37 37 $ hg status
38 38 $ cat foo
39 39 normal
40 40
41 41 Normal file in the working copy, keeping the largefile version:
42 42
43 43 $ hg update -q -C
44 44 $ echo "l" | hg merge --config ui.interactive=Yes
45 45 foo has been turned into a largefile
46 46 use (l)argefile or keep as (n)ormal file? 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
47 47 (branch merge, don't forget to commit)
48 48 getting changed largefiles
49 49 1 largefiles updated, 0 removed
50 50
51 51 $ hg status
52 52 M foo
53 53
54 54 $ hg diff --nodates
55 55 diff -r fa129ab6b5a7 .hglf/foo
56 56 --- /dev/null
57 57 +++ b/.hglf/foo
58 58 @@ -0,0 +1,1 @@
59 59 +7f7097b041ccf68cc5561e9600da4655d21c6d18
60 60 diff -r fa129ab6b5a7 foo
61 61 --- a/foo
62 62 +++ /dev/null
63 63 @@ -1,1 +0,0 @@
64 64 -normal
65 65
66 66 $ cat foo
67 67 large
68 68
69 69 Largefile in the working copy, keeping the normal version:
70 70
71 71 $ hg update -q -C -r 1
72 72 $ echo "n" | hg merge --config ui.interactive=Yes
73 73 foo has been turned into a normal file
74 74 keep as (l)argefile or use (n)ormal file? 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
75 75 (branch merge, don't forget to commit)
76 76 getting changed largefiles
77 77 0 largefiles updated, 0 removed
78 78
79 79 $ hg status
80 80 M foo
81 81
82 82 $ hg diff --nodates
83 83 diff -r ff521236428a .hglf/foo
84 84 --- a/.hglf/foo
85 85 +++ /dev/null
86 86 @@ -1,1 +0,0 @@
87 87 -7f7097b041ccf68cc5561e9600da4655d21c6d18
88 88 diff -r ff521236428a foo
89 89 --- /dev/null
90 90 +++ b/foo
91 91 @@ -0,0 +1,1 @@
92 92 +normal
93 93
94 94 $ cat foo
95 95 normal
96 96
97 97 Largefile in the working copy, keeping the largefile version:
98 98
99 99 $ hg update -q -C -r 1
100 100 $ echo "l" | hg merge --config ui.interactive=Yes
101 101 foo has been turned into a normal file
102 102 keep as (l)argefile or use (n)ormal file? 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
103 103 (branch merge, don't forget to commit)
104 104 getting changed largefiles
105 105 1 largefiles updated, 0 removed
106 106
107 107 $ hg status
108 108
109 109 $ cat foo
110 110 large
111 111
112 Whatever ... commit something so we can invoke merge when updating
113
114 $ hg commit -m '3: Merge'
115
116 Updating from largefile to normal - no reason to prompt
117
118 $ hg up -r 2
119 getting changed largefiles
120 0 largefiles updated, 0 removed
121 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
122 $ cat foo
123 normal
124
125 (the update above used to leave the working dir in a very weird state - clean it
126 $ hg up -qr null
127 $ hg up -qr 2
128 )
129
130 Updating from normal to largefile - no reason to prompt
131
132 $ hg up -r 3
133 getting changed largefiles
134 1 largefiles updated, 0 removed
135 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
136 $ cat foo
137 large
138
112 139 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now