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