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