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