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