##// END OF EJS Templates
rebase: use dirstateguard instead of dirstate.invalidate...
FUJIWARA Katsunori -
r24998:c8a97fa7 default
parent child Browse files
Show More
@@ -1,1116 +1,1114
1 1 # rebase.py - rebasing feature for mercurial
2 2 #
3 3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''command to move sets of revisions to a different ancestor
9 9
10 10 This extension lets you rebase changesets in an existing Mercurial
11 11 repository.
12 12
13 13 For more information:
14 14 http://mercurial.selenic.com/wiki/RebaseExtension
15 15 '''
16 16
17 17 from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks
18 18 from mercurial import extensions, patch, scmutil, phases, obsolete, error
19 19 from mercurial import copies, repoview
20 20 from mercurial.commands import templateopts
21 21 from mercurial.node import nullrev, nullid, hex, short
22 22 from mercurial.lock import release
23 23 from mercurial.i18n import _
24 24 import os, errno
25 25
26 26 revtodo = -1
27 27 nullmerge = -2
28 28 revignored = -3
29 29
30 30 cmdtable = {}
31 31 command = cmdutil.command(cmdtable)
32 32 testedwith = 'internal'
33 33
34 34 def _savegraft(ctx, extra):
35 35 s = ctx.extra().get('source', None)
36 36 if s is not None:
37 37 extra['source'] = s
38 38
39 39 def _savebranch(ctx, extra):
40 40 extra['branch'] = ctx.branch()
41 41
42 42 def _makeextrafn(copiers):
43 43 """make an extrafn out of the given copy-functions.
44 44
45 45 A copy function takes a context and an extra dict, and mutates the
46 46 extra dict as needed based on the given context.
47 47 """
48 48 def extrafn(ctx, extra):
49 49 for c in copiers:
50 50 c(ctx, extra)
51 51 return extrafn
52 52
53 53 @command('rebase',
54 54 [('s', 'source', '',
55 55 _('rebase the specified changeset and descendants'), _('REV')),
56 56 ('b', 'base', '',
57 57 _('rebase everything from branching point of specified changeset'),
58 58 _('REV')),
59 59 ('r', 'rev', [],
60 60 _('rebase these revisions'),
61 61 _('REV')),
62 62 ('d', 'dest', '',
63 63 _('rebase onto the specified changeset'), _('REV')),
64 64 ('', 'collapse', False, _('collapse the rebased changesets')),
65 65 ('m', 'message', '',
66 66 _('use text as collapse commit message'), _('TEXT')),
67 67 ('e', 'edit', False, _('invoke editor on commit messages')),
68 68 ('l', 'logfile', '',
69 69 _('read collapse commit message from file'), _('FILE')),
70 70 ('', 'keep', False, _('keep original changesets')),
71 71 ('', 'keepbranches', False, _('keep original branch names')),
72 72 ('D', 'detach', False, _('(DEPRECATED)')),
73 73 ('i', 'interactive', False, _('(DEPRECATED)')),
74 74 ('t', 'tool', '', _('specify merge tool')),
75 75 ('c', 'continue', False, _('continue an interrupted rebase')),
76 76 ('a', 'abort', False, _('abort an interrupted rebase'))] +
77 77 templateopts,
78 78 _('[-s REV | -b REV] [-d REV] [OPTION]'))
79 79 def rebase(ui, repo, **opts):
80 80 """move changeset (and descendants) to a different branch
81 81
82 82 Rebase uses repeated merging to graft changesets from one part of
83 83 history (the source) onto another (the destination). This can be
84 84 useful for linearizing *local* changes relative to a master
85 85 development tree.
86 86
87 87 You should not rebase changesets that have already been shared
88 88 with others. Doing so will force everybody else to perform the
89 89 same rebase or they will end up with duplicated changesets after
90 90 pulling in your rebased changesets.
91 91
92 92 In its default configuration, Mercurial will prevent you from
93 93 rebasing published changes. See :hg:`help phases` for details.
94 94
95 95 If you don't specify a destination changeset (``-d/--dest``),
96 96 rebase uses the current branch tip as the destination. (The
97 97 destination changeset is not modified by rebasing, but new
98 98 changesets are added as its descendants.)
99 99
100 100 You can specify which changesets to rebase in two ways: as a
101 101 "source" changeset or as a "base" changeset. Both are shorthand
102 102 for a topologically related set of changesets (the "source
103 103 branch"). If you specify source (``-s/--source``), rebase will
104 104 rebase that changeset and all of its descendants onto dest. If you
105 105 specify base (``-b/--base``), rebase will select ancestors of base
106 106 back to but not including the common ancestor with dest. Thus,
107 107 ``-b`` is less precise but more convenient than ``-s``: you can
108 108 specify any changeset in the source branch, and rebase will select
109 109 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
110 110 uses the parent of the working directory as the base.
111 111
112 112 For advanced usage, a third way is available through the ``--rev``
113 113 option. It allows you to specify an arbitrary set of changesets to
114 114 rebase. Descendants of revs you specify with this option are not
115 115 automatically included in the rebase.
116 116
117 117 By default, rebase recreates the changesets in the source branch
118 118 as descendants of dest and then destroys the originals. Use
119 119 ``--keep`` to preserve the original source changesets. Some
120 120 changesets in the source branch (e.g. merges from the destination
121 121 branch) may be dropped if they no longer contribute any change.
122 122
123 123 One result of the rules for selecting the destination changeset
124 124 and source branch is that, unlike ``merge``, rebase will do
125 125 nothing if you are at the branch tip of a named branch
126 126 with two heads. You need to explicitly specify source and/or
127 127 destination (or ``update`` to the other head, if it's the head of
128 128 the intended source branch).
129 129
130 130 If a rebase is interrupted to manually resolve a merge, it can be
131 131 continued with --continue/-c or aborted with --abort/-a.
132 132
133 133 .. container:: verbose
134 134
135 135 Examples:
136 136
137 137 - move "local changes" (current commit back to branching point)
138 138 to the current branch tip after a pull::
139 139
140 140 hg rebase
141 141
142 142 - move a single changeset to the stable branch::
143 143
144 144 hg rebase -r 5f493448 -d stable
145 145
146 146 - splice a commit and all its descendants onto another part of history::
147 147
148 148 hg rebase --source c0c3 --dest 4cf9
149 149
150 150 - rebase everything on a branch marked by a bookmark onto the
151 151 default branch::
152 152
153 153 hg rebase --base myfeature --dest default
154 154
155 155 - collapse a sequence of changes into a single commit::
156 156
157 157 hg rebase --collapse -r 1520:1525 -d .
158 158
159 159 - move a named branch while preserving its name::
160 160
161 161 hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
162 162
163 163 Returns 0 on success, 1 if nothing to rebase or there are
164 164 unresolved conflicts.
165 165
166 166 """
167 167 originalwd = target = None
168 168 activebookmark = None
169 169 external = nullrev
170 170 state = {}
171 171 skipped = set()
172 172 targetancestors = set()
173 173
174 174
175 175 lock = wlock = None
176 176 try:
177 177 wlock = repo.wlock()
178 178 lock = repo.lock()
179 179
180 180 # Validate input and define rebasing points
181 181 destf = opts.get('dest', None)
182 182 srcf = opts.get('source', None)
183 183 basef = opts.get('base', None)
184 184 revf = opts.get('rev', [])
185 185 contf = opts.get('continue')
186 186 abortf = opts.get('abort')
187 187 collapsef = opts.get('collapse', False)
188 188 collapsemsg = cmdutil.logmessage(ui, opts)
189 189 e = opts.get('extrafn') # internal, used by e.g. hgsubversion
190 190 extrafns = [_savegraft]
191 191 if e:
192 192 extrafns = [e]
193 193 keepf = opts.get('keep', False)
194 194 keepbranchesf = opts.get('keepbranches', False)
195 195 # keepopen is not meant for use on the command line, but by
196 196 # other extensions
197 197 keepopen = opts.get('keepopen', False)
198 198
199 199 if opts.get('interactive'):
200 200 msg = _("interactive history editing is supported by the "
201 201 "'histedit' extension (see \"hg help histedit\")")
202 202 raise util.Abort(msg)
203 203
204 204 if collapsemsg and not collapsef:
205 205 raise util.Abort(
206 206 _('message can only be specified with collapse'))
207 207
208 208 if contf or abortf:
209 209 if contf and abortf:
210 210 raise util.Abort(_('cannot use both abort and continue'))
211 211 if collapsef:
212 212 raise util.Abort(
213 213 _('cannot use collapse with continue or abort'))
214 214 if srcf or basef or destf:
215 215 raise util.Abort(
216 216 _('abort and continue do not allow specifying revisions'))
217 217 if opts.get('tool', False):
218 218 ui.warn(_('tool option will be ignored\n'))
219 219
220 220 try:
221 221 (originalwd, target, state, skipped, collapsef, keepf,
222 222 keepbranchesf, external, activebookmark) = restorestatus(repo)
223 223 except error.RepoLookupError:
224 224 if abortf:
225 225 clearstatus(repo)
226 226 repo.ui.warn(_('rebase aborted (no revision is removed,'
227 227 ' only broken state is cleared)\n'))
228 228 return 0
229 229 else:
230 230 msg = _('cannot continue inconsistent rebase')
231 231 hint = _('use "hg rebase --abort" to clear broken state')
232 232 raise util.Abort(msg, hint=hint)
233 233 if abortf:
234 234 return abort(repo, originalwd, target, state,
235 235 activebookmark=activebookmark)
236 236 else:
237 237 if srcf and basef:
238 238 raise util.Abort(_('cannot specify both a '
239 239 'source and a base'))
240 240 if revf and basef:
241 241 raise util.Abort(_('cannot specify both a '
242 242 'revision and a base'))
243 243 if revf and srcf:
244 244 raise util.Abort(_('cannot specify both a '
245 245 'revision and a source'))
246 246
247 247 cmdutil.checkunfinished(repo)
248 248 cmdutil.bailifchanged(repo)
249 249
250 250 if not destf:
251 251 # Destination defaults to the latest revision in the
252 252 # current branch
253 253 branch = repo[None].branch()
254 254 dest = repo[branch]
255 255 else:
256 256 dest = scmutil.revsingle(repo, destf)
257 257
258 258 if revf:
259 259 rebaseset = scmutil.revrange(repo, revf)
260 260 if not rebaseset:
261 261 ui.status(_('empty "rev" revision set - '
262 262 'nothing to rebase\n'))
263 263 return 1
264 264 elif srcf:
265 265 src = scmutil.revrange(repo, [srcf])
266 266 if not src:
267 267 ui.status(_('empty "source" revision set - '
268 268 'nothing to rebase\n'))
269 269 return 1
270 270 rebaseset = repo.revs('(%ld)::', src)
271 271 assert rebaseset
272 272 else:
273 273 base = scmutil.revrange(repo, [basef or '.'])
274 274 if not base:
275 275 ui.status(_('empty "base" revision set - '
276 276 "can't compute rebase set\n"))
277 277 return 1
278 278 commonanc = repo.revs('ancestor(%ld, %d)', base, dest).first()
279 279 if commonanc is not None:
280 280 rebaseset = repo.revs('(%d::(%ld) - %d)::',
281 281 commonanc, base, commonanc)
282 282 else:
283 283 rebaseset = []
284 284
285 285 if not rebaseset:
286 286 # transform to list because smartsets are not comparable to
287 287 # lists. This should be improved to honor laziness of
288 288 # smartset.
289 289 if list(base) == [dest.rev()]:
290 290 if basef:
291 291 ui.status(_('nothing to rebase - %s is both "base"'
292 292 ' and destination\n') % dest)
293 293 else:
294 294 ui.status(_('nothing to rebase - working directory '
295 295 'parent is also destination\n'))
296 296 elif not repo.revs('%ld - ::%d', base, dest):
297 297 if basef:
298 298 ui.status(_('nothing to rebase - "base" %s is '
299 299 'already an ancestor of destination '
300 300 '%s\n') %
301 301 ('+'.join(str(repo[r]) for r in base),
302 302 dest))
303 303 else:
304 304 ui.status(_('nothing to rebase - working '
305 305 'directory parent is already an '
306 306 'ancestor of destination %s\n') % dest)
307 307 else: # can it happen?
308 308 ui.status(_('nothing to rebase from %s to %s\n') %
309 309 ('+'.join(str(repo[r]) for r in base), dest))
310 310 return 1
311 311
312 312 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
313 313 if (not (keepf or allowunstable)
314 314 and repo.revs('first(children(%ld) - %ld)',
315 315 rebaseset, rebaseset)):
316 316 raise util.Abort(
317 317 _("can't remove original changesets with"
318 318 " unrebased descendants"),
319 319 hint=_('use --keep to keep original changesets'))
320 320
321 321 result = buildstate(repo, dest, rebaseset, collapsef)
322 322 if not result:
323 323 # Empty state built, nothing to rebase
324 324 ui.status(_('nothing to rebase\n'))
325 325 return 1
326 326
327 327 root = min(rebaseset)
328 328 if not keepf and not repo[root].mutable():
329 329 raise util.Abort(_("can't rebase immutable changeset %s")
330 330 % repo[root],
331 331 hint=_('see "hg help phases" for details'))
332 332
333 333 originalwd, target, state = result
334 334 if collapsef:
335 335 targetancestors = repo.changelog.ancestors([target],
336 336 inclusive=True)
337 337 external = externalparent(repo, state, targetancestors)
338 338
339 339 if dest.closesbranch() and not keepbranchesf:
340 340 ui.status(_('reopening closed branch head %s\n') % dest)
341 341
342 342 if keepbranchesf:
343 343 # insert _savebranch at the start of extrafns so if
344 344 # there's a user-provided extrafn it can clobber branch if
345 345 # desired
346 346 extrafns.insert(0, _savebranch)
347 347 if collapsef:
348 348 branches = set()
349 349 for rev in state:
350 350 branches.add(repo[rev].branch())
351 351 if len(branches) > 1:
352 352 raise util.Abort(_('cannot collapse multiple named '
353 353 'branches'))
354 354
355 355 # Rebase
356 356 if not targetancestors:
357 357 targetancestors = repo.changelog.ancestors([target], inclusive=True)
358 358
359 359 # Keep track of the current bookmarks in order to reset them later
360 360 currentbookmarks = repo._bookmarks.copy()
361 361 activebookmark = activebookmark or repo._activebookmark
362 362 if activebookmark:
363 363 bookmarks.deactivate(repo)
364 364
365 365 extrafn = _makeextrafn(extrafns)
366 366
367 367 sortedstate = sorted(state)
368 368 total = len(sortedstate)
369 369 pos = 0
370 370 for rev in sortedstate:
371 371 ctx = repo[rev]
372 372 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
373 373 ctx.description().split('\n', 1)[0])
374 374 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
375 375 if names:
376 376 desc += ' (%s)' % ' '.join(names)
377 377 pos += 1
378 378 if state[rev] == revtodo:
379 379 ui.status(_('rebasing %s\n') % desc)
380 380 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)),
381 381 _('changesets'), total)
382 382 p1, p2, base = defineparents(repo, rev, target, state,
383 383 targetancestors)
384 384 storestatus(repo, originalwd, target, state, collapsef, keepf,
385 385 keepbranchesf, external, activebookmark)
386 386 if len(repo.parents()) == 2:
387 387 repo.ui.debug('resuming interrupted rebase\n')
388 388 else:
389 389 try:
390 390 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
391 391 'rebase')
392 392 stats = rebasenode(repo, rev, p1, base, state,
393 393 collapsef, target)
394 394 if stats and stats[3] > 0:
395 395 raise error.InterventionRequired(
396 396 _('unresolved conflicts (see hg '
397 397 'resolve, then hg rebase --continue)'))
398 398 finally:
399 399 ui.setconfig('ui', 'forcemerge', '', 'rebase')
400 400 if not collapsef:
401 401 merging = p2 != nullrev
402 402 editform = cmdutil.mergeeditform(merging, 'rebase')
403 403 editor = cmdutil.getcommiteditor(editform=editform, **opts)
404 404 newnode = concludenode(repo, rev, p1, p2, extrafn=extrafn,
405 405 editor=editor)
406 406 else:
407 407 # Skip commit if we are collapsing
408 408 repo.dirstate.beginparentchange()
409 409 repo.setparents(repo[p1].node())
410 410 repo.dirstate.endparentchange()
411 411 newnode = None
412 412 # Update the state
413 413 if newnode is not None:
414 414 state[rev] = repo[newnode].rev()
415 415 ui.debug('rebased as %s\n' % short(newnode))
416 416 else:
417 417 ui.warn(_('note: rebase of %d:%s created no changes '
418 418 'to commit\n') % (rev, ctx))
419 419 if not collapsef:
420 420 skipped.add(rev)
421 421 state[rev] = p1
422 422 ui.debug('next revision set to %s\n' % p1)
423 423 elif state[rev] == nullmerge:
424 424 ui.debug('ignoring null merge rebase of %s\n' % rev)
425 425 elif state[rev] == revignored:
426 426 ui.status(_('not rebasing ignored %s\n') % desc)
427 427 else:
428 428 ui.status(_('already rebased %s as %s\n') %
429 429 (desc, repo[state[rev]]))
430 430
431 431 ui.progress(_('rebasing'), None)
432 432 ui.note(_('rebase merging completed\n'))
433 433
434 434 if collapsef and not keepopen:
435 435 p1, p2, _base = defineparents(repo, min(state), target,
436 436 state, targetancestors)
437 437 editopt = opts.get('edit')
438 438 editform = 'rebase.collapse'
439 439 if collapsemsg:
440 440 commitmsg = collapsemsg
441 441 else:
442 442 commitmsg = 'Collapsed revision'
443 443 for rebased in state:
444 444 if rebased not in skipped and state[rebased] > nullmerge:
445 445 commitmsg += '\n* %s' % repo[rebased].description()
446 446 editopt = True
447 447 editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
448 448 newnode = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
449 449 extrafn=extrafn, editor=editor)
450 450 if newnode is None:
451 451 newrev = target
452 452 else:
453 453 newrev = repo[newnode].rev()
454 454 for oldrev in state.iterkeys():
455 455 if state[oldrev] > nullmerge:
456 456 state[oldrev] = newrev
457 457
458 458 if 'qtip' in repo.tags():
459 459 updatemq(repo, state, skipped, **opts)
460 460
461 461 if currentbookmarks:
462 462 # Nodeids are needed to reset bookmarks
463 463 nstate = {}
464 464 for k, v in state.iteritems():
465 465 if v > nullmerge:
466 466 nstate[repo[k].node()] = repo[v].node()
467 467 # XXX this is the same as dest.node() for the non-continue path --
468 468 # this should probably be cleaned up
469 469 targetnode = repo[target].node()
470 470
471 471 # restore original working directory
472 472 # (we do this before stripping)
473 473 newwd = state.get(originalwd, originalwd)
474 474 if newwd < 0:
475 475 # original directory is a parent of rebase set root or ignored
476 476 newwd = originalwd
477 477 if newwd not in [c.rev() for c in repo[None].parents()]:
478 478 ui.note(_("update back to initial working directory parent\n"))
479 479 hg.updaterepo(repo, newwd, False)
480 480
481 481 if not keepf:
482 482 collapsedas = None
483 483 if collapsef:
484 484 collapsedas = newnode
485 485 clearrebased(ui, repo, state, skipped, collapsedas)
486 486
487 487 if currentbookmarks:
488 488 updatebookmarks(repo, targetnode, nstate, currentbookmarks)
489 489 if activebookmark not in repo._bookmarks:
490 490 # active bookmark was divergent one and has been deleted
491 491 activebookmark = None
492 492
493 493 clearstatus(repo)
494 494 ui.note(_("rebase completed\n"))
495 495 util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
496 496 if skipped:
497 497 ui.note(_("%d revisions have been skipped\n") % len(skipped))
498 498
499 499 if (activebookmark and
500 500 repo['.'].node() == repo._bookmarks[activebookmark]):
501 501 bookmarks.activate(repo, activebookmark)
502 502
503 503 finally:
504 504 release(lock, wlock)
505 505
506 506 def externalparent(repo, state, targetancestors):
507 507 """Return the revision that should be used as the second parent
508 508 when the revisions in state is collapsed on top of targetancestors.
509 509 Abort if there is more than one parent.
510 510 """
511 511 parents = set()
512 512 source = min(state)
513 513 for rev in state:
514 514 if rev == source:
515 515 continue
516 516 for p in repo[rev].parents():
517 517 if (p.rev() not in state
518 518 and p.rev() not in targetancestors):
519 519 parents.add(p.rev())
520 520 if not parents:
521 521 return nullrev
522 522 if len(parents) == 1:
523 523 return parents.pop()
524 524 raise util.Abort(_('unable to collapse on top of %s, there is more '
525 525 'than one external parent: %s') %
526 526 (max(targetancestors),
527 527 ', '.join(str(p) for p in sorted(parents))))
528 528
529 529 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
530 530 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
531 531 but also store useful information in extra.
532 532 Return node of committed revision.'''
533 dsguard = cmdutil.dirstateguard(repo, 'rebase')
533 534 try:
534 repo.dirstate.beginparentchange()
535 535 repo.setparents(repo[p1].node(), repo[p2].node())
536 repo.dirstate.endparentchange()
537 536 ctx = repo[rev]
538 537 if commitmsg is None:
539 538 commitmsg = ctx.description()
540 539 extra = {'rebase_source': ctx.hex()}
541 540 if extrafn:
542 541 extrafn(ctx, extra)
543 542
544 543 backup = repo.ui.backupconfig('phases', 'new-commit')
545 544 try:
546 545 targetphase = max(ctx.phase(), phases.draft)
547 546 repo.ui.setconfig('phases', 'new-commit', targetphase, 'rebase')
548 547 # Commit might fail if unresolved files exist
549 548 newnode = repo.commit(text=commitmsg, user=ctx.user(),
550 549 date=ctx.date(), extra=extra, editor=editor)
551 550 finally:
552 551 repo.ui.restoreconfig(backup)
553 552
554 553 repo.dirstate.setbranch(repo[newnode].branch())
554 dsguard.close()
555 555 return newnode
556 except util.Abort:
557 # Invalidate the previous setparents
558 repo.dirstate.invalidate()
559 raise
556 finally:
557 release(dsguard)
560 558
561 559 def rebasenode(repo, rev, p1, base, state, collapse, target):
562 560 'Rebase a single revision rev on top of p1 using base as merge ancestor'
563 561 # Merge phase
564 562 # Update to target and merge it with local
565 563 if repo['.'].rev() != p1:
566 564 repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
567 565 merge.update(repo, p1, False, True, False)
568 566 else:
569 567 repo.ui.debug(" already in target\n")
570 568 repo.dirstate.write()
571 569 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
572 570 if base is not None:
573 571 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
574 572 # When collapsing in-place, the parent is the common ancestor, we
575 573 # have to allow merging with it.
576 574 stats = merge.update(repo, rev, True, True, False, base, collapse,
577 575 labels=['dest', 'source'])
578 576 if collapse:
579 577 copies.duplicatecopies(repo, rev, target)
580 578 else:
581 579 # If we're not using --collapse, we need to
582 580 # duplicate copies between the revision we're
583 581 # rebasing and its first parent, but *not*
584 582 # duplicate any copies that have already been
585 583 # performed in the destination.
586 584 p1rev = repo[rev].p1().rev()
587 585 copies.duplicatecopies(repo, rev, p1rev, skiprev=target)
588 586 return stats
589 587
590 588 def nearestrebased(repo, rev, state):
591 589 """return the nearest ancestors of rev in the rebase result"""
592 590 rebased = [r for r in state if state[r] > nullmerge]
593 591 candidates = repo.revs('max(%ld and (::%d))', rebased, rev)
594 592 if candidates:
595 593 return state[candidates.first()]
596 594 else:
597 595 return None
598 596
599 597 def defineparents(repo, rev, target, state, targetancestors):
600 598 'Return the new parent relationship of the revision that will be rebased'
601 599 parents = repo[rev].parents()
602 600 p1 = p2 = nullrev
603 601
604 602 p1n = parents[0].rev()
605 603 if p1n in targetancestors:
606 604 p1 = target
607 605 elif p1n in state:
608 606 if state[p1n] == nullmerge:
609 607 p1 = target
610 608 elif state[p1n] == revignored:
611 609 p1 = nearestrebased(repo, p1n, state)
612 610 if p1 is None:
613 611 p1 = target
614 612 else:
615 613 p1 = state[p1n]
616 614 else: # p1n external
617 615 p1 = target
618 616 p2 = p1n
619 617
620 618 if len(parents) == 2 and parents[1].rev() not in targetancestors:
621 619 p2n = parents[1].rev()
622 620 # interesting second parent
623 621 if p2n in state:
624 622 if p1 == target: # p1n in targetancestors or external
625 623 p1 = state[p2n]
626 624 elif state[p2n] == revignored:
627 625 p2 = nearestrebased(repo, p2n, state)
628 626 if p2 is None:
629 627 # no ancestors rebased yet, detach
630 628 p2 = target
631 629 else:
632 630 p2 = state[p2n]
633 631 else: # p2n external
634 632 if p2 != nullrev: # p1n external too => rev is a merged revision
635 633 raise util.Abort(_('cannot use revision %d as base, result '
636 634 'would have 3 parents') % rev)
637 635 p2 = p2n
638 636 repo.ui.debug(" future parents are %d and %d\n" %
639 637 (repo[p1].rev(), repo[p2].rev()))
640 638
641 639 if rev == min(state):
642 640 # Case (1) initial changeset of a non-detaching rebase.
643 641 # Let the merge mechanism find the base itself.
644 642 base = None
645 643 elif not repo[rev].p2():
646 644 # Case (2) detaching the node with a single parent, use this parent
647 645 base = repo[rev].p1().rev()
648 646 else:
649 647 # Assuming there is a p1, this is the case where there also is a p2.
650 648 # We are thus rebasing a merge and need to pick the right merge base.
651 649 #
652 650 # Imagine we have:
653 651 # - M: current rebase revision in this step
654 652 # - A: one parent of M
655 653 # - B: other parent of M
656 654 # - D: destination of this merge step (p1 var)
657 655 #
658 656 # Consider the case where D is a descendant of A or B and the other is
659 657 # 'outside'. In this case, the right merge base is the D ancestor.
660 658 #
661 659 # An informal proof, assuming A is 'outside' and B is the D ancestor:
662 660 #
663 661 # If we pick B as the base, the merge involves:
664 662 # - changes from B to M (actual changeset payload)
665 663 # - changes from B to D (induced by rebase) as D is a rebased
666 664 # version of B)
667 665 # Which exactly represent the rebase operation.
668 666 #
669 667 # If we pick A as the base, the merge involves:
670 668 # - changes from A to M (actual changeset payload)
671 669 # - changes from A to D (with include changes between unrelated A and B
672 670 # plus changes induced by rebase)
673 671 # Which does not represent anything sensible and creates a lot of
674 672 # conflicts. A is thus not the right choice - B is.
675 673 #
676 674 # Note: The base found in this 'proof' is only correct in the specified
677 675 # case. This base does not make sense if is not D a descendant of A or B
678 676 # or if the other is not parent 'outside' (especially not if the other
679 677 # parent has been rebased). The current implementation does not
680 678 # make it feasible to consider different cases separately. In these
681 679 # other cases we currently just leave it to the user to correctly
682 680 # resolve an impossible merge using a wrong ancestor.
683 681 for p in repo[rev].parents():
684 682 if state.get(p.rev()) == p1:
685 683 base = p.rev()
686 684 break
687 685 else: # fallback when base not found
688 686 base = None
689 687
690 688 # Raise because this function is called wrong (see issue 4106)
691 689 raise AssertionError('no base found to rebase on '
692 690 '(defineparents called wrong)')
693 691 return p1, p2, base
694 692
695 693 def isagitpatch(repo, patchname):
696 694 'Return true if the given patch is in git format'
697 695 mqpatch = os.path.join(repo.mq.path, patchname)
698 696 for line in patch.linereader(file(mqpatch, 'rb')):
699 697 if line.startswith('diff --git'):
700 698 return True
701 699 return False
702 700
703 701 def updatemq(repo, state, skipped, **opts):
704 702 'Update rebased mq patches - finalize and then import them'
705 703 mqrebase = {}
706 704 mq = repo.mq
707 705 original_series = mq.fullseries[:]
708 706 skippedpatches = set()
709 707
710 708 for p in mq.applied:
711 709 rev = repo[p.node].rev()
712 710 if rev in state:
713 711 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
714 712 (rev, p.name))
715 713 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
716 714 else:
717 715 # Applied but not rebased, not sure this should happen
718 716 skippedpatches.add(p.name)
719 717
720 718 if mqrebase:
721 719 mq.finish(repo, mqrebase.keys())
722 720
723 721 # We must start import from the newest revision
724 722 for rev in sorted(mqrebase, reverse=True):
725 723 if rev not in skipped:
726 724 name, isgit = mqrebase[rev]
727 725 repo.ui.note(_('updating mq patch %s to %s:%s\n') %
728 726 (name, state[rev], repo[state[rev]]))
729 727 mq.qimport(repo, (), patchname=name, git=isgit,
730 728 rev=[str(state[rev])])
731 729 else:
732 730 # Rebased and skipped
733 731 skippedpatches.add(mqrebase[rev][0])
734 732
735 733 # Patches were either applied and rebased and imported in
736 734 # order, applied and removed or unapplied. Discard the removed
737 735 # ones while preserving the original series order and guards.
738 736 newseries = [s for s in original_series
739 737 if mq.guard_re.split(s, 1)[0] not in skippedpatches]
740 738 mq.fullseries[:] = newseries
741 739 mq.seriesdirty = True
742 740 mq.savedirty()
743 741
744 742 def updatebookmarks(repo, targetnode, nstate, originalbookmarks):
745 743 'Move bookmarks to their correct changesets, and delete divergent ones'
746 744 marks = repo._bookmarks
747 745 for k, v in originalbookmarks.iteritems():
748 746 if v in nstate:
749 747 # update the bookmarks for revs that have moved
750 748 marks[k] = nstate[v]
751 749 bookmarks.deletedivergent(repo, [targetnode], k)
752 750
753 751 marks.write()
754 752
755 753 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
756 754 external, activebookmark):
757 755 'Store the current status to allow recovery'
758 756 f = repo.vfs("rebasestate", "w")
759 757 f.write(repo[originalwd].hex() + '\n')
760 758 f.write(repo[target].hex() + '\n')
761 759 f.write(repo[external].hex() + '\n')
762 760 f.write('%d\n' % int(collapse))
763 761 f.write('%d\n' % int(keep))
764 762 f.write('%d\n' % int(keepbranches))
765 763 f.write('%s\n' % (activebookmark or ''))
766 764 for d, v in state.iteritems():
767 765 oldrev = repo[d].hex()
768 766 if v >= 0:
769 767 newrev = repo[v].hex()
770 768 elif v == revtodo:
771 769 # To maintain format compatibility, we have to use nullid.
772 770 # Please do remove this special case when upgrading the format.
773 771 newrev = hex(nullid)
774 772 else:
775 773 newrev = v
776 774 f.write("%s:%s\n" % (oldrev, newrev))
777 775 f.close()
778 776 repo.ui.debug('rebase status stored\n')
779 777
780 778 def clearstatus(repo):
781 779 'Remove the status files'
782 780 _clearrebasesetvisibiliy(repo)
783 781 util.unlinkpath(repo.join("rebasestate"), ignoremissing=True)
784 782
785 783 def restorestatus(repo):
786 784 'Restore a previously stored status'
787 785 try:
788 786 keepbranches = None
789 787 target = None
790 788 collapse = False
791 789 external = nullrev
792 790 activebookmark = None
793 791 state = {}
794 792 f = repo.vfs("rebasestate")
795 793 for i, l in enumerate(f.read().splitlines()):
796 794 if i == 0:
797 795 originalwd = repo[l].rev()
798 796 elif i == 1:
799 797 target = repo[l].rev()
800 798 elif i == 2:
801 799 external = repo[l].rev()
802 800 elif i == 3:
803 801 collapse = bool(int(l))
804 802 elif i == 4:
805 803 keep = bool(int(l))
806 804 elif i == 5:
807 805 keepbranches = bool(int(l))
808 806 elif i == 6 and not (len(l) == 81 and ':' in l):
809 807 # line 6 is a recent addition, so for backwards compatibility
810 808 # check that the line doesn't look like the oldrev:newrev lines
811 809 activebookmark = l
812 810 else:
813 811 oldrev, newrev = l.split(':')
814 812 if newrev in (str(nullmerge), str(revignored)):
815 813 state[repo[oldrev].rev()] = int(newrev)
816 814 elif newrev == nullid:
817 815 state[repo[oldrev].rev()] = revtodo
818 816 # Legacy compat special case
819 817 else:
820 818 state[repo[oldrev].rev()] = repo[newrev].rev()
821 819
822 820 if keepbranches is None:
823 821 raise util.Abort(_('.hg/rebasestate is incomplete'))
824 822
825 823 skipped = set()
826 824 # recompute the set of skipped revs
827 825 if not collapse:
828 826 seen = set([target])
829 827 for old, new in sorted(state.items()):
830 828 if new != revtodo and new in seen:
831 829 skipped.add(old)
832 830 seen.add(new)
833 831 repo.ui.debug('computed skipped revs: %s\n' %
834 832 (' '.join(str(r) for r in sorted(skipped)) or None))
835 833 repo.ui.debug('rebase status resumed\n')
836 834 _setrebasesetvisibility(repo, state.keys())
837 835 return (originalwd, target, state, skipped,
838 836 collapse, keep, keepbranches, external, activebookmark)
839 837 except IOError, err:
840 838 if err.errno != errno.ENOENT:
841 839 raise
842 840 raise util.Abort(_('no rebase in progress'))
843 841
844 842 def inrebase(repo, originalwd, state):
845 843 '''check whether the working dir is in an interrupted rebase'''
846 844 parents = [p.rev() for p in repo.parents()]
847 845 if originalwd in parents:
848 846 return True
849 847
850 848 for newrev in state.itervalues():
851 849 if newrev in parents:
852 850 return True
853 851
854 852 return False
855 853
856 854 def abort(repo, originalwd, target, state, activebookmark=None):
857 855 '''Restore the repository to its original state. Additional args:
858 856
859 857 activebookmark: the name of the bookmark that should be active after the
860 858 restore'''
861 859 dstates = [s for s in state.values() if s >= 0]
862 860 immutable = [d for d in dstates if not repo[d].mutable()]
863 861 cleanup = True
864 862 if immutable:
865 863 repo.ui.warn(_("warning: can't clean up immutable changesets %s\n")
866 864 % ', '.join(str(repo[r]) for r in immutable),
867 865 hint=_('see "hg help phases" for details'))
868 866 cleanup = False
869 867
870 868 descendants = set()
871 869 if dstates:
872 870 descendants = set(repo.changelog.descendants(dstates))
873 871 if descendants - set(dstates):
874 872 repo.ui.warn(_("warning: new changesets detected on target branch, "
875 873 "can't strip\n"))
876 874 cleanup = False
877 875
878 876 if cleanup:
879 877 # Update away from the rebase if necessary
880 878 if inrebase(repo, originalwd, state):
881 879 merge.update(repo, originalwd, False, True, False)
882 880
883 881 # Strip from the first rebased revision
884 882 rebased = filter(lambda x: x >= 0 and x != target, state.values())
885 883 if rebased:
886 884 strippoints = [c.node() for c in repo.set('roots(%ld)', rebased)]
887 885 # no backup of rebased cset versions needed
888 886 repair.strip(repo.ui, repo, strippoints)
889 887
890 888 if activebookmark:
891 889 bookmarks.activate(repo, activebookmark)
892 890
893 891 clearstatus(repo)
894 892 repo.ui.warn(_('rebase aborted\n'))
895 893 return 0
896 894
897 895 def buildstate(repo, dest, rebaseset, collapse):
898 896 '''Define which revisions are going to be rebased and where
899 897
900 898 repo: repo
901 899 dest: context
902 900 rebaseset: set of rev
903 901 '''
904 902 _setrebasesetvisibility(repo, rebaseset)
905 903
906 904 # This check isn't strictly necessary, since mq detects commits over an
907 905 # applied patch. But it prevents messing up the working directory when
908 906 # a partially completed rebase is blocked by mq.
909 907 if 'qtip' in repo.tags() and (dest.node() in
910 908 [s.node for s in repo.mq.applied]):
911 909 raise util.Abort(_('cannot rebase onto an applied mq patch'))
912 910
913 911 roots = list(repo.set('roots(%ld)', rebaseset))
914 912 if not roots:
915 913 raise util.Abort(_('no matching revisions'))
916 914 roots.sort()
917 915 state = {}
918 916 detachset = set()
919 917 for root in roots:
920 918 commonbase = root.ancestor(dest)
921 919 if commonbase == root:
922 920 raise util.Abort(_('source is ancestor of destination'))
923 921 if commonbase == dest:
924 922 samebranch = root.branch() == dest.branch()
925 923 if not collapse and samebranch and root in dest.children():
926 924 repo.ui.debug('source is a child of destination\n')
927 925 return None
928 926
929 927 repo.ui.debug('rebase onto %d starting from %s\n' % (dest, root))
930 928 state.update(dict.fromkeys(rebaseset, revtodo))
931 929 # Rebase tries to turn <dest> into a parent of <root> while
932 930 # preserving the number of parents of rebased changesets:
933 931 #
934 932 # - A changeset with a single parent will always be rebased as a
935 933 # changeset with a single parent.
936 934 #
937 935 # - A merge will be rebased as merge unless its parents are both
938 936 # ancestors of <dest> or are themselves in the rebased set and
939 937 # pruned while rebased.
940 938 #
941 939 # If one parent of <root> is an ancestor of <dest>, the rebased
942 940 # version of this parent will be <dest>. This is always true with
943 941 # --base option.
944 942 #
945 943 # Otherwise, we need to *replace* the original parents with
946 944 # <dest>. This "detaches" the rebased set from its former location
947 945 # and rebases it onto <dest>. Changes introduced by ancestors of
948 946 # <root> not common with <dest> (the detachset, marked as
949 947 # nullmerge) are "removed" from the rebased changesets.
950 948 #
951 949 # - If <root> has a single parent, set it to <dest>.
952 950 #
953 951 # - If <root> is a merge, we cannot decide which parent to
954 952 # replace, the rebase operation is not clearly defined.
955 953 #
956 954 # The table below sums up this behavior:
957 955 #
958 956 # +------------------+----------------------+-------------------------+
959 957 # | | one parent | merge |
960 958 # +------------------+----------------------+-------------------------+
961 959 # | parent in | new parent is <dest> | parents in ::<dest> are |
962 960 # | ::<dest> | | remapped to <dest> |
963 961 # +------------------+----------------------+-------------------------+
964 962 # | unrelated source | new parent is <dest> | ambiguous, abort |
965 963 # +------------------+----------------------+-------------------------+
966 964 #
967 965 # The actual abort is handled by `defineparents`
968 966 if len(root.parents()) <= 1:
969 967 # ancestors of <root> not ancestors of <dest>
970 968 detachset.update(repo.changelog.findmissingrevs([commonbase.rev()],
971 969 [root.rev()]))
972 970 for r in detachset:
973 971 if r not in state:
974 972 state[r] = nullmerge
975 973 if len(roots) > 1:
976 974 # If we have multiple roots, we may have "hole" in the rebase set.
977 975 # Rebase roots that descend from those "hole" should not be detached as
978 976 # other root are. We use the special `revignored` to inform rebase that
979 977 # the revision should be ignored but that `defineparents` should search
980 978 # a rebase destination that make sense regarding rebased topology.
981 979 rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
982 980 for ignored in set(rebasedomain) - set(rebaseset):
983 981 state[ignored] = revignored
984 982 return repo['.'].rev(), dest.rev(), state
985 983
986 984 def clearrebased(ui, repo, state, skipped, collapsedas=None):
987 985 """dispose of rebased revision at the end of the rebase
988 986
989 987 If `collapsedas` is not None, the rebase was a collapse whose result if the
990 988 `collapsedas` node."""
991 989 if obsolete.isenabled(repo, obsolete.createmarkersopt):
992 990 markers = []
993 991 for rev, newrev in sorted(state.items()):
994 992 if newrev >= 0:
995 993 if rev in skipped:
996 994 succs = ()
997 995 elif collapsedas is not None:
998 996 succs = (repo[collapsedas],)
999 997 else:
1000 998 succs = (repo[newrev],)
1001 999 markers.append((repo[rev], succs))
1002 1000 if markers:
1003 1001 obsolete.createmarkers(repo, markers)
1004 1002 else:
1005 1003 rebased = [rev for rev in state if state[rev] > nullmerge]
1006 1004 if rebased:
1007 1005 stripped = []
1008 1006 for root in repo.set('roots(%ld)', rebased):
1009 1007 if set(repo.changelog.descendants([root.rev()])) - set(state):
1010 1008 ui.warn(_("warning: new changesets detected "
1011 1009 "on source branch, not stripping\n"))
1012 1010 else:
1013 1011 stripped.append(root.node())
1014 1012 if stripped:
1015 1013 # backup the old csets by default
1016 1014 repair.strip(ui, repo, stripped, "all")
1017 1015
1018 1016
1019 1017 def pullrebase(orig, ui, repo, *args, **opts):
1020 1018 'Call rebase after pull if the latter has been invoked with --rebase'
1021 1019 if opts.get('rebase'):
1022 1020 if opts.get('update'):
1023 1021 del opts['update']
1024 1022 ui.debug('--update and --rebase are not compatible, ignoring '
1025 1023 'the update flag\n')
1026 1024
1027 1025 movemarkfrom = repo['.'].node()
1028 1026 revsprepull = len(repo)
1029 1027 origpostincoming = commands.postincoming
1030 1028 def _dummy(*args, **kwargs):
1031 1029 pass
1032 1030 commands.postincoming = _dummy
1033 1031 try:
1034 1032 orig(ui, repo, *args, **opts)
1035 1033 finally:
1036 1034 commands.postincoming = origpostincoming
1037 1035 revspostpull = len(repo)
1038 1036 if revspostpull > revsprepull:
1039 1037 # --rev option from pull conflict with rebase own --rev
1040 1038 # dropping it
1041 1039 if 'rev' in opts:
1042 1040 del opts['rev']
1043 1041 # positional argument from pull conflicts with rebase's own
1044 1042 # --source.
1045 1043 if 'source' in opts:
1046 1044 del opts['source']
1047 1045 rebase(ui, repo, **opts)
1048 1046 branch = repo[None].branch()
1049 1047 dest = repo[branch].rev()
1050 1048 if dest != repo['.'].rev():
1051 1049 # there was nothing to rebase we force an update
1052 1050 hg.update(repo, dest)
1053 1051 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
1054 1052 ui.status(_("updating bookmark %s\n")
1055 1053 % repo._activebookmark)
1056 1054 else:
1057 1055 if opts.get('tool'):
1058 1056 raise util.Abort(_('--tool can only be used with --rebase'))
1059 1057 orig(ui, repo, *args, **opts)
1060 1058
1061 1059 def _setrebasesetvisibility(repo, revs):
1062 1060 """store the currently rebased set on the repo object
1063 1061
1064 1062 This is used by another function to prevent rebased revision to because
1065 1063 hidden (see issue4505)"""
1066 1064 repo = repo.unfiltered()
1067 1065 revs = set(revs)
1068 1066 repo._rebaseset = revs
1069 1067 # invalidate cache if visibility changes
1070 1068 hiddens = repo.filteredrevcache.get('visible', set())
1071 1069 if revs & hiddens:
1072 1070 repo.invalidatevolatilesets()
1073 1071
1074 1072 def _clearrebasesetvisibiliy(repo):
1075 1073 """remove rebaseset data from the repo"""
1076 1074 repo = repo.unfiltered()
1077 1075 if '_rebaseset' in vars(repo):
1078 1076 del repo._rebaseset
1079 1077
1080 1078 def _rebasedvisible(orig, repo):
1081 1079 """ensure rebased revs stay visible (see issue4505)"""
1082 1080 blockers = orig(repo)
1083 1081 blockers.update(getattr(repo, '_rebaseset', ()))
1084 1082 return blockers
1085 1083
1086 1084 def summaryhook(ui, repo):
1087 1085 if not os.path.exists(repo.join('rebasestate')):
1088 1086 return
1089 1087 try:
1090 1088 state = restorestatus(repo)[2]
1091 1089 except error.RepoLookupError:
1092 1090 # i18n: column positioning for "hg summary"
1093 1091 msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
1094 1092 ui.write(msg)
1095 1093 return
1096 1094 numrebased = len([i for i in state.itervalues() if i >= 0])
1097 1095 # i18n: column positioning for "hg summary"
1098 1096 ui.write(_('rebase: %s, %s (rebase --continue)\n') %
1099 1097 (ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
1100 1098 ui.label(_('%d remaining'), 'rebase.remaining') %
1101 1099 (len(state) - numrebased)))
1102 1100
1103 1101 def uisetup(ui):
1104 1102 #Replace pull with a decorator to provide --rebase option
1105 1103 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
1106 1104 entry[1].append(('', 'rebase', None,
1107 1105 _("rebase working directory to branch head")))
1108 1106 entry[1].append(('t', 'tool', '',
1109 1107 _("specify merge tool for rebase")))
1110 1108 cmdutil.summaryhooks.add('rebase', summaryhook)
1111 1109 cmdutil.unfinishedstates.append(
1112 1110 ['rebasestate', False, False, _('rebase in progress'),
1113 1111 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
1114 1112 # ensure rebased rev are not hidden
1115 1113 extensions.wrapfunction(repoview, '_getdynamicblockers', _rebasedvisible)
1116 1114
@@ -1,1084 +1,1084
1 1 This file contains testcases that tend to be related to special cases or less
2 2 common commands affecting largefile.
3 3
4 4 Each sections should be independent of each others.
5 5
6 6 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
7 7 $ mkdir "${USERCACHE}"
8 8 $ cat >> $HGRCPATH <<EOF
9 9 > [extensions]
10 10 > largefiles=
11 11 > purge=
12 12 > rebase=
13 13 > transplant=
14 14 > [phases]
15 15 > publish=False
16 16 > [largefiles]
17 17 > minsize=2
18 18 > patterns=glob:**.dat
19 19 > usercache=${USERCACHE}
20 20 > [hooks]
21 21 > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status"
22 22 > EOF
23 23
24 24
25 25
26 26 Test copies and moves from a directory other than root (issue3516)
27 27 =========================================================================
28 28
29 29 $ hg init lf_cpmv
30 30 $ cd lf_cpmv
31 31 $ mkdir dira
32 32 $ mkdir dira/dirb
33 33 $ touch dira/dirb/largefile
34 34 $ hg add --large dira/dirb/largefile
35 35 $ hg commit -m "added"
36 36 Invoking status precommit hook
37 37 A dira/dirb/largefile
38 38 $ cd dira
39 39 $ hg cp dirb/largefile foo/largefile
40 40
41 41 TODO: Ideally, this should mention the largefile, not the standin
42 42 $ hg log -T '{rev}\n' --stat 'set:clean()'
43 43 0
44 44 .hglf/dira/dirb/largefile | 1 +
45 45 1 files changed, 1 insertions(+), 0 deletions(-)
46 46
47 47 $ hg ci -m "deep copy"
48 48 Invoking status precommit hook
49 49 A dira/foo/largefile
50 50 $ find . | sort
51 51 .
52 52 ./dirb
53 53 ./dirb/largefile
54 54 ./foo
55 55 ./foo/largefile
56 56 $ hg mv foo/largefile baz/largefile
57 57 $ hg ci -m "moved"
58 58 Invoking status precommit hook
59 59 A dira/baz/largefile
60 60 R dira/foo/largefile
61 61 $ find . | sort
62 62 .
63 63 ./baz
64 64 ./baz/largefile
65 65 ./dirb
66 66 ./dirb/largefile
67 67 $ cd ..
68 68 $ hg mv dira dirc
69 69 moving .hglf/dira/baz/largefile to .hglf/dirc/baz/largefile (glob)
70 70 moving .hglf/dira/dirb/largefile to .hglf/dirc/dirb/largefile (glob)
71 71 $ find * | sort
72 72 dirc
73 73 dirc/baz
74 74 dirc/baz/largefile
75 75 dirc/dirb
76 76 dirc/dirb/largefile
77 77
78 78 $ hg clone -q . ../fetch
79 79 $ hg --config extensions.fetch= fetch ../fetch
80 80 abort: uncommitted changes
81 81 [255]
82 82 $ hg up -qC
83 83 $ cd ..
84 84
85 85 Clone a local repository owned by another user
86 86 ===================================================
87 87
88 88 #if unix-permissions
89 89
90 90 We have to simulate that here by setting $HOME and removing write permissions
91 91 $ ORIGHOME="$HOME"
92 92 $ mkdir alice
93 93 $ HOME="`pwd`/alice"
94 94 $ cd alice
95 95 $ hg init pubrepo
96 96 $ cd pubrepo
97 97 $ dd if=/dev/zero bs=1k count=11k > a-large-file 2> /dev/null
98 98 $ hg add --large a-large-file
99 99 $ hg commit -m "Add a large file"
100 100 Invoking status precommit hook
101 101 A a-large-file
102 102 $ cd ..
103 103 $ chmod -R a-w pubrepo
104 104 $ cd ..
105 105 $ mkdir bob
106 106 $ HOME="`pwd`/bob"
107 107 $ cd bob
108 108 $ hg clone --pull ../alice/pubrepo pubrepo
109 109 requesting all changes
110 110 adding changesets
111 111 adding manifests
112 112 adding file changes
113 113 added 1 changesets with 1 changes to 1 files
114 114 updating to branch default
115 115 getting changed largefiles
116 116 1 largefiles updated, 0 removed
117 117 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 118 $ cd ..
119 119 $ chmod -R u+w alice/pubrepo
120 120 $ HOME="$ORIGHOME"
121 121
122 122 #endif
123 123
124 124
125 125 Symlink to a large largefile should behave the same as a symlink to a normal file
126 126 =====================================================================================
127 127
128 128 #if symlink
129 129
130 130 $ hg init largesymlink
131 131 $ cd largesymlink
132 132 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
133 133 $ hg add --large largefile
134 134 $ hg commit -m "commit a large file"
135 135 Invoking status precommit hook
136 136 A largefile
137 137 $ ln -s largefile largelink
138 138 $ hg add largelink
139 139 $ hg commit -m "commit a large symlink"
140 140 Invoking status precommit hook
141 141 A largelink
142 142 $ rm -f largelink
143 143 $ hg up >/dev/null
144 144 $ test -f largelink
145 145 [1]
146 146 $ test -L largelink
147 147 [1]
148 148 $ rm -f largelink # make next part of the test independent of the previous
149 149 $ hg up -C >/dev/null
150 150 $ test -f largelink
151 151 $ test -L largelink
152 152 $ cd ..
153 153
154 154 #endif
155 155
156 156
157 157 test for pattern matching on 'hg status':
158 158 ==============================================
159 159
160 160
161 161 to boost performance, largefiles checks whether specified patterns are
162 162 related to largefiles in working directory (NOT to STANDIN) or not.
163 163
164 164 $ hg init statusmatch
165 165 $ cd statusmatch
166 166
167 167 $ mkdir -p a/b/c/d
168 168 $ echo normal > a/b/c/d/e.normal.txt
169 169 $ hg add a/b/c/d/e.normal.txt
170 170 $ echo large > a/b/c/d/e.large.txt
171 171 $ hg add --large a/b/c/d/e.large.txt
172 172 $ mkdir -p a/b/c/x
173 173 $ echo normal > a/b/c/x/y.normal.txt
174 174 $ hg add a/b/c/x/y.normal.txt
175 175 $ hg commit -m 'add files'
176 176 Invoking status precommit hook
177 177 A a/b/c/d/e.large.txt
178 178 A a/b/c/d/e.normal.txt
179 179 A a/b/c/x/y.normal.txt
180 180
181 181 (1) no pattern: no performance boost
182 182 $ hg status -A
183 183 C a/b/c/d/e.large.txt
184 184 C a/b/c/d/e.normal.txt
185 185 C a/b/c/x/y.normal.txt
186 186
187 187 (2) pattern not related to largefiles: performance boost
188 188 $ hg status -A a/b/c/x
189 189 C a/b/c/x/y.normal.txt
190 190
191 191 (3) pattern related to largefiles: no performance boost
192 192 $ hg status -A a/b/c/d
193 193 C a/b/c/d/e.large.txt
194 194 C a/b/c/d/e.normal.txt
195 195
196 196 (4) pattern related to STANDIN (not to largefiles): performance boost
197 197 $ hg status -A .hglf/a
198 198 C .hglf/a/b/c/d/e.large.txt
199 199
200 200 (5) mixed case: no performance boost
201 201 $ hg status -A a/b/c/x a/b/c/d
202 202 C a/b/c/d/e.large.txt
203 203 C a/b/c/d/e.normal.txt
204 204 C a/b/c/x/y.normal.txt
205 205
206 206 verify that largefiles doesn't break filesets
207 207
208 208 $ hg log --rev . --exclude "set:binary()"
209 209 changeset: 0:41bd42f10efa
210 210 tag: tip
211 211 user: test
212 212 date: Thu Jan 01 00:00:00 1970 +0000
213 213 summary: add files
214 214
215 215 verify that large files in subrepos handled properly
216 216 $ hg init subrepo
217 217 $ echo "subrepo = subrepo" > .hgsub
218 218 $ hg add .hgsub
219 219 $ hg ci -m "add subrepo"
220 220 Invoking status precommit hook
221 221 A .hgsub
222 222 ? .hgsubstate
223 223 $ echo "rev 1" > subrepo/large.txt
224 224 $ hg add --large subrepo/large.txt
225 225 $ hg sum
226 226 parent: 1:8ee150ea2e9c tip
227 227 add subrepo
228 228 branch: default
229 229 commit: 1 subrepos
230 230 update: (current)
231 231 $ hg st
232 232 $ hg st -S
233 233 A subrepo/large.txt
234 234 $ hg ci -S -m "commit top repo"
235 235 committing subrepository subrepo
236 236 Invoking status precommit hook
237 237 A large.txt
238 238 Invoking status precommit hook
239 239 M .hgsubstate
240 240 # No differences
241 241 $ hg st -S
242 242 $ hg sum
243 243 parent: 2:ce4cd0c527a6 tip
244 244 commit top repo
245 245 branch: default
246 246 commit: (clean)
247 247 update: (current)
248 248 $ echo "rev 2" > subrepo/large.txt
249 249 $ hg st -S
250 250 M subrepo/large.txt
251 251 $ hg sum
252 252 parent: 2:ce4cd0c527a6 tip
253 253 commit top repo
254 254 branch: default
255 255 commit: 1 subrepos
256 256 update: (current)
257 257 $ hg ci -m "this commit should fail without -S"
258 258 abort: uncommitted changes in subrepository 'subrepo'
259 259 (use --subrepos for recursive commit)
260 260 [255]
261 261
262 262 Add a normal file to the subrepo, then test archiving
263 263
264 264 $ echo 'normal file' > subrepo/normal.txt
265 265 $ touch large.dat
266 266 $ mv subrepo/large.txt subrepo/renamed-large.txt
267 267 $ hg addremove -S --dry-run
268 268 adding large.dat as a largefile
269 269 removing subrepo/large.txt
270 270 adding subrepo/normal.txt
271 271 adding subrepo/renamed-large.txt
272 272 $ hg status -S
273 273 ! subrepo/large.txt
274 274 ? large.dat
275 275 ? subrepo/normal.txt
276 276 ? subrepo/renamed-large.txt
277 277
278 278 $ hg addremove --dry-run subrepo
279 279 removing subrepo/large.txt (glob)
280 280 adding subrepo/normal.txt (glob)
281 281 adding subrepo/renamed-large.txt (glob)
282 282 $ hg status -S
283 283 ! subrepo/large.txt
284 284 ? large.dat
285 285 ? subrepo/normal.txt
286 286 ? subrepo/renamed-large.txt
287 287 $ cd ..
288 288
289 289 $ hg -R statusmatch addremove --dry-run statusmatch/subrepo
290 290 removing statusmatch/subrepo/large.txt (glob)
291 291 adding statusmatch/subrepo/normal.txt (glob)
292 292 adding statusmatch/subrepo/renamed-large.txt (glob)
293 293 $ hg -R statusmatch status -S
294 294 ! subrepo/large.txt
295 295 ? large.dat
296 296 ? subrepo/normal.txt
297 297 ? subrepo/renamed-large.txt
298 298
299 299 $ hg -R statusmatch addremove --dry-run -S
300 300 adding large.dat as a largefile
301 301 removing subrepo/large.txt
302 302 adding subrepo/normal.txt
303 303 adding subrepo/renamed-large.txt
304 304 $ cd statusmatch
305 305
306 306 $ mv subrepo/renamed-large.txt subrepo/large.txt
307 307 $ hg addremove subrepo
308 308 adding subrepo/normal.txt (glob)
309 309 $ hg forget subrepo/normal.txt
310 310
311 311 $ hg addremove -S
312 312 adding large.dat as a largefile
313 313 adding subrepo/normal.txt
314 314 $ rm large.dat
315 315
316 316 $ hg addremove subrepo
317 317 $ hg addremove -S
318 318 removing large.dat
319 319
320 320 Lock in subrepo, otherwise the change isn't archived
321 321
322 322 $ hg ci -S -m "add normal file to top level"
323 323 committing subrepository subrepo
324 324 Invoking status precommit hook
325 325 M large.txt
326 326 A normal.txt
327 327 Invoking status precommit hook
328 328 M .hgsubstate
329 329 $ hg archive -S ../lf_subrepo_archive
330 330 $ find ../lf_subrepo_archive | sort
331 331 ../lf_subrepo_archive
332 332 ../lf_subrepo_archive/.hg_archival.txt
333 333 ../lf_subrepo_archive/.hgsub
334 334 ../lf_subrepo_archive/.hgsubstate
335 335 ../lf_subrepo_archive/a
336 336 ../lf_subrepo_archive/a/b
337 337 ../lf_subrepo_archive/a/b/c
338 338 ../lf_subrepo_archive/a/b/c/d
339 339 ../lf_subrepo_archive/a/b/c/d/e.large.txt
340 340 ../lf_subrepo_archive/a/b/c/d/e.normal.txt
341 341 ../lf_subrepo_archive/a/b/c/x
342 342 ../lf_subrepo_archive/a/b/c/x/y.normal.txt
343 343 ../lf_subrepo_archive/subrepo
344 344 ../lf_subrepo_archive/subrepo/large.txt
345 345 ../lf_subrepo_archive/subrepo/normal.txt
346 346 $ cat ../lf_subrepo_archive/.hg_archival.txt
347 347 repo: 41bd42f10efa43698cc02052ea0977771cba506d
348 348 node: d56a95e6522858bc08a724c4fe2bdee066d1c30b
349 349 branch: default
350 350 latesttag: null
351 351 latesttagdistance: 4
352 352 changessincelatesttag: 4
353 353
354 354 Test update with subrepos.
355 355
356 356 $ hg update 0
357 357 getting changed largefiles
358 358 0 largefiles updated, 1 removed
359 359 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
360 360 $ hg status -S
361 361 $ hg update tip
362 362 getting changed largefiles
363 363 1 largefiles updated, 0 removed
364 364 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
365 365 $ hg status -S
366 366 # modify a large file
367 367 $ echo "modified" > subrepo/large.txt
368 368 $ hg st -S
369 369 M subrepo/large.txt
370 370 # update -C should revert the change.
371 371 $ hg update -C
372 372 getting changed largefiles
373 373 1 largefiles updated, 0 removed
374 374 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
375 375 $ hg status -S
376 376
377 377 $ hg forget -v subrepo/large.txt
378 378 removing subrepo/large.txt (glob)
379 379
380 380 Test reverting a forgotten file
381 381 $ hg revert -R subrepo subrepo/large.txt
382 382 $ hg status -SA subrepo/large.txt
383 383 C subrepo/large.txt
384 384
385 385 $ hg rm -v subrepo/large.txt
386 386 removing subrepo/large.txt (glob)
387 387 $ hg revert -R subrepo subrepo/large.txt
388 388 $ rm subrepo/large.txt
389 389 $ hg addremove -S
390 390 removing subrepo/large.txt
391 391 $ hg st -S
392 392 R subrepo/large.txt
393 393
394 394 Test archiving a revision that references a subrepo that is not yet
395 395 cloned (see test-subrepo-recursion.t):
396 396
397 397 $ hg clone -U . ../empty
398 398 $ cd ../empty
399 399 $ hg archive --subrepos -r tip ../archive.tar.gz
400 400 cloning subrepo subrepo from $TESTTMP/statusmatch/subrepo
401 401 $ cd ..
402 402
403 403
404 404
405 405
406 406
407 407
408 408 Test addremove, forget and others
409 409 ==============================================
410 410
411 411 Test that addremove picks up largefiles prior to the initial commit (issue3541)
412 412
413 413 $ hg init addrm2
414 414 $ cd addrm2
415 415 $ touch large.dat
416 416 $ touch large2.dat
417 417 $ touch normal
418 418 $ hg add --large large.dat
419 419 $ hg addremove -v
420 420 adding large2.dat as a largefile
421 421 adding normal
422 422
423 423 Test that forgetting all largefiles reverts to islfilesrepo() == False
424 424 (addremove will add *.dat as normal files now)
425 425 $ hg forget large.dat
426 426 $ hg forget large2.dat
427 427 $ hg addremove -v
428 428 adding large.dat
429 429 adding large2.dat
430 430
431 431 Test commit's addremove option prior to the first commit
432 432 $ hg forget large.dat
433 433 $ hg forget large2.dat
434 434 $ hg add --large large.dat
435 435 $ hg ci -Am "commit"
436 436 adding large2.dat as a largefile
437 437 Invoking status precommit hook
438 438 A large.dat
439 439 A large2.dat
440 440 A normal
441 441 $ find .hglf | sort
442 442 .hglf
443 443 .hglf/large.dat
444 444 .hglf/large2.dat
445 445
446 446 Test actions on largefiles using relative paths from subdir
447 447
448 448 $ mkdir sub
449 449 $ cd sub
450 450 $ echo anotherlarge > anotherlarge
451 451 $ hg add --large anotherlarge
452 452 $ hg st
453 453 A sub/anotherlarge
454 454 $ hg st anotherlarge
455 455 A anotherlarge
456 456 $ hg commit -m anotherlarge anotherlarge
457 457 Invoking status precommit hook
458 458 A sub/anotherlarge
459 459 $ hg log anotherlarge
460 460 changeset: 1:9627a577c5e9
461 461 tag: tip
462 462 user: test
463 463 date: Thu Jan 01 00:00:00 1970 +0000
464 464 summary: anotherlarge
465 465
466 466 $ hg --debug log -T '{rev}: {desc}\n' ../sub/anotherlarge
467 467 updated patterns: ['../.hglf/sub/../sub/anotherlarge', '../sub/anotherlarge']
468 468 1: anotherlarge
469 469
470 470 $ hg log -G anotherlarge
471 471 @ changeset: 1:9627a577c5e9
472 472 | tag: tip
473 473 | user: test
474 474 | date: Thu Jan 01 00:00:00 1970 +0000
475 475 | summary: anotherlarge
476 476 |
477 477
478 478 $ hg log glob:another*
479 479 changeset: 1:9627a577c5e9
480 480 tag: tip
481 481 user: test
482 482 date: Thu Jan 01 00:00:00 1970 +0000
483 483 summary: anotherlarge
484 484
485 485 $ hg --debug log -T '{rev}: {desc}\n' -G glob:another*
486 486 updated patterns: ['glob:../.hglf/sub/another*', 'glob:another*']
487 487 @ 1: anotherlarge
488 488 |
489 489
490 490 #if no-msys
491 491 $ hg --debug log -T '{rev}: {desc}\n' 'glob:../.hglf/sub/another*' # no-msys
492 492 updated patterns: ['glob:../.hglf/sub/another*']
493 493 1: anotherlarge
494 494
495 495 $ hg --debug log -G -T '{rev}: {desc}\n' 'glob:../.hglf/sub/another*' # no-msys
496 496 updated patterns: ['glob:../.hglf/sub/another*']
497 497 @ 1: anotherlarge
498 498 |
499 499 #endif
500 500
501 501 $ echo more >> anotherlarge
502 502 $ hg st .
503 503 M anotherlarge
504 504 $ hg cat anotherlarge
505 505 anotherlarge
506 506 $ hg revert anotherlarge
507 507 $ hg st
508 508 ? sub/anotherlarge.orig
509 509 $ cd ..
510 510
511 511 Test glob logging from the root dir
512 512 $ hg log glob:**another*
513 513 changeset: 1:9627a577c5e9
514 514 tag: tip
515 515 user: test
516 516 date: Thu Jan 01 00:00:00 1970 +0000
517 517 summary: anotherlarge
518 518
519 519 $ hg log -G glob:**another*
520 520 @ changeset: 1:9627a577c5e9
521 521 | tag: tip
522 522 | user: test
523 523 | date: Thu Jan 01 00:00:00 1970 +0000
524 524 | summary: anotherlarge
525 525 |
526 526
527 527 $ cd ..
528 528
529 529 Log from outer space
530 530 $ hg --debug log -R addrm2 -T '{rev}: {desc}\n' 'addrm2/sub/anotherlarge'
531 531 updated patterns: ['addrm2/.hglf/sub/anotherlarge', 'addrm2/sub/anotherlarge']
532 532 1: anotherlarge
533 533 $ hg --debug log -R addrm2 -T '{rev}: {desc}\n' 'addrm2/.hglf/sub/anotherlarge'
534 534 updated patterns: ['addrm2/.hglf/sub/anotherlarge']
535 535 1: anotherlarge
536 536
537 537
538 538 Check error message while exchange
539 539 =========================================================
540 540
541 541 issue3651: summary/outgoing with largefiles shows "no remote repo"
542 542 unexpectedly
543 543
544 544 $ mkdir issue3651
545 545 $ cd issue3651
546 546
547 547 $ hg init src
548 548 $ echo a > src/a
549 549 $ hg -R src add --large src/a
550 550 $ hg -R src commit -m '#0'
551 551 Invoking status precommit hook
552 552 A a
553 553
554 554 check messages when no remote repository is specified:
555 555 "no remote repo" route for "hg outgoing --large" is not tested here,
556 556 because it can't be reproduced easily.
557 557
558 558 $ hg init clone1
559 559 $ hg -R clone1 -q pull src
560 560 $ hg -R clone1 -q update
561 561 $ hg -R clone1 paths | grep default
562 562 [1]
563 563
564 564 $ hg -R clone1 summary --large
565 565 parent: 0:fc0bd45326d3 tip
566 566 #0
567 567 branch: default
568 568 commit: (clean)
569 569 update: (current)
570 570 largefiles: (no remote repo)
571 571
572 572 check messages when there is no files to upload:
573 573
574 574 $ hg -q clone src clone2
575 575 $ hg -R clone2 paths | grep default
576 576 default = $TESTTMP/issue3651/src (glob)
577 577
578 578 $ hg -R clone2 summary --large
579 579 parent: 0:fc0bd45326d3 tip
580 580 #0
581 581 branch: default
582 582 commit: (clean)
583 583 update: (current)
584 584 largefiles: (no files to upload)
585 585 $ hg -R clone2 outgoing --large
586 586 comparing with $TESTTMP/issue3651/src (glob)
587 587 searching for changes
588 588 no changes found
589 589 largefiles: no files to upload
590 590 [1]
591 591
592 592 $ hg -R clone2 outgoing --large --graph --template "{rev}"
593 593 comparing with $TESTTMP/issue3651/src (glob)
594 594 searching for changes
595 595 no changes found
596 596 largefiles: no files to upload
597 597
598 598 check messages when there are files to upload:
599 599
600 600 $ echo b > clone2/b
601 601 $ hg -R clone2 add --large clone2/b
602 602 $ hg -R clone2 commit -m '#1'
603 603 Invoking status precommit hook
604 604 A b
605 605 $ hg -R clone2 summary --large
606 606 parent: 1:1acbe71ce432 tip
607 607 #1
608 608 branch: default
609 609 commit: (clean)
610 610 update: (current)
611 611 largefiles: 1 entities for 1 files to upload
612 612 $ hg -R clone2 outgoing --large
613 613 comparing with $TESTTMP/issue3651/src (glob)
614 614 searching for changes
615 615 changeset: 1:1acbe71ce432
616 616 tag: tip
617 617 user: test
618 618 date: Thu Jan 01 00:00:00 1970 +0000
619 619 summary: #1
620 620
621 621 largefiles to upload (1 entities):
622 622 b
623 623
624 624 $ hg -R clone2 outgoing --large --graph --template "{rev}"
625 625 comparing with $TESTTMP/issue3651/src (glob)
626 626 searching for changes
627 627 @ 1
628 628
629 629 largefiles to upload (1 entities):
630 630 b
631 631
632 632
633 633 $ cp clone2/b clone2/b1
634 634 $ cp clone2/b clone2/b2
635 635 $ hg -R clone2 add --large clone2/b1 clone2/b2
636 636 $ hg -R clone2 commit -m '#2: add largefiles referring same entity'
637 637 Invoking status precommit hook
638 638 A b1
639 639 A b2
640 640 $ hg -R clone2 summary --large
641 641 parent: 2:6095d0695d70 tip
642 642 #2: add largefiles referring same entity
643 643 branch: default
644 644 commit: (clean)
645 645 update: (current)
646 646 largefiles: 1 entities for 3 files to upload
647 647 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
648 648 comparing with $TESTTMP/issue3651/src (glob)
649 649 searching for changes
650 650 1:1acbe71ce432
651 651 2:6095d0695d70
652 652 largefiles to upload (1 entities):
653 653 b
654 654 b1
655 655 b2
656 656
657 657 $ hg -R clone2 cat -r 1 clone2/.hglf/b
658 658 89e6c98d92887913cadf06b2adb97f26cde4849b
659 659 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
660 660 comparing with $TESTTMP/issue3651/src (glob)
661 661 query 1; heads
662 662 searching for changes
663 663 all remote heads known locally
664 664 1:1acbe71ce432
665 665 2:6095d0695d70
666 666 finding outgoing largefiles: 0/2 revision (0.00%)
667 667 finding outgoing largefiles: 1/2 revision (50.00%)
668 668 largefiles to upload (1 entities):
669 669 b
670 670 89e6c98d92887913cadf06b2adb97f26cde4849b
671 671 b1
672 672 89e6c98d92887913cadf06b2adb97f26cde4849b
673 673 b2
674 674 89e6c98d92887913cadf06b2adb97f26cde4849b
675 675
676 676
677 677 $ echo bbb > clone2/b
678 678 $ hg -R clone2 commit -m '#3: add new largefile entity as existing file'
679 679 Invoking status precommit hook
680 680 M b
681 681 $ echo bbbb > clone2/b
682 682 $ hg -R clone2 commit -m '#4: add new largefile entity as existing file'
683 683 Invoking status precommit hook
684 684 M b
685 685 $ cp clone2/b1 clone2/b
686 686 $ hg -R clone2 commit -m '#5: refer existing largefile entity again'
687 687 Invoking status precommit hook
688 688 M b
689 689 $ hg -R clone2 summary --large
690 690 parent: 5:036794ea641c tip
691 691 #5: refer existing largefile entity again
692 692 branch: default
693 693 commit: (clean)
694 694 update: (current)
695 695 largefiles: 3 entities for 3 files to upload
696 696 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
697 697 comparing with $TESTTMP/issue3651/src (glob)
698 698 searching for changes
699 699 1:1acbe71ce432
700 700 2:6095d0695d70
701 701 3:7983dce246cc
702 702 4:233f12ada4ae
703 703 5:036794ea641c
704 704 largefiles to upload (3 entities):
705 705 b
706 706 b1
707 707 b2
708 708
709 709 $ hg -R clone2 cat -r 3 clone2/.hglf/b
710 710 c801c9cfe94400963fcb683246217d5db77f9a9a
711 711 $ hg -R clone2 cat -r 4 clone2/.hglf/b
712 712 13f9ed0898e315bf59dc2973fec52037b6f441a2
713 713 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
714 714 comparing with $TESTTMP/issue3651/src (glob)
715 715 query 1; heads
716 716 searching for changes
717 717 all remote heads known locally
718 718 1:1acbe71ce432
719 719 2:6095d0695d70
720 720 3:7983dce246cc
721 721 4:233f12ada4ae
722 722 5:036794ea641c
723 723 finding outgoing largefiles: 0/5 revision (0.00%)
724 724 finding outgoing largefiles: 1/5 revision (20.00%)
725 725 finding outgoing largefiles: 2/5 revision (40.00%)
726 726 finding outgoing largefiles: 3/5 revision (60.00%)
727 727 finding outgoing largefiles: 4/5 revision (80.00%)
728 728 largefiles to upload (3 entities):
729 729 b
730 730 13f9ed0898e315bf59dc2973fec52037b6f441a2
731 731 89e6c98d92887913cadf06b2adb97f26cde4849b
732 732 c801c9cfe94400963fcb683246217d5db77f9a9a
733 733 b1
734 734 89e6c98d92887913cadf06b2adb97f26cde4849b
735 735 b2
736 736 89e6c98d92887913cadf06b2adb97f26cde4849b
737 737
738 738
739 739 Pushing revision #1 causes uploading entity 89e6c98d9288, which is
740 740 shared also by largefiles b1, b2 in revision #2 and b in revision #5.
741 741
742 742 Then, entity 89e6c98d9288 is not treated as "outgoing entity" at "hg
743 743 summary" and "hg outgoing", even though files in outgoing revision #2
744 744 and #5 refer it.
745 745
746 746 $ hg -R clone2 push -r 1 -q
747 747 $ hg -R clone2 summary --large
748 748 parent: 5:036794ea641c tip
749 749 #5: refer existing largefile entity again
750 750 branch: default
751 751 commit: (clean)
752 752 update: (current)
753 753 largefiles: 2 entities for 1 files to upload
754 754 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
755 755 comparing with $TESTTMP/issue3651/src (glob)
756 756 searching for changes
757 757 2:6095d0695d70
758 758 3:7983dce246cc
759 759 4:233f12ada4ae
760 760 5:036794ea641c
761 761 largefiles to upload (2 entities):
762 762 b
763 763
764 764 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug
765 765 comparing with $TESTTMP/issue3651/src (glob)
766 766 query 1; heads
767 767 searching for changes
768 768 all remote heads known locally
769 769 2:6095d0695d70
770 770 3:7983dce246cc
771 771 4:233f12ada4ae
772 772 5:036794ea641c
773 773 finding outgoing largefiles: 0/4 revision (0.00%)
774 774 finding outgoing largefiles: 1/4 revision (25.00%)
775 775 finding outgoing largefiles: 2/4 revision (50.00%)
776 776 finding outgoing largefiles: 3/4 revision (75.00%)
777 777 largefiles to upload (2 entities):
778 778 b
779 779 13f9ed0898e315bf59dc2973fec52037b6f441a2
780 780 c801c9cfe94400963fcb683246217d5db77f9a9a
781 781
782 782
783 783 $ cd ..
784 784
785 785 merge action 'd' for 'local renamed directory to d2/g' which has no filename
786 786 ==================================================================================
787 787
788 788 $ hg init merge-action
789 789 $ cd merge-action
790 790 $ touch l
791 791 $ hg add --large l
792 792 $ mkdir d1
793 793 $ touch d1/f
794 794 $ hg ci -Aqm0
795 795 Invoking status precommit hook
796 796 A d1/f
797 797 A l
798 798 $ echo > d1/f
799 799 $ touch d1/g
800 800 $ hg ci -Aqm1
801 801 Invoking status precommit hook
802 802 M d1/f
803 803 A d1/g
804 804 $ hg up -qr0
805 805 $ hg mv d1 d2
806 806 moving d1/f to d2/f (glob)
807 807 $ hg ci -qm2
808 808 Invoking status precommit hook
809 809 A d2/f
810 810 R d1/f
811 811 $ hg merge
812 812 merging d2/f and d1/f to d2/f
813 813 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
814 814 (branch merge, don't forget to commit)
815 815 $ cd ..
816 816
817 817
818 818 Merge conflicts:
819 819 =====================
820 820
821 821 $ hg init merge
822 822 $ cd merge
823 823 $ echo 0 > f-different
824 824 $ echo 0 > f-same
825 825 $ echo 0 > f-unchanged-1
826 826 $ echo 0 > f-unchanged-2
827 827 $ hg add --large *
828 828 $ hg ci -m0
829 829 Invoking status precommit hook
830 830 A f-different
831 831 A f-same
832 832 A f-unchanged-1
833 833 A f-unchanged-2
834 834 $ echo tmp1 > f-unchanged-1
835 835 $ echo tmp1 > f-unchanged-2
836 836 $ echo tmp1 > f-same
837 837 $ hg ci -m1
838 838 Invoking status precommit hook
839 839 M f-same
840 840 M f-unchanged-1
841 841 M f-unchanged-2
842 842 $ echo 2 > f-different
843 843 $ echo 0 > f-unchanged-1
844 844 $ echo 1 > f-unchanged-2
845 845 $ echo 1 > f-same
846 846 $ hg ci -m2
847 847 Invoking status precommit hook
848 848 M f-different
849 849 M f-same
850 850 M f-unchanged-1
851 851 M f-unchanged-2
852 852 $ hg up -qr0
853 853 $ echo tmp2 > f-unchanged-1
854 854 $ echo tmp2 > f-unchanged-2
855 855 $ echo tmp2 > f-same
856 856 $ hg ci -m3
857 857 Invoking status precommit hook
858 858 M f-same
859 859 M f-unchanged-1
860 860 M f-unchanged-2
861 861 created new head
862 862 $ echo 1 > f-different
863 863 $ echo 1 > f-unchanged-1
864 864 $ echo 0 > f-unchanged-2
865 865 $ echo 1 > f-same
866 866 $ hg ci -m4
867 867 Invoking status precommit hook
868 868 M f-different
869 869 M f-same
870 870 M f-unchanged-1
871 871 M f-unchanged-2
872 872 $ hg merge
873 873 largefile f-different has a merge conflict
874 874 ancestor was 09d2af8dd22201dd8d48e5dcfcaed281ff9422c7
875 875 keep (l)ocal e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e or
876 876 take (o)ther 7448d8798a4380162d4b56f9b452e2f6f9e24e7a? l
877 877 getting changed largefiles
878 878 1 largefiles updated, 0 removed
879 879 0 files updated, 4 files merged, 0 files removed, 0 files unresolved
880 880 (branch merge, don't forget to commit)
881 881 $ cat f-different
882 882 1
883 883 $ cat f-same
884 884 1
885 885 $ cat f-unchanged-1
886 886 1
887 887 $ cat f-unchanged-2
888 888 1
889 889 $ cd ..
890 890
891 891 Test largefile insulation (do not enabled a side effect
892 892 ========================================================
893 893
894 894 Check whether "largefiles" feature is supported only in repositories
895 895 enabling largefiles extension.
896 896
897 897 $ mkdir individualenabling
898 898 $ cd individualenabling
899 899
900 900 $ hg init enabledlocally
901 901 $ echo large > enabledlocally/large
902 902 $ hg -R enabledlocally add --large enabledlocally/large
903 903 $ hg -R enabledlocally commit -m '#0'
904 904 Invoking status precommit hook
905 905 A large
906 906
907 907 $ hg init notenabledlocally
908 908 $ echo large > notenabledlocally/large
909 909 $ hg -R notenabledlocally add --large notenabledlocally/large
910 910 $ hg -R notenabledlocally commit -m '#0'
911 911 Invoking status precommit hook
912 912 A large
913 913
914 914 $ cat >> $HGRCPATH <<EOF
915 915 > [extensions]
916 916 > # disable globally
917 917 > largefiles=!
918 918 > EOF
919 919 $ cat >> enabledlocally/.hg/hgrc <<EOF
920 920 > [extensions]
921 921 > # enable locally
922 922 > largefiles=
923 923 > EOF
924 924 $ hg -R enabledlocally root
925 925 $TESTTMP/individualenabling/enabledlocally (glob)
926 926 $ hg -R notenabledlocally root
927 927 abort: repository requires features unknown to this Mercurial: largefiles!
928 928 (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
929 929 [255]
930 930
931 931 $ hg init push-dst
932 932 $ hg -R enabledlocally push push-dst
933 933 pushing to push-dst
934 934 abort: required features are not supported in the destination: largefiles
935 935 [255]
936 936
937 937 $ hg init pull-src
938 938 $ hg -R pull-src pull enabledlocally
939 939 pulling from enabledlocally
940 940 abort: required features are not supported in the destination: largefiles
941 941 [255]
942 942
943 943 $ hg clone enabledlocally clone-dst
944 944 abort: repository requires features unknown to this Mercurial: largefiles!
945 945 (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
946 946 [255]
947 947 $ test -d clone-dst
948 948 [1]
949 949 $ hg clone --pull enabledlocally clone-pull-dst
950 950 abort: required features are not supported in the destination: largefiles
951 951 [255]
952 952 $ test -d clone-pull-dst
953 953 [1]
954 954
955 955 #if serve
956 956
957 957 Test largefiles specific peer setup, when largefiles is enabled
958 958 locally (issue4109)
959 959
960 960 $ hg showconfig extensions | grep largefiles
961 961 extensions.largefiles=!
962 962 $ mkdir -p $TESTTMP/individualenabling/usercache
963 963
964 964 $ hg serve -R enabledlocally -d -p $HGPORT --pid-file hg.pid
965 965 $ cat hg.pid >> $DAEMON_PIDS
966 966
967 967 $ hg init pull-dst
968 968 $ cat > pull-dst/.hg/hgrc <<EOF
969 969 > [extensions]
970 970 > # enable locally
971 971 > largefiles=
972 972 > [largefiles]
973 973 > # ignore system cache to force largefiles specific wire proto access
974 974 > usercache=$TESTTMP/individualenabling/usercache
975 975 > EOF
976 976 $ hg -R pull-dst -q pull -u http://localhost:$HGPORT
977 977
978 978 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
979 979 #endif
980 980
981 981 Test overridden functions work correctly even for repos disabling
982 982 largefiles (issue4547)
983 983
984 984 $ hg showconfig extensions | grep largefiles
985 985 extensions.largefiles=!
986 986
987 987 (test updating implied by clone)
988 988
989 989 $ hg init enabled-but-no-largefiles
990 990 $ echo normal1 > enabled-but-no-largefiles/normal1
991 991 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal1
992 992 $ hg -R enabled-but-no-largefiles commit -m '#0@enabled-but-no-largefiles'
993 993 Invoking status precommit hook
994 994 A normal1
995 995 $ cat >> enabled-but-no-largefiles/.hg/hgrc <<EOF
996 996 > [extensions]
997 997 > # enable locally
998 998 > largefiles=
999 999 > EOF
1000 1000 $ hg clone -q enabled-but-no-largefiles no-largefiles
1001 1001
1002 1002 (test rebasing implied by pull: precommit while rebasing unexpectedly
1003 1003 shows "normal3" as "?", because lfdirstate isn't yet written out at
1004 1004 that time)
1005 1005
1006 1006 $ echo normal2 > enabled-but-no-largefiles/normal2
1007 1007 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal2
1008 1008 $ hg -R enabled-but-no-largefiles commit -m '#1@enabled-but-no-largefiles'
1009 1009 Invoking status precommit hook
1010 1010 A normal2
1011 1011
1012 1012 $ echo normal3 > no-largefiles/normal3
1013 1013 $ hg -R no-largefiles add no-largefiles/normal3
1014 1014 $ hg -R no-largefiles commit -m '#1@no-largefiles'
1015 1015 Invoking status precommit hook
1016 1016 A normal3
1017 1017
1018 1018 $ hg -R no-largefiles -q pull --rebase
1019 1019 Invoking status precommit hook
1020 ? normal3
1020 M normal3
1021 1021
1022 1022 (test reverting)
1023 1023
1024 1024 $ hg init subrepo-root
1025 1025 $ cat >> subrepo-root/.hg/hgrc <<EOF
1026 1026 > [extensions]
1027 1027 > # enable locally
1028 1028 > largefiles=
1029 1029 > EOF
1030 1030 $ echo large > subrepo-root/large
1031 1031 $ hg -R subrepo-root add --large subrepo-root/large
1032 1032 $ hg clone -q no-largefiles subrepo-root/no-largefiles
1033 1033 $ cat > subrepo-root/.hgsub <<EOF
1034 1034 > no-largefiles = no-largefiles
1035 1035 > EOF
1036 1036 $ hg -R subrepo-root add subrepo-root/.hgsub
1037 1037 $ hg -R subrepo-root commit -m '#0'
1038 1038 Invoking status precommit hook
1039 1039 A .hgsub
1040 1040 A large
1041 1041 ? .hgsubstate
1042 1042 $ echo dirty >> subrepo-root/large
1043 1043 $ echo dirty >> subrepo-root/no-largefiles/normal1
1044 1044 $ hg -R subrepo-root status -S
1045 1045 M large
1046 1046 M no-largefiles/normal1
1047 1047 $ hg -R subrepo-root revert --all
1048 1048 reverting subrepo-root/.hglf/large (glob)
1049 1049 reverting subrepo no-largefiles
1050 1050 reverting subrepo-root/no-largefiles/normal1 (glob)
1051 1051
1052 1052 $ cd ..
1053 1053
1054 1054
1055 1055 Test "pull --rebase" when rebase is enabled before largefiles (issue3861)
1056 1056 =========================================================================
1057 1057
1058 1058 $ hg showconfig extensions | grep largefiles
1059 1059 extensions.largefiles=!
1060 1060
1061 1061 $ mkdir issue3861
1062 1062 $ cd issue3861
1063 1063 $ hg init src
1064 1064 $ hg clone -q src dst
1065 1065 $ echo a > src/a
1066 1066 $ hg -R src commit -Aqm "#0"
1067 1067 Invoking status precommit hook
1068 1068 A a
1069 1069
1070 1070 $ cat >> dst/.hg/hgrc <<EOF
1071 1071 > [extensions]
1072 1072 > largefiles=
1073 1073 > EOF
1074 1074 $ hg -R dst pull --rebase
1075 1075 pulling from $TESTTMP/issue3861/src (glob)
1076 1076 requesting all changes
1077 1077 adding changesets
1078 1078 adding manifests
1079 1079 adding file changes
1080 1080 added 1 changesets with 1 changes to 1 files
1081 1081 nothing to rebase - working directory parent is already an ancestor of destination bf5e395ced2c
1082 1082 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1083 1083
1084 1084 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now