##// END OF EJS Templates
errors: introduce StateError and use it from commands and cmdutil...
Martin von Zweigbergk -
r46444:527ce85c default
parent child Browse files
Show More
@@ -1,927 +1,927 b''
1 1 # Patch transplanting extension for Mercurial
2 2 #
3 3 # Copyright 2006, 2007 Brendan Cully <brendan@kublai.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 transplant changesets from another branch
9 9
10 10 This extension allows you to transplant changes to another parent revision,
11 11 possibly in another repository. The transplant is done using 'diff' patches.
12 12
13 13 Transplanted patches are recorded in .hg/transplant/transplants, as a
14 14 map from a changeset hash to its hash in the source repository.
15 15 '''
16 16 from __future__ import absolute_import
17 17
18 18 import os
19 19
20 20 from mercurial.i18n import _
21 21 from mercurial.pycompat import open
22 22 from mercurial import (
23 23 bundlerepo,
24 24 cmdutil,
25 25 error,
26 26 exchange,
27 27 hg,
28 28 logcmdutil,
29 29 match,
30 30 merge,
31 31 node as nodemod,
32 32 patch,
33 33 pycompat,
34 34 registrar,
35 35 revlog,
36 36 revset,
37 37 scmutil,
38 38 smartset,
39 39 state as statemod,
40 40 util,
41 41 vfs as vfsmod,
42 42 )
43 43 from mercurial.utils import (
44 44 procutil,
45 45 stringutil,
46 46 )
47 47
48 48
49 49 class TransplantError(error.Abort):
50 50 pass
51 51
52 52
53 53 cmdtable = {}
54 54 command = registrar.command(cmdtable)
55 55 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
56 56 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
57 57 # be specifying the version(s) of Mercurial they are tested with, or
58 58 # leave the attribute unspecified.
59 59 testedwith = b'ships-with-hg-core'
60 60
61 61 configtable = {}
62 62 configitem = registrar.configitem(configtable)
63 63
64 64 configitem(
65 65 b'transplant', b'filter', default=None,
66 66 )
67 67 configitem(
68 68 b'transplant', b'log', default=None,
69 69 )
70 70
71 71
72 72 class transplantentry(object):
73 73 def __init__(self, lnode, rnode):
74 74 self.lnode = lnode
75 75 self.rnode = rnode
76 76
77 77
78 78 class transplants(object):
79 79 def __init__(self, path=None, transplantfile=None, opener=None):
80 80 self.path = path
81 81 self.transplantfile = transplantfile
82 82 self.opener = opener
83 83
84 84 if not opener:
85 85 self.opener = vfsmod.vfs(self.path)
86 86 self.transplants = {}
87 87 self.dirty = False
88 88 self.read()
89 89
90 90 def read(self):
91 91 abspath = os.path.join(self.path, self.transplantfile)
92 92 if self.transplantfile and os.path.exists(abspath):
93 93 for line in self.opener.read(self.transplantfile).splitlines():
94 94 lnode, rnode = map(revlog.bin, line.split(b':'))
95 95 list = self.transplants.setdefault(rnode, [])
96 96 list.append(transplantentry(lnode, rnode))
97 97
98 98 def write(self):
99 99 if self.dirty and self.transplantfile:
100 100 if not os.path.isdir(self.path):
101 101 os.mkdir(self.path)
102 102 fp = self.opener(self.transplantfile, b'w')
103 103 for list in pycompat.itervalues(self.transplants):
104 104 for t in list:
105 105 l, r = map(nodemod.hex, (t.lnode, t.rnode))
106 106 fp.write(l + b':' + r + b'\n')
107 107 fp.close()
108 108 self.dirty = False
109 109
110 110 def get(self, rnode):
111 111 return self.transplants.get(rnode) or []
112 112
113 113 def set(self, lnode, rnode):
114 114 list = self.transplants.setdefault(rnode, [])
115 115 list.append(transplantentry(lnode, rnode))
116 116 self.dirty = True
117 117
118 118 def remove(self, transplant):
119 119 list = self.transplants.get(transplant.rnode)
120 120 if list:
121 121 del list[list.index(transplant)]
122 122 self.dirty = True
123 123
124 124
125 125 class transplanter(object):
126 126 def __init__(self, ui, repo, opts):
127 127 self.ui = ui
128 128 self.path = repo.vfs.join(b'transplant')
129 129 self.opener = vfsmod.vfs(self.path)
130 130 self.transplants = transplants(
131 131 self.path, b'transplants', opener=self.opener
132 132 )
133 133
134 134 def getcommiteditor():
135 135 editform = cmdutil.mergeeditform(repo[None], b'transplant')
136 136 return cmdutil.getcommiteditor(
137 137 editform=editform, **pycompat.strkwargs(opts)
138 138 )
139 139
140 140 self.getcommiteditor = getcommiteditor
141 141
142 142 def applied(self, repo, node, parent):
143 143 '''returns True if a node is already an ancestor of parent
144 144 or is parent or has already been transplanted'''
145 145 if hasnode(repo, parent):
146 146 parentrev = repo.changelog.rev(parent)
147 147 if hasnode(repo, node):
148 148 rev = repo.changelog.rev(node)
149 149 reachable = repo.changelog.ancestors(
150 150 [parentrev], rev, inclusive=True
151 151 )
152 152 if rev in reachable:
153 153 return True
154 154 for t in self.transplants.get(node):
155 155 # it might have been stripped
156 156 if not hasnode(repo, t.lnode):
157 157 self.transplants.remove(t)
158 158 return False
159 159 lnoderev = repo.changelog.rev(t.lnode)
160 160 if lnoderev in repo.changelog.ancestors(
161 161 [parentrev], lnoderev, inclusive=True
162 162 ):
163 163 return True
164 164 return False
165 165
166 166 def apply(self, repo, source, revmap, merges, opts=None):
167 167 '''apply the revisions in revmap one by one in revision order'''
168 168 if opts is None:
169 169 opts = {}
170 170 revs = sorted(revmap)
171 171 p1 = repo.dirstate.p1()
172 172 pulls = []
173 173 diffopts = patch.difffeatureopts(self.ui, opts)
174 174 diffopts.git = True
175 175
176 176 lock = tr = None
177 177 try:
178 178 lock = repo.lock()
179 179 tr = repo.transaction(b'transplant')
180 180 for rev in revs:
181 181 node = revmap[rev]
182 182 revstr = b'%d:%s' % (rev, nodemod.short(node))
183 183
184 184 if self.applied(repo, node, p1):
185 185 self.ui.warn(
186 186 _(b'skipping already applied revision %s\n') % revstr
187 187 )
188 188 continue
189 189
190 190 parents = source.changelog.parents(node)
191 191 if not (opts.get(b'filter') or opts.get(b'log')):
192 192 # If the changeset parent is the same as the
193 193 # wdir's parent, just pull it.
194 194 if parents[0] == p1:
195 195 pulls.append(node)
196 196 p1 = node
197 197 continue
198 198 if pulls:
199 199 if source != repo:
200 200 exchange.pull(repo, source.peer(), heads=pulls)
201 201 merge.update(repo[pulls[-1]])
202 202 p1 = repo.dirstate.p1()
203 203 pulls = []
204 204
205 205 domerge = False
206 206 if node in merges:
207 207 # pulling all the merge revs at once would mean we
208 208 # couldn't transplant after the latest even if
209 209 # transplants before them fail.
210 210 domerge = True
211 211 if not hasnode(repo, node):
212 212 exchange.pull(repo, source.peer(), heads=[node])
213 213
214 214 skipmerge = False
215 215 if parents[1] != revlog.nullid:
216 216 if not opts.get(b'parent'):
217 217 self.ui.note(
218 218 _(b'skipping merge changeset %d:%s\n')
219 219 % (rev, nodemod.short(node))
220 220 )
221 221 skipmerge = True
222 222 else:
223 223 parent = source.lookup(opts[b'parent'])
224 224 if parent not in parents:
225 225 raise error.Abort(
226 226 _(b'%s is not a parent of %s')
227 227 % (nodemod.short(parent), nodemod.short(node))
228 228 )
229 229 else:
230 230 parent = parents[0]
231 231
232 232 if skipmerge:
233 233 patchfile = None
234 234 else:
235 235 fd, patchfile = pycompat.mkstemp(prefix=b'hg-transplant-')
236 236 fp = os.fdopen(fd, 'wb')
237 237 gen = patch.diff(source, parent, node, opts=diffopts)
238 238 for chunk in gen:
239 239 fp.write(chunk)
240 240 fp.close()
241 241
242 242 del revmap[rev]
243 243 if patchfile or domerge:
244 244 try:
245 245 try:
246 246 n = self.applyone(
247 247 repo,
248 248 node,
249 249 source.changelog.read(node),
250 250 patchfile,
251 251 merge=domerge,
252 252 log=opts.get(b'log'),
253 253 filter=opts.get(b'filter'),
254 254 )
255 255 except TransplantError:
256 256 # Do not rollback, it is up to the user to
257 257 # fix the merge or cancel everything
258 258 tr.close()
259 259 raise
260 260 if n and domerge:
261 261 self.ui.status(
262 262 _(b'%s merged at %s\n')
263 263 % (revstr, nodemod.short(n))
264 264 )
265 265 elif n:
266 266 self.ui.status(
267 267 _(b'%s transplanted to %s\n')
268 268 % (nodemod.short(node), nodemod.short(n))
269 269 )
270 270 finally:
271 271 if patchfile:
272 272 os.unlink(patchfile)
273 273 tr.close()
274 274 if pulls:
275 275 exchange.pull(repo, source.peer(), heads=pulls)
276 276 merge.update(repo[pulls[-1]])
277 277 finally:
278 278 self.saveseries(revmap, merges)
279 279 self.transplants.write()
280 280 if tr:
281 281 tr.release()
282 282 if lock:
283 283 lock.release()
284 284
285 285 def filter(self, filter, node, changelog, patchfile):
286 286 '''arbitrarily rewrite changeset before applying it'''
287 287
288 288 self.ui.status(_(b'filtering %s\n') % patchfile)
289 289 user, date, msg = (changelog[1], changelog[2], changelog[4])
290 290 fd, headerfile = pycompat.mkstemp(prefix=b'hg-transplant-')
291 291 fp = os.fdopen(fd, 'wb')
292 292 fp.write(b"# HG changeset patch\n")
293 293 fp.write(b"# User %s\n" % user)
294 294 fp.write(b"# Date %d %d\n" % date)
295 295 fp.write(msg + b'\n')
296 296 fp.close()
297 297
298 298 try:
299 299 self.ui.system(
300 300 b'%s %s %s'
301 301 % (
302 302 filter,
303 303 procutil.shellquote(headerfile),
304 304 procutil.shellquote(patchfile),
305 305 ),
306 306 environ={
307 307 b'HGUSER': changelog[1],
308 308 b'HGREVISION': nodemod.hex(node),
309 309 },
310 310 onerr=error.Abort,
311 311 errprefix=_(b'filter failed'),
312 312 blockedtag=b'transplant_filter',
313 313 )
314 314 user, date, msg = self.parselog(open(headerfile, b'rb'))[1:4]
315 315 finally:
316 316 os.unlink(headerfile)
317 317
318 318 return (user, date, msg)
319 319
320 320 def applyone(
321 321 self, repo, node, cl, patchfile, merge=False, log=False, filter=None
322 322 ):
323 323 '''apply the patch in patchfile to the repository as a transplant'''
324 324 (manifest, user, (time, timezone), files, message) = cl[:5]
325 325 date = b"%d %d" % (time, timezone)
326 326 extra = {b'transplant_source': node}
327 327 if filter:
328 328 (user, date, message) = self.filter(filter, node, cl, patchfile)
329 329
330 330 if log:
331 331 # we don't translate messages inserted into commits
332 332 message += b'\n(transplanted from %s)' % nodemod.hex(node)
333 333
334 334 self.ui.status(_(b'applying %s\n') % nodemod.short(node))
335 335 self.ui.note(b'%s %s\n%s\n' % (user, date, message))
336 336
337 337 if not patchfile and not merge:
338 338 raise error.Abort(_(b'can only omit patchfile if merging'))
339 339 if patchfile:
340 340 try:
341 341 files = set()
342 342 patch.patch(self.ui, repo, patchfile, files=files, eolmode=None)
343 343 files = list(files)
344 344 except Exception as inst:
345 345 seriespath = os.path.join(self.path, b'series')
346 346 if os.path.exists(seriespath):
347 347 os.unlink(seriespath)
348 348 p1 = repo.dirstate.p1()
349 349 p2 = node
350 350 self.log(user, date, message, p1, p2, merge=merge)
351 351 self.ui.write(stringutil.forcebytestr(inst) + b'\n')
352 352 raise TransplantError(
353 353 _(
354 354 b'fix up the working directory and run '
355 355 b'hg transplant --continue'
356 356 )
357 357 )
358 358 else:
359 359 files = None
360 360 if merge:
361 361 p1 = repo.dirstate.p1()
362 362 repo.setparents(p1, node)
363 363 m = match.always()
364 364 else:
365 365 m = match.exact(files)
366 366
367 367 n = repo.commit(
368 368 message,
369 369 user,
370 370 date,
371 371 extra=extra,
372 372 match=m,
373 373 editor=self.getcommiteditor(),
374 374 )
375 375 if not n:
376 376 self.ui.warn(
377 377 _(b'skipping emptied changeset %s\n') % nodemod.short(node)
378 378 )
379 379 return None
380 380 if not merge:
381 381 self.transplants.set(n, node)
382 382
383 383 return n
384 384
385 385 def canresume(self):
386 386 return os.path.exists(os.path.join(self.path, b'journal'))
387 387
388 388 def resume(self, repo, source, opts):
389 389 '''recover last transaction and apply remaining changesets'''
390 390 if os.path.exists(os.path.join(self.path, b'journal')):
391 391 n, node = self.recover(repo, source, opts)
392 392 if n:
393 393 self.ui.status(
394 394 _(b'%s transplanted as %s\n')
395 395 % (nodemod.short(node), nodemod.short(n))
396 396 )
397 397 else:
398 398 self.ui.status(
399 399 _(b'%s skipped due to empty diff\n')
400 400 % (nodemod.short(node),)
401 401 )
402 402 seriespath = os.path.join(self.path, b'series')
403 403 if not os.path.exists(seriespath):
404 404 self.transplants.write()
405 405 return
406 406 nodes, merges = self.readseries()
407 407 revmap = {}
408 408 for n in nodes:
409 409 revmap[source.changelog.rev(n)] = n
410 410 os.unlink(seriespath)
411 411
412 412 self.apply(repo, source, revmap, merges, opts)
413 413
414 414 def recover(self, repo, source, opts):
415 415 '''commit working directory using journal metadata'''
416 416 node, user, date, message, parents = self.readlog()
417 417 merge = False
418 418
419 419 if not user or not date or not message or not parents[0]:
420 420 raise error.Abort(_(b'transplant log file is corrupt'))
421 421
422 422 parent = parents[0]
423 423 if len(parents) > 1:
424 424 if opts.get(b'parent'):
425 425 parent = source.lookup(opts[b'parent'])
426 426 if parent not in parents:
427 427 raise error.Abort(
428 428 _(b'%s is not a parent of %s')
429 429 % (nodemod.short(parent), nodemod.short(node))
430 430 )
431 431 else:
432 432 merge = True
433 433
434 434 extra = {b'transplant_source': node}
435 435 try:
436 436 p1 = repo.dirstate.p1()
437 437 if p1 != parent:
438 438 raise error.Abort(
439 439 _(b'working directory not at transplant parent %s')
440 440 % nodemod.hex(parent)
441 441 )
442 442 if merge:
443 443 repo.setparents(p1, parents[1])
444 444 st = repo.status()
445 445 modified, added, removed, deleted = (
446 446 st.modified,
447 447 st.added,
448 448 st.removed,
449 449 st.deleted,
450 450 )
451 451 if merge or modified or added or removed or deleted:
452 452 n = repo.commit(
453 453 message,
454 454 user,
455 455 date,
456 456 extra=extra,
457 457 editor=self.getcommiteditor(),
458 458 )
459 459 if not n:
460 460 raise error.Abort(_(b'commit failed'))
461 461 if not merge:
462 462 self.transplants.set(n, node)
463 463 else:
464 464 n = None
465 465 self.unlog()
466 466
467 467 return n, node
468 468 finally:
469 469 # TODO: get rid of this meaningless try/finally enclosing.
470 470 # this is kept only to reduce changes in a patch.
471 471 pass
472 472
473 473 def stop(self, ui, repo):
474 474 """logic to stop an interrupted transplant"""
475 475 if self.canresume():
476 476 startctx = repo[b'.']
477 477 merge.clean_update(startctx)
478 478 ui.status(_(b"stopped the interrupted transplant\n"))
479 479 ui.status(
480 480 _(b"working directory is now at %s\n") % startctx.hex()[:12]
481 481 )
482 482 self.unlog()
483 483 return 0
484 484
485 485 def readseries(self):
486 486 nodes = []
487 487 merges = []
488 488 cur = nodes
489 489 for line in self.opener.read(b'series').splitlines():
490 490 if line.startswith(b'# Merges'):
491 491 cur = merges
492 492 continue
493 493 cur.append(revlog.bin(line))
494 494
495 495 return (nodes, merges)
496 496
497 497 def saveseries(self, revmap, merges):
498 498 if not revmap:
499 499 return
500 500
501 501 if not os.path.isdir(self.path):
502 502 os.mkdir(self.path)
503 503 series = self.opener(b'series', b'w')
504 504 for rev in sorted(revmap):
505 505 series.write(nodemod.hex(revmap[rev]) + b'\n')
506 506 if merges:
507 507 series.write(b'# Merges\n')
508 508 for m in merges:
509 509 series.write(nodemod.hex(m) + b'\n')
510 510 series.close()
511 511
512 512 def parselog(self, fp):
513 513 parents = []
514 514 message = []
515 515 node = revlog.nullid
516 516 inmsg = False
517 517 user = None
518 518 date = None
519 519 for line in fp.read().splitlines():
520 520 if inmsg:
521 521 message.append(line)
522 522 elif line.startswith(b'# User '):
523 523 user = line[7:]
524 524 elif line.startswith(b'# Date '):
525 525 date = line[7:]
526 526 elif line.startswith(b'# Node ID '):
527 527 node = revlog.bin(line[10:])
528 528 elif line.startswith(b'# Parent '):
529 529 parents.append(revlog.bin(line[9:]))
530 530 elif not line.startswith(b'# '):
531 531 inmsg = True
532 532 message.append(line)
533 533 if None in (user, date):
534 534 raise error.Abort(
535 535 _(b"filter corrupted changeset (no user or date)")
536 536 )
537 537 return (node, user, date, b'\n'.join(message), parents)
538 538
539 539 def log(self, user, date, message, p1, p2, merge=False):
540 540 '''journal changelog metadata for later recover'''
541 541
542 542 if not os.path.isdir(self.path):
543 543 os.mkdir(self.path)
544 544 fp = self.opener(b'journal', b'w')
545 545 fp.write(b'# User %s\n' % user)
546 546 fp.write(b'# Date %s\n' % date)
547 547 fp.write(b'# Node ID %s\n' % nodemod.hex(p2))
548 548 fp.write(b'# Parent ' + nodemod.hex(p1) + b'\n')
549 549 if merge:
550 550 fp.write(b'# Parent ' + nodemod.hex(p2) + b'\n')
551 551 fp.write(message.rstrip() + b'\n')
552 552 fp.close()
553 553
554 554 def readlog(self):
555 555 return self.parselog(self.opener(b'journal'))
556 556
557 557 def unlog(self):
558 558 '''remove changelog journal'''
559 559 absdst = os.path.join(self.path, b'journal')
560 560 if os.path.exists(absdst):
561 561 os.unlink(absdst)
562 562
563 563 def transplantfilter(self, repo, source, root):
564 564 def matchfn(node):
565 565 if self.applied(repo, node, root):
566 566 return False
567 567 if source.changelog.parents(node)[1] != revlog.nullid:
568 568 return False
569 569 extra = source.changelog.read(node)[5]
570 570 cnode = extra.get(b'transplant_source')
571 571 if cnode and self.applied(repo, cnode, root):
572 572 return False
573 573 return True
574 574
575 575 return matchfn
576 576
577 577
578 578 def hasnode(repo, node):
579 579 try:
580 580 return repo.changelog.rev(node) is not None
581 581 except error.StorageError:
582 582 return False
583 583
584 584
585 585 def browserevs(ui, repo, nodes, opts):
586 586 '''interactively transplant changesets'''
587 587 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
588 588 transplants = []
589 589 merges = []
590 590 prompt = _(
591 591 b'apply changeset? [ynmpcq?]:'
592 592 b'$$ &yes, transplant this changeset'
593 593 b'$$ &no, skip this changeset'
594 594 b'$$ &merge at this changeset'
595 595 b'$$ show &patch'
596 596 b'$$ &commit selected changesets'
597 597 b'$$ &quit and cancel transplant'
598 598 b'$$ &? (show this help)'
599 599 )
600 600 for node in nodes:
601 601 displayer.show(repo[node])
602 602 action = None
603 603 while not action:
604 604 choice = ui.promptchoice(prompt)
605 605 action = b'ynmpcq?'[choice : choice + 1]
606 606 if action == b'?':
607 607 for c, t in ui.extractchoices(prompt)[1]:
608 608 ui.write(b'%s: %s\n' % (c, t))
609 609 action = None
610 610 elif action == b'p':
611 611 parent = repo.changelog.parents(node)[0]
612 612 for chunk in patch.diff(repo, parent, node):
613 613 ui.write(chunk)
614 614 action = None
615 615 if action == b'y':
616 616 transplants.append(node)
617 617 elif action == b'm':
618 618 merges.append(node)
619 619 elif action == b'c':
620 620 break
621 621 elif action == b'q':
622 622 transplants = ()
623 623 merges = ()
624 624 break
625 625 displayer.close()
626 626 return (transplants, merges)
627 627
628 628
629 629 @command(
630 630 b'transplant',
631 631 [
632 632 (
633 633 b's',
634 634 b'source',
635 635 b'',
636 636 _(b'transplant changesets from REPO'),
637 637 _(b'REPO'),
638 638 ),
639 639 (
640 640 b'b',
641 641 b'branch',
642 642 [],
643 643 _(b'use this source changeset as head'),
644 644 _(b'REV'),
645 645 ),
646 646 (
647 647 b'a',
648 648 b'all',
649 649 None,
650 650 _(b'pull all changesets up to the --branch revisions'),
651 651 ),
652 652 (b'p', b'prune', [], _(b'skip over REV'), _(b'REV')),
653 653 (b'm', b'merge', [], _(b'merge at REV'), _(b'REV')),
654 654 (
655 655 b'',
656 656 b'parent',
657 657 b'',
658 658 _(b'parent to choose when transplanting merge'),
659 659 _(b'REV'),
660 660 ),
661 661 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
662 662 (b'', b'log', None, _(b'append transplant info to log message')),
663 663 (b'', b'stop', False, _(b'stop interrupted transplant')),
664 664 (
665 665 b'c',
666 666 b'continue',
667 667 None,
668 668 _(b'continue last transplant session after fixing conflicts'),
669 669 ),
670 670 (
671 671 b'',
672 672 b'filter',
673 673 b'',
674 674 _(b'filter changesets through command'),
675 675 _(b'CMD'),
676 676 ),
677 677 ],
678 678 _(
679 679 b'hg transplant [-s REPO] [-b BRANCH [-a]] [-p REV] '
680 680 b'[-m REV] [REV]...'
681 681 ),
682 682 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
683 683 )
684 684 def transplant(ui, repo, *revs, **opts):
685 685 '''transplant changesets from another branch
686 686
687 687 Selected changesets will be applied on top of the current working
688 688 directory with the log of the original changeset. The changesets
689 689 are copied and will thus appear twice in the history with different
690 690 identities.
691 691
692 692 Consider using the graft command if everything is inside the same
693 693 repository - it will use merges and will usually give a better result.
694 694 Use the rebase extension if the changesets are unpublished and you want
695 695 to move them instead of copying them.
696 696
697 697 If --log is specified, log messages will have a comment appended
698 698 of the form::
699 699
700 700 (transplanted from CHANGESETHASH)
701 701
702 702 You can rewrite the changelog message with the --filter option.
703 703 Its argument will be invoked with the current changelog message as
704 704 $1 and the patch as $2.
705 705
706 706 --source/-s specifies another repository to use for selecting changesets,
707 707 just as if it temporarily had been pulled.
708 708 If --branch/-b is specified, these revisions will be used as
709 709 heads when deciding which changesets to transplant, just as if only
710 710 these revisions had been pulled.
711 711 If --all/-a is specified, all the revisions up to the heads specified
712 712 with --branch will be transplanted.
713 713
714 714 Example:
715 715
716 716 - transplant all changes up to REV on top of your current revision::
717 717
718 718 hg transplant --branch REV --all
719 719
720 720 You can optionally mark selected transplanted changesets as merge
721 721 changesets. You will not be prompted to transplant any ancestors
722 722 of a merged transplant, and you can merge descendants of them
723 723 normally instead of transplanting them.
724 724
725 725 Merge changesets may be transplanted directly by specifying the
726 726 proper parent changeset by calling :hg:`transplant --parent`.
727 727
728 728 If no merges or revisions are provided, :hg:`transplant` will
729 729 start an interactive changeset browser.
730 730
731 731 If a changeset application fails, you can fix the merge by hand
732 732 and then resume where you left off by calling :hg:`transplant
733 733 --continue/-c`.
734 734 '''
735 735 with repo.wlock():
736 736 return _dotransplant(ui, repo, *revs, **opts)
737 737
738 738
739 739 def _dotransplant(ui, repo, *revs, **opts):
740 740 def incwalk(repo, csets, match=util.always):
741 741 for node in csets:
742 742 if match(node):
743 743 yield node
744 744
745 745 def transplantwalk(repo, dest, heads, match=util.always):
746 746 '''Yield all nodes that are ancestors of a head but not ancestors
747 747 of dest.
748 748 If no heads are specified, the heads of repo will be used.'''
749 749 if not heads:
750 750 heads = repo.heads()
751 751 ancestors = []
752 752 ctx = repo[dest]
753 753 for head in heads:
754 754 ancestors.append(ctx.ancestor(repo[head]).node())
755 755 for node in repo.changelog.nodesbetween(ancestors, heads)[0]:
756 756 if match(node):
757 757 yield node
758 758
759 759 def checkopts(opts, revs):
760 760 if opts.get(b'continue'):
761 761 cmdutil.check_incompatible_arguments(
762 762 opts, b'continue', [b'branch', b'all', b'merge']
763 763 )
764 764 return
765 765 if opts.get(b'stop'):
766 766 cmdutil.check_incompatible_arguments(
767 767 opts, b'stop', [b'branch', b'all', b'merge']
768 768 )
769 769 return
770 770 if not (
771 771 opts.get(b'source')
772 772 or revs
773 773 or opts.get(b'merge')
774 774 or opts.get(b'branch')
775 775 ):
776 776 raise error.Abort(
777 777 _(
778 778 b'no source URL, branch revision, or revision '
779 779 b'list provided'
780 780 )
781 781 )
782 782 if opts.get(b'all'):
783 783 if not opts.get(b'branch'):
784 784 raise error.Abort(_(b'--all requires a branch revision'))
785 785 if revs:
786 786 raise error.Abort(
787 787 _(b'--all is incompatible with a revision list')
788 788 )
789 789
790 790 opts = pycompat.byteskwargs(opts)
791 791 checkopts(opts, revs)
792 792
793 793 if not opts.get(b'log'):
794 794 # deprecated config: transplant.log
795 795 opts[b'log'] = ui.config(b'transplant', b'log')
796 796 if not opts.get(b'filter'):
797 797 # deprecated config: transplant.filter
798 798 opts[b'filter'] = ui.config(b'transplant', b'filter')
799 799
800 800 tp = transplanter(ui, repo, opts)
801 801
802 802 p1 = repo.dirstate.p1()
803 803 if len(repo) > 0 and p1 == revlog.nullid:
804 804 raise error.Abort(_(b'no revision checked out'))
805 805 if opts.get(b'continue'):
806 806 if not tp.canresume():
807 raise error.Abort(_(b'no transplant to continue'))
807 raise error.StateError(_(b'no transplant to continue'))
808 808 elif opts.get(b'stop'):
809 809 if not tp.canresume():
810 raise error.Abort(_(b'no interrupted transplant found'))
810 raise error.StateError(_(b'no interrupted transplant found'))
811 811 return tp.stop(ui, repo)
812 812 else:
813 813 cmdutil.checkunfinished(repo)
814 814 cmdutil.bailifchanged(repo)
815 815
816 816 sourcerepo = opts.get(b'source')
817 817 if sourcerepo:
818 818 peer = hg.peer(repo, opts, ui.expandpath(sourcerepo))
819 819 heads = pycompat.maplist(peer.lookup, opts.get(b'branch', ()))
820 820 target = set(heads)
821 821 for r in revs:
822 822 try:
823 823 target.add(peer.lookup(r))
824 824 except error.RepoError:
825 825 pass
826 826 source, csets, cleanupfn = bundlerepo.getremotechanges(
827 827 ui, repo, peer, onlyheads=sorted(target), force=True
828 828 )
829 829 else:
830 830 source = repo
831 831 heads = pycompat.maplist(source.lookup, opts.get(b'branch', ()))
832 832 cleanupfn = None
833 833
834 834 try:
835 835 if opts.get(b'continue'):
836 836 tp.resume(repo, source, opts)
837 837 return
838 838
839 839 tf = tp.transplantfilter(repo, source, p1)
840 840 if opts.get(b'prune'):
841 841 prune = {
842 842 source[r].node()
843 843 for r in scmutil.revrange(source, opts.get(b'prune'))
844 844 }
845 845 matchfn = lambda x: tf(x) and x not in prune
846 846 else:
847 847 matchfn = tf
848 848 merges = pycompat.maplist(source.lookup, opts.get(b'merge', ()))
849 849 revmap = {}
850 850 if revs:
851 851 for r in scmutil.revrange(source, revs):
852 852 revmap[int(r)] = source[r].node()
853 853 elif opts.get(b'all') or not merges:
854 854 if source != repo:
855 855 alltransplants = incwalk(source, csets, match=matchfn)
856 856 else:
857 857 alltransplants = transplantwalk(
858 858 source, p1, heads, match=matchfn
859 859 )
860 860 if opts.get(b'all'):
861 861 revs = alltransplants
862 862 else:
863 863 revs, newmerges = browserevs(ui, source, alltransplants, opts)
864 864 merges.extend(newmerges)
865 865 for r in revs:
866 866 revmap[source.changelog.rev(r)] = r
867 867 for r in merges:
868 868 revmap[source.changelog.rev(r)] = r
869 869
870 870 tp.apply(repo, source, revmap, merges, opts)
871 871 finally:
872 872 if cleanupfn:
873 873 cleanupfn()
874 874
875 875
876 876 def continuecmd(ui, repo):
877 877 """logic to resume an interrupted transplant using
878 878 'hg continue'"""
879 879 with repo.wlock():
880 880 tp = transplanter(ui, repo, {})
881 881 return tp.resume(repo, repo, {})
882 882
883 883
884 884 revsetpredicate = registrar.revsetpredicate()
885 885
886 886
887 887 @revsetpredicate(b'transplanted([set])')
888 888 def revsettransplanted(repo, subset, x):
889 889 """Transplanted changesets in set, or all transplanted changesets.
890 890 """
891 891 if x:
892 892 s = revset.getset(repo, subset, x)
893 893 else:
894 894 s = subset
895 895 return smartset.baseset(
896 896 [r for r in s if repo[r].extra().get(b'transplant_source')]
897 897 )
898 898
899 899
900 900 templatekeyword = registrar.templatekeyword()
901 901
902 902
903 903 @templatekeyword(b'transplanted', requires={b'ctx'})
904 904 def kwtransplanted(context, mapping):
905 905 """String. The node identifier of the transplanted
906 906 changeset if any."""
907 907 ctx = context.resource(mapping, b'ctx')
908 908 n = ctx.extra().get(b'transplant_source')
909 909 return n and nodemod.hex(n) or b''
910 910
911 911
912 912 def extsetup(ui):
913 913 statemod.addunfinished(
914 914 b'transplant',
915 915 fname=b'transplant/journal',
916 916 clearable=True,
917 917 continuefunc=continuecmd,
918 918 statushint=_(
919 919 b'To continue: hg transplant --continue\n'
920 920 b'To stop: hg transplant --stop'
921 921 ),
922 922 cmdhint=_(b"use 'hg transplant --continue' or 'hg transplant --stop'"),
923 923 )
924 924
925 925
926 926 # tell hggettext to extract docstrings from these functions:
927 927 i18nfunctions = [revsettransplanted, kwtransplanted]
@@ -1,3913 +1,3913 b''
1 1 # cmdutil.py - help for command processing in mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.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 from __future__ import absolute_import
9 9
10 10 import copy as copymod
11 11 import errno
12 12 import os
13 13 import re
14 14
15 15 from .i18n import _
16 16 from .node import (
17 17 hex,
18 18 nullid,
19 19 short,
20 20 )
21 21 from .pycompat import (
22 22 getattr,
23 23 open,
24 24 setattr,
25 25 )
26 26 from .thirdparty import attr
27 27
28 28 from . import (
29 29 bookmarks,
30 30 changelog,
31 31 copies,
32 32 crecord as crecordmod,
33 33 dirstateguard,
34 34 encoding,
35 35 error,
36 36 formatter,
37 37 logcmdutil,
38 38 match as matchmod,
39 39 merge as mergemod,
40 40 mergestate as mergestatemod,
41 41 mergeutil,
42 42 obsolete,
43 43 patch,
44 44 pathutil,
45 45 phases,
46 46 pycompat,
47 47 repair,
48 48 revlog,
49 49 rewriteutil,
50 50 scmutil,
51 51 state as statemod,
52 52 subrepoutil,
53 53 templatekw,
54 54 templater,
55 55 util,
56 56 vfs as vfsmod,
57 57 )
58 58
59 59 from .utils import (
60 60 dateutil,
61 61 stringutil,
62 62 )
63 63
64 64 if pycompat.TYPE_CHECKING:
65 65 from typing import (
66 66 Any,
67 67 Dict,
68 68 )
69 69
70 70 for t in (Any, Dict):
71 71 assert t
72 72
73 73 stringio = util.stringio
74 74
75 75 # templates of common command options
76 76
77 77 dryrunopts = [
78 78 (b'n', b'dry-run', None, _(b'do not perform actions, just print output')),
79 79 ]
80 80
81 81 confirmopts = [
82 82 (b'', b'confirm', None, _(b'ask before applying actions')),
83 83 ]
84 84
85 85 remoteopts = [
86 86 (b'e', b'ssh', b'', _(b'specify ssh command to use'), _(b'CMD')),
87 87 (
88 88 b'',
89 89 b'remotecmd',
90 90 b'',
91 91 _(b'specify hg command to run on the remote side'),
92 92 _(b'CMD'),
93 93 ),
94 94 (
95 95 b'',
96 96 b'insecure',
97 97 None,
98 98 _(b'do not verify server certificate (ignoring web.cacerts config)'),
99 99 ),
100 100 ]
101 101
102 102 walkopts = [
103 103 (
104 104 b'I',
105 105 b'include',
106 106 [],
107 107 _(b'include names matching the given patterns'),
108 108 _(b'PATTERN'),
109 109 ),
110 110 (
111 111 b'X',
112 112 b'exclude',
113 113 [],
114 114 _(b'exclude names matching the given patterns'),
115 115 _(b'PATTERN'),
116 116 ),
117 117 ]
118 118
119 119 commitopts = [
120 120 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
121 121 (b'l', b'logfile', b'', _(b'read commit message from file'), _(b'FILE')),
122 122 ]
123 123
124 124 commitopts2 = [
125 125 (
126 126 b'd',
127 127 b'date',
128 128 b'',
129 129 _(b'record the specified date as commit date'),
130 130 _(b'DATE'),
131 131 ),
132 132 (
133 133 b'u',
134 134 b'user',
135 135 b'',
136 136 _(b'record the specified user as committer'),
137 137 _(b'USER'),
138 138 ),
139 139 ]
140 140
141 141 commitopts3 = [
142 142 (b'D', b'currentdate', None, _(b'record the current date as commit date')),
143 143 (b'U', b'currentuser', None, _(b'record the current user as committer')),
144 144 ]
145 145
146 146 formatteropts = [
147 147 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
148 148 ]
149 149
150 150 templateopts = [
151 151 (
152 152 b'',
153 153 b'style',
154 154 b'',
155 155 _(b'display using template map file (DEPRECATED)'),
156 156 _(b'STYLE'),
157 157 ),
158 158 (b'T', b'template', b'', _(b'display with template'), _(b'TEMPLATE')),
159 159 ]
160 160
161 161 logopts = [
162 162 (b'p', b'patch', None, _(b'show patch')),
163 163 (b'g', b'git', None, _(b'use git extended diff format')),
164 164 (b'l', b'limit', b'', _(b'limit number of changes displayed'), _(b'NUM')),
165 165 (b'M', b'no-merges', None, _(b'do not show merges')),
166 166 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
167 167 (b'G', b'graph', None, _(b"show the revision DAG")),
168 168 ] + templateopts
169 169
170 170 diffopts = [
171 171 (b'a', b'text', None, _(b'treat all files as text')),
172 172 (
173 173 b'g',
174 174 b'git',
175 175 None,
176 176 _(b'use git extended diff format (DEFAULT: diff.git)'),
177 177 ),
178 178 (b'', b'binary', None, _(b'generate binary diffs in git mode (default)')),
179 179 (b'', b'nodates', None, _(b'omit dates from diff headers')),
180 180 ]
181 181
182 182 diffwsopts = [
183 183 (
184 184 b'w',
185 185 b'ignore-all-space',
186 186 None,
187 187 _(b'ignore white space when comparing lines'),
188 188 ),
189 189 (
190 190 b'b',
191 191 b'ignore-space-change',
192 192 None,
193 193 _(b'ignore changes in the amount of white space'),
194 194 ),
195 195 (
196 196 b'B',
197 197 b'ignore-blank-lines',
198 198 None,
199 199 _(b'ignore changes whose lines are all blank'),
200 200 ),
201 201 (
202 202 b'Z',
203 203 b'ignore-space-at-eol',
204 204 None,
205 205 _(b'ignore changes in whitespace at EOL'),
206 206 ),
207 207 ]
208 208
209 209 diffopts2 = (
210 210 [
211 211 (b'', b'noprefix', None, _(b'omit a/ and b/ prefixes from filenames')),
212 212 (
213 213 b'p',
214 214 b'show-function',
215 215 None,
216 216 _(
217 217 b'show which function each change is in (DEFAULT: diff.showfunc)'
218 218 ),
219 219 ),
220 220 (b'', b'reverse', None, _(b'produce a diff that undoes the changes')),
221 221 ]
222 222 + diffwsopts
223 223 + [
224 224 (
225 225 b'U',
226 226 b'unified',
227 227 b'',
228 228 _(b'number of lines of context to show'),
229 229 _(b'NUM'),
230 230 ),
231 231 (b'', b'stat', None, _(b'output diffstat-style summary of changes')),
232 232 (
233 233 b'',
234 234 b'root',
235 235 b'',
236 236 _(b'produce diffs relative to subdirectory'),
237 237 _(b'DIR'),
238 238 ),
239 239 ]
240 240 )
241 241
242 242 mergetoolopts = [
243 243 (b't', b'tool', b'', _(b'specify merge tool'), _(b'TOOL')),
244 244 ]
245 245
246 246 similarityopts = [
247 247 (
248 248 b's',
249 249 b'similarity',
250 250 b'',
251 251 _(b'guess renamed files by similarity (0<=s<=100)'),
252 252 _(b'SIMILARITY'),
253 253 )
254 254 ]
255 255
256 256 subrepoopts = [(b'S', b'subrepos', None, _(b'recurse into subrepositories'))]
257 257
258 258 debugrevlogopts = [
259 259 (b'c', b'changelog', False, _(b'open changelog')),
260 260 (b'm', b'manifest', False, _(b'open manifest')),
261 261 (b'', b'dir', b'', _(b'open directory manifest')),
262 262 ]
263 263
264 264 # special string such that everything below this line will be ingored in the
265 265 # editor text
266 266 _linebelow = b"^HG: ------------------------ >8 ------------------------$"
267 267
268 268
269 269 def check_at_most_one_arg(opts, *args):
270 270 """abort if more than one of the arguments are in opts
271 271
272 272 Returns the unique argument or None if none of them were specified.
273 273 """
274 274
275 275 def to_display(name):
276 276 return pycompat.sysbytes(name).replace(b'_', b'-')
277 277
278 278 previous = None
279 279 for x in args:
280 280 if opts.get(x):
281 281 if previous:
282 282 raise error.InputError(
283 283 _(b'cannot specify both --%s and --%s')
284 284 % (to_display(previous), to_display(x))
285 285 )
286 286 previous = x
287 287 return previous
288 288
289 289
290 290 def check_incompatible_arguments(opts, first, others):
291 291 """abort if the first argument is given along with any of the others
292 292
293 293 Unlike check_at_most_one_arg(), `others` are not mutually exclusive
294 294 among themselves, and they're passed as a single collection.
295 295 """
296 296 for other in others:
297 297 check_at_most_one_arg(opts, first, other)
298 298
299 299
300 300 def resolvecommitoptions(ui, opts):
301 301 """modify commit options dict to handle related options
302 302
303 303 The return value indicates that ``rewrite.update-timestamp`` is the reason
304 304 the ``date`` option is set.
305 305 """
306 306 check_at_most_one_arg(opts, b'date', b'currentdate')
307 307 check_at_most_one_arg(opts, b'user', b'currentuser')
308 308
309 309 datemaydiffer = False # date-only change should be ignored?
310 310
311 311 if opts.get(b'currentdate'):
312 312 opts[b'date'] = b'%d %d' % dateutil.makedate()
313 313 elif (
314 314 not opts.get(b'date')
315 315 and ui.configbool(b'rewrite', b'update-timestamp')
316 316 and opts.get(b'currentdate') is None
317 317 ):
318 318 opts[b'date'] = b'%d %d' % dateutil.makedate()
319 319 datemaydiffer = True
320 320
321 321 if opts.get(b'currentuser'):
322 322 opts[b'user'] = ui.username()
323 323
324 324 return datemaydiffer
325 325
326 326
327 327 def checknotesize(ui, opts):
328 328 """ make sure note is of valid format """
329 329
330 330 note = opts.get(b'note')
331 331 if not note:
332 332 return
333 333
334 334 if len(note) > 255:
335 335 raise error.InputError(_(b"cannot store a note of more than 255 bytes"))
336 336 if b'\n' in note:
337 337 raise error.InputError(_(b"note cannot contain a newline"))
338 338
339 339
340 340 def ishunk(x):
341 341 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
342 342 return isinstance(x, hunkclasses)
343 343
344 344
345 345 def newandmodified(chunks, originalchunks):
346 346 newlyaddedandmodifiedfiles = set()
347 347 alsorestore = set()
348 348 for chunk in chunks:
349 349 if (
350 350 ishunk(chunk)
351 351 and chunk.header.isnewfile()
352 352 and chunk not in originalchunks
353 353 ):
354 354 newlyaddedandmodifiedfiles.add(chunk.header.filename())
355 355 alsorestore.update(
356 356 set(chunk.header.files()) - {chunk.header.filename()}
357 357 )
358 358 return newlyaddedandmodifiedfiles, alsorestore
359 359
360 360
361 361 def parsealiases(cmd):
362 362 return cmd.split(b"|")
363 363
364 364
365 365 def setupwrapcolorwrite(ui):
366 366 # wrap ui.write so diff output can be labeled/colorized
367 367 def wrapwrite(orig, *args, **kw):
368 368 label = kw.pop('label', b'')
369 369 for chunk, l in patch.difflabel(lambda: args):
370 370 orig(chunk, label=label + l)
371 371
372 372 oldwrite = ui.write
373 373
374 374 def wrap(*args, **kwargs):
375 375 return wrapwrite(oldwrite, *args, **kwargs)
376 376
377 377 setattr(ui, 'write', wrap)
378 378 return oldwrite
379 379
380 380
381 381 def filterchunks(ui, originalhunks, usecurses, testfile, match, operation=None):
382 382 try:
383 383 if usecurses:
384 384 if testfile:
385 385 recordfn = crecordmod.testdecorator(
386 386 testfile, crecordmod.testchunkselector
387 387 )
388 388 else:
389 389 recordfn = crecordmod.chunkselector
390 390
391 391 return crecordmod.filterpatch(
392 392 ui, originalhunks, recordfn, operation
393 393 )
394 394 except crecordmod.fallbackerror as e:
395 395 ui.warn(b'%s\n' % e)
396 396 ui.warn(_(b'falling back to text mode\n'))
397 397
398 398 return patch.filterpatch(ui, originalhunks, match, operation)
399 399
400 400
401 401 def recordfilter(ui, originalhunks, match, operation=None):
402 402 """ Prompts the user to filter the originalhunks and return a list of
403 403 selected hunks.
404 404 *operation* is used for to build ui messages to indicate the user what
405 405 kind of filtering they are doing: reverting, committing, shelving, etc.
406 406 (see patch.filterpatch).
407 407 """
408 408 usecurses = crecordmod.checkcurses(ui)
409 409 testfile = ui.config(b'experimental', b'crecordtest')
410 410 oldwrite = setupwrapcolorwrite(ui)
411 411 try:
412 412 newchunks, newopts = filterchunks(
413 413 ui, originalhunks, usecurses, testfile, match, operation
414 414 )
415 415 finally:
416 416 ui.write = oldwrite
417 417 return newchunks, newopts
418 418
419 419
420 420 def dorecord(
421 421 ui, repo, commitfunc, cmdsuggest, backupall, filterfn, *pats, **opts
422 422 ):
423 423 opts = pycompat.byteskwargs(opts)
424 424 if not ui.interactive():
425 425 if cmdsuggest:
426 426 msg = _(b'running non-interactively, use %s instead') % cmdsuggest
427 427 else:
428 428 msg = _(b'running non-interactively')
429 429 raise error.InputError(msg)
430 430
431 431 # make sure username is set before going interactive
432 432 if not opts.get(b'user'):
433 433 ui.username() # raise exception, username not provided
434 434
435 435 def recordfunc(ui, repo, message, match, opts):
436 436 """This is generic record driver.
437 437
438 438 Its job is to interactively filter local changes, and
439 439 accordingly prepare working directory into a state in which the
440 440 job can be delegated to a non-interactive commit command such as
441 441 'commit' or 'qrefresh'.
442 442
443 443 After the actual job is done by non-interactive command, the
444 444 working directory is restored to its original state.
445 445
446 446 In the end we'll record interesting changes, and everything else
447 447 will be left in place, so the user can continue working.
448 448 """
449 449 if not opts.get(b'interactive-unshelve'):
450 450 checkunfinished(repo, commit=True)
451 451 wctx = repo[None]
452 452 merge = len(wctx.parents()) > 1
453 453 if merge:
454 454 raise error.InputError(
455 455 _(
456 456 b'cannot partially commit a merge '
457 457 b'(use "hg commit" instead)'
458 458 )
459 459 )
460 460
461 461 def fail(f, msg):
462 462 raise error.Abort(b'%s: %s' % (f, msg))
463 463
464 464 force = opts.get(b'force')
465 465 if not force:
466 466 match = matchmod.badmatch(match, fail)
467 467
468 468 status = repo.status(match=match)
469 469
470 470 overrides = {(b'ui', b'commitsubrepos'): True}
471 471
472 472 with repo.ui.configoverride(overrides, b'record'):
473 473 # subrepoutil.precommit() modifies the status
474 474 tmpstatus = scmutil.status(
475 475 copymod.copy(status.modified),
476 476 copymod.copy(status.added),
477 477 copymod.copy(status.removed),
478 478 copymod.copy(status.deleted),
479 479 copymod.copy(status.unknown),
480 480 copymod.copy(status.ignored),
481 481 copymod.copy(status.clean), # pytype: disable=wrong-arg-count
482 482 )
483 483
484 484 # Force allows -X subrepo to skip the subrepo.
485 485 subs, commitsubs, newstate = subrepoutil.precommit(
486 486 repo.ui, wctx, tmpstatus, match, force=True
487 487 )
488 488 for s in subs:
489 489 if s in commitsubs:
490 490 dirtyreason = wctx.sub(s).dirtyreason(True)
491 491 raise error.Abort(dirtyreason)
492 492
493 493 if not force:
494 494 repo.checkcommitpatterns(wctx, match, status, fail)
495 495 diffopts = patch.difffeatureopts(
496 496 ui,
497 497 opts=opts,
498 498 whitespace=True,
499 499 section=b'commands',
500 500 configprefix=b'commit.interactive.',
501 501 )
502 502 diffopts.nodates = True
503 503 diffopts.git = True
504 504 diffopts.showfunc = True
505 505 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
506 506 originalchunks = patch.parsepatch(originaldiff)
507 507 match = scmutil.match(repo[None], pats)
508 508
509 509 # 1. filter patch, since we are intending to apply subset of it
510 510 try:
511 511 chunks, newopts = filterfn(ui, originalchunks, match)
512 512 except error.PatchError as err:
513 513 raise error.InputError(_(b'error parsing patch: %s') % err)
514 514 opts.update(newopts)
515 515
516 516 # We need to keep a backup of files that have been newly added and
517 517 # modified during the recording process because there is a previous
518 518 # version without the edit in the workdir. We also will need to restore
519 519 # files that were the sources of renames so that the patch application
520 520 # works.
521 521 newlyaddedandmodifiedfiles, alsorestore = newandmodified(
522 522 chunks, originalchunks
523 523 )
524 524 contenders = set()
525 525 for h in chunks:
526 526 try:
527 527 contenders.update(set(h.files()))
528 528 except AttributeError:
529 529 pass
530 530
531 531 changed = status.modified + status.added + status.removed
532 532 newfiles = [f for f in changed if f in contenders]
533 533 if not newfiles:
534 534 ui.status(_(b'no changes to record\n'))
535 535 return 0
536 536
537 537 modified = set(status.modified)
538 538
539 539 # 2. backup changed files, so we can restore them in the end
540 540
541 541 if backupall:
542 542 tobackup = changed
543 543 else:
544 544 tobackup = [
545 545 f
546 546 for f in newfiles
547 547 if f in modified or f in newlyaddedandmodifiedfiles
548 548 ]
549 549 backups = {}
550 550 if tobackup:
551 551 backupdir = repo.vfs.join(b'record-backups')
552 552 try:
553 553 os.mkdir(backupdir)
554 554 except OSError as err:
555 555 if err.errno != errno.EEXIST:
556 556 raise
557 557 try:
558 558 # backup continues
559 559 for f in tobackup:
560 560 fd, tmpname = pycompat.mkstemp(
561 561 prefix=os.path.basename(f) + b'.', dir=backupdir
562 562 )
563 563 os.close(fd)
564 564 ui.debug(b'backup %r as %r\n' % (f, tmpname))
565 565 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
566 566 backups[f] = tmpname
567 567
568 568 fp = stringio()
569 569 for c in chunks:
570 570 fname = c.filename()
571 571 if fname in backups:
572 572 c.write(fp)
573 573 dopatch = fp.tell()
574 574 fp.seek(0)
575 575
576 576 # 2.5 optionally review / modify patch in text editor
577 577 if opts.get(b'review', False):
578 578 patchtext = (
579 579 crecordmod.diffhelptext
580 580 + crecordmod.patchhelptext
581 581 + fp.read()
582 582 )
583 583 reviewedpatch = ui.edit(
584 584 patchtext, b"", action=b"diff", repopath=repo.path
585 585 )
586 586 fp.truncate(0)
587 587 fp.write(reviewedpatch)
588 588 fp.seek(0)
589 589
590 590 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
591 591 # 3a. apply filtered patch to clean repo (clean)
592 592 if backups:
593 593 m = scmutil.matchfiles(repo, set(backups.keys()) | alsorestore)
594 594 mergemod.revert_to(repo[b'.'], matcher=m)
595 595
596 596 # 3b. (apply)
597 597 if dopatch:
598 598 try:
599 599 ui.debug(b'applying patch\n')
600 600 ui.debug(fp.getvalue())
601 601 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
602 602 except error.PatchError as err:
603 603 raise error.InputError(pycompat.bytestr(err))
604 604 del fp
605 605
606 606 # 4. We prepared working directory according to filtered
607 607 # patch. Now is the time to delegate the job to
608 608 # commit/qrefresh or the like!
609 609
610 610 # Make all of the pathnames absolute.
611 611 newfiles = [repo.wjoin(nf) for nf in newfiles]
612 612 return commitfunc(ui, repo, *newfiles, **pycompat.strkwargs(opts))
613 613 finally:
614 614 # 5. finally restore backed-up files
615 615 try:
616 616 dirstate = repo.dirstate
617 617 for realname, tmpname in pycompat.iteritems(backups):
618 618 ui.debug(b'restoring %r to %r\n' % (tmpname, realname))
619 619
620 620 if dirstate[realname] == b'n':
621 621 # without normallookup, restoring timestamp
622 622 # may cause partially committed files
623 623 # to be treated as unmodified
624 624 dirstate.normallookup(realname)
625 625
626 626 # copystat=True here and above are a hack to trick any
627 627 # editors that have f open that we haven't modified them.
628 628 #
629 629 # Also note that this racy as an editor could notice the
630 630 # file's mtime before we've finished writing it.
631 631 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
632 632 os.unlink(tmpname)
633 633 if tobackup:
634 634 os.rmdir(backupdir)
635 635 except OSError:
636 636 pass
637 637
638 638 def recordinwlock(ui, repo, message, match, opts):
639 639 with repo.wlock():
640 640 return recordfunc(ui, repo, message, match, opts)
641 641
642 642 return commit(ui, repo, recordinwlock, pats, opts)
643 643
644 644
645 645 class dirnode(object):
646 646 """
647 647 Represent a directory in user working copy with information required for
648 648 the purpose of tersing its status.
649 649
650 650 path is the path to the directory, without a trailing '/'
651 651
652 652 statuses is a set of statuses of all files in this directory (this includes
653 653 all the files in all the subdirectories too)
654 654
655 655 files is a list of files which are direct child of this directory
656 656
657 657 subdirs is a dictionary of sub-directory name as the key and it's own
658 658 dirnode object as the value
659 659 """
660 660
661 661 def __init__(self, dirpath):
662 662 self.path = dirpath
663 663 self.statuses = set()
664 664 self.files = []
665 665 self.subdirs = {}
666 666
667 667 def _addfileindir(self, filename, status):
668 668 """Add a file in this directory as a direct child."""
669 669 self.files.append((filename, status))
670 670
671 671 def addfile(self, filename, status):
672 672 """
673 673 Add a file to this directory or to its direct parent directory.
674 674
675 675 If the file is not direct child of this directory, we traverse to the
676 676 directory of which this file is a direct child of and add the file
677 677 there.
678 678 """
679 679
680 680 # the filename contains a path separator, it means it's not the direct
681 681 # child of this directory
682 682 if b'/' in filename:
683 683 subdir, filep = filename.split(b'/', 1)
684 684
685 685 # does the dirnode object for subdir exists
686 686 if subdir not in self.subdirs:
687 687 subdirpath = pathutil.join(self.path, subdir)
688 688 self.subdirs[subdir] = dirnode(subdirpath)
689 689
690 690 # try adding the file in subdir
691 691 self.subdirs[subdir].addfile(filep, status)
692 692
693 693 else:
694 694 self._addfileindir(filename, status)
695 695
696 696 if status not in self.statuses:
697 697 self.statuses.add(status)
698 698
699 699 def iterfilepaths(self):
700 700 """Yield (status, path) for files directly under this directory."""
701 701 for f, st in self.files:
702 702 yield st, pathutil.join(self.path, f)
703 703
704 704 def tersewalk(self, terseargs):
705 705 """
706 706 Yield (status, path) obtained by processing the status of this
707 707 dirnode.
708 708
709 709 terseargs is the string of arguments passed by the user with `--terse`
710 710 flag.
711 711
712 712 Following are the cases which can happen:
713 713
714 714 1) All the files in the directory (including all the files in its
715 715 subdirectories) share the same status and the user has asked us to terse
716 716 that status. -> yield (status, dirpath). dirpath will end in '/'.
717 717
718 718 2) Otherwise, we do following:
719 719
720 720 a) Yield (status, filepath) for all the files which are in this
721 721 directory (only the ones in this directory, not the subdirs)
722 722
723 723 b) Recurse the function on all the subdirectories of this
724 724 directory
725 725 """
726 726
727 727 if len(self.statuses) == 1:
728 728 onlyst = self.statuses.pop()
729 729
730 730 # Making sure we terse only when the status abbreviation is
731 731 # passed as terse argument
732 732 if onlyst in terseargs:
733 733 yield onlyst, self.path + b'/'
734 734 return
735 735
736 736 # add the files to status list
737 737 for st, fpath in self.iterfilepaths():
738 738 yield st, fpath
739 739
740 740 # recurse on the subdirs
741 741 for dirobj in self.subdirs.values():
742 742 for st, fpath in dirobj.tersewalk(terseargs):
743 743 yield st, fpath
744 744
745 745
746 746 def tersedir(statuslist, terseargs):
747 747 """
748 748 Terse the status if all the files in a directory shares the same status.
749 749
750 750 statuslist is scmutil.status() object which contains a list of files for
751 751 each status.
752 752 terseargs is string which is passed by the user as the argument to `--terse`
753 753 flag.
754 754
755 755 The function makes a tree of objects of dirnode class, and at each node it
756 756 stores the information required to know whether we can terse a certain
757 757 directory or not.
758 758 """
759 759 # the order matters here as that is used to produce final list
760 760 allst = (b'm', b'a', b'r', b'd', b'u', b'i', b'c')
761 761
762 762 # checking the argument validity
763 763 for s in pycompat.bytestr(terseargs):
764 764 if s not in allst:
765 765 raise error.InputError(_(b"'%s' not recognized") % s)
766 766
767 767 # creating a dirnode object for the root of the repo
768 768 rootobj = dirnode(b'')
769 769 pstatus = (
770 770 b'modified',
771 771 b'added',
772 772 b'deleted',
773 773 b'clean',
774 774 b'unknown',
775 775 b'ignored',
776 776 b'removed',
777 777 )
778 778
779 779 tersedict = {}
780 780 for attrname in pstatus:
781 781 statuschar = attrname[0:1]
782 782 for f in getattr(statuslist, attrname):
783 783 rootobj.addfile(f, statuschar)
784 784 tersedict[statuschar] = []
785 785
786 786 # we won't be tersing the root dir, so add files in it
787 787 for st, fpath in rootobj.iterfilepaths():
788 788 tersedict[st].append(fpath)
789 789
790 790 # process each sub-directory and build tersedict
791 791 for subdir in rootobj.subdirs.values():
792 792 for st, f in subdir.tersewalk(terseargs):
793 793 tersedict[st].append(f)
794 794
795 795 tersedlist = []
796 796 for st in allst:
797 797 tersedict[st].sort()
798 798 tersedlist.append(tersedict[st])
799 799
800 800 return scmutil.status(*tersedlist)
801 801
802 802
803 803 def _commentlines(raw):
804 804 '''Surround lineswith a comment char and a new line'''
805 805 lines = raw.splitlines()
806 806 commentedlines = [b'# %s' % line for line in lines]
807 807 return b'\n'.join(commentedlines) + b'\n'
808 808
809 809
810 810 @attr.s(frozen=True)
811 811 class morestatus(object):
812 812 reporoot = attr.ib()
813 813 unfinishedop = attr.ib()
814 814 unfinishedmsg = attr.ib()
815 815 activemerge = attr.ib()
816 816 unresolvedpaths = attr.ib()
817 817 _formattedpaths = attr.ib(init=False, default=set())
818 818 _label = b'status.morestatus'
819 819
820 820 def formatfile(self, path, fm):
821 821 self._formattedpaths.add(path)
822 822 if self.activemerge and path in self.unresolvedpaths:
823 823 fm.data(unresolved=True)
824 824
825 825 def formatfooter(self, fm):
826 826 if self.unfinishedop or self.unfinishedmsg:
827 827 fm.startitem()
828 828 fm.data(itemtype=b'morestatus')
829 829
830 830 if self.unfinishedop:
831 831 fm.data(unfinished=self.unfinishedop)
832 832 statemsg = (
833 833 _(b'The repository is in an unfinished *%s* state.')
834 834 % self.unfinishedop
835 835 )
836 836 fm.plain(b'%s\n' % _commentlines(statemsg), label=self._label)
837 837 if self.unfinishedmsg:
838 838 fm.data(unfinishedmsg=self.unfinishedmsg)
839 839
840 840 # May also start new data items.
841 841 self._formatconflicts(fm)
842 842
843 843 if self.unfinishedmsg:
844 844 fm.plain(
845 845 b'%s\n' % _commentlines(self.unfinishedmsg), label=self._label
846 846 )
847 847
848 848 def _formatconflicts(self, fm):
849 849 if not self.activemerge:
850 850 return
851 851
852 852 if self.unresolvedpaths:
853 853 mergeliststr = b'\n'.join(
854 854 [
855 855 b' %s'
856 856 % util.pathto(self.reporoot, encoding.getcwd(), path)
857 857 for path in self.unresolvedpaths
858 858 ]
859 859 )
860 860 msg = (
861 861 _(
862 862 '''Unresolved merge conflicts:
863 863
864 864 %s
865 865
866 866 To mark files as resolved: hg resolve --mark FILE'''
867 867 )
868 868 % mergeliststr
869 869 )
870 870
871 871 # If any paths with unresolved conflicts were not previously
872 872 # formatted, output them now.
873 873 for f in self.unresolvedpaths:
874 874 if f in self._formattedpaths:
875 875 # Already output.
876 876 continue
877 877 fm.startitem()
878 878 # We can't claim to know the status of the file - it may just
879 879 # have been in one of the states that were not requested for
880 880 # display, so it could be anything.
881 881 fm.data(itemtype=b'file', path=f, unresolved=True)
882 882
883 883 else:
884 884 msg = _(b'No unresolved merge conflicts.')
885 885
886 886 fm.plain(b'%s\n' % _commentlines(msg), label=self._label)
887 887
888 888
889 889 def readmorestatus(repo):
890 890 """Returns a morestatus object if the repo has unfinished state."""
891 891 statetuple = statemod.getrepostate(repo)
892 892 mergestate = mergestatemod.mergestate.read(repo)
893 893 activemerge = mergestate.active()
894 894 if not statetuple and not activemerge:
895 895 return None
896 896
897 897 unfinishedop = unfinishedmsg = unresolved = None
898 898 if statetuple:
899 899 unfinishedop, unfinishedmsg = statetuple
900 900 if activemerge:
901 901 unresolved = sorted(mergestate.unresolved())
902 902 return morestatus(
903 903 repo.root, unfinishedop, unfinishedmsg, activemerge, unresolved
904 904 )
905 905
906 906
907 907 def findpossible(cmd, table, strict=False):
908 908 """
909 909 Return cmd -> (aliases, command table entry)
910 910 for each matching command.
911 911 Return debug commands (or their aliases) only if no normal command matches.
912 912 """
913 913 choice = {}
914 914 debugchoice = {}
915 915
916 916 if cmd in table:
917 917 # short-circuit exact matches, "log" alias beats "log|history"
918 918 keys = [cmd]
919 919 else:
920 920 keys = table.keys()
921 921
922 922 allcmds = []
923 923 for e in keys:
924 924 aliases = parsealiases(e)
925 925 allcmds.extend(aliases)
926 926 found = None
927 927 if cmd in aliases:
928 928 found = cmd
929 929 elif not strict:
930 930 for a in aliases:
931 931 if a.startswith(cmd):
932 932 found = a
933 933 break
934 934 if found is not None:
935 935 if aliases[0].startswith(b"debug") or found.startswith(b"debug"):
936 936 debugchoice[found] = (aliases, table[e])
937 937 else:
938 938 choice[found] = (aliases, table[e])
939 939
940 940 if not choice and debugchoice:
941 941 choice = debugchoice
942 942
943 943 return choice, allcmds
944 944
945 945
946 946 def findcmd(cmd, table, strict=True):
947 947 """Return (aliases, command table entry) for command string."""
948 948 choice, allcmds = findpossible(cmd, table, strict)
949 949
950 950 if cmd in choice:
951 951 return choice[cmd]
952 952
953 953 if len(choice) > 1:
954 954 clist = sorted(choice)
955 955 raise error.AmbiguousCommand(cmd, clist)
956 956
957 957 if choice:
958 958 return list(choice.values())[0]
959 959
960 960 raise error.UnknownCommand(cmd, allcmds)
961 961
962 962
963 963 def changebranch(ui, repo, revs, label, opts):
964 964 """ Change the branch name of given revs to label """
965 965
966 966 with repo.wlock(), repo.lock(), repo.transaction(b'branches'):
967 967 # abort in case of uncommitted merge or dirty wdir
968 968 bailifchanged(repo)
969 969 revs = scmutil.revrange(repo, revs)
970 970 if not revs:
971 971 raise error.InputError(b"empty revision set")
972 972 roots = repo.revs(b'roots(%ld)', revs)
973 973 if len(roots) > 1:
974 974 raise error.InputError(
975 975 _(b"cannot change branch of non-linear revisions")
976 976 )
977 977 rewriteutil.precheck(repo, revs, b'change branch of')
978 978
979 979 root = repo[roots.first()]
980 980 rpb = {parent.branch() for parent in root.parents()}
981 981 if (
982 982 not opts.get(b'force')
983 983 and label not in rpb
984 984 and label in repo.branchmap()
985 985 ):
986 986 raise error.InputError(
987 987 _(b"a branch of the same name already exists")
988 988 )
989 989
990 990 if repo.revs(b'obsolete() and %ld', revs):
991 991 raise error.InputError(
992 992 _(b"cannot change branch of a obsolete changeset")
993 993 )
994 994
995 995 # make sure only topological heads
996 996 if repo.revs(b'heads(%ld) - head()', revs):
997 997 raise error.InputError(
998 998 _(b"cannot change branch in middle of a stack")
999 999 )
1000 1000
1001 1001 replacements = {}
1002 1002 # avoid import cycle mercurial.cmdutil -> mercurial.context ->
1003 1003 # mercurial.subrepo -> mercurial.cmdutil
1004 1004 from . import context
1005 1005
1006 1006 for rev in revs:
1007 1007 ctx = repo[rev]
1008 1008 oldbranch = ctx.branch()
1009 1009 # check if ctx has same branch
1010 1010 if oldbranch == label:
1011 1011 continue
1012 1012
1013 1013 def filectxfn(repo, newctx, path):
1014 1014 try:
1015 1015 return ctx[path]
1016 1016 except error.ManifestLookupError:
1017 1017 return None
1018 1018
1019 1019 ui.debug(
1020 1020 b"changing branch of '%s' from '%s' to '%s'\n"
1021 1021 % (hex(ctx.node()), oldbranch, label)
1022 1022 )
1023 1023 extra = ctx.extra()
1024 1024 extra[b'branch_change'] = hex(ctx.node())
1025 1025 # While changing branch of set of linear commits, make sure that
1026 1026 # we base our commits on new parent rather than old parent which
1027 1027 # was obsoleted while changing the branch
1028 1028 p1 = ctx.p1().node()
1029 1029 p2 = ctx.p2().node()
1030 1030 if p1 in replacements:
1031 1031 p1 = replacements[p1][0]
1032 1032 if p2 in replacements:
1033 1033 p2 = replacements[p2][0]
1034 1034
1035 1035 mc = context.memctx(
1036 1036 repo,
1037 1037 (p1, p2),
1038 1038 ctx.description(),
1039 1039 ctx.files(),
1040 1040 filectxfn,
1041 1041 user=ctx.user(),
1042 1042 date=ctx.date(),
1043 1043 extra=extra,
1044 1044 branch=label,
1045 1045 )
1046 1046
1047 1047 newnode = repo.commitctx(mc)
1048 1048 replacements[ctx.node()] = (newnode,)
1049 1049 ui.debug(b'new node id is %s\n' % hex(newnode))
1050 1050
1051 1051 # create obsmarkers and move bookmarks
1052 1052 scmutil.cleanupnodes(
1053 1053 repo, replacements, b'branch-change', fixphase=True
1054 1054 )
1055 1055
1056 1056 # move the working copy too
1057 1057 wctx = repo[None]
1058 1058 # in-progress merge is a bit too complex for now.
1059 1059 if len(wctx.parents()) == 1:
1060 1060 newid = replacements.get(wctx.p1().node())
1061 1061 if newid is not None:
1062 1062 # avoid import cycle mercurial.cmdutil -> mercurial.hg ->
1063 1063 # mercurial.cmdutil
1064 1064 from . import hg
1065 1065
1066 1066 hg.update(repo, newid[0], quietempty=True)
1067 1067
1068 1068 ui.status(_(b"changed branch on %d changesets\n") % len(replacements))
1069 1069
1070 1070
1071 1071 def findrepo(p):
1072 1072 while not os.path.isdir(os.path.join(p, b".hg")):
1073 1073 oldp, p = p, os.path.dirname(p)
1074 1074 if p == oldp:
1075 1075 return None
1076 1076
1077 1077 return p
1078 1078
1079 1079
1080 1080 def bailifchanged(repo, merge=True, hint=None):
1081 1081 """ enforce the precondition that working directory must be clean.
1082 1082
1083 1083 'merge' can be set to false if a pending uncommitted merge should be
1084 1084 ignored (such as when 'update --check' runs).
1085 1085
1086 1086 'hint' is the usual hint given to Abort exception.
1087 1087 """
1088 1088
1089 1089 if merge and repo.dirstate.p2() != nullid:
1090 raise error.Abort(_(b'outstanding uncommitted merge'), hint=hint)
1090 raise error.StateError(_(b'outstanding uncommitted merge'), hint=hint)
1091 1091 st = repo.status()
1092 1092 if st.modified or st.added or st.removed or st.deleted:
1093 raise error.Abort(_(b'uncommitted changes'), hint=hint)
1093 raise error.StateError(_(b'uncommitted changes'), hint=hint)
1094 1094 ctx = repo[None]
1095 1095 for s in sorted(ctx.substate):
1096 1096 ctx.sub(s).bailifchanged(hint=hint)
1097 1097
1098 1098
1099 1099 def logmessage(ui, opts):
1100 1100 """ get the log message according to -m and -l option """
1101 1101
1102 1102 check_at_most_one_arg(opts, b'message', b'logfile')
1103 1103
1104 1104 message = opts.get(b'message')
1105 1105 logfile = opts.get(b'logfile')
1106 1106
1107 1107 if not message and logfile:
1108 1108 try:
1109 1109 if isstdiofilename(logfile):
1110 1110 message = ui.fin.read()
1111 1111 else:
1112 1112 message = b'\n'.join(util.readfile(logfile).splitlines())
1113 1113 except IOError as inst:
1114 1114 raise error.Abort(
1115 1115 _(b"can't read commit message '%s': %s")
1116 1116 % (logfile, encoding.strtolocal(inst.strerror))
1117 1117 )
1118 1118 return message
1119 1119
1120 1120
1121 1121 def mergeeditform(ctxorbool, baseformname):
1122 1122 """return appropriate editform name (referencing a committemplate)
1123 1123
1124 1124 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
1125 1125 merging is committed.
1126 1126
1127 1127 This returns baseformname with '.merge' appended if it is a merge,
1128 1128 otherwise '.normal' is appended.
1129 1129 """
1130 1130 if isinstance(ctxorbool, bool):
1131 1131 if ctxorbool:
1132 1132 return baseformname + b".merge"
1133 1133 elif len(ctxorbool.parents()) > 1:
1134 1134 return baseformname + b".merge"
1135 1135
1136 1136 return baseformname + b".normal"
1137 1137
1138 1138
1139 1139 def getcommiteditor(
1140 1140 edit=False, finishdesc=None, extramsg=None, editform=b'', **opts
1141 1141 ):
1142 1142 """get appropriate commit message editor according to '--edit' option
1143 1143
1144 1144 'finishdesc' is a function to be called with edited commit message
1145 1145 (= 'description' of the new changeset) just after editing, but
1146 1146 before checking empty-ness. It should return actual text to be
1147 1147 stored into history. This allows to change description before
1148 1148 storing.
1149 1149
1150 1150 'extramsg' is a extra message to be shown in the editor instead of
1151 1151 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
1152 1152 is automatically added.
1153 1153
1154 1154 'editform' is a dot-separated list of names, to distinguish
1155 1155 the purpose of commit text editing.
1156 1156
1157 1157 'getcommiteditor' returns 'commitforceeditor' regardless of
1158 1158 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
1159 1159 they are specific for usage in MQ.
1160 1160 """
1161 1161 if edit or finishdesc or extramsg:
1162 1162 return lambda r, c, s: commitforceeditor(
1163 1163 r, c, s, finishdesc=finishdesc, extramsg=extramsg, editform=editform
1164 1164 )
1165 1165 elif editform:
1166 1166 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
1167 1167 else:
1168 1168 return commiteditor
1169 1169
1170 1170
1171 1171 def _escapecommandtemplate(tmpl):
1172 1172 parts = []
1173 1173 for typ, start, end in templater.scantemplate(tmpl, raw=True):
1174 1174 if typ == b'string':
1175 1175 parts.append(stringutil.escapestr(tmpl[start:end]))
1176 1176 else:
1177 1177 parts.append(tmpl[start:end])
1178 1178 return b''.join(parts)
1179 1179
1180 1180
1181 1181 def rendercommandtemplate(ui, tmpl, props):
1182 1182 r"""Expand a literal template 'tmpl' in a way suitable for command line
1183 1183
1184 1184 '\' in outermost string is not taken as an escape character because it
1185 1185 is a directory separator on Windows.
1186 1186
1187 1187 >>> from . import ui as uimod
1188 1188 >>> ui = uimod.ui()
1189 1189 >>> rendercommandtemplate(ui, b'c:\\{path}', {b'path': b'foo'})
1190 1190 'c:\\foo'
1191 1191 >>> rendercommandtemplate(ui, b'{"c:\\{path}"}', {'path': b'foo'})
1192 1192 'c:{path}'
1193 1193 """
1194 1194 if not tmpl:
1195 1195 return tmpl
1196 1196 t = formatter.maketemplater(ui, _escapecommandtemplate(tmpl))
1197 1197 return t.renderdefault(props)
1198 1198
1199 1199
1200 1200 def rendertemplate(ctx, tmpl, props=None):
1201 1201 """Expand a literal template 'tmpl' byte-string against one changeset
1202 1202
1203 1203 Each props item must be a stringify-able value or a callable returning
1204 1204 such value, i.e. no bare list nor dict should be passed.
1205 1205 """
1206 1206 repo = ctx.repo()
1207 1207 tres = formatter.templateresources(repo.ui, repo)
1208 1208 t = formatter.maketemplater(
1209 1209 repo.ui, tmpl, defaults=templatekw.keywords, resources=tres
1210 1210 )
1211 1211 mapping = {b'ctx': ctx}
1212 1212 if props:
1213 1213 mapping.update(props)
1214 1214 return t.renderdefault(mapping)
1215 1215
1216 1216
1217 1217 def format_changeset_summary(ui, ctx, command=None, default_spec=None):
1218 1218 """Format a changeset summary (one line)."""
1219 1219 spec = None
1220 1220 if command:
1221 1221 spec = ui.config(
1222 1222 b'command-templates', b'oneline-summary.%s' % command, None
1223 1223 )
1224 1224 if not spec:
1225 1225 spec = ui.config(b'command-templates', b'oneline-summary')
1226 1226 if not spec:
1227 1227 spec = default_spec
1228 1228 if not spec:
1229 1229 spec = (
1230 1230 b'{separate(" ", '
1231 1231 b'label("oneline-summary.changeset", "{rev}:{node|short}")'
1232 1232 b', '
1233 1233 b'join(filter(namespaces % "{ifeq(namespace, "branches", "", join(names % "{label("oneline-summary.{namespace}", name)}", " "))}"), " ")'
1234 1234 b')} '
1235 1235 b'"{label("oneline-summary.desc", desc|firstline)}"'
1236 1236 )
1237 1237 text = rendertemplate(ctx, spec)
1238 1238 return text.split(b'\n')[0]
1239 1239
1240 1240
1241 1241 def _buildfntemplate(pat, total=None, seqno=None, revwidth=None, pathname=None):
1242 1242 r"""Convert old-style filename format string to template string
1243 1243
1244 1244 >>> _buildfntemplate(b'foo-%b-%n.patch', seqno=0)
1245 1245 'foo-{reporoot|basename}-{seqno}.patch'
1246 1246 >>> _buildfntemplate(b'%R{tags % "{tag}"}%H')
1247 1247 '{rev}{tags % "{tag}"}{node}'
1248 1248
1249 1249 '\' in outermost strings has to be escaped because it is a directory
1250 1250 separator on Windows:
1251 1251
1252 1252 >>> _buildfntemplate(b'c:\\tmp\\%R\\%n.patch', seqno=0)
1253 1253 'c:\\\\tmp\\\\{rev}\\\\{seqno}.patch'
1254 1254 >>> _buildfntemplate(b'\\\\foo\\bar.patch')
1255 1255 '\\\\\\\\foo\\\\bar.patch'
1256 1256 >>> _buildfntemplate(b'\\{tags % "{tag}"}')
1257 1257 '\\\\{tags % "{tag}"}'
1258 1258
1259 1259 but inner strings follow the template rules (i.e. '\' is taken as an
1260 1260 escape character):
1261 1261
1262 1262 >>> _buildfntemplate(br'{"c:\tmp"}', seqno=0)
1263 1263 '{"c:\\tmp"}'
1264 1264 """
1265 1265 expander = {
1266 1266 b'H': b'{node}',
1267 1267 b'R': b'{rev}',
1268 1268 b'h': b'{node|short}',
1269 1269 b'm': br'{sub(r"[^\w]", "_", desc|firstline)}',
1270 1270 b'r': b'{if(revwidth, pad(rev, revwidth, "0", left=True), rev)}',
1271 1271 b'%': b'%',
1272 1272 b'b': b'{reporoot|basename}',
1273 1273 }
1274 1274 if total is not None:
1275 1275 expander[b'N'] = b'{total}'
1276 1276 if seqno is not None:
1277 1277 expander[b'n'] = b'{seqno}'
1278 1278 if total is not None and seqno is not None:
1279 1279 expander[b'n'] = b'{pad(seqno, total|stringify|count, "0", left=True)}'
1280 1280 if pathname is not None:
1281 1281 expander[b's'] = b'{pathname|basename}'
1282 1282 expander[b'd'] = b'{if(pathname|dirname, pathname|dirname, ".")}'
1283 1283 expander[b'p'] = b'{pathname}'
1284 1284
1285 1285 newname = []
1286 1286 for typ, start, end in templater.scantemplate(pat, raw=True):
1287 1287 if typ != b'string':
1288 1288 newname.append(pat[start:end])
1289 1289 continue
1290 1290 i = start
1291 1291 while i < end:
1292 1292 n = pat.find(b'%', i, end)
1293 1293 if n < 0:
1294 1294 newname.append(stringutil.escapestr(pat[i:end]))
1295 1295 break
1296 1296 newname.append(stringutil.escapestr(pat[i:n]))
1297 1297 if n + 2 > end:
1298 1298 raise error.Abort(
1299 1299 _(b"incomplete format spec in output filename")
1300 1300 )
1301 1301 c = pat[n + 1 : n + 2]
1302 1302 i = n + 2
1303 1303 try:
1304 1304 newname.append(expander[c])
1305 1305 except KeyError:
1306 1306 raise error.Abort(
1307 1307 _(b"invalid format spec '%%%s' in output filename") % c
1308 1308 )
1309 1309 return b''.join(newname)
1310 1310
1311 1311
1312 1312 def makefilename(ctx, pat, **props):
1313 1313 if not pat:
1314 1314 return pat
1315 1315 tmpl = _buildfntemplate(pat, **props)
1316 1316 # BUG: alias expansion shouldn't be made against template fragments
1317 1317 # rewritten from %-format strings, but we have no easy way to partially
1318 1318 # disable the expansion.
1319 1319 return rendertemplate(ctx, tmpl, pycompat.byteskwargs(props))
1320 1320
1321 1321
1322 1322 def isstdiofilename(pat):
1323 1323 """True if the given pat looks like a filename denoting stdin/stdout"""
1324 1324 return not pat or pat == b'-'
1325 1325
1326 1326
1327 1327 class _unclosablefile(object):
1328 1328 def __init__(self, fp):
1329 1329 self._fp = fp
1330 1330
1331 1331 def close(self):
1332 1332 pass
1333 1333
1334 1334 def __iter__(self):
1335 1335 return iter(self._fp)
1336 1336
1337 1337 def __getattr__(self, attr):
1338 1338 return getattr(self._fp, attr)
1339 1339
1340 1340 def __enter__(self):
1341 1341 return self
1342 1342
1343 1343 def __exit__(self, exc_type, exc_value, exc_tb):
1344 1344 pass
1345 1345
1346 1346
1347 1347 def makefileobj(ctx, pat, mode=b'wb', **props):
1348 1348 writable = mode not in (b'r', b'rb')
1349 1349
1350 1350 if isstdiofilename(pat):
1351 1351 repo = ctx.repo()
1352 1352 if writable:
1353 1353 fp = repo.ui.fout
1354 1354 else:
1355 1355 fp = repo.ui.fin
1356 1356 return _unclosablefile(fp)
1357 1357 fn = makefilename(ctx, pat, **props)
1358 1358 return open(fn, mode)
1359 1359
1360 1360
1361 1361 def openstorage(repo, cmd, file_, opts, returnrevlog=False):
1362 1362 """opens the changelog, manifest, a filelog or a given revlog"""
1363 1363 cl = opts[b'changelog']
1364 1364 mf = opts[b'manifest']
1365 1365 dir = opts[b'dir']
1366 1366 msg = None
1367 1367 if cl and mf:
1368 1368 msg = _(b'cannot specify --changelog and --manifest at the same time')
1369 1369 elif cl and dir:
1370 1370 msg = _(b'cannot specify --changelog and --dir at the same time')
1371 1371 elif cl or mf or dir:
1372 1372 if file_:
1373 1373 msg = _(b'cannot specify filename with --changelog or --manifest')
1374 1374 elif not repo:
1375 1375 msg = _(
1376 1376 b'cannot specify --changelog or --manifest or --dir '
1377 1377 b'without a repository'
1378 1378 )
1379 1379 if msg:
1380 1380 raise error.InputError(msg)
1381 1381
1382 1382 r = None
1383 1383 if repo:
1384 1384 if cl:
1385 1385 r = repo.unfiltered().changelog
1386 1386 elif dir:
1387 1387 if not scmutil.istreemanifest(repo):
1388 1388 raise error.InputError(
1389 1389 _(
1390 1390 b"--dir can only be used on repos with "
1391 1391 b"treemanifest enabled"
1392 1392 )
1393 1393 )
1394 1394 if not dir.endswith(b'/'):
1395 1395 dir = dir + b'/'
1396 1396 dirlog = repo.manifestlog.getstorage(dir)
1397 1397 if len(dirlog):
1398 1398 r = dirlog
1399 1399 elif mf:
1400 1400 r = repo.manifestlog.getstorage(b'')
1401 1401 elif file_:
1402 1402 filelog = repo.file(file_)
1403 1403 if len(filelog):
1404 1404 r = filelog
1405 1405
1406 1406 # Not all storage may be revlogs. If requested, try to return an actual
1407 1407 # revlog instance.
1408 1408 if returnrevlog:
1409 1409 if isinstance(r, revlog.revlog):
1410 1410 pass
1411 1411 elif util.safehasattr(r, b'_revlog'):
1412 1412 r = r._revlog # pytype: disable=attribute-error
1413 1413 elif r is not None:
1414 1414 raise error.InputError(
1415 1415 _(b'%r does not appear to be a revlog') % r
1416 1416 )
1417 1417
1418 1418 if not r:
1419 1419 if not returnrevlog:
1420 1420 raise error.InputError(_(b'cannot give path to non-revlog'))
1421 1421
1422 1422 if not file_:
1423 1423 raise error.CommandError(cmd, _(b'invalid arguments'))
1424 1424 if not os.path.isfile(file_):
1425 1425 raise error.InputError(_(b"revlog '%s' not found") % file_)
1426 1426 r = revlog.revlog(
1427 1427 vfsmod.vfs(encoding.getcwd(), audit=False), file_[:-2] + b".i"
1428 1428 )
1429 1429 return r
1430 1430
1431 1431
1432 1432 def openrevlog(repo, cmd, file_, opts):
1433 1433 """Obtain a revlog backing storage of an item.
1434 1434
1435 1435 This is similar to ``openstorage()`` except it always returns a revlog.
1436 1436
1437 1437 In most cases, a caller cares about the main storage object - not the
1438 1438 revlog backing it. Therefore, this function should only be used by code
1439 1439 that needs to examine low-level revlog implementation details. e.g. debug
1440 1440 commands.
1441 1441 """
1442 1442 return openstorage(repo, cmd, file_, opts, returnrevlog=True)
1443 1443
1444 1444
1445 1445 def copy(ui, repo, pats, opts, rename=False):
1446 1446 check_incompatible_arguments(opts, b'forget', [b'dry_run'])
1447 1447
1448 1448 # called with the repo lock held
1449 1449 #
1450 1450 # hgsep => pathname that uses "/" to separate directories
1451 1451 # ossep => pathname that uses os.sep to separate directories
1452 1452 cwd = repo.getcwd()
1453 1453 targets = {}
1454 1454 forget = opts.get(b"forget")
1455 1455 after = opts.get(b"after")
1456 1456 dryrun = opts.get(b"dry_run")
1457 1457 rev = opts.get(b'at_rev')
1458 1458 if rev:
1459 1459 if not forget and not after:
1460 1460 # TODO: Remove this restriction and make it also create the copy
1461 1461 # targets (and remove the rename source if rename==True).
1462 1462 raise error.InputError(_(b'--at-rev requires --after'))
1463 1463 ctx = scmutil.revsingle(repo, rev)
1464 1464 if len(ctx.parents()) > 1:
1465 1465 raise error.InputError(
1466 1466 _(b'cannot mark/unmark copy in merge commit')
1467 1467 )
1468 1468 else:
1469 1469 ctx = repo[None]
1470 1470
1471 1471 pctx = ctx.p1()
1472 1472
1473 1473 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
1474 1474
1475 1475 if forget:
1476 1476 if ctx.rev() is None:
1477 1477 new_ctx = ctx
1478 1478 else:
1479 1479 if len(ctx.parents()) > 1:
1480 1480 raise error.InputError(_(b'cannot unmark copy in merge commit'))
1481 1481 # avoid cycle context -> subrepo -> cmdutil
1482 1482 from . import context
1483 1483
1484 1484 rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
1485 1485 new_ctx = context.overlayworkingctx(repo)
1486 1486 new_ctx.setbase(ctx.p1())
1487 1487 mergemod.graft(repo, ctx, wctx=new_ctx)
1488 1488
1489 1489 match = scmutil.match(ctx, pats, opts)
1490 1490
1491 1491 current_copies = ctx.p1copies()
1492 1492 current_copies.update(ctx.p2copies())
1493 1493
1494 1494 uipathfn = scmutil.getuipathfn(repo)
1495 1495 for f in ctx.walk(match):
1496 1496 if f in current_copies:
1497 1497 new_ctx[f].markcopied(None)
1498 1498 elif match.exact(f):
1499 1499 ui.warn(
1500 1500 _(
1501 1501 b'%s: not unmarking as copy - file is not marked as copied\n'
1502 1502 )
1503 1503 % uipathfn(f)
1504 1504 )
1505 1505
1506 1506 if ctx.rev() is not None:
1507 1507 with repo.lock():
1508 1508 mem_ctx = new_ctx.tomemctx_for_amend(ctx)
1509 1509 new_node = mem_ctx.commit()
1510 1510
1511 1511 if repo.dirstate.p1() == ctx.node():
1512 1512 with repo.dirstate.parentchange():
1513 1513 scmutil.movedirstate(repo, repo[new_node])
1514 1514 replacements = {ctx.node(): [new_node]}
1515 1515 scmutil.cleanupnodes(
1516 1516 repo, replacements, b'uncopy', fixphase=True
1517 1517 )
1518 1518
1519 1519 return
1520 1520
1521 1521 pats = scmutil.expandpats(pats)
1522 1522 if not pats:
1523 1523 raise error.InputError(_(b'no source or destination specified'))
1524 1524 if len(pats) == 1:
1525 1525 raise error.InputError(_(b'no destination specified'))
1526 1526 dest = pats.pop()
1527 1527
1528 1528 def walkpat(pat):
1529 1529 srcs = []
1530 1530 # TODO: Inline and simplify the non-working-copy version of this code
1531 1531 # since it shares very little with the working-copy version of it.
1532 1532 ctx_to_walk = ctx if ctx.rev() is None else pctx
1533 1533 m = scmutil.match(ctx_to_walk, [pat], opts, globbed=True)
1534 1534 for abs in ctx_to_walk.walk(m):
1535 1535 rel = uipathfn(abs)
1536 1536 exact = m.exact(abs)
1537 1537 if abs not in ctx:
1538 1538 if abs in pctx:
1539 1539 if not after:
1540 1540 if exact:
1541 1541 ui.warn(
1542 1542 _(
1543 1543 b'%s: not copying - file has been marked '
1544 1544 b'for remove\n'
1545 1545 )
1546 1546 % rel
1547 1547 )
1548 1548 continue
1549 1549 else:
1550 1550 if exact:
1551 1551 ui.warn(
1552 1552 _(b'%s: not copying - file is not managed\n') % rel
1553 1553 )
1554 1554 continue
1555 1555
1556 1556 # abs: hgsep
1557 1557 # rel: ossep
1558 1558 srcs.append((abs, rel, exact))
1559 1559 return srcs
1560 1560
1561 1561 if ctx.rev() is not None:
1562 1562 rewriteutil.precheck(repo, [ctx.rev()], b'uncopy')
1563 1563 absdest = pathutil.canonpath(repo.root, cwd, dest)
1564 1564 if ctx.hasdir(absdest):
1565 1565 raise error.InputError(
1566 1566 _(b'%s: --at-rev does not support a directory as destination')
1567 1567 % uipathfn(absdest)
1568 1568 )
1569 1569 if absdest not in ctx:
1570 1570 raise error.InputError(
1571 1571 _(b'%s: copy destination does not exist in %s')
1572 1572 % (uipathfn(absdest), ctx)
1573 1573 )
1574 1574
1575 1575 # avoid cycle context -> subrepo -> cmdutil
1576 1576 from . import context
1577 1577
1578 1578 copylist = []
1579 1579 for pat in pats:
1580 1580 srcs = walkpat(pat)
1581 1581 if not srcs:
1582 1582 continue
1583 1583 for abs, rel, exact in srcs:
1584 1584 copylist.append(abs)
1585 1585
1586 1586 if not copylist:
1587 1587 raise error.InputError(_(b'no files to copy'))
1588 1588 # TODO: Add support for `hg cp --at-rev . foo bar dir` and
1589 1589 # `hg cp --at-rev . dir1 dir2`, preferably unifying the code with the
1590 1590 # existing functions below.
1591 1591 if len(copylist) != 1:
1592 1592 raise error.InputError(_(b'--at-rev requires a single source'))
1593 1593
1594 1594 new_ctx = context.overlayworkingctx(repo)
1595 1595 new_ctx.setbase(ctx.p1())
1596 1596 mergemod.graft(repo, ctx, wctx=new_ctx)
1597 1597
1598 1598 new_ctx.markcopied(absdest, copylist[0])
1599 1599
1600 1600 with repo.lock():
1601 1601 mem_ctx = new_ctx.tomemctx_for_amend(ctx)
1602 1602 new_node = mem_ctx.commit()
1603 1603
1604 1604 if repo.dirstate.p1() == ctx.node():
1605 1605 with repo.dirstate.parentchange():
1606 1606 scmutil.movedirstate(repo, repo[new_node])
1607 1607 replacements = {ctx.node(): [new_node]}
1608 1608 scmutil.cleanupnodes(repo, replacements, b'copy', fixphase=True)
1609 1609
1610 1610 return
1611 1611
1612 1612 # abssrc: hgsep
1613 1613 # relsrc: ossep
1614 1614 # otarget: ossep
1615 1615 def copyfile(abssrc, relsrc, otarget, exact):
1616 1616 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1617 1617 if b'/' in abstarget:
1618 1618 # We cannot normalize abstarget itself, this would prevent
1619 1619 # case only renames, like a => A.
1620 1620 abspath, absname = abstarget.rsplit(b'/', 1)
1621 1621 abstarget = repo.dirstate.normalize(abspath) + b'/' + absname
1622 1622 reltarget = repo.pathto(abstarget, cwd)
1623 1623 target = repo.wjoin(abstarget)
1624 1624 src = repo.wjoin(abssrc)
1625 1625 state = repo.dirstate[abstarget]
1626 1626
1627 1627 scmutil.checkportable(ui, abstarget)
1628 1628
1629 1629 # check for collisions
1630 1630 prevsrc = targets.get(abstarget)
1631 1631 if prevsrc is not None:
1632 1632 ui.warn(
1633 1633 _(b'%s: not overwriting - %s collides with %s\n')
1634 1634 % (
1635 1635 reltarget,
1636 1636 repo.pathto(abssrc, cwd),
1637 1637 repo.pathto(prevsrc, cwd),
1638 1638 )
1639 1639 )
1640 1640 return True # report a failure
1641 1641
1642 1642 # check for overwrites
1643 1643 exists = os.path.lexists(target)
1644 1644 samefile = False
1645 1645 if exists and abssrc != abstarget:
1646 1646 if repo.dirstate.normalize(abssrc) == repo.dirstate.normalize(
1647 1647 abstarget
1648 1648 ):
1649 1649 if not rename:
1650 1650 ui.warn(_(b"%s: can't copy - same file\n") % reltarget)
1651 1651 return True # report a failure
1652 1652 exists = False
1653 1653 samefile = True
1654 1654
1655 1655 if not after and exists or after and state in b'mn':
1656 1656 if not opts[b'force']:
1657 1657 if state in b'mn':
1658 1658 msg = _(b'%s: not overwriting - file already committed\n')
1659 1659 if after:
1660 1660 flags = b'--after --force'
1661 1661 else:
1662 1662 flags = b'--force'
1663 1663 if rename:
1664 1664 hint = (
1665 1665 _(
1666 1666 b"('hg rename %s' to replace the file by "
1667 1667 b'recording a rename)\n'
1668 1668 )
1669 1669 % flags
1670 1670 )
1671 1671 else:
1672 1672 hint = (
1673 1673 _(
1674 1674 b"('hg copy %s' to replace the file by "
1675 1675 b'recording a copy)\n'
1676 1676 )
1677 1677 % flags
1678 1678 )
1679 1679 else:
1680 1680 msg = _(b'%s: not overwriting - file exists\n')
1681 1681 if rename:
1682 1682 hint = _(
1683 1683 b"('hg rename --after' to record the rename)\n"
1684 1684 )
1685 1685 else:
1686 1686 hint = _(b"('hg copy --after' to record the copy)\n")
1687 1687 ui.warn(msg % reltarget)
1688 1688 ui.warn(hint)
1689 1689 return True # report a failure
1690 1690
1691 1691 if after:
1692 1692 if not exists:
1693 1693 if rename:
1694 1694 ui.warn(
1695 1695 _(b'%s: not recording move - %s does not exist\n')
1696 1696 % (relsrc, reltarget)
1697 1697 )
1698 1698 else:
1699 1699 ui.warn(
1700 1700 _(b'%s: not recording copy - %s does not exist\n')
1701 1701 % (relsrc, reltarget)
1702 1702 )
1703 1703 return True # report a failure
1704 1704 elif not dryrun:
1705 1705 try:
1706 1706 if exists:
1707 1707 os.unlink(target)
1708 1708 targetdir = os.path.dirname(target) or b'.'
1709 1709 if not os.path.isdir(targetdir):
1710 1710 os.makedirs(targetdir)
1711 1711 if samefile:
1712 1712 tmp = target + b"~hgrename"
1713 1713 os.rename(src, tmp)
1714 1714 os.rename(tmp, target)
1715 1715 else:
1716 1716 # Preserve stat info on renames, not on copies; this matches
1717 1717 # Linux CLI behavior.
1718 1718 util.copyfile(src, target, copystat=rename)
1719 1719 srcexists = True
1720 1720 except IOError as inst:
1721 1721 if inst.errno == errno.ENOENT:
1722 1722 ui.warn(_(b'%s: deleted in working directory\n') % relsrc)
1723 1723 srcexists = False
1724 1724 else:
1725 1725 ui.warn(
1726 1726 _(b'%s: cannot copy - %s\n')
1727 1727 % (relsrc, encoding.strtolocal(inst.strerror))
1728 1728 )
1729 1729 return True # report a failure
1730 1730
1731 1731 if ui.verbose or not exact:
1732 1732 if rename:
1733 1733 ui.status(_(b'moving %s to %s\n') % (relsrc, reltarget))
1734 1734 else:
1735 1735 ui.status(_(b'copying %s to %s\n') % (relsrc, reltarget))
1736 1736
1737 1737 targets[abstarget] = abssrc
1738 1738
1739 1739 # fix up dirstate
1740 1740 scmutil.dirstatecopy(
1741 1741 ui, repo, ctx, abssrc, abstarget, dryrun=dryrun, cwd=cwd
1742 1742 )
1743 1743 if rename and not dryrun:
1744 1744 if not after and srcexists and not samefile:
1745 1745 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
1746 1746 repo.wvfs.unlinkpath(abssrc, rmdir=rmdir)
1747 1747 ctx.forget([abssrc])
1748 1748
1749 1749 # pat: ossep
1750 1750 # dest ossep
1751 1751 # srcs: list of (hgsep, hgsep, ossep, bool)
1752 1752 # return: function that takes hgsep and returns ossep
1753 1753 def targetpathfn(pat, dest, srcs):
1754 1754 if os.path.isdir(pat):
1755 1755 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1756 1756 abspfx = util.localpath(abspfx)
1757 1757 if destdirexists:
1758 1758 striplen = len(os.path.split(abspfx)[0])
1759 1759 else:
1760 1760 striplen = len(abspfx)
1761 1761 if striplen:
1762 1762 striplen += len(pycompat.ossep)
1763 1763 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1764 1764 elif destdirexists:
1765 1765 res = lambda p: os.path.join(
1766 1766 dest, os.path.basename(util.localpath(p))
1767 1767 )
1768 1768 else:
1769 1769 res = lambda p: dest
1770 1770 return res
1771 1771
1772 1772 # pat: ossep
1773 1773 # dest ossep
1774 1774 # srcs: list of (hgsep, hgsep, ossep, bool)
1775 1775 # return: function that takes hgsep and returns ossep
1776 1776 def targetpathafterfn(pat, dest, srcs):
1777 1777 if matchmod.patkind(pat):
1778 1778 # a mercurial pattern
1779 1779 res = lambda p: os.path.join(
1780 1780 dest, os.path.basename(util.localpath(p))
1781 1781 )
1782 1782 else:
1783 1783 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1784 1784 if len(abspfx) < len(srcs[0][0]):
1785 1785 # A directory. Either the target path contains the last
1786 1786 # component of the source path or it does not.
1787 1787 def evalpath(striplen):
1788 1788 score = 0
1789 1789 for s in srcs:
1790 1790 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1791 1791 if os.path.lexists(t):
1792 1792 score += 1
1793 1793 return score
1794 1794
1795 1795 abspfx = util.localpath(abspfx)
1796 1796 striplen = len(abspfx)
1797 1797 if striplen:
1798 1798 striplen += len(pycompat.ossep)
1799 1799 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1800 1800 score = evalpath(striplen)
1801 1801 striplen1 = len(os.path.split(abspfx)[0])
1802 1802 if striplen1:
1803 1803 striplen1 += len(pycompat.ossep)
1804 1804 if evalpath(striplen1) > score:
1805 1805 striplen = striplen1
1806 1806 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1807 1807 else:
1808 1808 # a file
1809 1809 if destdirexists:
1810 1810 res = lambda p: os.path.join(
1811 1811 dest, os.path.basename(util.localpath(p))
1812 1812 )
1813 1813 else:
1814 1814 res = lambda p: dest
1815 1815 return res
1816 1816
1817 1817 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1818 1818 if not destdirexists:
1819 1819 if len(pats) > 1 or matchmod.patkind(pats[0]):
1820 1820 raise error.InputError(
1821 1821 _(
1822 1822 b'with multiple sources, destination must be an '
1823 1823 b'existing directory'
1824 1824 )
1825 1825 )
1826 1826 if util.endswithsep(dest):
1827 1827 raise error.InputError(
1828 1828 _(b'destination %s is not a directory') % dest
1829 1829 )
1830 1830
1831 1831 tfn = targetpathfn
1832 1832 if after:
1833 1833 tfn = targetpathafterfn
1834 1834 copylist = []
1835 1835 for pat in pats:
1836 1836 srcs = walkpat(pat)
1837 1837 if not srcs:
1838 1838 continue
1839 1839 copylist.append((tfn(pat, dest, srcs), srcs))
1840 1840 if not copylist:
1841 1841 raise error.InputError(_(b'no files to copy'))
1842 1842
1843 1843 errors = 0
1844 1844 for targetpath, srcs in copylist:
1845 1845 for abssrc, relsrc, exact in srcs:
1846 1846 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1847 1847 errors += 1
1848 1848
1849 1849 return errors != 0
1850 1850
1851 1851
1852 1852 ## facility to let extension process additional data into an import patch
1853 1853 # list of identifier to be executed in order
1854 1854 extrapreimport = [] # run before commit
1855 1855 extrapostimport = [] # run after commit
1856 1856 # mapping from identifier to actual import function
1857 1857 #
1858 1858 # 'preimport' are run before the commit is made and are provided the following
1859 1859 # arguments:
1860 1860 # - repo: the localrepository instance,
1861 1861 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1862 1862 # - extra: the future extra dictionary of the changeset, please mutate it,
1863 1863 # - opts: the import options.
1864 1864 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1865 1865 # mutation of in memory commit and more. Feel free to rework the code to get
1866 1866 # there.
1867 1867 extrapreimportmap = {}
1868 1868 # 'postimport' are run after the commit is made and are provided the following
1869 1869 # argument:
1870 1870 # - ctx: the changectx created by import.
1871 1871 extrapostimportmap = {}
1872 1872
1873 1873
1874 1874 def tryimportone(ui, repo, patchdata, parents, opts, msgs, updatefunc):
1875 1875 """Utility function used by commands.import to import a single patch
1876 1876
1877 1877 This function is explicitly defined here to help the evolve extension to
1878 1878 wrap this part of the import logic.
1879 1879
1880 1880 The API is currently a bit ugly because it a simple code translation from
1881 1881 the import command. Feel free to make it better.
1882 1882
1883 1883 :patchdata: a dictionary containing parsed patch data (such as from
1884 1884 ``patch.extract()``)
1885 1885 :parents: nodes that will be parent of the created commit
1886 1886 :opts: the full dict of option passed to the import command
1887 1887 :msgs: list to save commit message to.
1888 1888 (used in case we need to save it when failing)
1889 1889 :updatefunc: a function that update a repo to a given node
1890 1890 updatefunc(<repo>, <node>)
1891 1891 """
1892 1892 # avoid cycle context -> subrepo -> cmdutil
1893 1893 from . import context
1894 1894
1895 1895 tmpname = patchdata.get(b'filename')
1896 1896 message = patchdata.get(b'message')
1897 1897 user = opts.get(b'user') or patchdata.get(b'user')
1898 1898 date = opts.get(b'date') or patchdata.get(b'date')
1899 1899 branch = patchdata.get(b'branch')
1900 1900 nodeid = patchdata.get(b'nodeid')
1901 1901 p1 = patchdata.get(b'p1')
1902 1902 p2 = patchdata.get(b'p2')
1903 1903
1904 1904 nocommit = opts.get(b'no_commit')
1905 1905 importbranch = opts.get(b'import_branch')
1906 1906 update = not opts.get(b'bypass')
1907 1907 strip = opts[b"strip"]
1908 1908 prefix = opts[b"prefix"]
1909 1909 sim = float(opts.get(b'similarity') or 0)
1910 1910
1911 1911 if not tmpname:
1912 1912 return None, None, False
1913 1913
1914 1914 rejects = False
1915 1915
1916 1916 cmdline_message = logmessage(ui, opts)
1917 1917 if cmdline_message:
1918 1918 # pickup the cmdline msg
1919 1919 message = cmdline_message
1920 1920 elif message:
1921 1921 # pickup the patch msg
1922 1922 message = message.strip()
1923 1923 else:
1924 1924 # launch the editor
1925 1925 message = None
1926 1926 ui.debug(b'message:\n%s\n' % (message or b''))
1927 1927
1928 1928 if len(parents) == 1:
1929 1929 parents.append(repo[nullid])
1930 1930 if opts.get(b'exact'):
1931 1931 if not nodeid or not p1:
1932 1932 raise error.InputError(_(b'not a Mercurial patch'))
1933 1933 p1 = repo[p1]
1934 1934 p2 = repo[p2 or nullid]
1935 1935 elif p2:
1936 1936 try:
1937 1937 p1 = repo[p1]
1938 1938 p2 = repo[p2]
1939 1939 # Without any options, consider p2 only if the
1940 1940 # patch is being applied on top of the recorded
1941 1941 # first parent.
1942 1942 if p1 != parents[0]:
1943 1943 p1 = parents[0]
1944 1944 p2 = repo[nullid]
1945 1945 except error.RepoError:
1946 1946 p1, p2 = parents
1947 1947 if p2.node() == nullid:
1948 1948 ui.warn(
1949 1949 _(
1950 1950 b"warning: import the patch as a normal revision\n"
1951 1951 b"(use --exact to import the patch as a merge)\n"
1952 1952 )
1953 1953 )
1954 1954 else:
1955 1955 p1, p2 = parents
1956 1956
1957 1957 n = None
1958 1958 if update:
1959 1959 if p1 != parents[0]:
1960 1960 updatefunc(repo, p1.node())
1961 1961 if p2 != parents[1]:
1962 1962 repo.setparents(p1.node(), p2.node())
1963 1963
1964 1964 if opts.get(b'exact') or importbranch:
1965 1965 repo.dirstate.setbranch(branch or b'default')
1966 1966
1967 1967 partial = opts.get(b'partial', False)
1968 1968 files = set()
1969 1969 try:
1970 1970 patch.patch(
1971 1971 ui,
1972 1972 repo,
1973 1973 tmpname,
1974 1974 strip=strip,
1975 1975 prefix=prefix,
1976 1976 files=files,
1977 1977 eolmode=None,
1978 1978 similarity=sim / 100.0,
1979 1979 )
1980 1980 except error.PatchError as e:
1981 1981 if not partial:
1982 1982 raise error.Abort(pycompat.bytestr(e))
1983 1983 if partial:
1984 1984 rejects = True
1985 1985
1986 1986 files = list(files)
1987 1987 if nocommit:
1988 1988 if message:
1989 1989 msgs.append(message)
1990 1990 else:
1991 1991 if opts.get(b'exact') or p2:
1992 1992 # If you got here, you either use --force and know what
1993 1993 # you are doing or used --exact or a merge patch while
1994 1994 # being updated to its first parent.
1995 1995 m = None
1996 1996 else:
1997 1997 m = scmutil.matchfiles(repo, files or [])
1998 1998 editform = mergeeditform(repo[None], b'import.normal')
1999 1999 if opts.get(b'exact'):
2000 2000 editor = None
2001 2001 else:
2002 2002 editor = getcommiteditor(
2003 2003 editform=editform, **pycompat.strkwargs(opts)
2004 2004 )
2005 2005 extra = {}
2006 2006 for idfunc in extrapreimport:
2007 2007 extrapreimportmap[idfunc](repo, patchdata, extra, opts)
2008 2008 overrides = {}
2009 2009 if partial:
2010 2010 overrides[(b'ui', b'allowemptycommit')] = True
2011 2011 if opts.get(b'secret'):
2012 2012 overrides[(b'phases', b'new-commit')] = b'secret'
2013 2013 with repo.ui.configoverride(overrides, b'import'):
2014 2014 n = repo.commit(
2015 2015 message, user, date, match=m, editor=editor, extra=extra
2016 2016 )
2017 2017 for idfunc in extrapostimport:
2018 2018 extrapostimportmap[idfunc](repo[n])
2019 2019 else:
2020 2020 if opts.get(b'exact') or importbranch:
2021 2021 branch = branch or b'default'
2022 2022 else:
2023 2023 branch = p1.branch()
2024 2024 store = patch.filestore()
2025 2025 try:
2026 2026 files = set()
2027 2027 try:
2028 2028 patch.patchrepo(
2029 2029 ui,
2030 2030 repo,
2031 2031 p1,
2032 2032 store,
2033 2033 tmpname,
2034 2034 strip,
2035 2035 prefix,
2036 2036 files,
2037 2037 eolmode=None,
2038 2038 )
2039 2039 except error.PatchError as e:
2040 2040 raise error.Abort(stringutil.forcebytestr(e))
2041 2041 if opts.get(b'exact'):
2042 2042 editor = None
2043 2043 else:
2044 2044 editor = getcommiteditor(editform=b'import.bypass')
2045 2045 memctx = context.memctx(
2046 2046 repo,
2047 2047 (p1.node(), p2.node()),
2048 2048 message,
2049 2049 files=files,
2050 2050 filectxfn=store,
2051 2051 user=user,
2052 2052 date=date,
2053 2053 branch=branch,
2054 2054 editor=editor,
2055 2055 )
2056 2056
2057 2057 overrides = {}
2058 2058 if opts.get(b'secret'):
2059 2059 overrides[(b'phases', b'new-commit')] = b'secret'
2060 2060 with repo.ui.configoverride(overrides, b'import'):
2061 2061 n = memctx.commit()
2062 2062 finally:
2063 2063 store.close()
2064 2064 if opts.get(b'exact') and nocommit:
2065 2065 # --exact with --no-commit is still useful in that it does merge
2066 2066 # and branch bits
2067 2067 ui.warn(_(b"warning: can't check exact import with --no-commit\n"))
2068 2068 elif opts.get(b'exact') and (not n or hex(n) != nodeid):
2069 2069 raise error.Abort(_(b'patch is damaged or loses information'))
2070 2070 msg = _(b'applied to working directory')
2071 2071 if n:
2072 2072 # i18n: refers to a short changeset id
2073 2073 msg = _(b'created %s') % short(n)
2074 2074 return msg, n, rejects
2075 2075
2076 2076
2077 2077 # facility to let extensions include additional data in an exported patch
2078 2078 # list of identifiers to be executed in order
2079 2079 extraexport = []
2080 2080 # mapping from identifier to actual export function
2081 2081 # function as to return a string to be added to the header or None
2082 2082 # it is given two arguments (sequencenumber, changectx)
2083 2083 extraexportmap = {}
2084 2084
2085 2085
2086 2086 def _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts):
2087 2087 node = scmutil.binnode(ctx)
2088 2088 parents = [p.node() for p in ctx.parents() if p]
2089 2089 branch = ctx.branch()
2090 2090 if switch_parent:
2091 2091 parents.reverse()
2092 2092
2093 2093 if parents:
2094 2094 prev = parents[0]
2095 2095 else:
2096 2096 prev = nullid
2097 2097
2098 2098 fm.context(ctx=ctx)
2099 2099 fm.plain(b'# HG changeset patch\n')
2100 2100 fm.write(b'user', b'# User %s\n', ctx.user())
2101 2101 fm.plain(b'# Date %d %d\n' % ctx.date())
2102 2102 fm.write(b'date', b'# %s\n', fm.formatdate(ctx.date()))
2103 2103 fm.condwrite(
2104 2104 branch and branch != b'default', b'branch', b'# Branch %s\n', branch
2105 2105 )
2106 2106 fm.write(b'node', b'# Node ID %s\n', hex(node))
2107 2107 fm.plain(b'# Parent %s\n' % hex(prev))
2108 2108 if len(parents) > 1:
2109 2109 fm.plain(b'# Parent %s\n' % hex(parents[1]))
2110 2110 fm.data(parents=fm.formatlist(pycompat.maplist(hex, parents), name=b'node'))
2111 2111
2112 2112 # TODO: redesign extraexportmap function to support formatter
2113 2113 for headerid in extraexport:
2114 2114 header = extraexportmap[headerid](seqno, ctx)
2115 2115 if header is not None:
2116 2116 fm.plain(b'# %s\n' % header)
2117 2117
2118 2118 fm.write(b'desc', b'%s\n', ctx.description().rstrip())
2119 2119 fm.plain(b'\n')
2120 2120
2121 2121 if fm.isplain():
2122 2122 chunkiter = patch.diffui(repo, prev, node, match, opts=diffopts)
2123 2123 for chunk, label in chunkiter:
2124 2124 fm.plain(chunk, label=label)
2125 2125 else:
2126 2126 chunkiter = patch.diff(repo, prev, node, match, opts=diffopts)
2127 2127 # TODO: make it structured?
2128 2128 fm.data(diff=b''.join(chunkiter))
2129 2129
2130 2130
2131 2131 def _exportfile(repo, revs, fm, dest, switch_parent, diffopts, match):
2132 2132 """Export changesets to stdout or a single file"""
2133 2133 for seqno, rev in enumerate(revs, 1):
2134 2134 ctx = repo[rev]
2135 2135 if not dest.startswith(b'<'):
2136 2136 repo.ui.note(b"%s\n" % dest)
2137 2137 fm.startitem()
2138 2138 _exportsingle(repo, ctx, fm, match, switch_parent, seqno, diffopts)
2139 2139
2140 2140
2141 2141 def _exportfntemplate(
2142 2142 repo, revs, basefm, fntemplate, switch_parent, diffopts, match
2143 2143 ):
2144 2144 """Export changesets to possibly multiple files"""
2145 2145 total = len(revs)
2146 2146 revwidth = max(len(str(rev)) for rev in revs)
2147 2147 filemap = util.sortdict() # filename: [(seqno, rev), ...]
2148 2148
2149 2149 for seqno, rev in enumerate(revs, 1):
2150 2150 ctx = repo[rev]
2151 2151 dest = makefilename(
2152 2152 ctx, fntemplate, total=total, seqno=seqno, revwidth=revwidth
2153 2153 )
2154 2154 filemap.setdefault(dest, []).append((seqno, rev))
2155 2155
2156 2156 for dest in filemap:
2157 2157 with formatter.maybereopen(basefm, dest) as fm:
2158 2158 repo.ui.note(b"%s\n" % dest)
2159 2159 for seqno, rev in filemap[dest]:
2160 2160 fm.startitem()
2161 2161 ctx = repo[rev]
2162 2162 _exportsingle(
2163 2163 repo, ctx, fm, match, switch_parent, seqno, diffopts
2164 2164 )
2165 2165
2166 2166
2167 2167 def _prefetchchangedfiles(repo, revs, match):
2168 2168 allfiles = set()
2169 2169 for rev in revs:
2170 2170 for file in repo[rev].files():
2171 2171 if not match or match(file):
2172 2172 allfiles.add(file)
2173 2173 match = scmutil.matchfiles(repo, allfiles)
2174 2174 revmatches = [(rev, match) for rev in revs]
2175 2175 scmutil.prefetchfiles(repo, revmatches)
2176 2176
2177 2177
2178 2178 def export(
2179 2179 repo,
2180 2180 revs,
2181 2181 basefm,
2182 2182 fntemplate=b'hg-%h.patch',
2183 2183 switch_parent=False,
2184 2184 opts=None,
2185 2185 match=None,
2186 2186 ):
2187 2187 '''export changesets as hg patches
2188 2188
2189 2189 Args:
2190 2190 repo: The repository from which we're exporting revisions.
2191 2191 revs: A list of revisions to export as revision numbers.
2192 2192 basefm: A formatter to which patches should be written.
2193 2193 fntemplate: An optional string to use for generating patch file names.
2194 2194 switch_parent: If True, show diffs against second parent when not nullid.
2195 2195 Default is false, which always shows diff against p1.
2196 2196 opts: diff options to use for generating the patch.
2197 2197 match: If specified, only export changes to files matching this matcher.
2198 2198
2199 2199 Returns:
2200 2200 Nothing.
2201 2201
2202 2202 Side Effect:
2203 2203 "HG Changeset Patch" data is emitted to one of the following
2204 2204 destinations:
2205 2205 fntemplate specified: Each rev is written to a unique file named using
2206 2206 the given template.
2207 2207 Otherwise: All revs will be written to basefm.
2208 2208 '''
2209 2209 _prefetchchangedfiles(repo, revs, match)
2210 2210
2211 2211 if not fntemplate:
2212 2212 _exportfile(
2213 2213 repo, revs, basefm, b'<unnamed>', switch_parent, opts, match
2214 2214 )
2215 2215 else:
2216 2216 _exportfntemplate(
2217 2217 repo, revs, basefm, fntemplate, switch_parent, opts, match
2218 2218 )
2219 2219
2220 2220
2221 2221 def exportfile(repo, revs, fp, switch_parent=False, opts=None, match=None):
2222 2222 """Export changesets to the given file stream"""
2223 2223 _prefetchchangedfiles(repo, revs, match)
2224 2224
2225 2225 dest = getattr(fp, 'name', b'<unnamed>')
2226 2226 with formatter.formatter(repo.ui, fp, b'export', {}) as fm:
2227 2227 _exportfile(repo, revs, fm, dest, switch_parent, opts, match)
2228 2228
2229 2229
2230 2230 def showmarker(fm, marker, index=None):
2231 2231 """utility function to display obsolescence marker in a readable way
2232 2232
2233 2233 To be used by debug function."""
2234 2234 if index is not None:
2235 2235 fm.write(b'index', b'%i ', index)
2236 2236 fm.write(b'prednode', b'%s ', hex(marker.prednode()))
2237 2237 succs = marker.succnodes()
2238 2238 fm.condwrite(
2239 2239 succs,
2240 2240 b'succnodes',
2241 2241 b'%s ',
2242 2242 fm.formatlist(map(hex, succs), name=b'node'),
2243 2243 )
2244 2244 fm.write(b'flag', b'%X ', marker.flags())
2245 2245 parents = marker.parentnodes()
2246 2246 if parents is not None:
2247 2247 fm.write(
2248 2248 b'parentnodes',
2249 2249 b'{%s} ',
2250 2250 fm.formatlist(map(hex, parents), name=b'node', sep=b', '),
2251 2251 )
2252 2252 fm.write(b'date', b'(%s) ', fm.formatdate(marker.date()))
2253 2253 meta = marker.metadata().copy()
2254 2254 meta.pop(b'date', None)
2255 2255 smeta = pycompat.rapply(pycompat.maybebytestr, meta)
2256 2256 fm.write(
2257 2257 b'metadata', b'{%s}', fm.formatdict(smeta, fmt=b'%r: %r', sep=b', ')
2258 2258 )
2259 2259 fm.plain(b'\n')
2260 2260
2261 2261
2262 2262 def finddate(ui, repo, date):
2263 2263 """Find the tipmost changeset that matches the given date spec"""
2264 2264 mrevs = repo.revs(b'date(%s)', date)
2265 2265 try:
2266 2266 rev = mrevs.max()
2267 2267 except ValueError:
2268 2268 raise error.InputError(_(b"revision matching date not found"))
2269 2269
2270 2270 ui.status(
2271 2271 _(b"found revision %d from %s\n")
2272 2272 % (rev, dateutil.datestr(repo[rev].date()))
2273 2273 )
2274 2274 return b'%d' % rev
2275 2275
2276 2276
2277 2277 def add(ui, repo, match, prefix, uipathfn, explicitonly, **opts):
2278 2278 bad = []
2279 2279
2280 2280 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2281 2281 names = []
2282 2282 wctx = repo[None]
2283 2283 cca = None
2284 2284 abort, warn = scmutil.checkportabilityalert(ui)
2285 2285 if abort or warn:
2286 2286 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2287 2287
2288 2288 match = repo.narrowmatch(match, includeexact=True)
2289 2289 badmatch = matchmod.badmatch(match, badfn)
2290 2290 dirstate = repo.dirstate
2291 2291 # We don't want to just call wctx.walk here, since it would return a lot of
2292 2292 # clean files, which we aren't interested in and takes time.
2293 2293 for f in sorted(
2294 2294 dirstate.walk(
2295 2295 badmatch,
2296 2296 subrepos=sorted(wctx.substate),
2297 2297 unknown=True,
2298 2298 ignored=False,
2299 2299 full=False,
2300 2300 )
2301 2301 ):
2302 2302 exact = match.exact(f)
2303 2303 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2304 2304 if cca:
2305 2305 cca(f)
2306 2306 names.append(f)
2307 2307 if ui.verbose or not exact:
2308 2308 ui.status(
2309 2309 _(b'adding %s\n') % uipathfn(f), label=b'ui.addremove.added'
2310 2310 )
2311 2311
2312 2312 for subpath in sorted(wctx.substate):
2313 2313 sub = wctx.sub(subpath)
2314 2314 try:
2315 2315 submatch = matchmod.subdirmatcher(subpath, match)
2316 2316 subprefix = repo.wvfs.reljoin(prefix, subpath)
2317 2317 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2318 2318 if opts.get('subrepos'):
2319 2319 bad.extend(
2320 2320 sub.add(ui, submatch, subprefix, subuipathfn, False, **opts)
2321 2321 )
2322 2322 else:
2323 2323 bad.extend(
2324 2324 sub.add(ui, submatch, subprefix, subuipathfn, True, **opts)
2325 2325 )
2326 2326 except error.LookupError:
2327 2327 ui.status(
2328 2328 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2329 2329 )
2330 2330
2331 2331 if not opts.get('dry_run'):
2332 2332 rejected = wctx.add(names, prefix)
2333 2333 bad.extend(f for f in rejected if f in match.files())
2334 2334 return bad
2335 2335
2336 2336
2337 2337 def addwebdirpath(repo, serverpath, webconf):
2338 2338 webconf[serverpath] = repo.root
2339 2339 repo.ui.debug(b'adding %s = %s\n' % (serverpath, repo.root))
2340 2340
2341 2341 for r in repo.revs(b'filelog("path:.hgsub")'):
2342 2342 ctx = repo[r]
2343 2343 for subpath in ctx.substate:
2344 2344 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2345 2345
2346 2346
2347 2347 def forget(
2348 2348 ui, repo, match, prefix, uipathfn, explicitonly, dryrun, interactive
2349 2349 ):
2350 2350 if dryrun and interactive:
2351 2351 raise error.InputError(
2352 2352 _(b"cannot specify both --dry-run and --interactive")
2353 2353 )
2354 2354 bad = []
2355 2355 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2356 2356 wctx = repo[None]
2357 2357 forgot = []
2358 2358
2359 2359 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2360 2360 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2361 2361 if explicitonly:
2362 2362 forget = [f for f in forget if match.exact(f)]
2363 2363
2364 2364 for subpath in sorted(wctx.substate):
2365 2365 sub = wctx.sub(subpath)
2366 2366 submatch = matchmod.subdirmatcher(subpath, match)
2367 2367 subprefix = repo.wvfs.reljoin(prefix, subpath)
2368 2368 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2369 2369 try:
2370 2370 subbad, subforgot = sub.forget(
2371 2371 submatch,
2372 2372 subprefix,
2373 2373 subuipathfn,
2374 2374 dryrun=dryrun,
2375 2375 interactive=interactive,
2376 2376 )
2377 2377 bad.extend([subpath + b'/' + f for f in subbad])
2378 2378 forgot.extend([subpath + b'/' + f for f in subforgot])
2379 2379 except error.LookupError:
2380 2380 ui.status(
2381 2381 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2382 2382 )
2383 2383
2384 2384 if not explicitonly:
2385 2385 for f in match.files():
2386 2386 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2387 2387 if f not in forgot:
2388 2388 if repo.wvfs.exists(f):
2389 2389 # Don't complain if the exact case match wasn't given.
2390 2390 # But don't do this until after checking 'forgot', so
2391 2391 # that subrepo files aren't normalized, and this op is
2392 2392 # purely from data cached by the status walk above.
2393 2393 if repo.dirstate.normalize(f) in repo.dirstate:
2394 2394 continue
2395 2395 ui.warn(
2396 2396 _(
2397 2397 b'not removing %s: '
2398 2398 b'file is already untracked\n'
2399 2399 )
2400 2400 % uipathfn(f)
2401 2401 )
2402 2402 bad.append(f)
2403 2403
2404 2404 if interactive:
2405 2405 responses = _(
2406 2406 b'[Ynsa?]'
2407 2407 b'$$ &Yes, forget this file'
2408 2408 b'$$ &No, skip this file'
2409 2409 b'$$ &Skip remaining files'
2410 2410 b'$$ Include &all remaining files'
2411 2411 b'$$ &? (display help)'
2412 2412 )
2413 2413 for filename in forget[:]:
2414 2414 r = ui.promptchoice(
2415 2415 _(b'forget %s %s') % (uipathfn(filename), responses)
2416 2416 )
2417 2417 if r == 4: # ?
2418 2418 while r == 4:
2419 2419 for c, t in ui.extractchoices(responses)[1]:
2420 2420 ui.write(b'%s - %s\n' % (c, encoding.lower(t)))
2421 2421 r = ui.promptchoice(
2422 2422 _(b'forget %s %s') % (uipathfn(filename), responses)
2423 2423 )
2424 2424 if r == 0: # yes
2425 2425 continue
2426 2426 elif r == 1: # no
2427 2427 forget.remove(filename)
2428 2428 elif r == 2: # Skip
2429 2429 fnindex = forget.index(filename)
2430 2430 del forget[fnindex:]
2431 2431 break
2432 2432 elif r == 3: # All
2433 2433 break
2434 2434
2435 2435 for f in forget:
2436 2436 if ui.verbose or not match.exact(f) or interactive:
2437 2437 ui.status(
2438 2438 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2439 2439 )
2440 2440
2441 2441 if not dryrun:
2442 2442 rejected = wctx.forget(forget, prefix)
2443 2443 bad.extend(f for f in rejected if f in match.files())
2444 2444 forgot.extend(f for f in forget if f not in rejected)
2445 2445 return bad, forgot
2446 2446
2447 2447
2448 2448 def files(ui, ctx, m, uipathfn, fm, fmt, subrepos):
2449 2449 ret = 1
2450 2450
2451 2451 needsfctx = ui.verbose or {b'size', b'flags'} & fm.datahint()
2452 2452 if fm.isplain() and not needsfctx:
2453 2453 # Fast path. The speed-up comes from skipping the formatter, and batching
2454 2454 # calls to ui.write.
2455 2455 buf = []
2456 2456 for f in ctx.matches(m):
2457 2457 buf.append(fmt % uipathfn(f))
2458 2458 if len(buf) > 100:
2459 2459 ui.write(b''.join(buf))
2460 2460 del buf[:]
2461 2461 ret = 0
2462 2462 if buf:
2463 2463 ui.write(b''.join(buf))
2464 2464 else:
2465 2465 for f in ctx.matches(m):
2466 2466 fm.startitem()
2467 2467 fm.context(ctx=ctx)
2468 2468 if needsfctx:
2469 2469 fc = ctx[f]
2470 2470 fm.write(b'size flags', b'% 10d % 1s ', fc.size(), fc.flags())
2471 2471 fm.data(path=f)
2472 2472 fm.plain(fmt % uipathfn(f))
2473 2473 ret = 0
2474 2474
2475 2475 for subpath in sorted(ctx.substate):
2476 2476 submatch = matchmod.subdirmatcher(subpath, m)
2477 2477 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2478 2478 if subrepos or m.exact(subpath) or any(submatch.files()):
2479 2479 sub = ctx.sub(subpath)
2480 2480 try:
2481 2481 recurse = m.exact(subpath) or subrepos
2482 2482 if (
2483 2483 sub.printfiles(ui, submatch, subuipathfn, fm, fmt, recurse)
2484 2484 == 0
2485 2485 ):
2486 2486 ret = 0
2487 2487 except error.LookupError:
2488 2488 ui.status(
2489 2489 _(b"skipping missing subrepository: %s\n")
2490 2490 % uipathfn(subpath)
2491 2491 )
2492 2492
2493 2493 return ret
2494 2494
2495 2495
2496 2496 def remove(
2497 2497 ui, repo, m, prefix, uipathfn, after, force, subrepos, dryrun, warnings=None
2498 2498 ):
2499 2499 ret = 0
2500 2500 s = repo.status(match=m, clean=True)
2501 2501 modified, added, deleted, clean = s.modified, s.added, s.deleted, s.clean
2502 2502
2503 2503 wctx = repo[None]
2504 2504
2505 2505 if warnings is None:
2506 2506 warnings = []
2507 2507 warn = True
2508 2508 else:
2509 2509 warn = False
2510 2510
2511 2511 subs = sorted(wctx.substate)
2512 2512 progress = ui.makeprogress(
2513 2513 _(b'searching'), total=len(subs), unit=_(b'subrepos')
2514 2514 )
2515 2515 for subpath in subs:
2516 2516 submatch = matchmod.subdirmatcher(subpath, m)
2517 2517 subprefix = repo.wvfs.reljoin(prefix, subpath)
2518 2518 subuipathfn = scmutil.subdiruipathfn(subpath, uipathfn)
2519 2519 if subrepos or m.exact(subpath) or any(submatch.files()):
2520 2520 progress.increment()
2521 2521 sub = wctx.sub(subpath)
2522 2522 try:
2523 2523 if sub.removefiles(
2524 2524 submatch,
2525 2525 subprefix,
2526 2526 subuipathfn,
2527 2527 after,
2528 2528 force,
2529 2529 subrepos,
2530 2530 dryrun,
2531 2531 warnings,
2532 2532 ):
2533 2533 ret = 1
2534 2534 except error.LookupError:
2535 2535 warnings.append(
2536 2536 _(b"skipping missing subrepository: %s\n")
2537 2537 % uipathfn(subpath)
2538 2538 )
2539 2539 progress.complete()
2540 2540
2541 2541 # warn about failure to delete explicit files/dirs
2542 2542 deleteddirs = pathutil.dirs(deleted)
2543 2543 files = m.files()
2544 2544 progress = ui.makeprogress(
2545 2545 _(b'deleting'), total=len(files), unit=_(b'files')
2546 2546 )
2547 2547 for f in files:
2548 2548
2549 2549 def insubrepo():
2550 2550 for subpath in wctx.substate:
2551 2551 if f.startswith(subpath + b'/'):
2552 2552 return True
2553 2553 return False
2554 2554
2555 2555 progress.increment()
2556 2556 isdir = f in deleteddirs or wctx.hasdir(f)
2557 2557 if f in repo.dirstate or isdir or f == b'.' or insubrepo() or f in subs:
2558 2558 continue
2559 2559
2560 2560 if repo.wvfs.exists(f):
2561 2561 if repo.wvfs.isdir(f):
2562 2562 warnings.append(
2563 2563 _(b'not removing %s: no tracked files\n') % uipathfn(f)
2564 2564 )
2565 2565 else:
2566 2566 warnings.append(
2567 2567 _(b'not removing %s: file is untracked\n') % uipathfn(f)
2568 2568 )
2569 2569 # missing files will generate a warning elsewhere
2570 2570 ret = 1
2571 2571 progress.complete()
2572 2572
2573 2573 if force:
2574 2574 list = modified + deleted + clean + added
2575 2575 elif after:
2576 2576 list = deleted
2577 2577 remaining = modified + added + clean
2578 2578 progress = ui.makeprogress(
2579 2579 _(b'skipping'), total=len(remaining), unit=_(b'files')
2580 2580 )
2581 2581 for f in remaining:
2582 2582 progress.increment()
2583 2583 if ui.verbose or (f in files):
2584 2584 warnings.append(
2585 2585 _(b'not removing %s: file still exists\n') % uipathfn(f)
2586 2586 )
2587 2587 ret = 1
2588 2588 progress.complete()
2589 2589 else:
2590 2590 list = deleted + clean
2591 2591 progress = ui.makeprogress(
2592 2592 _(b'skipping'), total=(len(modified) + len(added)), unit=_(b'files')
2593 2593 )
2594 2594 for f in modified:
2595 2595 progress.increment()
2596 2596 warnings.append(
2597 2597 _(
2598 2598 b'not removing %s: file is modified (use -f'
2599 2599 b' to force removal)\n'
2600 2600 )
2601 2601 % uipathfn(f)
2602 2602 )
2603 2603 ret = 1
2604 2604 for f in added:
2605 2605 progress.increment()
2606 2606 warnings.append(
2607 2607 _(
2608 2608 b"not removing %s: file has been marked for add"
2609 2609 b" (use 'hg forget' to undo add)\n"
2610 2610 )
2611 2611 % uipathfn(f)
2612 2612 )
2613 2613 ret = 1
2614 2614 progress.complete()
2615 2615
2616 2616 list = sorted(list)
2617 2617 progress = ui.makeprogress(
2618 2618 _(b'deleting'), total=len(list), unit=_(b'files')
2619 2619 )
2620 2620 for f in list:
2621 2621 if ui.verbose or not m.exact(f):
2622 2622 progress.increment()
2623 2623 ui.status(
2624 2624 _(b'removing %s\n') % uipathfn(f), label=b'ui.addremove.removed'
2625 2625 )
2626 2626 progress.complete()
2627 2627
2628 2628 if not dryrun:
2629 2629 with repo.wlock():
2630 2630 if not after:
2631 2631 for f in list:
2632 2632 if f in added:
2633 2633 continue # we never unlink added files on remove
2634 2634 rmdir = repo.ui.configbool(
2635 2635 b'experimental', b'removeemptydirs'
2636 2636 )
2637 2637 repo.wvfs.unlinkpath(f, ignoremissing=True, rmdir=rmdir)
2638 2638 repo[None].forget(list)
2639 2639
2640 2640 if warn:
2641 2641 for warning in warnings:
2642 2642 ui.warn(warning)
2643 2643
2644 2644 return ret
2645 2645
2646 2646
2647 2647 def _catfmtneedsdata(fm):
2648 2648 return not fm.datahint() or b'data' in fm.datahint()
2649 2649
2650 2650
2651 2651 def _updatecatformatter(fm, ctx, matcher, path, decode):
2652 2652 """Hook for adding data to the formatter used by ``hg cat``.
2653 2653
2654 2654 Extensions (e.g., lfs) can wrap this to inject keywords/data, but must call
2655 2655 this method first."""
2656 2656
2657 2657 # data() can be expensive to fetch (e.g. lfs), so don't fetch it if it
2658 2658 # wasn't requested.
2659 2659 data = b''
2660 2660 if _catfmtneedsdata(fm):
2661 2661 data = ctx[path].data()
2662 2662 if decode:
2663 2663 data = ctx.repo().wwritedata(path, data)
2664 2664 fm.startitem()
2665 2665 fm.context(ctx=ctx)
2666 2666 fm.write(b'data', b'%s', data)
2667 2667 fm.data(path=path)
2668 2668
2669 2669
2670 2670 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2671 2671 err = 1
2672 2672 opts = pycompat.byteskwargs(opts)
2673 2673
2674 2674 def write(path):
2675 2675 filename = None
2676 2676 if fntemplate:
2677 2677 filename = makefilename(
2678 2678 ctx, fntemplate, pathname=os.path.join(prefix, path)
2679 2679 )
2680 2680 # attempt to create the directory if it does not already exist
2681 2681 try:
2682 2682 os.makedirs(os.path.dirname(filename))
2683 2683 except OSError:
2684 2684 pass
2685 2685 with formatter.maybereopen(basefm, filename) as fm:
2686 2686 _updatecatformatter(fm, ctx, matcher, path, opts.get(b'decode'))
2687 2687
2688 2688 # Automation often uses hg cat on single files, so special case it
2689 2689 # for performance to avoid the cost of parsing the manifest.
2690 2690 if len(matcher.files()) == 1 and not matcher.anypats():
2691 2691 file = matcher.files()[0]
2692 2692 mfl = repo.manifestlog
2693 2693 mfnode = ctx.manifestnode()
2694 2694 try:
2695 2695 if mfnode and mfl[mfnode].find(file)[0]:
2696 2696 if _catfmtneedsdata(basefm):
2697 2697 scmutil.prefetchfiles(repo, [(ctx.rev(), matcher)])
2698 2698 write(file)
2699 2699 return 0
2700 2700 except KeyError:
2701 2701 pass
2702 2702
2703 2703 if _catfmtneedsdata(basefm):
2704 2704 scmutil.prefetchfiles(repo, [(ctx.rev(), matcher)])
2705 2705
2706 2706 for abs in ctx.walk(matcher):
2707 2707 write(abs)
2708 2708 err = 0
2709 2709
2710 2710 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2711 2711 for subpath in sorted(ctx.substate):
2712 2712 sub = ctx.sub(subpath)
2713 2713 try:
2714 2714 submatch = matchmod.subdirmatcher(subpath, matcher)
2715 2715 subprefix = os.path.join(prefix, subpath)
2716 2716 if not sub.cat(
2717 2717 submatch,
2718 2718 basefm,
2719 2719 fntemplate,
2720 2720 subprefix,
2721 2721 **pycompat.strkwargs(opts)
2722 2722 ):
2723 2723 err = 0
2724 2724 except error.RepoLookupError:
2725 2725 ui.status(
2726 2726 _(b"skipping missing subrepository: %s\n") % uipathfn(subpath)
2727 2727 )
2728 2728
2729 2729 return err
2730 2730
2731 2731
2732 2732 def commit(ui, repo, commitfunc, pats, opts):
2733 2733 '''commit the specified files or all outstanding changes'''
2734 2734 date = opts.get(b'date')
2735 2735 if date:
2736 2736 opts[b'date'] = dateutil.parsedate(date)
2737 2737 message = logmessage(ui, opts)
2738 2738 matcher = scmutil.match(repo[None], pats, opts)
2739 2739
2740 2740 dsguard = None
2741 2741 # extract addremove carefully -- this function can be called from a command
2742 2742 # that doesn't support addremove
2743 2743 if opts.get(b'addremove'):
2744 2744 dsguard = dirstateguard.dirstateguard(repo, b'commit')
2745 2745 with dsguard or util.nullcontextmanager():
2746 2746 if dsguard:
2747 2747 relative = scmutil.anypats(pats, opts)
2748 2748 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2749 2749 if scmutil.addremove(repo, matcher, b"", uipathfn, opts) != 0:
2750 2750 raise error.Abort(
2751 2751 _(b"failed to mark all new/missing files as added/removed")
2752 2752 )
2753 2753
2754 2754 return commitfunc(ui, repo, message, matcher, opts)
2755 2755
2756 2756
2757 2757 def samefile(f, ctx1, ctx2):
2758 2758 if f in ctx1.manifest():
2759 2759 a = ctx1.filectx(f)
2760 2760 if f in ctx2.manifest():
2761 2761 b = ctx2.filectx(f)
2762 2762 return not a.cmp(b) and a.flags() == b.flags()
2763 2763 else:
2764 2764 return False
2765 2765 else:
2766 2766 return f not in ctx2.manifest()
2767 2767
2768 2768
2769 2769 def amend(ui, repo, old, extra, pats, opts):
2770 2770 # avoid cycle context -> subrepo -> cmdutil
2771 2771 from . import context
2772 2772
2773 2773 # amend will reuse the existing user if not specified, but the obsolete
2774 2774 # marker creation requires that the current user's name is specified.
2775 2775 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2776 2776 ui.username() # raise exception if username not set
2777 2777
2778 2778 ui.note(_(b'amending changeset %s\n') % old)
2779 2779 base = old.p1()
2780 2780
2781 2781 with repo.wlock(), repo.lock(), repo.transaction(b'amend'):
2782 2782 # Participating changesets:
2783 2783 #
2784 2784 # wctx o - workingctx that contains changes from working copy
2785 2785 # | to go into amending commit
2786 2786 # |
2787 2787 # old o - changeset to amend
2788 2788 # |
2789 2789 # base o - first parent of the changeset to amend
2790 2790 wctx = repo[None]
2791 2791
2792 2792 # Copy to avoid mutating input
2793 2793 extra = extra.copy()
2794 2794 # Update extra dict from amended commit (e.g. to preserve graft
2795 2795 # source)
2796 2796 extra.update(old.extra())
2797 2797
2798 2798 # Also update it from the from the wctx
2799 2799 extra.update(wctx.extra())
2800 2800
2801 2801 # date-only change should be ignored?
2802 2802 datemaydiffer = resolvecommitoptions(ui, opts)
2803 2803
2804 2804 date = old.date()
2805 2805 if opts.get(b'date'):
2806 2806 date = dateutil.parsedate(opts.get(b'date'))
2807 2807 user = opts.get(b'user') or old.user()
2808 2808
2809 2809 if len(old.parents()) > 1:
2810 2810 # ctx.files() isn't reliable for merges, so fall back to the
2811 2811 # slower repo.status() method
2812 2812 st = base.status(old)
2813 2813 files = set(st.modified) | set(st.added) | set(st.removed)
2814 2814 else:
2815 2815 files = set(old.files())
2816 2816
2817 2817 # add/remove the files to the working copy if the "addremove" option
2818 2818 # was specified.
2819 2819 matcher = scmutil.match(wctx, pats, opts)
2820 2820 relative = scmutil.anypats(pats, opts)
2821 2821 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
2822 2822 if opts.get(b'addremove') and scmutil.addremove(
2823 2823 repo, matcher, b"", uipathfn, opts
2824 2824 ):
2825 2825 raise error.Abort(
2826 2826 _(b"failed to mark all new/missing files as added/removed")
2827 2827 )
2828 2828
2829 2829 # Check subrepos. This depends on in-place wctx._status update in
2830 2830 # subrepo.precommit(). To minimize the risk of this hack, we do
2831 2831 # nothing if .hgsub does not exist.
2832 2832 if b'.hgsub' in wctx or b'.hgsub' in old:
2833 2833 subs, commitsubs, newsubstate = subrepoutil.precommit(
2834 2834 ui, wctx, wctx._status, matcher
2835 2835 )
2836 2836 # amend should abort if commitsubrepos is enabled
2837 2837 assert not commitsubs
2838 2838 if subs:
2839 2839 subrepoutil.writestate(repo, newsubstate)
2840 2840
2841 2841 ms = mergestatemod.mergestate.read(repo)
2842 2842 mergeutil.checkunresolved(ms)
2843 2843
2844 2844 filestoamend = {f for f in wctx.files() if matcher(f)}
2845 2845
2846 2846 changes = len(filestoamend) > 0
2847 2847 if changes:
2848 2848 # Recompute copies (avoid recording a -> b -> a)
2849 2849 copied = copies.pathcopies(base, wctx, matcher)
2850 2850 if old.p2:
2851 2851 copied.update(copies.pathcopies(old.p2(), wctx, matcher))
2852 2852
2853 2853 # Prune files which were reverted by the updates: if old
2854 2854 # introduced file X and the file was renamed in the working
2855 2855 # copy, then those two files are the same and
2856 2856 # we can discard X from our list of files. Likewise if X
2857 2857 # was removed, it's no longer relevant. If X is missing (aka
2858 2858 # deleted), old X must be preserved.
2859 2859 files.update(filestoamend)
2860 2860 files = [
2861 2861 f
2862 2862 for f in files
2863 2863 if (f not in filestoamend or not samefile(f, wctx, base))
2864 2864 ]
2865 2865
2866 2866 def filectxfn(repo, ctx_, path):
2867 2867 try:
2868 2868 # If the file being considered is not amongst the files
2869 2869 # to be amended, we should return the file context from the
2870 2870 # old changeset. This avoids issues when only some files in
2871 2871 # the working copy are being amended but there are also
2872 2872 # changes to other files from the old changeset.
2873 2873 if path not in filestoamend:
2874 2874 return old.filectx(path)
2875 2875
2876 2876 # Return None for removed files.
2877 2877 if path in wctx.removed():
2878 2878 return None
2879 2879
2880 2880 fctx = wctx[path]
2881 2881 flags = fctx.flags()
2882 2882 mctx = context.memfilectx(
2883 2883 repo,
2884 2884 ctx_,
2885 2885 fctx.path(),
2886 2886 fctx.data(),
2887 2887 islink=b'l' in flags,
2888 2888 isexec=b'x' in flags,
2889 2889 copysource=copied.get(path),
2890 2890 )
2891 2891 return mctx
2892 2892 except KeyError:
2893 2893 return None
2894 2894
2895 2895 else:
2896 2896 ui.note(_(b'copying changeset %s to %s\n') % (old, base))
2897 2897
2898 2898 # Use version of files as in the old cset
2899 2899 def filectxfn(repo, ctx_, path):
2900 2900 try:
2901 2901 return old.filectx(path)
2902 2902 except KeyError:
2903 2903 return None
2904 2904
2905 2905 # See if we got a message from -m or -l, if not, open the editor with
2906 2906 # the message of the changeset to amend.
2907 2907 message = logmessage(ui, opts)
2908 2908
2909 2909 editform = mergeeditform(old, b'commit.amend')
2910 2910
2911 2911 if not message:
2912 2912 message = old.description()
2913 2913 # Default if message isn't provided and --edit is not passed is to
2914 2914 # invoke editor, but allow --no-edit. If somehow we don't have any
2915 2915 # description, let's always start the editor.
2916 2916 doedit = not message or opts.get(b'edit') in [True, None]
2917 2917 else:
2918 2918 # Default if message is provided is to not invoke editor, but allow
2919 2919 # --edit.
2920 2920 doedit = opts.get(b'edit') is True
2921 2921 editor = getcommiteditor(edit=doedit, editform=editform)
2922 2922
2923 2923 pureextra = extra.copy()
2924 2924 extra[b'amend_source'] = old.hex()
2925 2925
2926 2926 new = context.memctx(
2927 2927 repo,
2928 2928 parents=[base.node(), old.p2().node()],
2929 2929 text=message,
2930 2930 files=files,
2931 2931 filectxfn=filectxfn,
2932 2932 user=user,
2933 2933 date=date,
2934 2934 extra=extra,
2935 2935 editor=editor,
2936 2936 )
2937 2937
2938 2938 newdesc = changelog.stripdesc(new.description())
2939 2939 if (
2940 2940 (not changes)
2941 2941 and newdesc == old.description()
2942 2942 and user == old.user()
2943 2943 and (date == old.date() or datemaydiffer)
2944 2944 and pureextra == old.extra()
2945 2945 ):
2946 2946 # nothing changed. continuing here would create a new node
2947 2947 # anyway because of the amend_source noise.
2948 2948 #
2949 2949 # This not what we expect from amend.
2950 2950 return old.node()
2951 2951
2952 2952 commitphase = None
2953 2953 if opts.get(b'secret'):
2954 2954 commitphase = phases.secret
2955 2955 newid = repo.commitctx(new)
2956 2956 ms.reset()
2957 2957
2958 2958 # Reroute the working copy parent to the new changeset
2959 2959 repo.setparents(newid, nullid)
2960 2960 mapping = {old.node(): (newid,)}
2961 2961 obsmetadata = None
2962 2962 if opts.get(b'note'):
2963 2963 obsmetadata = {b'note': encoding.fromlocal(opts[b'note'])}
2964 2964 backup = ui.configbool(b'rewrite', b'backup-bundle')
2965 2965 scmutil.cleanupnodes(
2966 2966 repo,
2967 2967 mapping,
2968 2968 b'amend',
2969 2969 metadata=obsmetadata,
2970 2970 fixphase=True,
2971 2971 targetphase=commitphase,
2972 2972 backup=backup,
2973 2973 )
2974 2974
2975 2975 # Fixing the dirstate because localrepo.commitctx does not update
2976 2976 # it. This is rather convenient because we did not need to update
2977 2977 # the dirstate for all the files in the new commit which commitctx
2978 2978 # could have done if it updated the dirstate. Now, we can
2979 2979 # selectively update the dirstate only for the amended files.
2980 2980 dirstate = repo.dirstate
2981 2981
2982 2982 # Update the state of the files which were added and modified in the
2983 2983 # amend to "normal" in the dirstate. We need to use "normallookup" since
2984 2984 # the files may have changed since the command started; using "normal"
2985 2985 # would mark them as clean but with uncommitted contents.
2986 2986 normalfiles = set(wctx.modified() + wctx.added()) & filestoamend
2987 2987 for f in normalfiles:
2988 2988 dirstate.normallookup(f)
2989 2989
2990 2990 # Update the state of files which were removed in the amend
2991 2991 # to "removed" in the dirstate.
2992 2992 removedfiles = set(wctx.removed()) & filestoamend
2993 2993 for f in removedfiles:
2994 2994 dirstate.drop(f)
2995 2995
2996 2996 return newid
2997 2997
2998 2998
2999 2999 def commiteditor(repo, ctx, subs, editform=b''):
3000 3000 if ctx.description():
3001 3001 return ctx.description()
3002 3002 return commitforceeditor(
3003 3003 repo, ctx, subs, editform=editform, unchangedmessagedetection=True
3004 3004 )
3005 3005
3006 3006
3007 3007 def commitforceeditor(
3008 3008 repo,
3009 3009 ctx,
3010 3010 subs,
3011 3011 finishdesc=None,
3012 3012 extramsg=None,
3013 3013 editform=b'',
3014 3014 unchangedmessagedetection=False,
3015 3015 ):
3016 3016 if not extramsg:
3017 3017 extramsg = _(b"Leave message empty to abort commit.")
3018 3018
3019 3019 forms = [e for e in editform.split(b'.') if e]
3020 3020 forms.insert(0, b'changeset')
3021 3021 templatetext = None
3022 3022 while forms:
3023 3023 ref = b'.'.join(forms)
3024 3024 if repo.ui.config(b'committemplate', ref):
3025 3025 templatetext = committext = buildcommittemplate(
3026 3026 repo, ctx, subs, extramsg, ref
3027 3027 )
3028 3028 break
3029 3029 forms.pop()
3030 3030 else:
3031 3031 committext = buildcommittext(repo, ctx, subs, extramsg)
3032 3032
3033 3033 # run editor in the repository root
3034 3034 olddir = encoding.getcwd()
3035 3035 os.chdir(repo.root)
3036 3036
3037 3037 # make in-memory changes visible to external process
3038 3038 tr = repo.currenttransaction()
3039 3039 repo.dirstate.write(tr)
3040 3040 pending = tr and tr.writepending() and repo.root
3041 3041
3042 3042 editortext = repo.ui.edit(
3043 3043 committext,
3044 3044 ctx.user(),
3045 3045 ctx.extra(),
3046 3046 editform=editform,
3047 3047 pending=pending,
3048 3048 repopath=repo.path,
3049 3049 action=b'commit',
3050 3050 )
3051 3051 text = editortext
3052 3052
3053 3053 # strip away anything below this special string (used for editors that want
3054 3054 # to display the diff)
3055 3055 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3056 3056 if stripbelow:
3057 3057 text = text[: stripbelow.start()]
3058 3058
3059 3059 text = re.sub(b"(?m)^HG:.*(\n|$)", b"", text)
3060 3060 os.chdir(olddir)
3061 3061
3062 3062 if finishdesc:
3063 3063 text = finishdesc(text)
3064 3064 if not text.strip():
3065 3065 raise error.InputError(_(b"empty commit message"))
3066 3066 if unchangedmessagedetection and editortext == templatetext:
3067 3067 raise error.InputError(_(b"commit message unchanged"))
3068 3068
3069 3069 return text
3070 3070
3071 3071
3072 3072 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3073 3073 ui = repo.ui
3074 3074 spec = formatter.reference_templatespec(ref)
3075 3075 t = logcmdutil.changesettemplater(ui, repo, spec)
3076 3076 t.t.cache.update(
3077 3077 (k, templater.unquotestring(v))
3078 3078 for k, v in repo.ui.configitems(b'committemplate')
3079 3079 )
3080 3080
3081 3081 if not extramsg:
3082 3082 extramsg = b'' # ensure that extramsg is string
3083 3083
3084 3084 ui.pushbuffer()
3085 3085 t.show(ctx, extramsg=extramsg)
3086 3086 return ui.popbuffer()
3087 3087
3088 3088
3089 3089 def hgprefix(msg):
3090 3090 return b"\n".join([b"HG: %s" % a for a in msg.split(b"\n") if a])
3091 3091
3092 3092
3093 3093 def buildcommittext(repo, ctx, subs, extramsg):
3094 3094 edittext = []
3095 3095 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3096 3096 if ctx.description():
3097 3097 edittext.append(ctx.description())
3098 3098 edittext.append(b"")
3099 3099 edittext.append(b"") # Empty line between message and comments.
3100 3100 edittext.append(
3101 3101 hgprefix(
3102 3102 _(
3103 3103 b"Enter commit message."
3104 3104 b" Lines beginning with 'HG:' are removed."
3105 3105 )
3106 3106 )
3107 3107 )
3108 3108 edittext.append(hgprefix(extramsg))
3109 3109 edittext.append(b"HG: --")
3110 3110 edittext.append(hgprefix(_(b"user: %s") % ctx.user()))
3111 3111 if ctx.p2():
3112 3112 edittext.append(hgprefix(_(b"branch merge")))
3113 3113 if ctx.branch():
3114 3114 edittext.append(hgprefix(_(b"branch '%s'") % ctx.branch()))
3115 3115 if bookmarks.isactivewdirparent(repo):
3116 3116 edittext.append(hgprefix(_(b"bookmark '%s'") % repo._activebookmark))
3117 3117 edittext.extend([hgprefix(_(b"subrepo %s") % s) for s in subs])
3118 3118 edittext.extend([hgprefix(_(b"added %s") % f) for f in added])
3119 3119 edittext.extend([hgprefix(_(b"changed %s") % f) for f in modified])
3120 3120 edittext.extend([hgprefix(_(b"removed %s") % f) for f in removed])
3121 3121 if not added and not modified and not removed:
3122 3122 edittext.append(hgprefix(_(b"no files changed")))
3123 3123 edittext.append(b"")
3124 3124
3125 3125 return b"\n".join(edittext)
3126 3126
3127 3127
3128 3128 def commitstatus(repo, node, branch, bheads=None, tip=None, opts=None):
3129 3129 if opts is None:
3130 3130 opts = {}
3131 3131 ctx = repo[node]
3132 3132 parents = ctx.parents()
3133 3133
3134 3134 if tip is not None and repo.changelog.tip() == tip:
3135 3135 # avoid reporting something like "committed new head" when
3136 3136 # recommitting old changesets, and issue a helpful warning
3137 3137 # for most instances
3138 3138 repo.ui.warn(_("warning: commit already existed in the repository!\n"))
3139 3139 elif (
3140 3140 not opts.get(b'amend')
3141 3141 and bheads
3142 3142 and node not in bheads
3143 3143 and not any(
3144 3144 p.node() in bheads and p.branch() == branch for p in parents
3145 3145 )
3146 3146 ):
3147 3147 repo.ui.status(_(b'created new head\n'))
3148 3148 # The message is not printed for initial roots. For the other
3149 3149 # changesets, it is printed in the following situations:
3150 3150 #
3151 3151 # Par column: for the 2 parents with ...
3152 3152 # N: null or no parent
3153 3153 # B: parent is on another named branch
3154 3154 # C: parent is a regular non head changeset
3155 3155 # H: parent was a branch head of the current branch
3156 3156 # Msg column: whether we print "created new head" message
3157 3157 # In the following, it is assumed that there already exists some
3158 3158 # initial branch heads of the current branch, otherwise nothing is
3159 3159 # printed anyway.
3160 3160 #
3161 3161 # Par Msg Comment
3162 3162 # N N y additional topo root
3163 3163 #
3164 3164 # B N y additional branch root
3165 3165 # C N y additional topo head
3166 3166 # H N n usual case
3167 3167 #
3168 3168 # B B y weird additional branch root
3169 3169 # C B y branch merge
3170 3170 # H B n merge with named branch
3171 3171 #
3172 3172 # C C y additional head from merge
3173 3173 # C H n merge with a head
3174 3174 #
3175 3175 # H H n head merge: head count decreases
3176 3176
3177 3177 if not opts.get(b'close_branch'):
3178 3178 for r in parents:
3179 3179 if r.closesbranch() and r.branch() == branch:
3180 3180 repo.ui.status(
3181 3181 _(b'reopening closed branch head %d\n') % r.rev()
3182 3182 )
3183 3183
3184 3184 if repo.ui.debugflag:
3185 3185 repo.ui.write(
3186 3186 _(b'committed changeset %d:%s\n') % (ctx.rev(), ctx.hex())
3187 3187 )
3188 3188 elif repo.ui.verbose:
3189 3189 repo.ui.write(_(b'committed changeset %d:%s\n') % (ctx.rev(), ctx))
3190 3190
3191 3191
3192 3192 def postcommitstatus(repo, pats, opts):
3193 3193 return repo.status(match=scmutil.match(repo[None], pats, opts))
3194 3194
3195 3195
3196 3196 def revert(ui, repo, ctx, *pats, **opts):
3197 3197 opts = pycompat.byteskwargs(opts)
3198 3198 parent, p2 = repo.dirstate.parents()
3199 3199 node = ctx.node()
3200 3200
3201 3201 mf = ctx.manifest()
3202 3202 if node == p2:
3203 3203 parent = p2
3204 3204
3205 3205 # need all matching names in dirstate and manifest of target rev,
3206 3206 # so have to walk both. do not print errors if files exist in one
3207 3207 # but not other. in both cases, filesets should be evaluated against
3208 3208 # workingctx to get consistent result (issue4497). this means 'set:**'
3209 3209 # cannot be used to select missing files from target rev.
3210 3210
3211 3211 # `names` is a mapping for all elements in working copy and target revision
3212 3212 # The mapping is in the form:
3213 3213 # <abs path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3214 3214 names = {}
3215 3215 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
3216 3216
3217 3217 with repo.wlock():
3218 3218 ## filling of the `names` mapping
3219 3219 # walk dirstate to fill `names`
3220 3220
3221 3221 interactive = opts.get(b'interactive', False)
3222 3222 wctx = repo[None]
3223 3223 m = scmutil.match(wctx, pats, opts)
3224 3224
3225 3225 # we'll need this later
3226 3226 targetsubs = sorted(s for s in wctx.substate if m(s))
3227 3227
3228 3228 if not m.always():
3229 3229 matcher = matchmod.badmatch(m, lambda x, y: False)
3230 3230 for abs in wctx.walk(matcher):
3231 3231 names[abs] = m.exact(abs)
3232 3232
3233 3233 # walk target manifest to fill `names`
3234 3234
3235 3235 def badfn(path, msg):
3236 3236 if path in names:
3237 3237 return
3238 3238 if path in ctx.substate:
3239 3239 return
3240 3240 path_ = path + b'/'
3241 3241 for f in names:
3242 3242 if f.startswith(path_):
3243 3243 return
3244 3244 ui.warn(b"%s: %s\n" % (uipathfn(path), msg))
3245 3245
3246 3246 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3247 3247 if abs not in names:
3248 3248 names[abs] = m.exact(abs)
3249 3249
3250 3250 # Find status of all file in `names`.
3251 3251 m = scmutil.matchfiles(repo, names)
3252 3252
3253 3253 changes = repo.status(
3254 3254 node1=node, match=m, unknown=True, ignored=True, clean=True
3255 3255 )
3256 3256 else:
3257 3257 changes = repo.status(node1=node, match=m)
3258 3258 for kind in changes:
3259 3259 for abs in kind:
3260 3260 names[abs] = m.exact(abs)
3261 3261
3262 3262 m = scmutil.matchfiles(repo, names)
3263 3263
3264 3264 modified = set(changes.modified)
3265 3265 added = set(changes.added)
3266 3266 removed = set(changes.removed)
3267 3267 _deleted = set(changes.deleted)
3268 3268 unknown = set(changes.unknown)
3269 3269 unknown.update(changes.ignored)
3270 3270 clean = set(changes.clean)
3271 3271 modadded = set()
3272 3272
3273 3273 # We need to account for the state of the file in the dirstate,
3274 3274 # even when we revert against something else than parent. This will
3275 3275 # slightly alter the behavior of revert (doing back up or not, delete
3276 3276 # or just forget etc).
3277 3277 if parent == node:
3278 3278 dsmodified = modified
3279 3279 dsadded = added
3280 3280 dsremoved = removed
3281 3281 # store all local modifications, useful later for rename detection
3282 3282 localchanges = dsmodified | dsadded
3283 3283 modified, added, removed = set(), set(), set()
3284 3284 else:
3285 3285 changes = repo.status(node1=parent, match=m)
3286 3286 dsmodified = set(changes.modified)
3287 3287 dsadded = set(changes.added)
3288 3288 dsremoved = set(changes.removed)
3289 3289 # store all local modifications, useful later for rename detection
3290 3290 localchanges = dsmodified | dsadded
3291 3291
3292 3292 # only take into account for removes between wc and target
3293 3293 clean |= dsremoved - removed
3294 3294 dsremoved &= removed
3295 3295 # distinct between dirstate remove and other
3296 3296 removed -= dsremoved
3297 3297
3298 3298 modadded = added & dsmodified
3299 3299 added -= modadded
3300 3300
3301 3301 # tell newly modified apart.
3302 3302 dsmodified &= modified
3303 3303 dsmodified |= modified & dsadded # dirstate added may need backup
3304 3304 modified -= dsmodified
3305 3305
3306 3306 # We need to wait for some post-processing to update this set
3307 3307 # before making the distinction. The dirstate will be used for
3308 3308 # that purpose.
3309 3309 dsadded = added
3310 3310
3311 3311 # in case of merge, files that are actually added can be reported as
3312 3312 # modified, we need to post process the result
3313 3313 if p2 != nullid:
3314 3314 mergeadd = set(dsmodified)
3315 3315 for path in dsmodified:
3316 3316 if path in mf:
3317 3317 mergeadd.remove(path)
3318 3318 dsadded |= mergeadd
3319 3319 dsmodified -= mergeadd
3320 3320
3321 3321 # if f is a rename, update `names` to also revert the source
3322 3322 for f in localchanges:
3323 3323 src = repo.dirstate.copied(f)
3324 3324 # XXX should we check for rename down to target node?
3325 3325 if src and src not in names and repo.dirstate[src] == b'r':
3326 3326 dsremoved.add(src)
3327 3327 names[src] = True
3328 3328
3329 3329 # determine the exact nature of the deleted changesets
3330 3330 deladded = set(_deleted)
3331 3331 for path in _deleted:
3332 3332 if path in mf:
3333 3333 deladded.remove(path)
3334 3334 deleted = _deleted - deladded
3335 3335
3336 3336 # distinguish between file to forget and the other
3337 3337 added = set()
3338 3338 for abs in dsadded:
3339 3339 if repo.dirstate[abs] != b'a':
3340 3340 added.add(abs)
3341 3341 dsadded -= added
3342 3342
3343 3343 for abs in deladded:
3344 3344 if repo.dirstate[abs] == b'a':
3345 3345 dsadded.add(abs)
3346 3346 deladded -= dsadded
3347 3347
3348 3348 # For files marked as removed, we check if an unknown file is present at
3349 3349 # the same path. If a such file exists it may need to be backed up.
3350 3350 # Making the distinction at this stage helps have simpler backup
3351 3351 # logic.
3352 3352 removunk = set()
3353 3353 for abs in removed:
3354 3354 target = repo.wjoin(abs)
3355 3355 if os.path.lexists(target):
3356 3356 removunk.add(abs)
3357 3357 removed -= removunk
3358 3358
3359 3359 dsremovunk = set()
3360 3360 for abs in dsremoved:
3361 3361 target = repo.wjoin(abs)
3362 3362 if os.path.lexists(target):
3363 3363 dsremovunk.add(abs)
3364 3364 dsremoved -= dsremovunk
3365 3365
3366 3366 # action to be actually performed by revert
3367 3367 # (<list of file>, message>) tuple
3368 3368 actions = {
3369 3369 b'revert': ([], _(b'reverting %s\n')),
3370 3370 b'add': ([], _(b'adding %s\n')),
3371 3371 b'remove': ([], _(b'removing %s\n')),
3372 3372 b'drop': ([], _(b'removing %s\n')),
3373 3373 b'forget': ([], _(b'forgetting %s\n')),
3374 3374 b'undelete': ([], _(b'undeleting %s\n')),
3375 3375 b'noop': (None, _(b'no changes needed to %s\n')),
3376 3376 b'unknown': (None, _(b'file not managed: %s\n')),
3377 3377 }
3378 3378
3379 3379 # "constant" that convey the backup strategy.
3380 3380 # All set to `discard` if `no-backup` is set do avoid checking
3381 3381 # no_backup lower in the code.
3382 3382 # These values are ordered for comparison purposes
3383 3383 backupinteractive = 3 # do backup if interactively modified
3384 3384 backup = 2 # unconditionally do backup
3385 3385 check = 1 # check if the existing file differs from target
3386 3386 discard = 0 # never do backup
3387 3387 if opts.get(b'no_backup'):
3388 3388 backupinteractive = backup = check = discard
3389 3389 if interactive:
3390 3390 dsmodifiedbackup = backupinteractive
3391 3391 else:
3392 3392 dsmodifiedbackup = backup
3393 3393 tobackup = set()
3394 3394
3395 3395 backupanddel = actions[b'remove']
3396 3396 if not opts.get(b'no_backup'):
3397 3397 backupanddel = actions[b'drop']
3398 3398
3399 3399 disptable = (
3400 3400 # dispatch table:
3401 3401 # file state
3402 3402 # action
3403 3403 # make backup
3404 3404 ## Sets that results that will change file on disk
3405 3405 # Modified compared to target, no local change
3406 3406 (modified, actions[b'revert'], discard),
3407 3407 # Modified compared to target, but local file is deleted
3408 3408 (deleted, actions[b'revert'], discard),
3409 3409 # Modified compared to target, local change
3410 3410 (dsmodified, actions[b'revert'], dsmodifiedbackup),
3411 3411 # Added since target
3412 3412 (added, actions[b'remove'], discard),
3413 3413 # Added in working directory
3414 3414 (dsadded, actions[b'forget'], discard),
3415 3415 # Added since target, have local modification
3416 3416 (modadded, backupanddel, backup),
3417 3417 # Added since target but file is missing in working directory
3418 3418 (deladded, actions[b'drop'], discard),
3419 3419 # Removed since target, before working copy parent
3420 3420 (removed, actions[b'add'], discard),
3421 3421 # Same as `removed` but an unknown file exists at the same path
3422 3422 (removunk, actions[b'add'], check),
3423 3423 # Removed since targe, marked as such in working copy parent
3424 3424 (dsremoved, actions[b'undelete'], discard),
3425 3425 # Same as `dsremoved` but an unknown file exists at the same path
3426 3426 (dsremovunk, actions[b'undelete'], check),
3427 3427 ## the following sets does not result in any file changes
3428 3428 # File with no modification
3429 3429 (clean, actions[b'noop'], discard),
3430 3430 # Existing file, not tracked anywhere
3431 3431 (unknown, actions[b'unknown'], discard),
3432 3432 )
3433 3433
3434 3434 for abs, exact in sorted(names.items()):
3435 3435 # target file to be touch on disk (relative to cwd)
3436 3436 target = repo.wjoin(abs)
3437 3437 # search the entry in the dispatch table.
3438 3438 # if the file is in any of these sets, it was touched in the working
3439 3439 # directory parent and we are sure it needs to be reverted.
3440 3440 for table, (xlist, msg), dobackup in disptable:
3441 3441 if abs not in table:
3442 3442 continue
3443 3443 if xlist is not None:
3444 3444 xlist.append(abs)
3445 3445 if dobackup:
3446 3446 # If in interactive mode, don't automatically create
3447 3447 # .orig files (issue4793)
3448 3448 if dobackup == backupinteractive:
3449 3449 tobackup.add(abs)
3450 3450 elif backup <= dobackup or wctx[abs].cmp(ctx[abs]):
3451 3451 absbakname = scmutil.backuppath(ui, repo, abs)
3452 3452 bakname = os.path.relpath(
3453 3453 absbakname, start=repo.root
3454 3454 )
3455 3455 ui.note(
3456 3456 _(b'saving current version of %s as %s\n')
3457 3457 % (uipathfn(abs), uipathfn(bakname))
3458 3458 )
3459 3459 if not opts.get(b'dry_run'):
3460 3460 if interactive:
3461 3461 util.copyfile(target, absbakname)
3462 3462 else:
3463 3463 util.rename(target, absbakname)
3464 3464 if opts.get(b'dry_run'):
3465 3465 if ui.verbose or not exact:
3466 3466 ui.status(msg % uipathfn(abs))
3467 3467 elif exact:
3468 3468 ui.warn(msg % uipathfn(abs))
3469 3469 break
3470 3470
3471 3471 if not opts.get(b'dry_run'):
3472 3472 needdata = (b'revert', b'add', b'undelete')
3473 3473 oplist = [actions[name][0] for name in needdata]
3474 3474 prefetch = scmutil.prefetchfiles
3475 3475 matchfiles = scmutil.matchfiles(
3476 3476 repo, [f for sublist in oplist for f in sublist]
3477 3477 )
3478 3478 prefetch(
3479 3479 repo, [(ctx.rev(), matchfiles)],
3480 3480 )
3481 3481 match = scmutil.match(repo[None], pats)
3482 3482 _performrevert(
3483 3483 repo,
3484 3484 ctx,
3485 3485 names,
3486 3486 uipathfn,
3487 3487 actions,
3488 3488 match,
3489 3489 interactive,
3490 3490 tobackup,
3491 3491 )
3492 3492
3493 3493 if targetsubs:
3494 3494 # Revert the subrepos on the revert list
3495 3495 for sub in targetsubs:
3496 3496 try:
3497 3497 wctx.sub(sub).revert(
3498 3498 ctx.substate[sub], *pats, **pycompat.strkwargs(opts)
3499 3499 )
3500 3500 except KeyError:
3501 3501 raise error.Abort(
3502 3502 b"subrepository '%s' does not exist in %s!"
3503 3503 % (sub, short(ctx.node()))
3504 3504 )
3505 3505
3506 3506
3507 3507 def _performrevert(
3508 3508 repo,
3509 3509 ctx,
3510 3510 names,
3511 3511 uipathfn,
3512 3512 actions,
3513 3513 match,
3514 3514 interactive=False,
3515 3515 tobackup=None,
3516 3516 ):
3517 3517 """function that actually perform all the actions computed for revert
3518 3518
3519 3519 This is an independent function to let extension to plug in and react to
3520 3520 the imminent revert.
3521 3521
3522 3522 Make sure you have the working directory locked when calling this function.
3523 3523 """
3524 3524 parent, p2 = repo.dirstate.parents()
3525 3525 node = ctx.node()
3526 3526 excluded_files = []
3527 3527
3528 3528 def checkout(f):
3529 3529 fc = ctx[f]
3530 3530 repo.wwrite(f, fc.data(), fc.flags())
3531 3531
3532 3532 def doremove(f):
3533 3533 try:
3534 3534 rmdir = repo.ui.configbool(b'experimental', b'removeemptydirs')
3535 3535 repo.wvfs.unlinkpath(f, rmdir=rmdir)
3536 3536 except OSError:
3537 3537 pass
3538 3538 repo.dirstate.remove(f)
3539 3539
3540 3540 def prntstatusmsg(action, f):
3541 3541 exact = names[f]
3542 3542 if repo.ui.verbose or not exact:
3543 3543 repo.ui.status(actions[action][1] % uipathfn(f))
3544 3544
3545 3545 audit_path = pathutil.pathauditor(repo.root, cached=True)
3546 3546 for f in actions[b'forget'][0]:
3547 3547 if interactive:
3548 3548 choice = repo.ui.promptchoice(
3549 3549 _(b"forget added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3550 3550 )
3551 3551 if choice == 0:
3552 3552 prntstatusmsg(b'forget', f)
3553 3553 repo.dirstate.drop(f)
3554 3554 else:
3555 3555 excluded_files.append(f)
3556 3556 else:
3557 3557 prntstatusmsg(b'forget', f)
3558 3558 repo.dirstate.drop(f)
3559 3559 for f in actions[b'remove'][0]:
3560 3560 audit_path(f)
3561 3561 if interactive:
3562 3562 choice = repo.ui.promptchoice(
3563 3563 _(b"remove added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)
3564 3564 )
3565 3565 if choice == 0:
3566 3566 prntstatusmsg(b'remove', f)
3567 3567 doremove(f)
3568 3568 else:
3569 3569 excluded_files.append(f)
3570 3570 else:
3571 3571 prntstatusmsg(b'remove', f)
3572 3572 doremove(f)
3573 3573 for f in actions[b'drop'][0]:
3574 3574 audit_path(f)
3575 3575 prntstatusmsg(b'drop', f)
3576 3576 repo.dirstate.remove(f)
3577 3577
3578 3578 normal = None
3579 3579 if node == parent:
3580 3580 # We're reverting to our parent. If possible, we'd like status
3581 3581 # to report the file as clean. We have to use normallookup for
3582 3582 # merges to avoid losing information about merged/dirty files.
3583 3583 if p2 != nullid:
3584 3584 normal = repo.dirstate.normallookup
3585 3585 else:
3586 3586 normal = repo.dirstate.normal
3587 3587
3588 3588 newlyaddedandmodifiedfiles = set()
3589 3589 if interactive:
3590 3590 # Prompt the user for changes to revert
3591 3591 torevert = [f for f in actions[b'revert'][0] if f not in excluded_files]
3592 3592 m = scmutil.matchfiles(repo, torevert)
3593 3593 diffopts = patch.difffeatureopts(
3594 3594 repo.ui,
3595 3595 whitespace=True,
3596 3596 section=b'commands',
3597 3597 configprefix=b'revert.interactive.',
3598 3598 )
3599 3599 diffopts.nodates = True
3600 3600 diffopts.git = True
3601 3601 operation = b'apply'
3602 3602 if node == parent:
3603 3603 if repo.ui.configbool(
3604 3604 b'experimental', b'revert.interactive.select-to-keep'
3605 3605 ):
3606 3606 operation = b'keep'
3607 3607 else:
3608 3608 operation = b'discard'
3609 3609
3610 3610 if operation == b'apply':
3611 3611 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3612 3612 else:
3613 3613 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3614 3614 originalchunks = patch.parsepatch(diff)
3615 3615
3616 3616 try:
3617 3617
3618 3618 chunks, opts = recordfilter(
3619 3619 repo.ui, originalchunks, match, operation=operation
3620 3620 )
3621 3621 if operation == b'discard':
3622 3622 chunks = patch.reversehunks(chunks)
3623 3623
3624 3624 except error.PatchError as err:
3625 3625 raise error.Abort(_(b'error parsing patch: %s') % err)
3626 3626
3627 3627 # FIXME: when doing an interactive revert of a copy, there's no way of
3628 3628 # performing a partial revert of the added file, the only option is
3629 3629 # "remove added file <name> (Yn)?", so we don't need to worry about the
3630 3630 # alsorestore value. Ideally we'd be able to partially revert
3631 3631 # copied/renamed files.
3632 3632 newlyaddedandmodifiedfiles, unusedalsorestore = newandmodified(
3633 3633 chunks, originalchunks
3634 3634 )
3635 3635 if tobackup is None:
3636 3636 tobackup = set()
3637 3637 # Apply changes
3638 3638 fp = stringio()
3639 3639 # chunks are serialized per file, but files aren't sorted
3640 3640 for f in sorted({c.header.filename() for c in chunks if ishunk(c)}):
3641 3641 prntstatusmsg(b'revert', f)
3642 3642 files = set()
3643 3643 for c in chunks:
3644 3644 if ishunk(c):
3645 3645 abs = c.header.filename()
3646 3646 # Create a backup file only if this hunk should be backed up
3647 3647 if c.header.filename() in tobackup:
3648 3648 target = repo.wjoin(abs)
3649 3649 bakname = scmutil.backuppath(repo.ui, repo, abs)
3650 3650 util.copyfile(target, bakname)
3651 3651 tobackup.remove(abs)
3652 3652 if abs not in files:
3653 3653 files.add(abs)
3654 3654 if operation == b'keep':
3655 3655 checkout(abs)
3656 3656 c.write(fp)
3657 3657 dopatch = fp.tell()
3658 3658 fp.seek(0)
3659 3659 if dopatch:
3660 3660 try:
3661 3661 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3662 3662 except error.PatchError as err:
3663 3663 raise error.Abort(pycompat.bytestr(err))
3664 3664 del fp
3665 3665 else:
3666 3666 for f in actions[b'revert'][0]:
3667 3667 prntstatusmsg(b'revert', f)
3668 3668 checkout(f)
3669 3669 if normal:
3670 3670 normal(f)
3671 3671
3672 3672 for f in actions[b'add'][0]:
3673 3673 # Don't checkout modified files, they are already created by the diff
3674 3674 if f not in newlyaddedandmodifiedfiles:
3675 3675 prntstatusmsg(b'add', f)
3676 3676 checkout(f)
3677 3677 repo.dirstate.add(f)
3678 3678
3679 3679 normal = repo.dirstate.normallookup
3680 3680 if node == parent and p2 == nullid:
3681 3681 normal = repo.dirstate.normal
3682 3682 for f in actions[b'undelete'][0]:
3683 3683 if interactive:
3684 3684 choice = repo.ui.promptchoice(
3685 3685 _(b"add back removed file %s (Yn)?$$ &Yes $$ &No") % f
3686 3686 )
3687 3687 if choice == 0:
3688 3688 prntstatusmsg(b'undelete', f)
3689 3689 checkout(f)
3690 3690 normal(f)
3691 3691 else:
3692 3692 excluded_files.append(f)
3693 3693 else:
3694 3694 prntstatusmsg(b'undelete', f)
3695 3695 checkout(f)
3696 3696 normal(f)
3697 3697
3698 3698 copied = copies.pathcopies(repo[parent], ctx)
3699 3699
3700 3700 for f in (
3701 3701 actions[b'add'][0] + actions[b'undelete'][0] + actions[b'revert'][0]
3702 3702 ):
3703 3703 if f in copied:
3704 3704 repo.dirstate.copy(copied[f], f)
3705 3705
3706 3706
3707 3707 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3708 3708 # commands.outgoing. "missing" is "missing" of the result of
3709 3709 # "findcommonoutgoing()"
3710 3710 outgoinghooks = util.hooks()
3711 3711
3712 3712 # a list of (ui, repo) functions called by commands.summary
3713 3713 summaryhooks = util.hooks()
3714 3714
3715 3715 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3716 3716 #
3717 3717 # functions should return tuple of booleans below, if 'changes' is None:
3718 3718 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3719 3719 #
3720 3720 # otherwise, 'changes' is a tuple of tuples below:
3721 3721 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3722 3722 # - (desturl, destbranch, destpeer, outgoing)
3723 3723 summaryremotehooks = util.hooks()
3724 3724
3725 3725
3726 3726 def checkunfinished(repo, commit=False, skipmerge=False):
3727 3727 '''Look for an unfinished multistep operation, like graft, and abort
3728 3728 if found. It's probably good to check this right before
3729 3729 bailifchanged().
3730 3730 '''
3731 3731 # Check for non-clearable states first, so things like rebase will take
3732 3732 # precedence over update.
3733 3733 for state in statemod._unfinishedstates:
3734 3734 if (
3735 3735 state._clearable
3736 3736 or (commit and state._allowcommit)
3737 3737 or state._reportonly
3738 3738 ):
3739 3739 continue
3740 3740 if state.isunfinished(repo):
3741 raise error.Abort(state.msg(), hint=state.hint())
3741 raise error.StateError(state.msg(), hint=state.hint())
3742 3742
3743 3743 for s in statemod._unfinishedstates:
3744 3744 if (
3745 3745 not s._clearable
3746 3746 or (commit and s._allowcommit)
3747 3747 or (s._opname == b'merge' and skipmerge)
3748 3748 or s._reportonly
3749 3749 ):
3750 3750 continue
3751 3751 if s.isunfinished(repo):
3752 raise error.Abort(s.msg(), hint=s.hint())
3752 raise error.StateError(s.msg(), hint=s.hint())
3753 3753
3754 3754
3755 3755 def clearunfinished(repo):
3756 3756 '''Check for unfinished operations (as above), and clear the ones
3757 3757 that are clearable.
3758 3758 '''
3759 3759 for state in statemod._unfinishedstates:
3760 3760 if state._reportonly:
3761 3761 continue
3762 3762 if not state._clearable and state.isunfinished(repo):
3763 raise error.Abort(state.msg(), hint=state.hint())
3763 raise error.StateError(state.msg(), hint=state.hint())
3764 3764
3765 3765 for s in statemod._unfinishedstates:
3766 3766 if s._opname == b'merge' or state._reportonly:
3767 3767 continue
3768 3768 if s._clearable and s.isunfinished(repo):
3769 3769 util.unlink(repo.vfs.join(s._fname))
3770 3770
3771 3771
3772 3772 def getunfinishedstate(repo):
3773 3773 ''' Checks for unfinished operations and returns statecheck object
3774 3774 for it'''
3775 3775 for state in statemod._unfinishedstates:
3776 3776 if state.isunfinished(repo):
3777 3777 return state
3778 3778 return None
3779 3779
3780 3780
3781 3781 def howtocontinue(repo):
3782 3782 '''Check for an unfinished operation and return the command to finish
3783 3783 it.
3784 3784
3785 3785 statemod._unfinishedstates list is checked for an unfinished operation
3786 3786 and the corresponding message to finish it is generated if a method to
3787 3787 continue is supported by the operation.
3788 3788
3789 3789 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3790 3790 a boolean.
3791 3791 '''
3792 3792 contmsg = _(b"continue: %s")
3793 3793 for state in statemod._unfinishedstates:
3794 3794 if not state._continueflag:
3795 3795 continue
3796 3796 if state.isunfinished(repo):
3797 3797 return contmsg % state.continuemsg(), True
3798 3798 if repo[None].dirty(missing=True, merge=False, branch=False):
3799 3799 return contmsg % _(b"hg commit"), False
3800 3800 return None, None
3801 3801
3802 3802
3803 3803 def checkafterresolved(repo):
3804 3804 '''Inform the user about the next action after completing hg resolve
3805 3805
3806 3806 If there's a an unfinished operation that supports continue flag,
3807 3807 howtocontinue will yield repo.ui.warn as the reporter.
3808 3808
3809 3809 Otherwise, it will yield repo.ui.note.
3810 3810 '''
3811 3811 msg, warning = howtocontinue(repo)
3812 3812 if msg is not None:
3813 3813 if warning:
3814 3814 repo.ui.warn(b"%s\n" % msg)
3815 3815 else:
3816 3816 repo.ui.note(b"%s\n" % msg)
3817 3817
3818 3818
3819 3819 def wrongtooltocontinue(repo, task):
3820 3820 '''Raise an abort suggesting how to properly continue if there is an
3821 3821 active task.
3822 3822
3823 3823 Uses howtocontinue() to find the active task.
3824 3824
3825 3825 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3826 3826 a hint.
3827 3827 '''
3828 3828 after = howtocontinue(repo)
3829 3829 hint = None
3830 3830 if after[1]:
3831 3831 hint = after[0]
3832 raise error.Abort(_(b'no %s in progress') % task, hint=hint)
3832 raise error.StateError(_(b'no %s in progress') % task, hint=hint)
3833 3833
3834 3834
3835 3835 def abortgraft(ui, repo, graftstate):
3836 3836 """abort the interrupted graft and rollbacks to the state before interrupted
3837 3837 graft"""
3838 3838 if not graftstate.exists():
3839 raise error.Abort(_(b"no interrupted graft to abort"))
3839 raise error.StateError(_(b"no interrupted graft to abort"))
3840 3840 statedata = readgraftstate(repo, graftstate)
3841 3841 newnodes = statedata.get(b'newnodes')
3842 3842 if newnodes is None:
3843 3843 # and old graft state which does not have all the data required to abort
3844 3844 # the graft
3845 3845 raise error.Abort(_(b"cannot abort using an old graftstate"))
3846 3846
3847 3847 # changeset from which graft operation was started
3848 3848 if len(newnodes) > 0:
3849 3849 startctx = repo[newnodes[0]].p1()
3850 3850 else:
3851 3851 startctx = repo[b'.']
3852 3852 # whether to strip or not
3853 3853 cleanup = False
3854 3854
3855 3855 if newnodes:
3856 3856 newnodes = [repo[r].rev() for r in newnodes]
3857 3857 cleanup = True
3858 3858 # checking that none of the newnodes turned public or is public
3859 3859 immutable = [c for c in newnodes if not repo[c].mutable()]
3860 3860 if immutable:
3861 3861 repo.ui.warn(
3862 3862 _(b"cannot clean up public changesets %s\n")
3863 3863 % b', '.join(bytes(repo[r]) for r in immutable),
3864 3864 hint=_(b"see 'hg help phases' for details"),
3865 3865 )
3866 3866 cleanup = False
3867 3867
3868 3868 # checking that no new nodes are created on top of grafted revs
3869 3869 desc = set(repo.changelog.descendants(newnodes))
3870 3870 if desc - set(newnodes):
3871 3871 repo.ui.warn(
3872 3872 _(
3873 3873 b"new changesets detected on destination "
3874 3874 b"branch, can't strip\n"
3875 3875 )
3876 3876 )
3877 3877 cleanup = False
3878 3878
3879 3879 if cleanup:
3880 3880 with repo.wlock(), repo.lock():
3881 3881 mergemod.clean_update(startctx)
3882 3882 # stripping the new nodes created
3883 3883 strippoints = [
3884 3884 c.node() for c in repo.set(b"roots(%ld)", newnodes)
3885 3885 ]
3886 3886 repair.strip(repo.ui, repo, strippoints, backup=False)
3887 3887
3888 3888 if not cleanup:
3889 3889 # we don't update to the startnode if we can't strip
3890 3890 startctx = repo[b'.']
3891 3891 mergemod.clean_update(startctx)
3892 3892
3893 3893 ui.status(_(b"graft aborted\n"))
3894 3894 ui.status(_(b"working directory is now at %s\n") % startctx.hex()[:12])
3895 3895 graftstate.delete()
3896 3896 return 0
3897 3897
3898 3898
3899 3899 def readgraftstate(repo, graftstate):
3900 3900 # type: (Any, statemod.cmdstate) -> Dict[bytes, Any]
3901 3901 """read the graft state file and return a dict of the data stored in it"""
3902 3902 try:
3903 3903 return graftstate.read()
3904 3904 except error.CorruptedState:
3905 3905 nodes = repo.vfs.read(b'graftstate').splitlines()
3906 3906 return {b'nodes': nodes}
3907 3907
3908 3908
3909 3909 def hgabortgraft(ui, repo):
3910 3910 """ abort logic for aborting graft using 'hg abort'"""
3911 3911 with repo.wlock():
3912 3912 graftstate = statemod.cmdstate(repo, b'graftstate')
3913 3913 return abortgraft(ui, repo, graftstate)
@@ -1,7706 +1,7706 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.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 from __future__ import absolute_import
9 9
10 10 import errno
11 11 import os
12 12 import re
13 13 import sys
14 14
15 15 from .i18n import _
16 16 from .node import (
17 17 hex,
18 18 nullid,
19 19 nullrev,
20 20 short,
21 21 wdirhex,
22 22 wdirrev,
23 23 )
24 24 from .pycompat import open
25 25 from . import (
26 26 archival,
27 27 bookmarks,
28 28 bundle2,
29 29 bundlecaches,
30 30 changegroup,
31 31 cmdutil,
32 32 copies,
33 33 debugcommands as debugcommandsmod,
34 34 destutil,
35 35 dirstateguard,
36 36 discovery,
37 37 encoding,
38 38 error,
39 39 exchange,
40 40 extensions,
41 41 filemerge,
42 42 formatter,
43 43 graphmod,
44 44 grep as grepmod,
45 45 hbisect,
46 46 help,
47 47 hg,
48 48 logcmdutil,
49 49 merge as mergemod,
50 50 mergestate as mergestatemod,
51 51 narrowspec,
52 52 obsolete,
53 53 obsutil,
54 54 patch,
55 55 phases,
56 56 pycompat,
57 57 rcutil,
58 58 registrar,
59 59 requirements,
60 60 revsetlang,
61 61 rewriteutil,
62 62 scmutil,
63 63 server,
64 64 shelve as shelvemod,
65 65 state as statemod,
66 66 streamclone,
67 67 tags as tagsmod,
68 68 ui as uimod,
69 69 util,
70 70 verify as verifymod,
71 71 vfs as vfsmod,
72 72 wireprotoserver,
73 73 )
74 74 from .utils import (
75 75 dateutil,
76 76 stringutil,
77 77 )
78 78
79 79 table = {}
80 80 table.update(debugcommandsmod.command._table)
81 81
82 82 command = registrar.command(table)
83 83 INTENT_READONLY = registrar.INTENT_READONLY
84 84
85 85 # common command options
86 86
87 87 globalopts = [
88 88 (
89 89 b'R',
90 90 b'repository',
91 91 b'',
92 92 _(b'repository root directory or name of overlay bundle file'),
93 93 _(b'REPO'),
94 94 ),
95 95 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
96 96 (
97 97 b'y',
98 98 b'noninteractive',
99 99 None,
100 100 _(
101 101 b'do not prompt, automatically pick the first choice for all prompts'
102 102 ),
103 103 ),
104 104 (b'q', b'quiet', None, _(b'suppress output')),
105 105 (b'v', b'verbose', None, _(b'enable additional output')),
106 106 (
107 107 b'',
108 108 b'color',
109 109 b'',
110 110 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
111 111 # and should not be translated
112 112 _(b"when to colorize (boolean, always, auto, never, or debug)"),
113 113 _(b'TYPE'),
114 114 ),
115 115 (
116 116 b'',
117 117 b'config',
118 118 [],
119 119 _(b'set/override config option (use \'section.name=value\')'),
120 120 _(b'CONFIG'),
121 121 ),
122 122 (b'', b'debug', None, _(b'enable debugging output')),
123 123 (b'', b'debugger', None, _(b'start debugger')),
124 124 (
125 125 b'',
126 126 b'encoding',
127 127 encoding.encoding,
128 128 _(b'set the charset encoding'),
129 129 _(b'ENCODE'),
130 130 ),
131 131 (
132 132 b'',
133 133 b'encodingmode',
134 134 encoding.encodingmode,
135 135 _(b'set the charset encoding mode'),
136 136 _(b'MODE'),
137 137 ),
138 138 (b'', b'traceback', None, _(b'always print a traceback on exception')),
139 139 (b'', b'time', None, _(b'time how long the command takes')),
140 140 (b'', b'profile', None, _(b'print command execution profile')),
141 141 (b'', b'version', None, _(b'output version information and exit')),
142 142 (b'h', b'help', None, _(b'display help and exit')),
143 143 (b'', b'hidden', False, _(b'consider hidden changesets')),
144 144 (
145 145 b'',
146 146 b'pager',
147 147 b'auto',
148 148 _(b"when to paginate (boolean, always, auto, or never)"),
149 149 _(b'TYPE'),
150 150 ),
151 151 ]
152 152
153 153 dryrunopts = cmdutil.dryrunopts
154 154 remoteopts = cmdutil.remoteopts
155 155 walkopts = cmdutil.walkopts
156 156 commitopts = cmdutil.commitopts
157 157 commitopts2 = cmdutil.commitopts2
158 158 commitopts3 = cmdutil.commitopts3
159 159 formatteropts = cmdutil.formatteropts
160 160 templateopts = cmdutil.templateopts
161 161 logopts = cmdutil.logopts
162 162 diffopts = cmdutil.diffopts
163 163 diffwsopts = cmdutil.diffwsopts
164 164 diffopts2 = cmdutil.diffopts2
165 165 mergetoolopts = cmdutil.mergetoolopts
166 166 similarityopts = cmdutil.similarityopts
167 167 subrepoopts = cmdutil.subrepoopts
168 168 debugrevlogopts = cmdutil.debugrevlogopts
169 169
170 170 # Commands start here, listed alphabetically
171 171
172 172
173 173 @command(
174 174 b'abort',
175 175 dryrunopts,
176 176 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
177 177 helpbasic=True,
178 178 )
179 179 def abort(ui, repo, **opts):
180 180 """abort an unfinished operation (EXPERIMENTAL)
181 181
182 182 Aborts a multistep operation like graft, histedit, rebase, merge,
183 183 and unshelve if they are in an unfinished state.
184 184
185 185 use --dry-run/-n to dry run the command.
186 186 """
187 187 dryrun = opts.get('dry_run')
188 188 abortstate = cmdutil.getunfinishedstate(repo)
189 189 if not abortstate:
190 raise error.Abort(_(b'no operation in progress'))
190 raise error.StateError(_(b'no operation in progress'))
191 191 if not abortstate.abortfunc:
192 192 raise error.InputError(
193 193 (
194 194 _(b"%s in progress but does not support 'hg abort'")
195 195 % (abortstate._opname)
196 196 ),
197 197 hint=abortstate.hint(),
198 198 )
199 199 if dryrun:
200 200 ui.status(
201 201 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
202 202 )
203 203 return
204 204 return abortstate.abortfunc(ui, repo)
205 205
206 206
207 207 @command(
208 208 b'add',
209 209 walkopts + subrepoopts + dryrunopts,
210 210 _(b'[OPTION]... [FILE]...'),
211 211 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
212 212 helpbasic=True,
213 213 inferrepo=True,
214 214 )
215 215 def add(ui, repo, *pats, **opts):
216 216 """add the specified files on the next commit
217 217
218 218 Schedule files to be version controlled and added to the
219 219 repository.
220 220
221 221 The files will be added to the repository at the next commit. To
222 222 undo an add before that, see :hg:`forget`.
223 223
224 224 If no names are given, add all files to the repository (except
225 225 files matching ``.hgignore``).
226 226
227 227 .. container:: verbose
228 228
229 229 Examples:
230 230
231 231 - New (unknown) files are added
232 232 automatically by :hg:`add`::
233 233
234 234 $ ls
235 235 foo.c
236 236 $ hg status
237 237 ? foo.c
238 238 $ hg add
239 239 adding foo.c
240 240 $ hg status
241 241 A foo.c
242 242
243 243 - Specific files to be added can be specified::
244 244
245 245 $ ls
246 246 bar.c foo.c
247 247 $ hg status
248 248 ? bar.c
249 249 ? foo.c
250 250 $ hg add bar.c
251 251 $ hg status
252 252 A bar.c
253 253 ? foo.c
254 254
255 255 Returns 0 if all files are successfully added.
256 256 """
257 257
258 258 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
259 259 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
260 260 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
261 261 return rejected and 1 or 0
262 262
263 263
264 264 @command(
265 265 b'addremove',
266 266 similarityopts + subrepoopts + walkopts + dryrunopts,
267 267 _(b'[OPTION]... [FILE]...'),
268 268 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
269 269 inferrepo=True,
270 270 )
271 271 def addremove(ui, repo, *pats, **opts):
272 272 """add all new files, delete all missing files
273 273
274 274 Add all new files and remove all missing files from the
275 275 repository.
276 276
277 277 Unless names are given, new files are ignored if they match any of
278 278 the patterns in ``.hgignore``. As with add, these changes take
279 279 effect at the next commit.
280 280
281 281 Use the -s/--similarity option to detect renamed files. This
282 282 option takes a percentage between 0 (disabled) and 100 (files must
283 283 be identical) as its parameter. With a parameter greater than 0,
284 284 this compares every removed file with every added file and records
285 285 those similar enough as renames. Detecting renamed files this way
286 286 can be expensive. After using this option, :hg:`status -C` can be
287 287 used to check which files were identified as moved or renamed. If
288 288 not specified, -s/--similarity defaults to 100 and only renames of
289 289 identical files are detected.
290 290
291 291 .. container:: verbose
292 292
293 293 Examples:
294 294
295 295 - A number of files (bar.c and foo.c) are new,
296 296 while foobar.c has been removed (without using :hg:`remove`)
297 297 from the repository::
298 298
299 299 $ ls
300 300 bar.c foo.c
301 301 $ hg status
302 302 ! foobar.c
303 303 ? bar.c
304 304 ? foo.c
305 305 $ hg addremove
306 306 adding bar.c
307 307 adding foo.c
308 308 removing foobar.c
309 309 $ hg status
310 310 A bar.c
311 311 A foo.c
312 312 R foobar.c
313 313
314 314 - A file foobar.c was moved to foo.c without using :hg:`rename`.
315 315 Afterwards, it was edited slightly::
316 316
317 317 $ ls
318 318 foo.c
319 319 $ hg status
320 320 ! foobar.c
321 321 ? foo.c
322 322 $ hg addremove --similarity 90
323 323 removing foobar.c
324 324 adding foo.c
325 325 recording removal of foobar.c as rename to foo.c (94% similar)
326 326 $ hg status -C
327 327 A foo.c
328 328 foobar.c
329 329 R foobar.c
330 330
331 331 Returns 0 if all files are successfully added.
332 332 """
333 333 opts = pycompat.byteskwargs(opts)
334 334 if not opts.get(b'similarity'):
335 335 opts[b'similarity'] = b'100'
336 336 matcher = scmutil.match(repo[None], pats, opts)
337 337 relative = scmutil.anypats(pats, opts)
338 338 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
339 339 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
340 340
341 341
342 342 @command(
343 343 b'annotate|blame',
344 344 [
345 345 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
346 346 (
347 347 b'',
348 348 b'follow',
349 349 None,
350 350 _(b'follow copies/renames and list the filename (DEPRECATED)'),
351 351 ),
352 352 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
353 353 (b'a', b'text', None, _(b'treat all files as text')),
354 354 (b'u', b'user', None, _(b'list the author (long with -v)')),
355 355 (b'f', b'file', None, _(b'list the filename')),
356 356 (b'd', b'date', None, _(b'list the date (short with -q)')),
357 357 (b'n', b'number', None, _(b'list the revision number (default)')),
358 358 (b'c', b'changeset', None, _(b'list the changeset')),
359 359 (
360 360 b'l',
361 361 b'line-number',
362 362 None,
363 363 _(b'show line number at the first appearance'),
364 364 ),
365 365 (
366 366 b'',
367 367 b'skip',
368 368 [],
369 369 _(b'revset to not display (EXPERIMENTAL)'),
370 370 _(b'REV'),
371 371 ),
372 372 ]
373 373 + diffwsopts
374 374 + walkopts
375 375 + formatteropts,
376 376 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
377 377 helpcategory=command.CATEGORY_FILE_CONTENTS,
378 378 helpbasic=True,
379 379 inferrepo=True,
380 380 )
381 381 def annotate(ui, repo, *pats, **opts):
382 382 """show changeset information by line for each file
383 383
384 384 List changes in files, showing the revision id responsible for
385 385 each line.
386 386
387 387 This command is useful for discovering when a change was made and
388 388 by whom.
389 389
390 390 If you include --file, --user, or --date, the revision number is
391 391 suppressed unless you also include --number.
392 392
393 393 Without the -a/--text option, annotate will avoid processing files
394 394 it detects as binary. With -a, annotate will annotate the file
395 395 anyway, although the results will probably be neither useful
396 396 nor desirable.
397 397
398 398 .. container:: verbose
399 399
400 400 Template:
401 401
402 402 The following keywords are supported in addition to the common template
403 403 keywords and functions. See also :hg:`help templates`.
404 404
405 405 :lines: List of lines with annotation data.
406 406 :path: String. Repository-absolute path of the specified file.
407 407
408 408 And each entry of ``{lines}`` provides the following sub-keywords in
409 409 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
410 410
411 411 :line: String. Line content.
412 412 :lineno: Integer. Line number at that revision.
413 413 :path: String. Repository-absolute path of the file at that revision.
414 414
415 415 See :hg:`help templates.operators` for the list expansion syntax.
416 416
417 417 Returns 0 on success.
418 418 """
419 419 opts = pycompat.byteskwargs(opts)
420 420 if not pats:
421 421 raise error.InputError(
422 422 _(b'at least one filename or pattern is required')
423 423 )
424 424
425 425 if opts.get(b'follow'):
426 426 # --follow is deprecated and now just an alias for -f/--file
427 427 # to mimic the behavior of Mercurial before version 1.5
428 428 opts[b'file'] = True
429 429
430 430 if (
431 431 not opts.get(b'user')
432 432 and not opts.get(b'changeset')
433 433 and not opts.get(b'date')
434 434 and not opts.get(b'file')
435 435 ):
436 436 opts[b'number'] = True
437 437
438 438 linenumber = opts.get(b'line_number') is not None
439 439 if (
440 440 linenumber
441 441 and (not opts.get(b'changeset'))
442 442 and (not opts.get(b'number'))
443 443 ):
444 444 raise error.InputError(_(b'at least one of -n/-c is required for -l'))
445 445
446 446 rev = opts.get(b'rev')
447 447 if rev:
448 448 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
449 449 ctx = scmutil.revsingle(repo, rev)
450 450
451 451 ui.pager(b'annotate')
452 452 rootfm = ui.formatter(b'annotate', opts)
453 453 if ui.debugflag:
454 454 shorthex = pycompat.identity
455 455 else:
456 456
457 457 def shorthex(h):
458 458 return h[:12]
459 459
460 460 if ui.quiet:
461 461 datefunc = dateutil.shortdate
462 462 else:
463 463 datefunc = dateutil.datestr
464 464 if ctx.rev() is None:
465 465 if opts.get(b'changeset'):
466 466 # omit "+" suffix which is appended to node hex
467 467 def formatrev(rev):
468 468 if rev == wdirrev:
469 469 return b'%d' % ctx.p1().rev()
470 470 else:
471 471 return b'%d' % rev
472 472
473 473 else:
474 474
475 475 def formatrev(rev):
476 476 if rev == wdirrev:
477 477 return b'%d+' % ctx.p1().rev()
478 478 else:
479 479 return b'%d ' % rev
480 480
481 481 def formathex(h):
482 482 if h == wdirhex:
483 483 return b'%s+' % shorthex(hex(ctx.p1().node()))
484 484 else:
485 485 return b'%s ' % shorthex(h)
486 486
487 487 else:
488 488 formatrev = b'%d'.__mod__
489 489 formathex = shorthex
490 490
491 491 opmap = [
492 492 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
493 493 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
494 494 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
495 495 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
496 496 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
497 497 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
498 498 ]
499 499 opnamemap = {
500 500 b'rev': b'number',
501 501 b'node': b'changeset',
502 502 b'path': b'file',
503 503 b'lineno': b'line_number',
504 504 }
505 505
506 506 if rootfm.isplain():
507 507
508 508 def makefunc(get, fmt):
509 509 return lambda x: fmt(get(x))
510 510
511 511 else:
512 512
513 513 def makefunc(get, fmt):
514 514 return get
515 515
516 516 datahint = rootfm.datahint()
517 517 funcmap = [
518 518 (makefunc(get, fmt), sep)
519 519 for fn, sep, get, fmt in opmap
520 520 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
521 521 ]
522 522 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
523 523 fields = b' '.join(
524 524 fn
525 525 for fn, sep, get, fmt in opmap
526 526 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
527 527 )
528 528
529 529 def bad(x, y):
530 530 raise error.Abort(b"%s: %s" % (x, y))
531 531
532 532 m = scmutil.match(ctx, pats, opts, badfn=bad)
533 533
534 534 follow = not opts.get(b'no_follow')
535 535 diffopts = patch.difffeatureopts(
536 536 ui, opts, section=b'annotate', whitespace=True
537 537 )
538 538 skiprevs = opts.get(b'skip')
539 539 if skiprevs:
540 540 skiprevs = scmutil.revrange(repo, skiprevs)
541 541
542 542 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
543 543 for abs in ctx.walk(m):
544 544 fctx = ctx[abs]
545 545 rootfm.startitem()
546 546 rootfm.data(path=abs)
547 547 if not opts.get(b'text') and fctx.isbinary():
548 548 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
549 549 continue
550 550
551 551 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
552 552 lines = fctx.annotate(
553 553 follow=follow, skiprevs=skiprevs, diffopts=diffopts
554 554 )
555 555 if not lines:
556 556 fm.end()
557 557 continue
558 558 formats = []
559 559 pieces = []
560 560
561 561 for f, sep in funcmap:
562 562 l = [f(n) for n in lines]
563 563 if fm.isplain():
564 564 sizes = [encoding.colwidth(x) for x in l]
565 565 ml = max(sizes)
566 566 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
567 567 else:
568 568 formats.append([b'%s'] * len(l))
569 569 pieces.append(l)
570 570
571 571 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
572 572 fm.startitem()
573 573 fm.context(fctx=n.fctx)
574 574 fm.write(fields, b"".join(f), *p)
575 575 if n.skip:
576 576 fmt = b"* %s"
577 577 else:
578 578 fmt = b": %s"
579 579 fm.write(b'line', fmt, n.text)
580 580
581 581 if not lines[-1].text.endswith(b'\n'):
582 582 fm.plain(b'\n')
583 583 fm.end()
584 584
585 585 rootfm.end()
586 586
587 587
588 588 @command(
589 589 b'archive',
590 590 [
591 591 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
592 592 (
593 593 b'p',
594 594 b'prefix',
595 595 b'',
596 596 _(b'directory prefix for files in archive'),
597 597 _(b'PREFIX'),
598 598 ),
599 599 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
600 600 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
601 601 ]
602 602 + subrepoopts
603 603 + walkopts,
604 604 _(b'[OPTION]... DEST'),
605 605 helpcategory=command.CATEGORY_IMPORT_EXPORT,
606 606 )
607 607 def archive(ui, repo, dest, **opts):
608 608 '''create an unversioned archive of a repository revision
609 609
610 610 By default, the revision used is the parent of the working
611 611 directory; use -r/--rev to specify a different revision.
612 612
613 613 The archive type is automatically detected based on file
614 614 extension (to override, use -t/--type).
615 615
616 616 .. container:: verbose
617 617
618 618 Examples:
619 619
620 620 - create a zip file containing the 1.0 release::
621 621
622 622 hg archive -r 1.0 project-1.0.zip
623 623
624 624 - create a tarball excluding .hg files::
625 625
626 626 hg archive project.tar.gz -X ".hg*"
627 627
628 628 Valid types are:
629 629
630 630 :``files``: a directory full of files (default)
631 631 :``tar``: tar archive, uncompressed
632 632 :``tbz2``: tar archive, compressed using bzip2
633 633 :``tgz``: tar archive, compressed using gzip
634 634 :``txz``: tar archive, compressed using lzma (only in Python 3)
635 635 :``uzip``: zip archive, uncompressed
636 636 :``zip``: zip archive, compressed using deflate
637 637
638 638 The exact name of the destination archive or directory is given
639 639 using a format string; see :hg:`help export` for details.
640 640
641 641 Each member added to an archive file has a directory prefix
642 642 prepended. Use -p/--prefix to specify a format string for the
643 643 prefix. The default is the basename of the archive, with suffixes
644 644 removed.
645 645
646 646 Returns 0 on success.
647 647 '''
648 648
649 649 opts = pycompat.byteskwargs(opts)
650 650 rev = opts.get(b'rev')
651 651 if rev:
652 652 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
653 653 ctx = scmutil.revsingle(repo, rev)
654 654 if not ctx:
655 655 raise error.InputError(
656 656 _(b'no working directory: please specify a revision')
657 657 )
658 658 node = ctx.node()
659 659 dest = cmdutil.makefilename(ctx, dest)
660 660 if os.path.realpath(dest) == repo.root:
661 661 raise error.InputError(_(b'repository root cannot be destination'))
662 662
663 663 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
664 664 prefix = opts.get(b'prefix')
665 665
666 666 if dest == b'-':
667 667 if kind == b'files':
668 668 raise error.InputError(_(b'cannot archive plain files to stdout'))
669 669 dest = cmdutil.makefileobj(ctx, dest)
670 670 if not prefix:
671 671 prefix = os.path.basename(repo.root) + b'-%h'
672 672
673 673 prefix = cmdutil.makefilename(ctx, prefix)
674 674 match = scmutil.match(ctx, [], opts)
675 675 archival.archive(
676 676 repo,
677 677 dest,
678 678 node,
679 679 kind,
680 680 not opts.get(b'no_decode'),
681 681 match,
682 682 prefix,
683 683 subrepos=opts.get(b'subrepos'),
684 684 )
685 685
686 686
687 687 @command(
688 688 b'backout',
689 689 [
690 690 (
691 691 b'',
692 692 b'merge',
693 693 None,
694 694 _(b'merge with old dirstate parent after backout'),
695 695 ),
696 696 (
697 697 b'',
698 698 b'commit',
699 699 None,
700 700 _(b'commit if no conflicts were encountered (DEPRECATED)'),
701 701 ),
702 702 (b'', b'no-commit', None, _(b'do not commit')),
703 703 (
704 704 b'',
705 705 b'parent',
706 706 b'',
707 707 _(b'parent to choose when backing out merge (DEPRECATED)'),
708 708 _(b'REV'),
709 709 ),
710 710 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
711 711 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
712 712 ]
713 713 + mergetoolopts
714 714 + walkopts
715 715 + commitopts
716 716 + commitopts2,
717 717 _(b'[OPTION]... [-r] REV'),
718 718 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
719 719 )
720 720 def backout(ui, repo, node=None, rev=None, **opts):
721 721 '''reverse effect of earlier changeset
722 722
723 723 Prepare a new changeset with the effect of REV undone in the
724 724 current working directory. If no conflicts were encountered,
725 725 it will be committed immediately.
726 726
727 727 If REV is the parent of the working directory, then this new changeset
728 728 is committed automatically (unless --no-commit is specified).
729 729
730 730 .. note::
731 731
732 732 :hg:`backout` cannot be used to fix either an unwanted or
733 733 incorrect merge.
734 734
735 735 .. container:: verbose
736 736
737 737 Examples:
738 738
739 739 - Reverse the effect of the parent of the working directory.
740 740 This backout will be committed immediately::
741 741
742 742 hg backout -r .
743 743
744 744 - Reverse the effect of previous bad revision 23::
745 745
746 746 hg backout -r 23
747 747
748 748 - Reverse the effect of previous bad revision 23 and
749 749 leave changes uncommitted::
750 750
751 751 hg backout -r 23 --no-commit
752 752 hg commit -m "Backout revision 23"
753 753
754 754 By default, the pending changeset will have one parent,
755 755 maintaining a linear history. With --merge, the pending
756 756 changeset will instead have two parents: the old parent of the
757 757 working directory and a new child of REV that simply undoes REV.
758 758
759 759 Before version 1.7, the behavior without --merge was equivalent
760 760 to specifying --merge followed by :hg:`update --clean .` to
761 761 cancel the merge and leave the child of REV as a head to be
762 762 merged separately.
763 763
764 764 See :hg:`help dates` for a list of formats valid for -d/--date.
765 765
766 766 See :hg:`help revert` for a way to restore files to the state
767 767 of another revision.
768 768
769 769 Returns 0 on success, 1 if nothing to backout or there are unresolved
770 770 files.
771 771 '''
772 772 with repo.wlock(), repo.lock():
773 773 return _dobackout(ui, repo, node, rev, **opts)
774 774
775 775
776 776 def _dobackout(ui, repo, node=None, rev=None, **opts):
777 777 cmdutil.check_incompatible_arguments(opts, 'no_commit', ['commit', 'merge'])
778 778 opts = pycompat.byteskwargs(opts)
779 779
780 780 if rev and node:
781 781 raise error.InputError(_(b"please specify just one revision"))
782 782
783 783 if not rev:
784 784 rev = node
785 785
786 786 if not rev:
787 787 raise error.InputError(_(b"please specify a revision to backout"))
788 788
789 789 date = opts.get(b'date')
790 790 if date:
791 791 opts[b'date'] = dateutil.parsedate(date)
792 792
793 793 cmdutil.checkunfinished(repo)
794 794 cmdutil.bailifchanged(repo)
795 795 ctx = scmutil.revsingle(repo, rev)
796 796 node = ctx.node()
797 797
798 798 op1, op2 = repo.dirstate.parents()
799 799 if not repo.changelog.isancestor(node, op1):
800 800 raise error.InputError(
801 801 _(b'cannot backout change that is not an ancestor')
802 802 )
803 803
804 804 p1, p2 = repo.changelog.parents(node)
805 805 if p1 == nullid:
806 806 raise error.InputError(_(b'cannot backout a change with no parents'))
807 807 if p2 != nullid:
808 808 if not opts.get(b'parent'):
809 809 raise error.InputError(_(b'cannot backout a merge changeset'))
810 810 p = repo.lookup(opts[b'parent'])
811 811 if p not in (p1, p2):
812 812 raise error.InputError(
813 813 _(b'%s is not a parent of %s') % (short(p), short(node))
814 814 )
815 815 parent = p
816 816 else:
817 817 if opts.get(b'parent'):
818 818 raise error.InputError(
819 819 _(b'cannot use --parent on non-merge changeset')
820 820 )
821 821 parent = p1
822 822
823 823 # the backout should appear on the same branch
824 824 branch = repo.dirstate.branch()
825 825 bheads = repo.branchheads(branch)
826 826 rctx = scmutil.revsingle(repo, hex(parent))
827 827 if not opts.get(b'merge') and op1 != node:
828 828 with dirstateguard.dirstateguard(repo, b'backout'):
829 829 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
830 830 with ui.configoverride(overrides, b'backout'):
831 831 stats = mergemod.back_out(ctx, parent=repo[parent])
832 832 repo.setparents(op1, op2)
833 833 hg._showstats(repo, stats)
834 834 if stats.unresolvedcount:
835 835 repo.ui.status(
836 836 _(b"use 'hg resolve' to retry unresolved file merges\n")
837 837 )
838 838 return 1
839 839 else:
840 840 hg.clean(repo, node, show_stats=False)
841 841 repo.dirstate.setbranch(branch)
842 842 cmdutil.revert(ui, repo, rctx)
843 843
844 844 if opts.get(b'no_commit'):
845 845 msg = _(b"changeset %s backed out, don't forget to commit.\n")
846 846 ui.status(msg % short(node))
847 847 return 0
848 848
849 849 def commitfunc(ui, repo, message, match, opts):
850 850 editform = b'backout'
851 851 e = cmdutil.getcommiteditor(
852 852 editform=editform, **pycompat.strkwargs(opts)
853 853 )
854 854 if not message:
855 855 # we don't translate commit messages
856 856 message = b"Backed out changeset %s" % short(node)
857 857 e = cmdutil.getcommiteditor(edit=True, editform=editform)
858 858 return repo.commit(
859 859 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
860 860 )
861 861
862 862 # save to detect changes
863 863 tip = repo.changelog.tip()
864 864
865 865 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
866 866 if not newnode:
867 867 ui.status(_(b"nothing changed\n"))
868 868 return 1
869 869 cmdutil.commitstatus(repo, newnode, branch, bheads, tip)
870 870
871 871 def nice(node):
872 872 return b'%d:%s' % (repo.changelog.rev(node), short(node))
873 873
874 874 ui.status(
875 875 _(b'changeset %s backs out changeset %s\n')
876 876 % (nice(newnode), nice(node))
877 877 )
878 878 if opts.get(b'merge') and op1 != node:
879 879 hg.clean(repo, op1, show_stats=False)
880 880 ui.status(_(b'merging with changeset %s\n') % nice(newnode))
881 881 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
882 882 with ui.configoverride(overrides, b'backout'):
883 883 return hg.merge(repo[b'tip'])
884 884 return 0
885 885
886 886
887 887 @command(
888 888 b'bisect',
889 889 [
890 890 (b'r', b'reset', False, _(b'reset bisect state')),
891 891 (b'g', b'good', False, _(b'mark changeset good')),
892 892 (b'b', b'bad', False, _(b'mark changeset bad')),
893 893 (b's', b'skip', False, _(b'skip testing changeset')),
894 894 (b'e', b'extend', False, _(b'extend the bisect range')),
895 895 (
896 896 b'c',
897 897 b'command',
898 898 b'',
899 899 _(b'use command to check changeset state'),
900 900 _(b'CMD'),
901 901 ),
902 902 (b'U', b'noupdate', False, _(b'do not update to target')),
903 903 ],
904 904 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
905 905 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
906 906 )
907 907 def bisect(
908 908 ui,
909 909 repo,
910 910 rev=None,
911 911 extra=None,
912 912 command=None,
913 913 reset=None,
914 914 good=None,
915 915 bad=None,
916 916 skip=None,
917 917 extend=None,
918 918 noupdate=None,
919 919 ):
920 920 """subdivision search of changesets
921 921
922 922 This command helps to find changesets which introduce problems. To
923 923 use, mark the earliest changeset you know exhibits the problem as
924 924 bad, then mark the latest changeset which is free from the problem
925 925 as good. Bisect will update your working directory to a revision
926 926 for testing (unless the -U/--noupdate option is specified). Once
927 927 you have performed tests, mark the working directory as good or
928 928 bad, and bisect will either update to another candidate changeset
929 929 or announce that it has found the bad revision.
930 930
931 931 As a shortcut, you can also use the revision argument to mark a
932 932 revision as good or bad without checking it out first.
933 933
934 934 If you supply a command, it will be used for automatic bisection.
935 935 The environment variable HG_NODE will contain the ID of the
936 936 changeset being tested. The exit status of the command will be
937 937 used to mark revisions as good or bad: status 0 means good, 125
938 938 means to skip the revision, 127 (command not found) will abort the
939 939 bisection, and any other non-zero exit status means the revision
940 940 is bad.
941 941
942 942 .. container:: verbose
943 943
944 944 Some examples:
945 945
946 946 - start a bisection with known bad revision 34, and good revision 12::
947 947
948 948 hg bisect --bad 34
949 949 hg bisect --good 12
950 950
951 951 - advance the current bisection by marking current revision as good or
952 952 bad::
953 953
954 954 hg bisect --good
955 955 hg bisect --bad
956 956
957 957 - mark the current revision, or a known revision, to be skipped (e.g. if
958 958 that revision is not usable because of another issue)::
959 959
960 960 hg bisect --skip
961 961 hg bisect --skip 23
962 962
963 963 - skip all revisions that do not touch directories ``foo`` or ``bar``::
964 964
965 965 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
966 966
967 967 - forget the current bisection::
968 968
969 969 hg bisect --reset
970 970
971 971 - use 'make && make tests' to automatically find the first broken
972 972 revision::
973 973
974 974 hg bisect --reset
975 975 hg bisect --bad 34
976 976 hg bisect --good 12
977 977 hg bisect --command "make && make tests"
978 978
979 979 - see all changesets whose states are already known in the current
980 980 bisection::
981 981
982 982 hg log -r "bisect(pruned)"
983 983
984 984 - see the changeset currently being bisected (especially useful
985 985 if running with -U/--noupdate)::
986 986
987 987 hg log -r "bisect(current)"
988 988
989 989 - see all changesets that took part in the current bisection::
990 990
991 991 hg log -r "bisect(range)"
992 992
993 993 - you can even get a nice graph::
994 994
995 995 hg log --graph -r "bisect(range)"
996 996
997 997 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
998 998
999 999 Returns 0 on success.
1000 1000 """
1001 1001 # backward compatibility
1002 1002 if rev in b"good bad reset init".split():
1003 1003 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1004 1004 cmd, rev, extra = rev, extra, None
1005 1005 if cmd == b"good":
1006 1006 good = True
1007 1007 elif cmd == b"bad":
1008 1008 bad = True
1009 1009 else:
1010 1010 reset = True
1011 1011 elif extra:
1012 1012 raise error.InputError(_(b'incompatible arguments'))
1013 1013
1014 1014 incompatibles = {
1015 1015 b'--bad': bad,
1016 1016 b'--command': bool(command),
1017 1017 b'--extend': extend,
1018 1018 b'--good': good,
1019 1019 b'--reset': reset,
1020 1020 b'--skip': skip,
1021 1021 }
1022 1022
1023 1023 enabled = [x for x in incompatibles if incompatibles[x]]
1024 1024
1025 1025 if len(enabled) > 1:
1026 1026 raise error.InputError(
1027 1027 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1028 1028 )
1029 1029
1030 1030 if reset:
1031 1031 hbisect.resetstate(repo)
1032 1032 return
1033 1033
1034 1034 state = hbisect.load_state(repo)
1035 1035
1036 1036 # update state
1037 1037 if good or bad or skip:
1038 1038 if rev:
1039 1039 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
1040 1040 else:
1041 1041 nodes = [repo.lookup(b'.')]
1042 1042 if good:
1043 1043 state[b'good'] += nodes
1044 1044 elif bad:
1045 1045 state[b'bad'] += nodes
1046 1046 elif skip:
1047 1047 state[b'skip'] += nodes
1048 1048 hbisect.save_state(repo, state)
1049 1049 if not (state[b'good'] and state[b'bad']):
1050 1050 return
1051 1051
1052 1052 def mayupdate(repo, node, show_stats=True):
1053 1053 """common used update sequence"""
1054 1054 if noupdate:
1055 1055 return
1056 1056 cmdutil.checkunfinished(repo)
1057 1057 cmdutil.bailifchanged(repo)
1058 1058 return hg.clean(repo, node, show_stats=show_stats)
1059 1059
1060 1060 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1061 1061
1062 1062 if command:
1063 1063 changesets = 1
1064 1064 if noupdate:
1065 1065 try:
1066 1066 node = state[b'current'][0]
1067 1067 except LookupError:
1068 raise error.Abort(
1068 raise error.StateError(
1069 1069 _(
1070 1070 b'current bisect revision is unknown - '
1071 1071 b'start a new bisect to fix'
1072 1072 )
1073 1073 )
1074 1074 else:
1075 1075 node, p2 = repo.dirstate.parents()
1076 1076 if p2 != nullid:
1077 raise error.Abort(_(b'current bisect revision is a merge'))
1077 raise error.StateError(_(b'current bisect revision is a merge'))
1078 1078 if rev:
1079 1079 node = repo[scmutil.revsingle(repo, rev, node)].node()
1080 1080 with hbisect.restore_state(repo, state, node):
1081 1081 while changesets:
1082 1082 # update state
1083 1083 state[b'current'] = [node]
1084 1084 hbisect.save_state(repo, state)
1085 1085 status = ui.system(
1086 1086 command,
1087 1087 environ={b'HG_NODE': hex(node)},
1088 1088 blockedtag=b'bisect_check',
1089 1089 )
1090 1090 if status == 125:
1091 1091 transition = b"skip"
1092 1092 elif status == 0:
1093 1093 transition = b"good"
1094 1094 # status < 0 means process was killed
1095 1095 elif status == 127:
1096 1096 raise error.Abort(_(b"failed to execute %s") % command)
1097 1097 elif status < 0:
1098 1098 raise error.Abort(_(b"%s killed") % command)
1099 1099 else:
1100 1100 transition = b"bad"
1101 1101 state[transition].append(node)
1102 1102 ctx = repo[node]
1103 1103 ui.status(
1104 1104 _(b'changeset %d:%s: %s\n') % (ctx.rev(), ctx, transition)
1105 1105 )
1106 1106 hbisect.checkstate(state)
1107 1107 # bisect
1108 1108 nodes, changesets, bgood = hbisect.bisect(repo, state)
1109 1109 # update to next check
1110 1110 node = nodes[0]
1111 1111 mayupdate(repo, node, show_stats=False)
1112 1112 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1113 1113 return
1114 1114
1115 1115 hbisect.checkstate(state)
1116 1116
1117 1117 # actually bisect
1118 1118 nodes, changesets, good = hbisect.bisect(repo, state)
1119 1119 if extend:
1120 1120 if not changesets:
1121 1121 extendnode = hbisect.extendrange(repo, state, nodes, good)
1122 1122 if extendnode is not None:
1123 1123 ui.write(
1124 1124 _(b"Extending search to changeset %d:%s\n")
1125 1125 % (extendnode.rev(), extendnode)
1126 1126 )
1127 1127 state[b'current'] = [extendnode.node()]
1128 1128 hbisect.save_state(repo, state)
1129 1129 return mayupdate(repo, extendnode.node())
1130 raise error.Abort(_(b"nothing to extend"))
1130 raise error.StateError(_(b"nothing to extend"))
1131 1131
1132 1132 if changesets == 0:
1133 1133 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1134 1134 else:
1135 1135 assert len(nodes) == 1 # only a single node can be tested next
1136 1136 node = nodes[0]
1137 1137 # compute the approximate number of remaining tests
1138 1138 tests, size = 0, 2
1139 1139 while size <= changesets:
1140 1140 tests, size = tests + 1, size * 2
1141 1141 rev = repo.changelog.rev(node)
1142 1142 ui.write(
1143 1143 _(
1144 1144 b"Testing changeset %d:%s "
1145 1145 b"(%d changesets remaining, ~%d tests)\n"
1146 1146 )
1147 1147 % (rev, short(node), changesets, tests)
1148 1148 )
1149 1149 state[b'current'] = [node]
1150 1150 hbisect.save_state(repo, state)
1151 1151 return mayupdate(repo, node)
1152 1152
1153 1153
1154 1154 @command(
1155 1155 b'bookmarks|bookmark',
1156 1156 [
1157 1157 (b'f', b'force', False, _(b'force')),
1158 1158 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1159 1159 (b'd', b'delete', False, _(b'delete a given bookmark')),
1160 1160 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1161 1161 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1162 1162 (b'l', b'list', False, _(b'list existing bookmarks')),
1163 1163 ]
1164 1164 + formatteropts,
1165 1165 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1166 1166 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1167 1167 )
1168 1168 def bookmark(ui, repo, *names, **opts):
1169 1169 '''create a new bookmark or list existing bookmarks
1170 1170
1171 1171 Bookmarks are labels on changesets to help track lines of development.
1172 1172 Bookmarks are unversioned and can be moved, renamed and deleted.
1173 1173 Deleting or moving a bookmark has no effect on the associated changesets.
1174 1174
1175 1175 Creating or updating to a bookmark causes it to be marked as 'active'.
1176 1176 The active bookmark is indicated with a '*'.
1177 1177 When a commit is made, the active bookmark will advance to the new commit.
1178 1178 A plain :hg:`update` will also advance an active bookmark, if possible.
1179 1179 Updating away from a bookmark will cause it to be deactivated.
1180 1180
1181 1181 Bookmarks can be pushed and pulled between repositories (see
1182 1182 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1183 1183 diverged, a new 'divergent bookmark' of the form 'name@path' will
1184 1184 be created. Using :hg:`merge` will resolve the divergence.
1185 1185
1186 1186 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1187 1187 the active bookmark's name.
1188 1188
1189 1189 A bookmark named '@' has the special property that :hg:`clone` will
1190 1190 check it out by default if it exists.
1191 1191
1192 1192 .. container:: verbose
1193 1193
1194 1194 Template:
1195 1195
1196 1196 The following keywords are supported in addition to the common template
1197 1197 keywords and functions such as ``{bookmark}``. See also
1198 1198 :hg:`help templates`.
1199 1199
1200 1200 :active: Boolean. True if the bookmark is active.
1201 1201
1202 1202 Examples:
1203 1203
1204 1204 - create an active bookmark for a new line of development::
1205 1205
1206 1206 hg book new-feature
1207 1207
1208 1208 - create an inactive bookmark as a place marker::
1209 1209
1210 1210 hg book -i reviewed
1211 1211
1212 1212 - create an inactive bookmark on another changeset::
1213 1213
1214 1214 hg book -r .^ tested
1215 1215
1216 1216 - rename bookmark turkey to dinner::
1217 1217
1218 1218 hg book -m turkey dinner
1219 1219
1220 1220 - move the '@' bookmark from another branch::
1221 1221
1222 1222 hg book -f @
1223 1223
1224 1224 - print only the active bookmark name::
1225 1225
1226 1226 hg book -ql .
1227 1227 '''
1228 1228 opts = pycompat.byteskwargs(opts)
1229 1229 force = opts.get(b'force')
1230 1230 rev = opts.get(b'rev')
1231 1231 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1232 1232
1233 1233 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1234 1234 if action:
1235 1235 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1236 1236 elif names or rev:
1237 1237 action = b'add'
1238 1238 elif inactive:
1239 1239 action = b'inactive' # meaning deactivate
1240 1240 else:
1241 1241 action = b'list'
1242 1242
1243 1243 cmdutil.check_incompatible_arguments(
1244 1244 opts, b'inactive', [b'delete', b'list']
1245 1245 )
1246 1246 if not names and action in {b'add', b'delete'}:
1247 1247 raise error.InputError(_(b"bookmark name required"))
1248 1248
1249 1249 if action in {b'add', b'delete', b'rename', b'inactive'}:
1250 1250 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1251 1251 if action == b'delete':
1252 1252 names = pycompat.maplist(repo._bookmarks.expandname, names)
1253 1253 bookmarks.delete(repo, tr, names)
1254 1254 elif action == b'rename':
1255 1255 if not names:
1256 1256 raise error.InputError(_(b"new bookmark name required"))
1257 1257 elif len(names) > 1:
1258 1258 raise error.InputError(
1259 1259 _(b"only one new bookmark name allowed")
1260 1260 )
1261 1261 oldname = repo._bookmarks.expandname(opts[b'rename'])
1262 1262 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1263 1263 elif action == b'add':
1264 1264 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1265 1265 elif action == b'inactive':
1266 1266 if len(repo._bookmarks) == 0:
1267 1267 ui.status(_(b"no bookmarks set\n"))
1268 1268 elif not repo._activebookmark:
1269 1269 ui.status(_(b"no active bookmark\n"))
1270 1270 else:
1271 1271 bookmarks.deactivate(repo)
1272 1272 elif action == b'list':
1273 1273 names = pycompat.maplist(repo._bookmarks.expandname, names)
1274 1274 with ui.formatter(b'bookmarks', opts) as fm:
1275 1275 bookmarks.printbookmarks(ui, repo, fm, names)
1276 1276 else:
1277 1277 raise error.ProgrammingError(b'invalid action: %s' % action)
1278 1278
1279 1279
1280 1280 @command(
1281 1281 b'branch',
1282 1282 [
1283 1283 (
1284 1284 b'f',
1285 1285 b'force',
1286 1286 None,
1287 1287 _(b'set branch name even if it shadows an existing branch'),
1288 1288 ),
1289 1289 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1290 1290 (
1291 1291 b'r',
1292 1292 b'rev',
1293 1293 [],
1294 1294 _(b'change branches of the given revs (EXPERIMENTAL)'),
1295 1295 ),
1296 1296 ],
1297 1297 _(b'[-fC] [NAME]'),
1298 1298 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1299 1299 )
1300 1300 def branch(ui, repo, label=None, **opts):
1301 1301 """set or show the current branch name
1302 1302
1303 1303 .. note::
1304 1304
1305 1305 Branch names are permanent and global. Use :hg:`bookmark` to create a
1306 1306 light-weight bookmark instead. See :hg:`help glossary` for more
1307 1307 information about named branches and bookmarks.
1308 1308
1309 1309 With no argument, show the current branch name. With one argument,
1310 1310 set the working directory branch name (the branch will not exist
1311 1311 in the repository until the next commit). Standard practice
1312 1312 recommends that primary development take place on the 'default'
1313 1313 branch.
1314 1314
1315 1315 Unless -f/--force is specified, branch will not let you set a
1316 1316 branch name that already exists.
1317 1317
1318 1318 Use -C/--clean to reset the working directory branch to that of
1319 1319 the parent of the working directory, negating a previous branch
1320 1320 change.
1321 1321
1322 1322 Use the command :hg:`update` to switch to an existing branch. Use
1323 1323 :hg:`commit --close-branch` to mark this branch head as closed.
1324 1324 When all heads of a branch are closed, the branch will be
1325 1325 considered closed.
1326 1326
1327 1327 Returns 0 on success.
1328 1328 """
1329 1329 opts = pycompat.byteskwargs(opts)
1330 1330 revs = opts.get(b'rev')
1331 1331 if label:
1332 1332 label = label.strip()
1333 1333
1334 1334 if not opts.get(b'clean') and not label:
1335 1335 if revs:
1336 1336 raise error.InputError(
1337 1337 _(b"no branch name specified for the revisions")
1338 1338 )
1339 1339 ui.write(b"%s\n" % repo.dirstate.branch())
1340 1340 return
1341 1341
1342 1342 with repo.wlock():
1343 1343 if opts.get(b'clean'):
1344 1344 label = repo[b'.'].branch()
1345 1345 repo.dirstate.setbranch(label)
1346 1346 ui.status(_(b'reset working directory to branch %s\n') % label)
1347 1347 elif label:
1348 1348
1349 1349 scmutil.checknewlabel(repo, label, b'branch')
1350 1350 if revs:
1351 1351 return cmdutil.changebranch(ui, repo, revs, label, opts)
1352 1352
1353 1353 if not opts.get(b'force') and label in repo.branchmap():
1354 1354 if label not in [p.branch() for p in repo[None].parents()]:
1355 1355 raise error.InputError(
1356 1356 _(b'a branch of the same name already exists'),
1357 1357 # i18n: "it" refers to an existing branch
1358 1358 hint=_(b"use 'hg update' to switch to it"),
1359 1359 )
1360 1360
1361 1361 repo.dirstate.setbranch(label)
1362 1362 ui.status(_(b'marked working directory as branch %s\n') % label)
1363 1363
1364 1364 # find any open named branches aside from default
1365 1365 for n, h, t, c in repo.branchmap().iterbranches():
1366 1366 if n != b"default" and not c:
1367 1367 return 0
1368 1368 ui.status(
1369 1369 _(
1370 1370 b'(branches are permanent and global, '
1371 1371 b'did you want a bookmark?)\n'
1372 1372 )
1373 1373 )
1374 1374
1375 1375
1376 1376 @command(
1377 1377 b'branches',
1378 1378 [
1379 1379 (
1380 1380 b'a',
1381 1381 b'active',
1382 1382 False,
1383 1383 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1384 1384 ),
1385 1385 (b'c', b'closed', False, _(b'show normal and closed branches')),
1386 1386 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1387 1387 ]
1388 1388 + formatteropts,
1389 1389 _(b'[-c]'),
1390 1390 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1391 1391 intents={INTENT_READONLY},
1392 1392 )
1393 1393 def branches(ui, repo, active=False, closed=False, **opts):
1394 1394 """list repository named branches
1395 1395
1396 1396 List the repository's named branches, indicating which ones are
1397 1397 inactive. If -c/--closed is specified, also list branches which have
1398 1398 been marked closed (see :hg:`commit --close-branch`).
1399 1399
1400 1400 Use the command :hg:`update` to switch to an existing branch.
1401 1401
1402 1402 .. container:: verbose
1403 1403
1404 1404 Template:
1405 1405
1406 1406 The following keywords are supported in addition to the common template
1407 1407 keywords and functions such as ``{branch}``. See also
1408 1408 :hg:`help templates`.
1409 1409
1410 1410 :active: Boolean. True if the branch is active.
1411 1411 :closed: Boolean. True if the branch is closed.
1412 1412 :current: Boolean. True if it is the current branch.
1413 1413
1414 1414 Returns 0.
1415 1415 """
1416 1416
1417 1417 opts = pycompat.byteskwargs(opts)
1418 1418 revs = opts.get(b'rev')
1419 1419 selectedbranches = None
1420 1420 if revs:
1421 1421 revs = scmutil.revrange(repo, revs)
1422 1422 getbi = repo.revbranchcache().branchinfo
1423 1423 selectedbranches = {getbi(r)[0] for r in revs}
1424 1424
1425 1425 ui.pager(b'branches')
1426 1426 fm = ui.formatter(b'branches', opts)
1427 1427 hexfunc = fm.hexfunc
1428 1428
1429 1429 allheads = set(repo.heads())
1430 1430 branches = []
1431 1431 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1432 1432 if selectedbranches is not None and tag not in selectedbranches:
1433 1433 continue
1434 1434 isactive = False
1435 1435 if not isclosed:
1436 1436 openheads = set(repo.branchmap().iteropen(heads))
1437 1437 isactive = bool(openheads & allheads)
1438 1438 branches.append((tag, repo[tip], isactive, not isclosed))
1439 1439 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1440 1440
1441 1441 for tag, ctx, isactive, isopen in branches:
1442 1442 if active and not isactive:
1443 1443 continue
1444 1444 if isactive:
1445 1445 label = b'branches.active'
1446 1446 notice = b''
1447 1447 elif not isopen:
1448 1448 if not closed:
1449 1449 continue
1450 1450 label = b'branches.closed'
1451 1451 notice = _(b' (closed)')
1452 1452 else:
1453 1453 label = b'branches.inactive'
1454 1454 notice = _(b' (inactive)')
1455 1455 current = tag == repo.dirstate.branch()
1456 1456 if current:
1457 1457 label = b'branches.current'
1458 1458
1459 1459 fm.startitem()
1460 1460 fm.write(b'branch', b'%s', tag, label=label)
1461 1461 rev = ctx.rev()
1462 1462 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1463 1463 fmt = b' ' * padsize + b' %d:%s'
1464 1464 fm.condwrite(
1465 1465 not ui.quiet,
1466 1466 b'rev node',
1467 1467 fmt,
1468 1468 rev,
1469 1469 hexfunc(ctx.node()),
1470 1470 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1471 1471 )
1472 1472 fm.context(ctx=ctx)
1473 1473 fm.data(active=isactive, closed=not isopen, current=current)
1474 1474 if not ui.quiet:
1475 1475 fm.plain(notice)
1476 1476 fm.plain(b'\n')
1477 1477 fm.end()
1478 1478
1479 1479
1480 1480 @command(
1481 1481 b'bundle',
1482 1482 [
1483 1483 (
1484 1484 b'f',
1485 1485 b'force',
1486 1486 None,
1487 1487 _(b'run even when the destination is unrelated'),
1488 1488 ),
1489 1489 (
1490 1490 b'r',
1491 1491 b'rev',
1492 1492 [],
1493 1493 _(b'a changeset intended to be added to the destination'),
1494 1494 _(b'REV'),
1495 1495 ),
1496 1496 (
1497 1497 b'b',
1498 1498 b'branch',
1499 1499 [],
1500 1500 _(b'a specific branch you would like to bundle'),
1501 1501 _(b'BRANCH'),
1502 1502 ),
1503 1503 (
1504 1504 b'',
1505 1505 b'base',
1506 1506 [],
1507 1507 _(b'a base changeset assumed to be available at the destination'),
1508 1508 _(b'REV'),
1509 1509 ),
1510 1510 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1511 1511 (
1512 1512 b't',
1513 1513 b'type',
1514 1514 b'bzip2',
1515 1515 _(b'bundle compression type to use'),
1516 1516 _(b'TYPE'),
1517 1517 ),
1518 1518 ]
1519 1519 + remoteopts,
1520 1520 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1521 1521 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1522 1522 )
1523 1523 def bundle(ui, repo, fname, dest=None, **opts):
1524 1524 """create a bundle file
1525 1525
1526 1526 Generate a bundle file containing data to be transferred to another
1527 1527 repository.
1528 1528
1529 1529 To create a bundle containing all changesets, use -a/--all
1530 1530 (or --base null). Otherwise, hg assumes the destination will have
1531 1531 all the nodes you specify with --base parameters. Otherwise, hg
1532 1532 will assume the repository has all the nodes in destination, or
1533 1533 default-push/default if no destination is specified, where destination
1534 1534 is the repository you provide through DEST option.
1535 1535
1536 1536 You can change bundle format with the -t/--type option. See
1537 1537 :hg:`help bundlespec` for documentation on this format. By default,
1538 1538 the most appropriate format is used and compression defaults to
1539 1539 bzip2.
1540 1540
1541 1541 The bundle file can then be transferred using conventional means
1542 1542 and applied to another repository with the unbundle or pull
1543 1543 command. This is useful when direct push and pull are not
1544 1544 available or when exporting an entire repository is undesirable.
1545 1545
1546 1546 Applying bundles preserves all changeset contents including
1547 1547 permissions, copy/rename information, and revision history.
1548 1548
1549 1549 Returns 0 on success, 1 if no changes found.
1550 1550 """
1551 1551 opts = pycompat.byteskwargs(opts)
1552 1552 revs = None
1553 1553 if b'rev' in opts:
1554 1554 revstrings = opts[b'rev']
1555 1555 revs = scmutil.revrange(repo, revstrings)
1556 1556 if revstrings and not revs:
1557 1557 raise error.InputError(_(b'no commits to bundle'))
1558 1558
1559 1559 bundletype = opts.get(b'type', b'bzip2').lower()
1560 1560 try:
1561 1561 bundlespec = bundlecaches.parsebundlespec(
1562 1562 repo, bundletype, strict=False
1563 1563 )
1564 1564 except error.UnsupportedBundleSpecification as e:
1565 1565 raise error.InputError(
1566 1566 pycompat.bytestr(e),
1567 1567 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1568 1568 )
1569 1569 cgversion = bundlespec.contentopts[b"cg.version"]
1570 1570
1571 1571 # Packed bundles are a pseudo bundle format for now.
1572 1572 if cgversion == b's1':
1573 1573 raise error.InputError(
1574 1574 _(b'packed bundles cannot be produced by "hg bundle"'),
1575 1575 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1576 1576 )
1577 1577
1578 1578 if opts.get(b'all'):
1579 1579 if dest:
1580 1580 raise error.InputError(
1581 1581 _(b"--all is incompatible with specifying a destination")
1582 1582 )
1583 1583 if opts.get(b'base'):
1584 1584 ui.warn(_(b"ignoring --base because --all was specified\n"))
1585 1585 base = [nullrev]
1586 1586 else:
1587 1587 base = scmutil.revrange(repo, opts.get(b'base'))
1588 1588 if cgversion not in changegroup.supportedoutgoingversions(repo):
1589 1589 raise error.Abort(
1590 1590 _(b"repository does not support bundle version %s") % cgversion
1591 1591 )
1592 1592
1593 1593 if base:
1594 1594 if dest:
1595 1595 raise error.InputError(
1596 1596 _(b"--base is incompatible with specifying a destination")
1597 1597 )
1598 1598 common = [repo[rev].node() for rev in base]
1599 1599 heads = [repo[r].node() for r in revs] if revs else None
1600 1600 outgoing = discovery.outgoing(repo, common, heads)
1601 1601 else:
1602 1602 dest = ui.expandpath(dest or b'default-push', dest or b'default')
1603 1603 dest, branches = hg.parseurl(dest, opts.get(b'branch'))
1604 1604 other = hg.peer(repo, opts, dest)
1605 1605 revs = [repo[r].hex() for r in revs]
1606 1606 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1607 1607 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1608 1608 outgoing = discovery.findcommonoutgoing(
1609 1609 repo,
1610 1610 other,
1611 1611 onlyheads=heads,
1612 1612 force=opts.get(b'force'),
1613 1613 portable=True,
1614 1614 )
1615 1615
1616 1616 if not outgoing.missing:
1617 1617 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1618 1618 return 1
1619 1619
1620 1620 if cgversion == b'01': # bundle1
1621 1621 bversion = b'HG10' + bundlespec.wirecompression
1622 1622 bcompression = None
1623 1623 elif cgversion in (b'02', b'03'):
1624 1624 bversion = b'HG20'
1625 1625 bcompression = bundlespec.wirecompression
1626 1626 else:
1627 1627 raise error.ProgrammingError(
1628 1628 b'bundle: unexpected changegroup version %s' % cgversion
1629 1629 )
1630 1630
1631 1631 # TODO compression options should be derived from bundlespec parsing.
1632 1632 # This is a temporary hack to allow adjusting bundle compression
1633 1633 # level without a) formalizing the bundlespec changes to declare it
1634 1634 # b) introducing a command flag.
1635 1635 compopts = {}
1636 1636 complevel = ui.configint(
1637 1637 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1638 1638 )
1639 1639 if complevel is None:
1640 1640 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1641 1641 if complevel is not None:
1642 1642 compopts[b'level'] = complevel
1643 1643
1644 1644 # Allow overriding the bundling of obsmarker in phases through
1645 1645 # configuration while we don't have a bundle version that include them
1646 1646 if repo.ui.configbool(b'experimental', b'evolution.bundle-obsmarker'):
1647 1647 bundlespec.contentopts[b'obsolescence'] = True
1648 1648 if repo.ui.configbool(b'experimental', b'bundle-phases'):
1649 1649 bundlespec.contentopts[b'phases'] = True
1650 1650
1651 1651 bundle2.writenewbundle(
1652 1652 ui,
1653 1653 repo,
1654 1654 b'bundle',
1655 1655 fname,
1656 1656 bversion,
1657 1657 outgoing,
1658 1658 bundlespec.contentopts,
1659 1659 compression=bcompression,
1660 1660 compopts=compopts,
1661 1661 )
1662 1662
1663 1663
1664 1664 @command(
1665 1665 b'cat',
1666 1666 [
1667 1667 (
1668 1668 b'o',
1669 1669 b'output',
1670 1670 b'',
1671 1671 _(b'print output to file with formatted name'),
1672 1672 _(b'FORMAT'),
1673 1673 ),
1674 1674 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1675 1675 (b'', b'decode', None, _(b'apply any matching decode filter')),
1676 1676 ]
1677 1677 + walkopts
1678 1678 + formatteropts,
1679 1679 _(b'[OPTION]... FILE...'),
1680 1680 helpcategory=command.CATEGORY_FILE_CONTENTS,
1681 1681 inferrepo=True,
1682 1682 intents={INTENT_READONLY},
1683 1683 )
1684 1684 def cat(ui, repo, file1, *pats, **opts):
1685 1685 """output the current or given revision of files
1686 1686
1687 1687 Print the specified files as they were at the given revision. If
1688 1688 no revision is given, the parent of the working directory is used.
1689 1689
1690 1690 Output may be to a file, in which case the name of the file is
1691 1691 given using a template string. See :hg:`help templates`. In addition
1692 1692 to the common template keywords, the following formatting rules are
1693 1693 supported:
1694 1694
1695 1695 :``%%``: literal "%" character
1696 1696 :``%s``: basename of file being printed
1697 1697 :``%d``: dirname of file being printed, or '.' if in repository root
1698 1698 :``%p``: root-relative path name of file being printed
1699 1699 :``%H``: changeset hash (40 hexadecimal digits)
1700 1700 :``%R``: changeset revision number
1701 1701 :``%h``: short-form changeset hash (12 hexadecimal digits)
1702 1702 :``%r``: zero-padded changeset revision number
1703 1703 :``%b``: basename of the exporting repository
1704 1704 :``\\``: literal "\\" character
1705 1705
1706 1706 .. container:: verbose
1707 1707
1708 1708 Template:
1709 1709
1710 1710 The following keywords are supported in addition to the common template
1711 1711 keywords and functions. See also :hg:`help templates`.
1712 1712
1713 1713 :data: String. File content.
1714 1714 :path: String. Repository-absolute path of the file.
1715 1715
1716 1716 Returns 0 on success.
1717 1717 """
1718 1718 opts = pycompat.byteskwargs(opts)
1719 1719 rev = opts.get(b'rev')
1720 1720 if rev:
1721 1721 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1722 1722 ctx = scmutil.revsingle(repo, rev)
1723 1723 m = scmutil.match(ctx, (file1,) + pats, opts)
1724 1724 fntemplate = opts.pop(b'output', b'')
1725 1725 if cmdutil.isstdiofilename(fntemplate):
1726 1726 fntemplate = b''
1727 1727
1728 1728 if fntemplate:
1729 1729 fm = formatter.nullformatter(ui, b'cat', opts)
1730 1730 else:
1731 1731 ui.pager(b'cat')
1732 1732 fm = ui.formatter(b'cat', opts)
1733 1733 with fm:
1734 1734 return cmdutil.cat(
1735 1735 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1736 1736 )
1737 1737
1738 1738
1739 1739 @command(
1740 1740 b'clone',
1741 1741 [
1742 1742 (
1743 1743 b'U',
1744 1744 b'noupdate',
1745 1745 None,
1746 1746 _(
1747 1747 b'the clone will include an empty working '
1748 1748 b'directory (only a repository)'
1749 1749 ),
1750 1750 ),
1751 1751 (
1752 1752 b'u',
1753 1753 b'updaterev',
1754 1754 b'',
1755 1755 _(b'revision, tag, or branch to check out'),
1756 1756 _(b'REV'),
1757 1757 ),
1758 1758 (
1759 1759 b'r',
1760 1760 b'rev',
1761 1761 [],
1762 1762 _(
1763 1763 b'do not clone everything, but include this changeset'
1764 1764 b' and its ancestors'
1765 1765 ),
1766 1766 _(b'REV'),
1767 1767 ),
1768 1768 (
1769 1769 b'b',
1770 1770 b'branch',
1771 1771 [],
1772 1772 _(
1773 1773 b'do not clone everything, but include this branch\'s'
1774 1774 b' changesets and their ancestors'
1775 1775 ),
1776 1776 _(b'BRANCH'),
1777 1777 ),
1778 1778 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1779 1779 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1780 1780 (b'', b'stream', None, _(b'clone with minimal data processing')),
1781 1781 ]
1782 1782 + remoteopts,
1783 1783 _(b'[OPTION]... SOURCE [DEST]'),
1784 1784 helpcategory=command.CATEGORY_REPO_CREATION,
1785 1785 helpbasic=True,
1786 1786 norepo=True,
1787 1787 )
1788 1788 def clone(ui, source, dest=None, **opts):
1789 1789 """make a copy of an existing repository
1790 1790
1791 1791 Create a copy of an existing repository in a new directory.
1792 1792
1793 1793 If no destination directory name is specified, it defaults to the
1794 1794 basename of the source.
1795 1795
1796 1796 The location of the source is added to the new repository's
1797 1797 ``.hg/hgrc`` file, as the default to be used for future pulls.
1798 1798
1799 1799 Only local paths and ``ssh://`` URLs are supported as
1800 1800 destinations. For ``ssh://`` destinations, no working directory or
1801 1801 ``.hg/hgrc`` will be created on the remote side.
1802 1802
1803 1803 If the source repository has a bookmark called '@' set, that
1804 1804 revision will be checked out in the new repository by default.
1805 1805
1806 1806 To check out a particular version, use -u/--update, or
1807 1807 -U/--noupdate to create a clone with no working directory.
1808 1808
1809 1809 To pull only a subset of changesets, specify one or more revisions
1810 1810 identifiers with -r/--rev or branches with -b/--branch. The
1811 1811 resulting clone will contain only the specified changesets and
1812 1812 their ancestors. These options (or 'clone src#rev dest') imply
1813 1813 --pull, even for local source repositories.
1814 1814
1815 1815 In normal clone mode, the remote normalizes repository data into a common
1816 1816 exchange format and the receiving end translates this data into its local
1817 1817 storage format. --stream activates a different clone mode that essentially
1818 1818 copies repository files from the remote with minimal data processing. This
1819 1819 significantly reduces the CPU cost of a clone both remotely and locally.
1820 1820 However, it often increases the transferred data size by 30-40%. This can
1821 1821 result in substantially faster clones where I/O throughput is plentiful,
1822 1822 especially for larger repositories. A side-effect of --stream clones is
1823 1823 that storage settings and requirements on the remote are applied locally:
1824 1824 a modern client may inherit legacy or inefficient storage used by the
1825 1825 remote or a legacy Mercurial client may not be able to clone from a
1826 1826 modern Mercurial remote.
1827 1827
1828 1828 .. note::
1829 1829
1830 1830 Specifying a tag will include the tagged changeset but not the
1831 1831 changeset containing the tag.
1832 1832
1833 1833 .. container:: verbose
1834 1834
1835 1835 For efficiency, hardlinks are used for cloning whenever the
1836 1836 source and destination are on the same filesystem (note this
1837 1837 applies only to the repository data, not to the working
1838 1838 directory). Some filesystems, such as AFS, implement hardlinking
1839 1839 incorrectly, but do not report errors. In these cases, use the
1840 1840 --pull option to avoid hardlinking.
1841 1841
1842 1842 Mercurial will update the working directory to the first applicable
1843 1843 revision from this list:
1844 1844
1845 1845 a) null if -U or the source repository has no changesets
1846 1846 b) if -u . and the source repository is local, the first parent of
1847 1847 the source repository's working directory
1848 1848 c) the changeset specified with -u (if a branch name, this means the
1849 1849 latest head of that branch)
1850 1850 d) the changeset specified with -r
1851 1851 e) the tipmost head specified with -b
1852 1852 f) the tipmost head specified with the url#branch source syntax
1853 1853 g) the revision marked with the '@' bookmark, if present
1854 1854 h) the tipmost head of the default branch
1855 1855 i) tip
1856 1856
1857 1857 When cloning from servers that support it, Mercurial may fetch
1858 1858 pre-generated data from a server-advertised URL or inline from the
1859 1859 same stream. When this is done, hooks operating on incoming changesets
1860 1860 and changegroups may fire more than once, once for each pre-generated
1861 1861 bundle and as well as for any additional remaining data. In addition,
1862 1862 if an error occurs, the repository may be rolled back to a partial
1863 1863 clone. This behavior may change in future releases.
1864 1864 See :hg:`help -e clonebundles` for more.
1865 1865
1866 1866 Examples:
1867 1867
1868 1868 - clone a remote repository to a new directory named hg/::
1869 1869
1870 1870 hg clone https://www.mercurial-scm.org/repo/hg/
1871 1871
1872 1872 - create a lightweight local clone::
1873 1873
1874 1874 hg clone project/ project-feature/
1875 1875
1876 1876 - clone from an absolute path on an ssh server (note double-slash)::
1877 1877
1878 1878 hg clone ssh://user@server//home/projects/alpha/
1879 1879
1880 1880 - do a streaming clone while checking out a specified version::
1881 1881
1882 1882 hg clone --stream http://server/repo -u 1.5
1883 1883
1884 1884 - create a repository without changesets after a particular revision::
1885 1885
1886 1886 hg clone -r 04e544 experimental/ good/
1887 1887
1888 1888 - clone (and track) a particular named branch::
1889 1889
1890 1890 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1891 1891
1892 1892 See :hg:`help urls` for details on specifying URLs.
1893 1893
1894 1894 Returns 0 on success.
1895 1895 """
1896 1896 opts = pycompat.byteskwargs(opts)
1897 1897 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1898 1898
1899 1899 # --include/--exclude can come from narrow or sparse.
1900 1900 includepats, excludepats = None, None
1901 1901
1902 1902 # hg.clone() differentiates between None and an empty set. So make sure
1903 1903 # patterns are sets if narrow is requested without patterns.
1904 1904 if opts.get(b'narrow'):
1905 1905 includepats = set()
1906 1906 excludepats = set()
1907 1907
1908 1908 if opts.get(b'include'):
1909 1909 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1910 1910 if opts.get(b'exclude'):
1911 1911 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1912 1912
1913 1913 r = hg.clone(
1914 1914 ui,
1915 1915 opts,
1916 1916 source,
1917 1917 dest,
1918 1918 pull=opts.get(b'pull'),
1919 1919 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1920 1920 revs=opts.get(b'rev'),
1921 1921 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1922 1922 branch=opts.get(b'branch'),
1923 1923 shareopts=opts.get(b'shareopts'),
1924 1924 storeincludepats=includepats,
1925 1925 storeexcludepats=excludepats,
1926 1926 depth=opts.get(b'depth') or None,
1927 1927 )
1928 1928
1929 1929 return r is None
1930 1930
1931 1931
1932 1932 @command(
1933 1933 b'commit|ci',
1934 1934 [
1935 1935 (
1936 1936 b'A',
1937 1937 b'addremove',
1938 1938 None,
1939 1939 _(b'mark new/missing files as added/removed before committing'),
1940 1940 ),
1941 1941 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1942 1942 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1943 1943 (b's', b'secret', None, _(b'use the secret phase for committing')),
1944 1944 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1945 1945 (
1946 1946 b'',
1947 1947 b'force-close-branch',
1948 1948 None,
1949 1949 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1950 1950 ),
1951 1951 (b'i', b'interactive', None, _(b'use interactive mode')),
1952 1952 ]
1953 1953 + walkopts
1954 1954 + commitopts
1955 1955 + commitopts2
1956 1956 + subrepoopts,
1957 1957 _(b'[OPTION]... [FILE]...'),
1958 1958 helpcategory=command.CATEGORY_COMMITTING,
1959 1959 helpbasic=True,
1960 1960 inferrepo=True,
1961 1961 )
1962 1962 def commit(ui, repo, *pats, **opts):
1963 1963 """commit the specified files or all outstanding changes
1964 1964
1965 1965 Commit changes to the given files into the repository. Unlike a
1966 1966 centralized SCM, this operation is a local operation. See
1967 1967 :hg:`push` for a way to actively distribute your changes.
1968 1968
1969 1969 If a list of files is omitted, all changes reported by :hg:`status`
1970 1970 will be committed.
1971 1971
1972 1972 If you are committing the result of a merge, do not provide any
1973 1973 filenames or -I/-X filters.
1974 1974
1975 1975 If no commit message is specified, Mercurial starts your
1976 1976 configured editor where you can enter a message. In case your
1977 1977 commit fails, you will find a backup of your message in
1978 1978 ``.hg/last-message.txt``.
1979 1979
1980 1980 The --close-branch flag can be used to mark the current branch
1981 1981 head closed. When all heads of a branch are closed, the branch
1982 1982 will be considered closed and no longer listed.
1983 1983
1984 1984 The --amend flag can be used to amend the parent of the
1985 1985 working directory with a new commit that contains the changes
1986 1986 in the parent in addition to those currently reported by :hg:`status`,
1987 1987 if there are any. The old commit is stored in a backup bundle in
1988 1988 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1989 1989 on how to restore it).
1990 1990
1991 1991 Message, user and date are taken from the amended commit unless
1992 1992 specified. When a message isn't specified on the command line,
1993 1993 the editor will open with the message of the amended commit.
1994 1994
1995 1995 It is not possible to amend public changesets (see :hg:`help phases`)
1996 1996 or changesets that have children.
1997 1997
1998 1998 See :hg:`help dates` for a list of formats valid for -d/--date.
1999 1999
2000 2000 Returns 0 on success, 1 if nothing changed.
2001 2001
2002 2002 .. container:: verbose
2003 2003
2004 2004 Examples:
2005 2005
2006 2006 - commit all files ending in .py::
2007 2007
2008 2008 hg commit --include "set:**.py"
2009 2009
2010 2010 - commit all non-binary files::
2011 2011
2012 2012 hg commit --exclude "set:binary()"
2013 2013
2014 2014 - amend the current commit and set the date to now::
2015 2015
2016 2016 hg commit --amend --date now
2017 2017 """
2018 2018 with repo.wlock(), repo.lock():
2019 2019 return _docommit(ui, repo, *pats, **opts)
2020 2020
2021 2021
2022 2022 def _docommit(ui, repo, *pats, **opts):
2023 2023 if opts.get('interactive'):
2024 2024 opts.pop('interactive')
2025 2025 ret = cmdutil.dorecord(
2026 2026 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2027 2027 )
2028 2028 # ret can be 0 (no changes to record) or the value returned by
2029 2029 # commit(), 1 if nothing changed or None on success.
2030 2030 return 1 if ret == 0 else ret
2031 2031
2032 2032 opts = pycompat.byteskwargs(opts)
2033 2033 if opts.get(b'subrepos'):
2034 2034 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'amend'])
2035 2035 # Let --subrepos on the command line override config setting.
2036 2036 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2037 2037
2038 2038 cmdutil.checkunfinished(repo, commit=True)
2039 2039
2040 2040 branch = repo[None].branch()
2041 2041 bheads = repo.branchheads(branch)
2042 2042 tip = repo.changelog.tip()
2043 2043
2044 2044 extra = {}
2045 2045 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2046 2046 extra[b'close'] = b'1'
2047 2047
2048 2048 if repo[b'.'].closesbranch():
2049 2049 raise error.InputError(
2050 2050 _(b'current revision is already a branch closing head')
2051 2051 )
2052 2052 elif not bheads:
2053 2053 raise error.InputError(
2054 2054 _(b'branch "%s" has no heads to close') % branch
2055 2055 )
2056 2056 elif (
2057 2057 branch == repo[b'.'].branch()
2058 2058 and repo[b'.'].node() not in bheads
2059 2059 and not opts.get(b'force_close_branch')
2060 2060 ):
2061 2061 hint = _(
2062 2062 b'use --force-close-branch to close branch from a non-head'
2063 2063 b' changeset'
2064 2064 )
2065 2065 raise error.InputError(_(b'can only close branch heads'), hint=hint)
2066 2066 elif opts.get(b'amend'):
2067 2067 if (
2068 2068 repo[b'.'].p1().branch() != branch
2069 2069 and repo[b'.'].p2().branch() != branch
2070 2070 ):
2071 2071 raise error.InputError(_(b'can only close branch heads'))
2072 2072
2073 2073 if opts.get(b'amend'):
2074 2074 if ui.configbool(b'ui', b'commitsubrepos'):
2075 2075 raise error.InputError(
2076 2076 _(b'cannot amend with ui.commitsubrepos enabled')
2077 2077 )
2078 2078
2079 2079 old = repo[b'.']
2080 2080 rewriteutil.precheck(repo, [old.rev()], b'amend')
2081 2081
2082 2082 # Currently histedit gets confused if an amend happens while histedit
2083 2083 # is in progress. Since we have a checkunfinished command, we are
2084 2084 # temporarily honoring it.
2085 2085 #
2086 2086 # Note: eventually this guard will be removed. Please do not expect
2087 2087 # this behavior to remain.
2088 2088 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2089 2089 cmdutil.checkunfinished(repo)
2090 2090
2091 2091 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2092 2092 if node == old.node():
2093 2093 ui.status(_(b"nothing changed\n"))
2094 2094 return 1
2095 2095 else:
2096 2096
2097 2097 def commitfunc(ui, repo, message, match, opts):
2098 2098 overrides = {}
2099 2099 if opts.get(b'secret'):
2100 2100 overrides[(b'phases', b'new-commit')] = b'secret'
2101 2101
2102 2102 baseui = repo.baseui
2103 2103 with baseui.configoverride(overrides, b'commit'):
2104 2104 with ui.configoverride(overrides, b'commit'):
2105 2105 editform = cmdutil.mergeeditform(
2106 2106 repo[None], b'commit.normal'
2107 2107 )
2108 2108 editor = cmdutil.getcommiteditor(
2109 2109 editform=editform, **pycompat.strkwargs(opts)
2110 2110 )
2111 2111 return repo.commit(
2112 2112 message,
2113 2113 opts.get(b'user'),
2114 2114 opts.get(b'date'),
2115 2115 match,
2116 2116 editor=editor,
2117 2117 extra=extra,
2118 2118 )
2119 2119
2120 2120 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2121 2121
2122 2122 if not node:
2123 2123 stat = cmdutil.postcommitstatus(repo, pats, opts)
2124 2124 if stat.deleted:
2125 2125 ui.status(
2126 2126 _(
2127 2127 b"nothing changed (%d missing files, see "
2128 2128 b"'hg status')\n"
2129 2129 )
2130 2130 % len(stat.deleted)
2131 2131 )
2132 2132 else:
2133 2133 ui.status(_(b"nothing changed\n"))
2134 2134 return 1
2135 2135
2136 2136 cmdutil.commitstatus(repo, node, branch, bheads, tip, opts)
2137 2137
2138 2138 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2139 2139 status(
2140 2140 ui,
2141 2141 repo,
2142 2142 modified=True,
2143 2143 added=True,
2144 2144 removed=True,
2145 2145 deleted=True,
2146 2146 unknown=True,
2147 2147 subrepos=opts.get(b'subrepos'),
2148 2148 )
2149 2149
2150 2150
2151 2151 @command(
2152 2152 b'config|showconfig|debugconfig',
2153 2153 [
2154 2154 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2155 2155 (b'e', b'edit', None, _(b'edit user config')),
2156 2156 (b'l', b'local', None, _(b'edit repository config')),
2157 2157 (
2158 2158 b'',
2159 2159 b'shared',
2160 2160 None,
2161 2161 _(b'edit shared source repository config (EXPERIMENTAL)'),
2162 2162 ),
2163 2163 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2164 2164 (b'g', b'global', None, _(b'edit global config')),
2165 2165 ]
2166 2166 + formatteropts,
2167 2167 _(b'[-u] [NAME]...'),
2168 2168 helpcategory=command.CATEGORY_HELP,
2169 2169 optionalrepo=True,
2170 2170 intents={INTENT_READONLY},
2171 2171 )
2172 2172 def config(ui, repo, *values, **opts):
2173 2173 """show combined config settings from all hgrc files
2174 2174
2175 2175 With no arguments, print names and values of all config items.
2176 2176
2177 2177 With one argument of the form section.name, print just the value
2178 2178 of that config item.
2179 2179
2180 2180 With multiple arguments, print names and values of all config
2181 2181 items with matching section names or section.names.
2182 2182
2183 2183 With --edit, start an editor on the user-level config file. With
2184 2184 --global, edit the system-wide config file. With --local, edit the
2185 2185 repository-level config file.
2186 2186
2187 2187 With --debug, the source (filename and line number) is printed
2188 2188 for each config item.
2189 2189
2190 2190 See :hg:`help config` for more information about config files.
2191 2191
2192 2192 .. container:: verbose
2193 2193
2194 2194 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2195 2195 This file is not shared across shares when in share-safe mode.
2196 2196
2197 2197 Template:
2198 2198
2199 2199 The following keywords are supported. See also :hg:`help templates`.
2200 2200
2201 2201 :name: String. Config name.
2202 2202 :source: String. Filename and line number where the item is defined.
2203 2203 :value: String. Config value.
2204 2204
2205 2205 The --shared flag can be used to edit the config file of shared source
2206 2206 repository. It only works when you have shared using the experimental
2207 2207 share safe feature.
2208 2208
2209 2209 Returns 0 on success, 1 if NAME does not exist.
2210 2210
2211 2211 """
2212 2212
2213 2213 opts = pycompat.byteskwargs(opts)
2214 2214 editopts = (b'edit', b'local', b'global', b'shared', b'non_shared')
2215 2215 if any(opts.get(o) for o in editopts):
2216 2216 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2217 2217 if opts.get(b'local'):
2218 2218 if not repo:
2219 2219 raise error.InputError(
2220 2220 _(b"can't use --local outside a repository")
2221 2221 )
2222 2222 paths = [repo.vfs.join(b'hgrc')]
2223 2223 elif opts.get(b'global'):
2224 2224 paths = rcutil.systemrcpath()
2225 2225 elif opts.get(b'shared'):
2226 2226 if not repo.shared():
2227 2227 raise error.InputError(
2228 2228 _(b"repository is not shared; can't use --shared")
2229 2229 )
2230 2230 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2231 2231 raise error.InputError(
2232 2232 _(
2233 2233 b"share safe feature not unabled; "
2234 2234 b"unable to edit shared source repository config"
2235 2235 )
2236 2236 )
2237 2237 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2238 2238 elif opts.get(b'non_shared'):
2239 2239 paths = [repo.vfs.join(b'hgrc-not-shared')]
2240 2240 else:
2241 2241 paths = rcutil.userrcpath()
2242 2242
2243 2243 for f in paths:
2244 2244 if os.path.exists(f):
2245 2245 break
2246 2246 else:
2247 2247 if opts.get(b'global'):
2248 2248 samplehgrc = uimod.samplehgrcs[b'global']
2249 2249 elif opts.get(b'local'):
2250 2250 samplehgrc = uimod.samplehgrcs[b'local']
2251 2251 else:
2252 2252 samplehgrc = uimod.samplehgrcs[b'user']
2253 2253
2254 2254 f = paths[0]
2255 2255 fp = open(f, b"wb")
2256 2256 fp.write(util.tonativeeol(samplehgrc))
2257 2257 fp.close()
2258 2258
2259 2259 editor = ui.geteditor()
2260 2260 ui.system(
2261 2261 b"%s \"%s\"" % (editor, f),
2262 2262 onerr=error.InputError,
2263 2263 errprefix=_(b"edit failed"),
2264 2264 blockedtag=b'config_edit',
2265 2265 )
2266 2266 return
2267 2267 ui.pager(b'config')
2268 2268 fm = ui.formatter(b'config', opts)
2269 2269 for t, f in rcutil.rccomponents():
2270 2270 if t == b'path':
2271 2271 ui.debug(b'read config from: %s\n' % f)
2272 2272 elif t == b'resource':
2273 2273 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2274 2274 elif t == b'items':
2275 2275 # Don't print anything for 'items'.
2276 2276 pass
2277 2277 else:
2278 2278 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2279 2279 untrusted = bool(opts.get(b'untrusted'))
2280 2280
2281 2281 selsections = selentries = []
2282 2282 if values:
2283 2283 selsections = [v for v in values if b'.' not in v]
2284 2284 selentries = [v for v in values if b'.' in v]
2285 2285 uniquesel = len(selentries) == 1 and not selsections
2286 2286 selsections = set(selsections)
2287 2287 selentries = set(selentries)
2288 2288
2289 2289 matched = False
2290 2290 for section, name, value in ui.walkconfig(untrusted=untrusted):
2291 2291 source = ui.configsource(section, name, untrusted)
2292 2292 value = pycompat.bytestr(value)
2293 2293 defaultvalue = ui.configdefault(section, name)
2294 2294 if fm.isplain():
2295 2295 source = source or b'none'
2296 2296 value = value.replace(b'\n', b'\\n')
2297 2297 entryname = section + b'.' + name
2298 2298 if values and not (section in selsections or entryname in selentries):
2299 2299 continue
2300 2300 fm.startitem()
2301 2301 fm.condwrite(ui.debugflag, b'source', b'%s: ', source)
2302 2302 if uniquesel:
2303 2303 fm.data(name=entryname)
2304 2304 fm.write(b'value', b'%s\n', value)
2305 2305 else:
2306 2306 fm.write(b'name value', b'%s=%s\n', entryname, value)
2307 2307 if formatter.isprintable(defaultvalue):
2308 2308 fm.data(defaultvalue=defaultvalue)
2309 2309 elif isinstance(defaultvalue, list) and all(
2310 2310 formatter.isprintable(e) for e in defaultvalue
2311 2311 ):
2312 2312 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2313 2313 # TODO: no idea how to process unsupported defaultvalue types
2314 2314 matched = True
2315 2315 fm.end()
2316 2316 if matched:
2317 2317 return 0
2318 2318 return 1
2319 2319
2320 2320
2321 2321 @command(
2322 2322 b'continue',
2323 2323 dryrunopts,
2324 2324 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2325 2325 helpbasic=True,
2326 2326 )
2327 2327 def continuecmd(ui, repo, **opts):
2328 2328 """resumes an interrupted operation (EXPERIMENTAL)
2329 2329
2330 2330 Finishes a multistep operation like graft, histedit, rebase, merge,
2331 2331 and unshelve if they are in an interrupted state.
2332 2332
2333 2333 use --dry-run/-n to dry run the command.
2334 2334 """
2335 2335 dryrun = opts.get('dry_run')
2336 2336 contstate = cmdutil.getunfinishedstate(repo)
2337 2337 if not contstate:
2338 raise error.Abort(_(b'no operation in progress'))
2338 raise error.StateError(_(b'no operation in progress'))
2339 2339 if not contstate.continuefunc:
2340 raise error.Abort(
2340 raise error.StateError(
2341 2341 (
2342 2342 _(b"%s in progress but does not support 'hg continue'")
2343 2343 % (contstate._opname)
2344 2344 ),
2345 2345 hint=contstate.continuemsg(),
2346 2346 )
2347 2347 if dryrun:
2348 2348 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2349 2349 return
2350 2350 return contstate.continuefunc(ui, repo)
2351 2351
2352 2352
2353 2353 @command(
2354 2354 b'copy|cp',
2355 2355 [
2356 2356 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2357 2357 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2358 2358 (
2359 2359 b'',
2360 2360 b'at-rev',
2361 2361 b'',
2362 2362 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2363 2363 _(b'REV'),
2364 2364 ),
2365 2365 (
2366 2366 b'f',
2367 2367 b'force',
2368 2368 None,
2369 2369 _(b'forcibly copy over an existing managed file'),
2370 2370 ),
2371 2371 ]
2372 2372 + walkopts
2373 2373 + dryrunopts,
2374 2374 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2375 2375 helpcategory=command.CATEGORY_FILE_CONTENTS,
2376 2376 )
2377 2377 def copy(ui, repo, *pats, **opts):
2378 2378 """mark files as copied for the next commit
2379 2379
2380 2380 Mark dest as having copies of source files. If dest is a
2381 2381 directory, copies are put in that directory. If dest is a file,
2382 2382 the source must be a single file.
2383 2383
2384 2384 By default, this command copies the contents of files as they
2385 2385 exist in the working directory. If invoked with -A/--after, the
2386 2386 operation is recorded, but no copying is performed.
2387 2387
2388 2388 To undo marking a destination file as copied, use --forget. With that
2389 2389 option, all given (positional) arguments are unmarked as copies. The
2390 2390 destination file(s) will be left in place (still tracked).
2391 2391
2392 2392 This command takes effect with the next commit by default.
2393 2393
2394 2394 Returns 0 on success, 1 if errors are encountered.
2395 2395 """
2396 2396 opts = pycompat.byteskwargs(opts)
2397 2397 with repo.wlock():
2398 2398 return cmdutil.copy(ui, repo, pats, opts)
2399 2399
2400 2400
2401 2401 @command(
2402 2402 b'debugcommands',
2403 2403 [],
2404 2404 _(b'[COMMAND]'),
2405 2405 helpcategory=command.CATEGORY_HELP,
2406 2406 norepo=True,
2407 2407 )
2408 2408 def debugcommands(ui, cmd=b'', *args):
2409 2409 """list all available commands and options"""
2410 2410 for cmd, vals in sorted(pycompat.iteritems(table)):
2411 2411 cmd = cmd.split(b'|')[0]
2412 2412 opts = b', '.join([i[1] for i in vals[1]])
2413 2413 ui.write(b'%s: %s\n' % (cmd, opts))
2414 2414
2415 2415
2416 2416 @command(
2417 2417 b'debugcomplete',
2418 2418 [(b'o', b'options', None, _(b'show the command options'))],
2419 2419 _(b'[-o] CMD'),
2420 2420 helpcategory=command.CATEGORY_HELP,
2421 2421 norepo=True,
2422 2422 )
2423 2423 def debugcomplete(ui, cmd=b'', **opts):
2424 2424 """returns the completion list associated with the given command"""
2425 2425
2426 2426 if opts.get('options'):
2427 2427 options = []
2428 2428 otables = [globalopts]
2429 2429 if cmd:
2430 2430 aliases, entry = cmdutil.findcmd(cmd, table, False)
2431 2431 otables.append(entry[1])
2432 2432 for t in otables:
2433 2433 for o in t:
2434 2434 if b"(DEPRECATED)" in o[3]:
2435 2435 continue
2436 2436 if o[0]:
2437 2437 options.append(b'-%s' % o[0])
2438 2438 options.append(b'--%s' % o[1])
2439 2439 ui.write(b"%s\n" % b"\n".join(options))
2440 2440 return
2441 2441
2442 2442 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2443 2443 if ui.verbose:
2444 2444 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2445 2445 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2446 2446
2447 2447
2448 2448 @command(
2449 2449 b'diff',
2450 2450 [
2451 2451 (b'r', b'rev', [], _(b'revision'), _(b'REV')),
2452 2452 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2453 2453 ]
2454 2454 + diffopts
2455 2455 + diffopts2
2456 2456 + walkopts
2457 2457 + subrepoopts,
2458 2458 _(b'[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2459 2459 helpcategory=command.CATEGORY_FILE_CONTENTS,
2460 2460 helpbasic=True,
2461 2461 inferrepo=True,
2462 2462 intents={INTENT_READONLY},
2463 2463 )
2464 2464 def diff(ui, repo, *pats, **opts):
2465 2465 """diff repository (or selected files)
2466 2466
2467 2467 Show differences between revisions for the specified files.
2468 2468
2469 2469 Differences between files are shown using the unified diff format.
2470 2470
2471 2471 .. note::
2472 2472
2473 2473 :hg:`diff` may generate unexpected results for merges, as it will
2474 2474 default to comparing against the working directory's first
2475 2475 parent changeset if no revisions are specified.
2476 2476
2477 2477 When two revision arguments are given, then changes are shown
2478 2478 between those revisions. If only one revision is specified then
2479 2479 that revision is compared to the working directory, and, when no
2480 2480 revisions are specified, the working directory files are compared
2481 2481 to its first parent.
2482 2482
2483 2483 Alternatively you can specify -c/--change with a revision to see
2484 2484 the changes in that changeset relative to its first parent.
2485 2485
2486 2486 Without the -a/--text option, diff will avoid generating diffs of
2487 2487 files it detects as binary. With -a, diff will generate a diff
2488 2488 anyway, probably with undesirable results.
2489 2489
2490 2490 Use the -g/--git option to generate diffs in the git extended diff
2491 2491 format. For more information, read :hg:`help diffs`.
2492 2492
2493 2493 .. container:: verbose
2494 2494
2495 2495 Examples:
2496 2496
2497 2497 - compare a file in the current working directory to its parent::
2498 2498
2499 2499 hg diff foo.c
2500 2500
2501 2501 - compare two historical versions of a directory, with rename info::
2502 2502
2503 2503 hg diff --git -r 1.0:1.2 lib/
2504 2504
2505 2505 - get change stats relative to the last change on some date::
2506 2506
2507 2507 hg diff --stat -r "date('may 2')"
2508 2508
2509 2509 - diff all newly-added files that contain a keyword::
2510 2510
2511 2511 hg diff "set:added() and grep(GNU)"
2512 2512
2513 2513 - compare a revision and its parents::
2514 2514
2515 2515 hg diff -c 9353 # compare against first parent
2516 2516 hg diff -r 9353^:9353 # same using revset syntax
2517 2517 hg diff -r 9353^2:9353 # compare against the second parent
2518 2518
2519 2519 Returns 0 on success.
2520 2520 """
2521 2521
2522 2522 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2523 2523 opts = pycompat.byteskwargs(opts)
2524 2524 revs = opts.get(b'rev')
2525 2525 change = opts.get(b'change')
2526 2526 stat = opts.get(b'stat')
2527 2527 reverse = opts.get(b'reverse')
2528 2528
2529 2529 if change:
2530 2530 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2531 2531 ctx2 = scmutil.revsingle(repo, change, None)
2532 2532 ctx1 = ctx2.p1()
2533 2533 else:
2534 2534 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2535 2535 ctx1, ctx2 = scmutil.revpair(repo, revs)
2536 2536
2537 2537 if reverse:
2538 2538 ctxleft = ctx2
2539 2539 ctxright = ctx1
2540 2540 else:
2541 2541 ctxleft = ctx1
2542 2542 ctxright = ctx2
2543 2543
2544 2544 diffopts = patch.diffallopts(ui, opts)
2545 2545 m = scmutil.match(ctx2, pats, opts)
2546 2546 m = repo.narrowmatch(m)
2547 2547 ui.pager(b'diff')
2548 2548 logcmdutil.diffordiffstat(
2549 2549 ui,
2550 2550 repo,
2551 2551 diffopts,
2552 2552 ctxleft,
2553 2553 ctxright,
2554 2554 m,
2555 2555 stat=stat,
2556 2556 listsubrepos=opts.get(b'subrepos'),
2557 2557 root=opts.get(b'root'),
2558 2558 )
2559 2559
2560 2560
2561 2561 @command(
2562 2562 b'export',
2563 2563 [
2564 2564 (
2565 2565 b'B',
2566 2566 b'bookmark',
2567 2567 b'',
2568 2568 _(b'export changes only reachable by given bookmark'),
2569 2569 _(b'BOOKMARK'),
2570 2570 ),
2571 2571 (
2572 2572 b'o',
2573 2573 b'output',
2574 2574 b'',
2575 2575 _(b'print output to file with formatted name'),
2576 2576 _(b'FORMAT'),
2577 2577 ),
2578 2578 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2579 2579 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2580 2580 ]
2581 2581 + diffopts
2582 2582 + formatteropts,
2583 2583 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2584 2584 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2585 2585 helpbasic=True,
2586 2586 intents={INTENT_READONLY},
2587 2587 )
2588 2588 def export(ui, repo, *changesets, **opts):
2589 2589 """dump the header and diffs for one or more changesets
2590 2590
2591 2591 Print the changeset header and diffs for one or more revisions.
2592 2592 If no revision is given, the parent of the working directory is used.
2593 2593
2594 2594 The information shown in the changeset header is: author, date,
2595 2595 branch name (if non-default), changeset hash, parent(s) and commit
2596 2596 comment.
2597 2597
2598 2598 .. note::
2599 2599
2600 2600 :hg:`export` may generate unexpected diff output for merge
2601 2601 changesets, as it will compare the merge changeset against its
2602 2602 first parent only.
2603 2603
2604 2604 Output may be to a file, in which case the name of the file is
2605 2605 given using a template string. See :hg:`help templates`. In addition
2606 2606 to the common template keywords, the following formatting rules are
2607 2607 supported:
2608 2608
2609 2609 :``%%``: literal "%" character
2610 2610 :``%H``: changeset hash (40 hexadecimal digits)
2611 2611 :``%N``: number of patches being generated
2612 2612 :``%R``: changeset revision number
2613 2613 :``%b``: basename of the exporting repository
2614 2614 :``%h``: short-form changeset hash (12 hexadecimal digits)
2615 2615 :``%m``: first line of the commit message (only alphanumeric characters)
2616 2616 :``%n``: zero-padded sequence number, starting at 1
2617 2617 :``%r``: zero-padded changeset revision number
2618 2618 :``\\``: literal "\\" character
2619 2619
2620 2620 Without the -a/--text option, export will avoid generating diffs
2621 2621 of files it detects as binary. With -a, export will generate a
2622 2622 diff anyway, probably with undesirable results.
2623 2623
2624 2624 With -B/--bookmark changesets reachable by the given bookmark are
2625 2625 selected.
2626 2626
2627 2627 Use the -g/--git option to generate diffs in the git extended diff
2628 2628 format. See :hg:`help diffs` for more information.
2629 2629
2630 2630 With the --switch-parent option, the diff will be against the
2631 2631 second parent. It can be useful to review a merge.
2632 2632
2633 2633 .. container:: verbose
2634 2634
2635 2635 Template:
2636 2636
2637 2637 The following keywords are supported in addition to the common template
2638 2638 keywords and functions. See also :hg:`help templates`.
2639 2639
2640 2640 :diff: String. Diff content.
2641 2641 :parents: List of strings. Parent nodes of the changeset.
2642 2642
2643 2643 Examples:
2644 2644
2645 2645 - use export and import to transplant a bugfix to the current
2646 2646 branch::
2647 2647
2648 2648 hg export -r 9353 | hg import -
2649 2649
2650 2650 - export all the changesets between two revisions to a file with
2651 2651 rename information::
2652 2652
2653 2653 hg export --git -r 123:150 > changes.txt
2654 2654
2655 2655 - split outgoing changes into a series of patches with
2656 2656 descriptive names::
2657 2657
2658 2658 hg export -r "outgoing()" -o "%n-%m.patch"
2659 2659
2660 2660 Returns 0 on success.
2661 2661 """
2662 2662 opts = pycompat.byteskwargs(opts)
2663 2663 bookmark = opts.get(b'bookmark')
2664 2664 changesets += tuple(opts.get(b'rev', []))
2665 2665
2666 2666 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2667 2667
2668 2668 if bookmark:
2669 2669 if bookmark not in repo._bookmarks:
2670 2670 raise error.InputError(_(b"bookmark '%s' not found") % bookmark)
2671 2671
2672 2672 revs = scmutil.bookmarkrevs(repo, bookmark)
2673 2673 else:
2674 2674 if not changesets:
2675 2675 changesets = [b'.']
2676 2676
2677 2677 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2678 2678 revs = scmutil.revrange(repo, changesets)
2679 2679
2680 2680 if not revs:
2681 2681 raise error.InputError(_(b"export requires at least one changeset"))
2682 2682 if len(revs) > 1:
2683 2683 ui.note(_(b'exporting patches:\n'))
2684 2684 else:
2685 2685 ui.note(_(b'exporting patch:\n'))
2686 2686
2687 2687 fntemplate = opts.get(b'output')
2688 2688 if cmdutil.isstdiofilename(fntemplate):
2689 2689 fntemplate = b''
2690 2690
2691 2691 if fntemplate:
2692 2692 fm = formatter.nullformatter(ui, b'export', opts)
2693 2693 else:
2694 2694 ui.pager(b'export')
2695 2695 fm = ui.formatter(b'export', opts)
2696 2696 with fm:
2697 2697 cmdutil.export(
2698 2698 repo,
2699 2699 revs,
2700 2700 fm,
2701 2701 fntemplate=fntemplate,
2702 2702 switch_parent=opts.get(b'switch_parent'),
2703 2703 opts=patch.diffallopts(ui, opts),
2704 2704 )
2705 2705
2706 2706
2707 2707 @command(
2708 2708 b'files',
2709 2709 [
2710 2710 (
2711 2711 b'r',
2712 2712 b'rev',
2713 2713 b'',
2714 2714 _(b'search the repository as it is in REV'),
2715 2715 _(b'REV'),
2716 2716 ),
2717 2717 (
2718 2718 b'0',
2719 2719 b'print0',
2720 2720 None,
2721 2721 _(b'end filenames with NUL, for use with xargs'),
2722 2722 ),
2723 2723 ]
2724 2724 + walkopts
2725 2725 + formatteropts
2726 2726 + subrepoopts,
2727 2727 _(b'[OPTION]... [FILE]...'),
2728 2728 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2729 2729 intents={INTENT_READONLY},
2730 2730 )
2731 2731 def files(ui, repo, *pats, **opts):
2732 2732 """list tracked files
2733 2733
2734 2734 Print files under Mercurial control in the working directory or
2735 2735 specified revision for given files (excluding removed files).
2736 2736 Files can be specified as filenames or filesets.
2737 2737
2738 2738 If no files are given to match, this command prints the names
2739 2739 of all files under Mercurial control.
2740 2740
2741 2741 .. container:: verbose
2742 2742
2743 2743 Template:
2744 2744
2745 2745 The following keywords are supported in addition to the common template
2746 2746 keywords and functions. See also :hg:`help templates`.
2747 2747
2748 2748 :flags: String. Character denoting file's symlink and executable bits.
2749 2749 :path: String. Repository-absolute path of the file.
2750 2750 :size: Integer. Size of the file in bytes.
2751 2751
2752 2752 Examples:
2753 2753
2754 2754 - list all files under the current directory::
2755 2755
2756 2756 hg files .
2757 2757
2758 2758 - shows sizes and flags for current revision::
2759 2759
2760 2760 hg files -vr .
2761 2761
2762 2762 - list all files named README::
2763 2763
2764 2764 hg files -I "**/README"
2765 2765
2766 2766 - list all binary files::
2767 2767
2768 2768 hg files "set:binary()"
2769 2769
2770 2770 - find files containing a regular expression::
2771 2771
2772 2772 hg files "set:grep('bob')"
2773 2773
2774 2774 - search tracked file contents with xargs and grep::
2775 2775
2776 2776 hg files -0 | xargs -0 grep foo
2777 2777
2778 2778 See :hg:`help patterns` and :hg:`help filesets` for more information
2779 2779 on specifying file patterns.
2780 2780
2781 2781 Returns 0 if a match is found, 1 otherwise.
2782 2782
2783 2783 """
2784 2784
2785 2785 opts = pycompat.byteskwargs(opts)
2786 2786 rev = opts.get(b'rev')
2787 2787 if rev:
2788 2788 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2789 2789 ctx = scmutil.revsingle(repo, rev, None)
2790 2790
2791 2791 end = b'\n'
2792 2792 if opts.get(b'print0'):
2793 2793 end = b'\0'
2794 2794 fmt = b'%s' + end
2795 2795
2796 2796 m = scmutil.match(ctx, pats, opts)
2797 2797 ui.pager(b'files')
2798 2798 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2799 2799 with ui.formatter(b'files', opts) as fm:
2800 2800 return cmdutil.files(
2801 2801 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2802 2802 )
2803 2803
2804 2804
2805 2805 @command(
2806 2806 b'forget',
2807 2807 [(b'i', b'interactive', None, _(b'use interactive mode')),]
2808 2808 + walkopts
2809 2809 + dryrunopts,
2810 2810 _(b'[OPTION]... FILE...'),
2811 2811 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2812 2812 helpbasic=True,
2813 2813 inferrepo=True,
2814 2814 )
2815 2815 def forget(ui, repo, *pats, **opts):
2816 2816 """forget the specified files on the next commit
2817 2817
2818 2818 Mark the specified files so they will no longer be tracked
2819 2819 after the next commit.
2820 2820
2821 2821 This only removes files from the current branch, not from the
2822 2822 entire project history, and it does not delete them from the
2823 2823 working directory.
2824 2824
2825 2825 To delete the file from the working directory, see :hg:`remove`.
2826 2826
2827 2827 To undo a forget before the next commit, see :hg:`add`.
2828 2828
2829 2829 .. container:: verbose
2830 2830
2831 2831 Examples:
2832 2832
2833 2833 - forget newly-added binary files::
2834 2834
2835 2835 hg forget "set:added() and binary()"
2836 2836
2837 2837 - forget files that would be excluded by .hgignore::
2838 2838
2839 2839 hg forget "set:hgignore()"
2840 2840
2841 2841 Returns 0 on success.
2842 2842 """
2843 2843
2844 2844 opts = pycompat.byteskwargs(opts)
2845 2845 if not pats:
2846 2846 raise error.InputError(_(b'no files specified'))
2847 2847
2848 2848 m = scmutil.match(repo[None], pats, opts)
2849 2849 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2850 2850 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2851 2851 rejected = cmdutil.forget(
2852 2852 ui,
2853 2853 repo,
2854 2854 m,
2855 2855 prefix=b"",
2856 2856 uipathfn=uipathfn,
2857 2857 explicitonly=False,
2858 2858 dryrun=dryrun,
2859 2859 interactive=interactive,
2860 2860 )[0]
2861 2861 return rejected and 1 or 0
2862 2862
2863 2863
2864 2864 @command(
2865 2865 b'graft',
2866 2866 [
2867 2867 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2868 2868 (
2869 2869 b'',
2870 2870 b'base',
2871 2871 b'',
2872 2872 _(b'base revision when doing the graft merge (ADVANCED)'),
2873 2873 _(b'REV'),
2874 2874 ),
2875 2875 (b'c', b'continue', False, _(b'resume interrupted graft')),
2876 2876 (b'', b'stop', False, _(b'stop interrupted graft')),
2877 2877 (b'', b'abort', False, _(b'abort interrupted graft')),
2878 2878 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2879 2879 (b'', b'log', None, _(b'append graft info to log message')),
2880 2880 (
2881 2881 b'',
2882 2882 b'no-commit',
2883 2883 None,
2884 2884 _(b"don't commit, just apply the changes in working directory"),
2885 2885 ),
2886 2886 (b'f', b'force', False, _(b'force graft')),
2887 2887 (
2888 2888 b'D',
2889 2889 b'currentdate',
2890 2890 False,
2891 2891 _(b'record the current date as commit date'),
2892 2892 ),
2893 2893 (
2894 2894 b'U',
2895 2895 b'currentuser',
2896 2896 False,
2897 2897 _(b'record the current user as committer'),
2898 2898 ),
2899 2899 ]
2900 2900 + commitopts2
2901 2901 + mergetoolopts
2902 2902 + dryrunopts,
2903 2903 _(b'[OPTION]... [-r REV]... REV...'),
2904 2904 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2905 2905 )
2906 2906 def graft(ui, repo, *revs, **opts):
2907 2907 '''copy changes from other branches onto the current branch
2908 2908
2909 2909 This command uses Mercurial's merge logic to copy individual
2910 2910 changes from other branches without merging branches in the
2911 2911 history graph. This is sometimes known as 'backporting' or
2912 2912 'cherry-picking'. By default, graft will copy user, date, and
2913 2913 description from the source changesets.
2914 2914
2915 2915 Changesets that are ancestors of the current revision, that have
2916 2916 already been grafted, or that are merges will be skipped.
2917 2917
2918 2918 If --log is specified, log messages will have a comment appended
2919 2919 of the form::
2920 2920
2921 2921 (grafted from CHANGESETHASH)
2922 2922
2923 2923 If --force is specified, revisions will be grafted even if they
2924 2924 are already ancestors of, or have been grafted to, the destination.
2925 2925 This is useful when the revisions have since been backed out.
2926 2926
2927 2927 If a graft merge results in conflicts, the graft process is
2928 2928 interrupted so that the current merge can be manually resolved.
2929 2929 Once all conflicts are addressed, the graft process can be
2930 2930 continued with the -c/--continue option.
2931 2931
2932 2932 The -c/--continue option reapplies all the earlier options.
2933 2933
2934 2934 .. container:: verbose
2935 2935
2936 2936 The --base option exposes more of how graft internally uses merge with a
2937 2937 custom base revision. --base can be used to specify another ancestor than
2938 2938 the first and only parent.
2939 2939
2940 2940 The command::
2941 2941
2942 2942 hg graft -r 345 --base 234
2943 2943
2944 2944 is thus pretty much the same as::
2945 2945
2946 2946 hg diff -r 234 -r 345 | hg import
2947 2947
2948 2948 but using merge to resolve conflicts and track moved files.
2949 2949
2950 2950 The result of a merge can thus be backported as a single commit by
2951 2951 specifying one of the merge parents as base, and thus effectively
2952 2952 grafting the changes from the other side.
2953 2953
2954 2954 It is also possible to collapse multiple changesets and clean up history
2955 2955 by specifying another ancestor as base, much like rebase --collapse
2956 2956 --keep.
2957 2957
2958 2958 The commit message can be tweaked after the fact using commit --amend .
2959 2959
2960 2960 For using non-ancestors as the base to backout changes, see the backout
2961 2961 command and the hidden --parent option.
2962 2962
2963 2963 .. container:: verbose
2964 2964
2965 2965 Examples:
2966 2966
2967 2967 - copy a single change to the stable branch and edit its description::
2968 2968
2969 2969 hg update stable
2970 2970 hg graft --edit 9393
2971 2971
2972 2972 - graft a range of changesets with one exception, updating dates::
2973 2973
2974 2974 hg graft -D "2085::2093 and not 2091"
2975 2975
2976 2976 - continue a graft after resolving conflicts::
2977 2977
2978 2978 hg graft -c
2979 2979
2980 2980 - show the source of a grafted changeset::
2981 2981
2982 2982 hg log --debug -r .
2983 2983
2984 2984 - show revisions sorted by date::
2985 2985
2986 2986 hg log -r "sort(all(), date)"
2987 2987
2988 2988 - backport the result of a merge as a single commit::
2989 2989
2990 2990 hg graft -r 123 --base 123^
2991 2991
2992 2992 - land a feature branch as one changeset::
2993 2993
2994 2994 hg up -cr default
2995 2995 hg graft -r featureX --base "ancestor('featureX', 'default')"
2996 2996
2997 2997 See :hg:`help revisions` for more about specifying revisions.
2998 2998
2999 2999 Returns 0 on successful completion, 1 if there are unresolved files.
3000 3000 '''
3001 3001 with repo.wlock():
3002 3002 return _dograft(ui, repo, *revs, **opts)
3003 3003
3004 3004
3005 3005 def _dograft(ui, repo, *revs, **opts):
3006 3006 opts = pycompat.byteskwargs(opts)
3007 3007 if revs and opts.get(b'rev'):
3008 3008 ui.warn(
3009 3009 _(
3010 3010 b'warning: inconsistent use of --rev might give unexpected '
3011 3011 b'revision ordering!\n'
3012 3012 )
3013 3013 )
3014 3014
3015 3015 revs = list(revs)
3016 3016 revs.extend(opts.get(b'rev'))
3017 3017 # a dict of data to be stored in state file
3018 3018 statedata = {}
3019 3019 # list of new nodes created by ongoing graft
3020 3020 statedata[b'newnodes'] = []
3021 3021
3022 3022 cmdutil.resolvecommitoptions(ui, opts)
3023 3023
3024 3024 editor = cmdutil.getcommiteditor(
3025 3025 editform=b'graft', **pycompat.strkwargs(opts)
3026 3026 )
3027 3027
3028 3028 cmdutil.check_at_most_one_arg(opts, b'abort', b'stop', b'continue')
3029 3029
3030 3030 cont = False
3031 3031 if opts.get(b'no_commit'):
3032 3032 cmdutil.check_incompatible_arguments(
3033 3033 opts,
3034 3034 b'no_commit',
3035 3035 [b'edit', b'currentuser', b'currentdate', b'log'],
3036 3036 )
3037 3037
3038 3038 graftstate = statemod.cmdstate(repo, b'graftstate')
3039 3039
3040 3040 if opts.get(b'stop'):
3041 3041 cmdutil.check_incompatible_arguments(
3042 3042 opts,
3043 3043 b'stop',
3044 3044 [
3045 3045 b'edit',
3046 3046 b'log',
3047 3047 b'user',
3048 3048 b'date',
3049 3049 b'currentdate',
3050 3050 b'currentuser',
3051 3051 b'rev',
3052 3052 ],
3053 3053 )
3054 3054 return _stopgraft(ui, repo, graftstate)
3055 3055 elif opts.get(b'abort'):
3056 3056 cmdutil.check_incompatible_arguments(
3057 3057 opts,
3058 3058 b'abort',
3059 3059 [
3060 3060 b'edit',
3061 3061 b'log',
3062 3062 b'user',
3063 3063 b'date',
3064 3064 b'currentdate',
3065 3065 b'currentuser',
3066 3066 b'rev',
3067 3067 ],
3068 3068 )
3069 3069 return cmdutil.abortgraft(ui, repo, graftstate)
3070 3070 elif opts.get(b'continue'):
3071 3071 cont = True
3072 3072 if revs:
3073 3073 raise error.InputError(_(b"can't specify --continue and revisions"))
3074 3074 # read in unfinished revisions
3075 3075 if graftstate.exists():
3076 3076 statedata = cmdutil.readgraftstate(repo, graftstate)
3077 3077 if statedata.get(b'date'):
3078 3078 opts[b'date'] = statedata[b'date']
3079 3079 if statedata.get(b'user'):
3080 3080 opts[b'user'] = statedata[b'user']
3081 3081 if statedata.get(b'log'):
3082 3082 opts[b'log'] = True
3083 3083 if statedata.get(b'no_commit'):
3084 3084 opts[b'no_commit'] = statedata.get(b'no_commit')
3085 3085 if statedata.get(b'base'):
3086 3086 opts[b'base'] = statedata.get(b'base')
3087 3087 nodes = statedata[b'nodes']
3088 3088 revs = [repo[node].rev() for node in nodes]
3089 3089 else:
3090 3090 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3091 3091 else:
3092 3092 if not revs:
3093 3093 raise error.InputError(_(b'no revisions specified'))
3094 3094 cmdutil.checkunfinished(repo)
3095 3095 cmdutil.bailifchanged(repo)
3096 3096 revs = scmutil.revrange(repo, revs)
3097 3097
3098 3098 skipped = set()
3099 3099 basectx = None
3100 3100 if opts.get(b'base'):
3101 3101 basectx = scmutil.revsingle(repo, opts[b'base'], None)
3102 3102 if basectx is None:
3103 3103 # check for merges
3104 3104 for rev in repo.revs(b'%ld and merge()', revs):
3105 3105 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3106 3106 skipped.add(rev)
3107 3107 revs = [r for r in revs if r not in skipped]
3108 3108 if not revs:
3109 3109 return -1
3110 3110 if basectx is not None and len(revs) != 1:
3111 3111 raise error.InputError(_(b'only one revision allowed with --base '))
3112 3112
3113 3113 # Don't check in the --continue case, in effect retaining --force across
3114 3114 # --continues. That's because without --force, any revisions we decided to
3115 3115 # skip would have been filtered out here, so they wouldn't have made their
3116 3116 # way to the graftstate. With --force, any revisions we would have otherwise
3117 3117 # skipped would not have been filtered out, and if they hadn't been applied
3118 3118 # already, they'd have been in the graftstate.
3119 3119 if not (cont or opts.get(b'force')) and basectx is None:
3120 3120 # check for ancestors of dest branch
3121 3121 ancestors = repo.revs(b'%ld & (::.)', revs)
3122 3122 for rev in ancestors:
3123 3123 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3124 3124
3125 3125 revs = [r for r in revs if r not in ancestors]
3126 3126
3127 3127 if not revs:
3128 3128 return -1
3129 3129
3130 3130 # analyze revs for earlier grafts
3131 3131 ids = {}
3132 3132 for ctx in repo.set(b"%ld", revs):
3133 3133 ids[ctx.hex()] = ctx.rev()
3134 3134 n = ctx.extra().get(b'source')
3135 3135 if n:
3136 3136 ids[n] = ctx.rev()
3137 3137
3138 3138 # check ancestors for earlier grafts
3139 3139 ui.debug(b'scanning for duplicate grafts\n')
3140 3140
3141 3141 # The only changesets we can be sure doesn't contain grafts of any
3142 3142 # revs, are the ones that are common ancestors of *all* revs:
3143 3143 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3144 3144 ctx = repo[rev]
3145 3145 n = ctx.extra().get(b'source')
3146 3146 if n in ids:
3147 3147 try:
3148 3148 r = repo[n].rev()
3149 3149 except error.RepoLookupError:
3150 3150 r = None
3151 3151 if r in revs:
3152 3152 ui.warn(
3153 3153 _(
3154 3154 b'skipping revision %d:%s '
3155 3155 b'(already grafted to %d:%s)\n'
3156 3156 )
3157 3157 % (r, repo[r], rev, ctx)
3158 3158 )
3159 3159 revs.remove(r)
3160 3160 elif ids[n] in revs:
3161 3161 if r is None:
3162 3162 ui.warn(
3163 3163 _(
3164 3164 b'skipping already grafted revision %d:%s '
3165 3165 b'(%d:%s also has unknown origin %s)\n'
3166 3166 )
3167 3167 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3168 3168 )
3169 3169 else:
3170 3170 ui.warn(
3171 3171 _(
3172 3172 b'skipping already grafted revision %d:%s '
3173 3173 b'(%d:%s also has origin %d:%s)\n'
3174 3174 )
3175 3175 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3176 3176 )
3177 3177 revs.remove(ids[n])
3178 3178 elif ctx.hex() in ids:
3179 3179 r = ids[ctx.hex()]
3180 3180 if r in revs:
3181 3181 ui.warn(
3182 3182 _(
3183 3183 b'skipping already grafted revision %d:%s '
3184 3184 b'(was grafted from %d:%s)\n'
3185 3185 )
3186 3186 % (r, repo[r], rev, ctx)
3187 3187 )
3188 3188 revs.remove(r)
3189 3189 if not revs:
3190 3190 return -1
3191 3191
3192 3192 if opts.get(b'no_commit'):
3193 3193 statedata[b'no_commit'] = True
3194 3194 if opts.get(b'base'):
3195 3195 statedata[b'base'] = opts[b'base']
3196 3196 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3197 3197 desc = b'%d:%s "%s"' % (
3198 3198 ctx.rev(),
3199 3199 ctx,
3200 3200 ctx.description().split(b'\n', 1)[0],
3201 3201 )
3202 3202 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3203 3203 if names:
3204 3204 desc += b' (%s)' % b' '.join(names)
3205 3205 ui.status(_(b'grafting %s\n') % desc)
3206 3206 if opts.get(b'dry_run'):
3207 3207 continue
3208 3208
3209 3209 source = ctx.extra().get(b'source')
3210 3210 extra = {}
3211 3211 if source:
3212 3212 extra[b'source'] = source
3213 3213 extra[b'intermediate-source'] = ctx.hex()
3214 3214 else:
3215 3215 extra[b'source'] = ctx.hex()
3216 3216 user = ctx.user()
3217 3217 if opts.get(b'user'):
3218 3218 user = opts[b'user']
3219 3219 statedata[b'user'] = user
3220 3220 date = ctx.date()
3221 3221 if opts.get(b'date'):
3222 3222 date = opts[b'date']
3223 3223 statedata[b'date'] = date
3224 3224 message = ctx.description()
3225 3225 if opts.get(b'log'):
3226 3226 message += b'\n(grafted from %s)' % ctx.hex()
3227 3227 statedata[b'log'] = True
3228 3228
3229 3229 # we don't merge the first commit when continuing
3230 3230 if not cont:
3231 3231 # perform the graft merge with p1(rev) as 'ancestor'
3232 3232 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3233 3233 base = ctx.p1() if basectx is None else basectx
3234 3234 with ui.configoverride(overrides, b'graft'):
3235 3235 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3236 3236 # report any conflicts
3237 3237 if stats.unresolvedcount > 0:
3238 3238 # write out state for --continue
3239 3239 nodes = [repo[rev].hex() for rev in revs[pos:]]
3240 3240 statedata[b'nodes'] = nodes
3241 3241 stateversion = 1
3242 3242 graftstate.save(stateversion, statedata)
3243 3243 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3244 3244 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3245 3245 return 1
3246 3246 else:
3247 3247 cont = False
3248 3248
3249 3249 # commit if --no-commit is false
3250 3250 if not opts.get(b'no_commit'):
3251 3251 node = repo.commit(
3252 3252 text=message, user=user, date=date, extra=extra, editor=editor
3253 3253 )
3254 3254 if node is None:
3255 3255 ui.warn(
3256 3256 _(b'note: graft of %d:%s created no changes to commit\n')
3257 3257 % (ctx.rev(), ctx)
3258 3258 )
3259 3259 # checking that newnodes exist because old state files won't have it
3260 3260 elif statedata.get(b'newnodes') is not None:
3261 3261 statedata[b'newnodes'].append(node)
3262 3262
3263 3263 # remove state when we complete successfully
3264 3264 if not opts.get(b'dry_run'):
3265 3265 graftstate.delete()
3266 3266
3267 3267 return 0
3268 3268
3269 3269
3270 3270 def _stopgraft(ui, repo, graftstate):
3271 3271 """stop the interrupted graft"""
3272 3272 if not graftstate.exists():
3273 raise error.Abort(_(b"no interrupted graft found"))
3273 raise error.StateError(_(b"no interrupted graft found"))
3274 3274 pctx = repo[b'.']
3275 3275 mergemod.clean_update(pctx)
3276 3276 graftstate.delete()
3277 3277 ui.status(_(b"stopped the interrupted graft\n"))
3278 3278 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3279 3279 return 0
3280 3280
3281 3281
3282 3282 statemod.addunfinished(
3283 3283 b'graft',
3284 3284 fname=b'graftstate',
3285 3285 clearable=True,
3286 3286 stopflag=True,
3287 3287 continueflag=True,
3288 3288 abortfunc=cmdutil.hgabortgraft,
3289 3289 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3290 3290 )
3291 3291
3292 3292
3293 3293 @command(
3294 3294 b'grep',
3295 3295 [
3296 3296 (b'0', b'print0', None, _(b'end fields with NUL')),
3297 3297 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3298 3298 (
3299 3299 b'',
3300 3300 b'diff',
3301 3301 None,
3302 3302 _(
3303 3303 b'search revision differences for when the pattern was added '
3304 3304 b'or removed'
3305 3305 ),
3306 3306 ),
3307 3307 (b'a', b'text', None, _(b'treat all files as text')),
3308 3308 (
3309 3309 b'f',
3310 3310 b'follow',
3311 3311 None,
3312 3312 _(
3313 3313 b'follow changeset history,'
3314 3314 b' or file history across copies and renames'
3315 3315 ),
3316 3316 ),
3317 3317 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3318 3318 (
3319 3319 b'l',
3320 3320 b'files-with-matches',
3321 3321 None,
3322 3322 _(b'print only filenames and revisions that match'),
3323 3323 ),
3324 3324 (b'n', b'line-number', None, _(b'print matching line numbers')),
3325 3325 (
3326 3326 b'r',
3327 3327 b'rev',
3328 3328 [],
3329 3329 _(b'search files changed within revision range'),
3330 3330 _(b'REV'),
3331 3331 ),
3332 3332 (
3333 3333 b'',
3334 3334 b'all-files',
3335 3335 None,
3336 3336 _(
3337 3337 b'include all files in the changeset while grepping (DEPRECATED)'
3338 3338 ),
3339 3339 ),
3340 3340 (b'u', b'user', None, _(b'list the author (long with -v)')),
3341 3341 (b'd', b'date', None, _(b'list the date (short with -q)')),
3342 3342 ]
3343 3343 + formatteropts
3344 3344 + walkopts,
3345 3345 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3346 3346 helpcategory=command.CATEGORY_FILE_CONTENTS,
3347 3347 inferrepo=True,
3348 3348 intents={INTENT_READONLY},
3349 3349 )
3350 3350 def grep(ui, repo, pattern, *pats, **opts):
3351 3351 """search for a pattern in specified files
3352 3352
3353 3353 Search the working directory or revision history for a regular
3354 3354 expression in the specified files for the entire repository.
3355 3355
3356 3356 By default, grep searches the repository files in the working
3357 3357 directory and prints the files where it finds a match. To specify
3358 3358 historical revisions instead of the working directory, use the
3359 3359 --rev flag.
3360 3360
3361 3361 To search instead historical revision differences that contains a
3362 3362 change in match status ("-" for a match that becomes a non-match,
3363 3363 or "+" for a non-match that becomes a match), use the --diff flag.
3364 3364
3365 3365 PATTERN can be any Python (roughly Perl-compatible) regular
3366 3366 expression.
3367 3367
3368 3368 If no FILEs are specified and the --rev flag isn't supplied, all
3369 3369 files in the working directory are searched. When using the --rev
3370 3370 flag and specifying FILEs, use the --follow argument to also
3371 3371 follow the specified FILEs across renames and copies.
3372 3372
3373 3373 .. container:: verbose
3374 3374
3375 3375 Template:
3376 3376
3377 3377 The following keywords are supported in addition to the common template
3378 3378 keywords and functions. See also :hg:`help templates`.
3379 3379
3380 3380 :change: String. Character denoting insertion ``+`` or removal ``-``.
3381 3381 Available if ``--diff`` is specified.
3382 3382 :lineno: Integer. Line number of the match.
3383 3383 :path: String. Repository-absolute path of the file.
3384 3384 :texts: List of text chunks.
3385 3385
3386 3386 And each entry of ``{texts}`` provides the following sub-keywords.
3387 3387
3388 3388 :matched: Boolean. True if the chunk matches the specified pattern.
3389 3389 :text: String. Chunk content.
3390 3390
3391 3391 See :hg:`help templates.operators` for the list expansion syntax.
3392 3392
3393 3393 Returns 0 if a match is found, 1 otherwise.
3394 3394
3395 3395 """
3396 3396 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3397 3397 opts = pycompat.byteskwargs(opts)
3398 3398 diff = opts.get(b'all') or opts.get(b'diff')
3399 3399 follow = opts.get(b'follow')
3400 3400 if opts.get(b'all_files') is None and not diff:
3401 3401 opts[b'all_files'] = True
3402 3402 plaingrep = (
3403 3403 opts.get(b'all_files')
3404 3404 and not opts.get(b'rev')
3405 3405 and not opts.get(b'follow')
3406 3406 )
3407 3407 all_files = opts.get(b'all_files')
3408 3408 if plaingrep:
3409 3409 opts[b'rev'] = [b'wdir()']
3410 3410
3411 3411 reflags = re.M
3412 3412 if opts.get(b'ignore_case'):
3413 3413 reflags |= re.I
3414 3414 try:
3415 3415 regexp = util.re.compile(pattern, reflags)
3416 3416 except re.error as inst:
3417 3417 ui.warn(
3418 3418 _(b"grep: invalid match pattern: %s\n") % pycompat.bytestr(inst)
3419 3419 )
3420 3420 return 1
3421 3421 sep, eol = b':', b'\n'
3422 3422 if opts.get(b'print0'):
3423 3423 sep = eol = b'\0'
3424 3424
3425 3425 searcher = grepmod.grepsearcher(
3426 3426 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3427 3427 )
3428 3428
3429 3429 getfile = searcher._getfile
3430 3430
3431 3431 uipathfn = scmutil.getuipathfn(repo)
3432 3432
3433 3433 def display(fm, fn, ctx, pstates, states):
3434 3434 rev = scmutil.intrev(ctx)
3435 3435 if fm.isplain():
3436 3436 formatuser = ui.shortuser
3437 3437 else:
3438 3438 formatuser = pycompat.bytestr
3439 3439 if ui.quiet:
3440 3440 datefmt = b'%Y-%m-%d'
3441 3441 else:
3442 3442 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3443 3443 found = False
3444 3444
3445 3445 @util.cachefunc
3446 3446 def binary():
3447 3447 flog = getfile(fn)
3448 3448 try:
3449 3449 return stringutil.binary(flog.read(ctx.filenode(fn)))
3450 3450 except error.WdirUnsupported:
3451 3451 return ctx[fn].isbinary()
3452 3452
3453 3453 fieldnamemap = {b'linenumber': b'lineno'}
3454 3454 if diff:
3455 3455 iter = grepmod.difflinestates(pstates, states)
3456 3456 else:
3457 3457 iter = [(b'', l) for l in states]
3458 3458 for change, l in iter:
3459 3459 fm.startitem()
3460 3460 fm.context(ctx=ctx)
3461 3461 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3462 3462 fm.plain(uipathfn(fn), label=b'grep.filename')
3463 3463
3464 3464 cols = [
3465 3465 (b'rev', b'%d', rev, not plaingrep, b''),
3466 3466 (
3467 3467 b'linenumber',
3468 3468 b'%d',
3469 3469 l.linenum,
3470 3470 opts.get(b'line_number'),
3471 3471 b'',
3472 3472 ),
3473 3473 ]
3474 3474 if diff:
3475 3475 cols.append(
3476 3476 (
3477 3477 b'change',
3478 3478 b'%s',
3479 3479 change,
3480 3480 True,
3481 3481 b'grep.inserted '
3482 3482 if change == b'+'
3483 3483 else b'grep.deleted ',
3484 3484 )
3485 3485 )
3486 3486 cols.extend(
3487 3487 [
3488 3488 (
3489 3489 b'user',
3490 3490 b'%s',
3491 3491 formatuser(ctx.user()),
3492 3492 opts.get(b'user'),
3493 3493 b'',
3494 3494 ),
3495 3495 (
3496 3496 b'date',
3497 3497 b'%s',
3498 3498 fm.formatdate(ctx.date(), datefmt),
3499 3499 opts.get(b'date'),
3500 3500 b'',
3501 3501 ),
3502 3502 ]
3503 3503 )
3504 3504 for name, fmt, data, cond, extra_label in cols:
3505 3505 if cond:
3506 3506 fm.plain(sep, label=b'grep.sep')
3507 3507 field = fieldnamemap.get(name, name)
3508 3508 label = extra_label + (b'grep.%s' % name)
3509 3509 fm.condwrite(cond, field, fmt, data, label=label)
3510 3510 if not opts.get(b'files_with_matches'):
3511 3511 fm.plain(sep, label=b'grep.sep')
3512 3512 if not opts.get(b'text') and binary():
3513 3513 fm.plain(_(b" Binary file matches"))
3514 3514 else:
3515 3515 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3516 3516 fm.plain(eol)
3517 3517 found = True
3518 3518 if opts.get(b'files_with_matches'):
3519 3519 break
3520 3520 return found
3521 3521
3522 3522 def displaymatches(fm, l):
3523 3523 p = 0
3524 3524 for s, e in l.findpos(regexp):
3525 3525 if p < s:
3526 3526 fm.startitem()
3527 3527 fm.write(b'text', b'%s', l.line[p:s])
3528 3528 fm.data(matched=False)
3529 3529 fm.startitem()
3530 3530 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3531 3531 fm.data(matched=True)
3532 3532 p = e
3533 3533 if p < len(l.line):
3534 3534 fm.startitem()
3535 3535 fm.write(b'text', b'%s', l.line[p:])
3536 3536 fm.data(matched=False)
3537 3537 fm.end()
3538 3538
3539 3539 found = False
3540 3540
3541 3541 wopts = logcmdutil.walkopts(
3542 3542 pats=pats,
3543 3543 opts=opts,
3544 3544 revspec=opts[b'rev'],
3545 3545 include_pats=opts[b'include'],
3546 3546 exclude_pats=opts[b'exclude'],
3547 3547 follow=follow,
3548 3548 force_changelog_traversal=all_files,
3549 3549 filter_revisions_by_pats=not all_files,
3550 3550 )
3551 3551 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3552 3552
3553 3553 ui.pager(b'grep')
3554 3554 fm = ui.formatter(b'grep', opts)
3555 3555 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3556 3556 r = display(fm, fn, ctx, pstates, states)
3557 3557 found = found or r
3558 3558 if r and not diff and not all_files:
3559 3559 searcher.skipfile(fn, ctx.rev())
3560 3560 fm.end()
3561 3561
3562 3562 return not found
3563 3563
3564 3564
3565 3565 @command(
3566 3566 b'heads',
3567 3567 [
3568 3568 (
3569 3569 b'r',
3570 3570 b'rev',
3571 3571 b'',
3572 3572 _(b'show only heads which are descendants of STARTREV'),
3573 3573 _(b'STARTREV'),
3574 3574 ),
3575 3575 (b't', b'topo', False, _(b'show topological heads only')),
3576 3576 (
3577 3577 b'a',
3578 3578 b'active',
3579 3579 False,
3580 3580 _(b'show active branchheads only (DEPRECATED)'),
3581 3581 ),
3582 3582 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3583 3583 ]
3584 3584 + templateopts,
3585 3585 _(b'[-ct] [-r STARTREV] [REV]...'),
3586 3586 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3587 3587 intents={INTENT_READONLY},
3588 3588 )
3589 3589 def heads(ui, repo, *branchrevs, **opts):
3590 3590 """show branch heads
3591 3591
3592 3592 With no arguments, show all open branch heads in the repository.
3593 3593 Branch heads are changesets that have no descendants on the
3594 3594 same branch. They are where development generally takes place and
3595 3595 are the usual targets for update and merge operations.
3596 3596
3597 3597 If one or more REVs are given, only open branch heads on the
3598 3598 branches associated with the specified changesets are shown. This
3599 3599 means that you can use :hg:`heads .` to see the heads on the
3600 3600 currently checked-out branch.
3601 3601
3602 3602 If -c/--closed is specified, also show branch heads marked closed
3603 3603 (see :hg:`commit --close-branch`).
3604 3604
3605 3605 If STARTREV is specified, only those heads that are descendants of
3606 3606 STARTREV will be displayed.
3607 3607
3608 3608 If -t/--topo is specified, named branch mechanics will be ignored and only
3609 3609 topological heads (changesets with no children) will be shown.
3610 3610
3611 3611 Returns 0 if matching heads are found, 1 if not.
3612 3612 """
3613 3613
3614 3614 opts = pycompat.byteskwargs(opts)
3615 3615 start = None
3616 3616 rev = opts.get(b'rev')
3617 3617 if rev:
3618 3618 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3619 3619 start = scmutil.revsingle(repo, rev, None).node()
3620 3620
3621 3621 if opts.get(b'topo'):
3622 3622 heads = [repo[h] for h in repo.heads(start)]
3623 3623 else:
3624 3624 heads = []
3625 3625 for branch in repo.branchmap():
3626 3626 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3627 3627 heads = [repo[h] for h in heads]
3628 3628
3629 3629 if branchrevs:
3630 3630 branches = {
3631 3631 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3632 3632 }
3633 3633 heads = [h for h in heads if h.branch() in branches]
3634 3634
3635 3635 if opts.get(b'active') and branchrevs:
3636 3636 dagheads = repo.heads(start)
3637 3637 heads = [h for h in heads if h.node() in dagheads]
3638 3638
3639 3639 if branchrevs:
3640 3640 haveheads = {h.branch() for h in heads}
3641 3641 if branches - haveheads:
3642 3642 headless = b', '.join(b for b in branches - haveheads)
3643 3643 msg = _(b'no open branch heads found on branches %s')
3644 3644 if opts.get(b'rev'):
3645 3645 msg += _(b' (started at %s)') % opts[b'rev']
3646 3646 ui.warn((msg + b'\n') % headless)
3647 3647
3648 3648 if not heads:
3649 3649 return 1
3650 3650
3651 3651 ui.pager(b'heads')
3652 3652 heads = sorted(heads, key=lambda x: -(x.rev()))
3653 3653 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3654 3654 for ctx in heads:
3655 3655 displayer.show(ctx)
3656 3656 displayer.close()
3657 3657
3658 3658
3659 3659 @command(
3660 3660 b'help',
3661 3661 [
3662 3662 (b'e', b'extension', None, _(b'show only help for extensions')),
3663 3663 (b'c', b'command', None, _(b'show only help for commands')),
3664 3664 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3665 3665 (
3666 3666 b's',
3667 3667 b'system',
3668 3668 [],
3669 3669 _(b'show help for specific platform(s)'),
3670 3670 _(b'PLATFORM'),
3671 3671 ),
3672 3672 ],
3673 3673 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3674 3674 helpcategory=command.CATEGORY_HELP,
3675 3675 norepo=True,
3676 3676 intents={INTENT_READONLY},
3677 3677 )
3678 3678 def help_(ui, name=None, **opts):
3679 3679 """show help for a given topic or a help overview
3680 3680
3681 3681 With no arguments, print a list of commands with short help messages.
3682 3682
3683 3683 Given a topic, extension, or command name, print help for that
3684 3684 topic.
3685 3685
3686 3686 Returns 0 if successful.
3687 3687 """
3688 3688
3689 3689 keep = opts.get('system') or []
3690 3690 if len(keep) == 0:
3691 3691 if pycompat.sysplatform.startswith(b'win'):
3692 3692 keep.append(b'windows')
3693 3693 elif pycompat.sysplatform == b'OpenVMS':
3694 3694 keep.append(b'vms')
3695 3695 elif pycompat.sysplatform == b'plan9':
3696 3696 keep.append(b'plan9')
3697 3697 else:
3698 3698 keep.append(b'unix')
3699 3699 keep.append(pycompat.sysplatform.lower())
3700 3700 if ui.verbose:
3701 3701 keep.append(b'verbose')
3702 3702
3703 3703 commands = sys.modules[__name__]
3704 3704 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3705 3705 ui.pager(b'help')
3706 3706 ui.write(formatted)
3707 3707
3708 3708
3709 3709 @command(
3710 3710 b'identify|id',
3711 3711 [
3712 3712 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3713 3713 (b'n', b'num', None, _(b'show local revision number')),
3714 3714 (b'i', b'id', None, _(b'show global revision id')),
3715 3715 (b'b', b'branch', None, _(b'show branch')),
3716 3716 (b't', b'tags', None, _(b'show tags')),
3717 3717 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3718 3718 ]
3719 3719 + remoteopts
3720 3720 + formatteropts,
3721 3721 _(b'[-nibtB] [-r REV] [SOURCE]'),
3722 3722 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3723 3723 optionalrepo=True,
3724 3724 intents={INTENT_READONLY},
3725 3725 )
3726 3726 def identify(
3727 3727 ui,
3728 3728 repo,
3729 3729 source=None,
3730 3730 rev=None,
3731 3731 num=None,
3732 3732 id=None,
3733 3733 branch=None,
3734 3734 tags=None,
3735 3735 bookmarks=None,
3736 3736 **opts
3737 3737 ):
3738 3738 """identify the working directory or specified revision
3739 3739
3740 3740 Print a summary identifying the repository state at REV using one or
3741 3741 two parent hash identifiers, followed by a "+" if the working
3742 3742 directory has uncommitted changes, the branch name (if not default),
3743 3743 a list of tags, and a list of bookmarks.
3744 3744
3745 3745 When REV is not given, print a summary of the current state of the
3746 3746 repository including the working directory. Specify -r. to get information
3747 3747 of the working directory parent without scanning uncommitted changes.
3748 3748
3749 3749 Specifying a path to a repository root or Mercurial bundle will
3750 3750 cause lookup to operate on that repository/bundle.
3751 3751
3752 3752 .. container:: verbose
3753 3753
3754 3754 Template:
3755 3755
3756 3756 The following keywords are supported in addition to the common template
3757 3757 keywords and functions. See also :hg:`help templates`.
3758 3758
3759 3759 :dirty: String. Character ``+`` denoting if the working directory has
3760 3760 uncommitted changes.
3761 3761 :id: String. One or two nodes, optionally followed by ``+``.
3762 3762 :parents: List of strings. Parent nodes of the changeset.
3763 3763
3764 3764 Examples:
3765 3765
3766 3766 - generate a build identifier for the working directory::
3767 3767
3768 3768 hg id --id > build-id.dat
3769 3769
3770 3770 - find the revision corresponding to a tag::
3771 3771
3772 3772 hg id -n -r 1.3
3773 3773
3774 3774 - check the most recent revision of a remote repository::
3775 3775
3776 3776 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3777 3777
3778 3778 See :hg:`log` for generating more information about specific revisions,
3779 3779 including full hash identifiers.
3780 3780
3781 3781 Returns 0 if successful.
3782 3782 """
3783 3783
3784 3784 opts = pycompat.byteskwargs(opts)
3785 3785 if not repo and not source:
3786 3786 raise error.InputError(
3787 3787 _(b"there is no Mercurial repository here (.hg not found)")
3788 3788 )
3789 3789
3790 3790 default = not (num or id or branch or tags or bookmarks)
3791 3791 output = []
3792 3792 revs = []
3793 3793
3794 3794 if source:
3795 3795 source, branches = hg.parseurl(ui.expandpath(source))
3796 3796 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3797 3797 repo = peer.local()
3798 3798 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3799 3799
3800 3800 fm = ui.formatter(b'identify', opts)
3801 3801 fm.startitem()
3802 3802
3803 3803 if not repo:
3804 3804 if num or branch or tags:
3805 3805 raise error.InputError(
3806 3806 _(b"can't query remote revision number, branch, or tags")
3807 3807 )
3808 3808 if not rev and revs:
3809 3809 rev = revs[0]
3810 3810 if not rev:
3811 3811 rev = b"tip"
3812 3812
3813 3813 remoterev = peer.lookup(rev)
3814 3814 hexrev = fm.hexfunc(remoterev)
3815 3815 if default or id:
3816 3816 output = [hexrev]
3817 3817 fm.data(id=hexrev)
3818 3818
3819 3819 @util.cachefunc
3820 3820 def getbms():
3821 3821 bms = []
3822 3822
3823 3823 if b'bookmarks' in peer.listkeys(b'namespaces'):
3824 3824 hexremoterev = hex(remoterev)
3825 3825 bms = [
3826 3826 bm
3827 3827 for bm, bmr in pycompat.iteritems(
3828 3828 peer.listkeys(b'bookmarks')
3829 3829 )
3830 3830 if bmr == hexremoterev
3831 3831 ]
3832 3832
3833 3833 return sorted(bms)
3834 3834
3835 3835 if fm.isplain():
3836 3836 if bookmarks:
3837 3837 output.extend(getbms())
3838 3838 elif default and not ui.quiet:
3839 3839 # multiple bookmarks for a single parent separated by '/'
3840 3840 bm = b'/'.join(getbms())
3841 3841 if bm:
3842 3842 output.append(bm)
3843 3843 else:
3844 3844 fm.data(node=hex(remoterev))
3845 3845 if bookmarks or b'bookmarks' in fm.datahint():
3846 3846 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3847 3847 else:
3848 3848 if rev:
3849 3849 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3850 3850 ctx = scmutil.revsingle(repo, rev, None)
3851 3851
3852 3852 if ctx.rev() is None:
3853 3853 ctx = repo[None]
3854 3854 parents = ctx.parents()
3855 3855 taglist = []
3856 3856 for p in parents:
3857 3857 taglist.extend(p.tags())
3858 3858
3859 3859 dirty = b""
3860 3860 if ctx.dirty(missing=True, merge=False, branch=False):
3861 3861 dirty = b'+'
3862 3862 fm.data(dirty=dirty)
3863 3863
3864 3864 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3865 3865 if default or id:
3866 3866 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3867 3867 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3868 3868
3869 3869 if num:
3870 3870 numoutput = [b"%d" % p.rev() for p in parents]
3871 3871 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3872 3872
3873 3873 fm.data(
3874 3874 parents=fm.formatlist(
3875 3875 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3876 3876 )
3877 3877 )
3878 3878 else:
3879 3879 hexoutput = fm.hexfunc(ctx.node())
3880 3880 if default or id:
3881 3881 output = [hexoutput]
3882 3882 fm.data(id=hexoutput)
3883 3883
3884 3884 if num:
3885 3885 output.append(pycompat.bytestr(ctx.rev()))
3886 3886 taglist = ctx.tags()
3887 3887
3888 3888 if default and not ui.quiet:
3889 3889 b = ctx.branch()
3890 3890 if b != b'default':
3891 3891 output.append(b"(%s)" % b)
3892 3892
3893 3893 # multiple tags for a single parent separated by '/'
3894 3894 t = b'/'.join(taglist)
3895 3895 if t:
3896 3896 output.append(t)
3897 3897
3898 3898 # multiple bookmarks for a single parent separated by '/'
3899 3899 bm = b'/'.join(ctx.bookmarks())
3900 3900 if bm:
3901 3901 output.append(bm)
3902 3902 else:
3903 3903 if branch:
3904 3904 output.append(ctx.branch())
3905 3905
3906 3906 if tags:
3907 3907 output.extend(taglist)
3908 3908
3909 3909 if bookmarks:
3910 3910 output.extend(ctx.bookmarks())
3911 3911
3912 3912 fm.data(node=ctx.hex())
3913 3913 fm.data(branch=ctx.branch())
3914 3914 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
3915 3915 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
3916 3916 fm.context(ctx=ctx)
3917 3917
3918 3918 fm.plain(b"%s\n" % b' '.join(output))
3919 3919 fm.end()
3920 3920
3921 3921
3922 3922 @command(
3923 3923 b'import|patch',
3924 3924 [
3925 3925 (
3926 3926 b'p',
3927 3927 b'strip',
3928 3928 1,
3929 3929 _(
3930 3930 b'directory strip option for patch. This has the same '
3931 3931 b'meaning as the corresponding patch option'
3932 3932 ),
3933 3933 _(b'NUM'),
3934 3934 ),
3935 3935 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
3936 3936 (b'', b'secret', None, _(b'use the secret phase for committing')),
3937 3937 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
3938 3938 (
3939 3939 b'f',
3940 3940 b'force',
3941 3941 None,
3942 3942 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
3943 3943 ),
3944 3944 (
3945 3945 b'',
3946 3946 b'no-commit',
3947 3947 None,
3948 3948 _(b"don't commit, just update the working directory"),
3949 3949 ),
3950 3950 (
3951 3951 b'',
3952 3952 b'bypass',
3953 3953 None,
3954 3954 _(b"apply patch without touching the working directory"),
3955 3955 ),
3956 3956 (b'', b'partial', None, _(b'commit even if some hunks fail')),
3957 3957 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
3958 3958 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
3959 3959 (
3960 3960 b'',
3961 3961 b'import-branch',
3962 3962 None,
3963 3963 _(b'use any branch information in patch (implied by --exact)'),
3964 3964 ),
3965 3965 ]
3966 3966 + commitopts
3967 3967 + commitopts2
3968 3968 + similarityopts,
3969 3969 _(b'[OPTION]... PATCH...'),
3970 3970 helpcategory=command.CATEGORY_IMPORT_EXPORT,
3971 3971 )
3972 3972 def import_(ui, repo, patch1=None, *patches, **opts):
3973 3973 """import an ordered set of patches
3974 3974
3975 3975 Import a list of patches and commit them individually (unless
3976 3976 --no-commit is specified).
3977 3977
3978 3978 To read a patch from standard input (stdin), use "-" as the patch
3979 3979 name. If a URL is specified, the patch will be downloaded from
3980 3980 there.
3981 3981
3982 3982 Import first applies changes to the working directory (unless
3983 3983 --bypass is specified), import will abort if there are outstanding
3984 3984 changes.
3985 3985
3986 3986 Use --bypass to apply and commit patches directly to the
3987 3987 repository, without affecting the working directory. Without
3988 3988 --exact, patches will be applied on top of the working directory
3989 3989 parent revision.
3990 3990
3991 3991 You can import a patch straight from a mail message. Even patches
3992 3992 as attachments work (to use the body part, it must have type
3993 3993 text/plain or text/x-patch). From and Subject headers of email
3994 3994 message are used as default committer and commit message. All
3995 3995 text/plain body parts before first diff are added to the commit
3996 3996 message.
3997 3997
3998 3998 If the imported patch was generated by :hg:`export`, user and
3999 3999 description from patch override values from message headers and
4000 4000 body. Values given on command line with -m/--message and -u/--user
4001 4001 override these.
4002 4002
4003 4003 If --exact is specified, import will set the working directory to
4004 4004 the parent of each patch before applying it, and will abort if the
4005 4005 resulting changeset has a different ID than the one recorded in
4006 4006 the patch. This will guard against various ways that portable
4007 4007 patch formats and mail systems might fail to transfer Mercurial
4008 4008 data or metadata. See :hg:`bundle` for lossless transmission.
4009 4009
4010 4010 Use --partial to ensure a changeset will be created from the patch
4011 4011 even if some hunks fail to apply. Hunks that fail to apply will be
4012 4012 written to a <target-file>.rej file. Conflicts can then be resolved
4013 4013 by hand before :hg:`commit --amend` is run to update the created
4014 4014 changeset. This flag exists to let people import patches that
4015 4015 partially apply without losing the associated metadata (author,
4016 4016 date, description, ...).
4017 4017
4018 4018 .. note::
4019 4019
4020 4020 When no hunks apply cleanly, :hg:`import --partial` will create
4021 4021 an empty changeset, importing only the patch metadata.
4022 4022
4023 4023 With -s/--similarity, hg will attempt to discover renames and
4024 4024 copies in the patch in the same way as :hg:`addremove`.
4025 4025
4026 4026 It is possible to use external patch programs to perform the patch
4027 4027 by setting the ``ui.patch`` configuration option. For the default
4028 4028 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4029 4029 See :hg:`help config` for more information about configuration
4030 4030 files and how to use these options.
4031 4031
4032 4032 See :hg:`help dates` for a list of formats valid for -d/--date.
4033 4033
4034 4034 .. container:: verbose
4035 4035
4036 4036 Examples:
4037 4037
4038 4038 - import a traditional patch from a website and detect renames::
4039 4039
4040 4040 hg import -s 80 http://example.com/bugfix.patch
4041 4041
4042 4042 - import a changeset from an hgweb server::
4043 4043
4044 4044 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4045 4045
4046 4046 - import all the patches in an Unix-style mbox::
4047 4047
4048 4048 hg import incoming-patches.mbox
4049 4049
4050 4050 - import patches from stdin::
4051 4051
4052 4052 hg import -
4053 4053
4054 4054 - attempt to exactly restore an exported changeset (not always
4055 4055 possible)::
4056 4056
4057 4057 hg import --exact proposed-fix.patch
4058 4058
4059 4059 - use an external tool to apply a patch which is too fuzzy for
4060 4060 the default internal tool.
4061 4061
4062 4062 hg import --config ui.patch="patch --merge" fuzzy.patch
4063 4063
4064 4064 - change the default fuzzing from 2 to a less strict 7
4065 4065
4066 4066 hg import --config ui.fuzz=7 fuzz.patch
4067 4067
4068 4068 Returns 0 on success, 1 on partial success (see --partial).
4069 4069 """
4070 4070
4071 4071 cmdutil.check_incompatible_arguments(
4072 4072 opts, 'no_commit', ['bypass', 'secret']
4073 4073 )
4074 4074 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
4075 4075 opts = pycompat.byteskwargs(opts)
4076 4076 if not patch1:
4077 4077 raise error.InputError(_(b'need at least one patch to import'))
4078 4078
4079 4079 patches = (patch1,) + patches
4080 4080
4081 4081 date = opts.get(b'date')
4082 4082 if date:
4083 4083 opts[b'date'] = dateutil.parsedate(date)
4084 4084
4085 4085 exact = opts.get(b'exact')
4086 4086 update = not opts.get(b'bypass')
4087 4087 try:
4088 4088 sim = float(opts.get(b'similarity') or 0)
4089 4089 except ValueError:
4090 4090 raise error.InputError(_(b'similarity must be a number'))
4091 4091 if sim < 0 or sim > 100:
4092 4092 raise error.InputError(_(b'similarity must be between 0 and 100'))
4093 4093 if sim and not update:
4094 4094 raise error.InputError(_(b'cannot use --similarity with --bypass'))
4095 4095
4096 4096 base = opts[b"base"]
4097 4097 msgs = []
4098 4098 ret = 0
4099 4099
4100 4100 with repo.wlock():
4101 4101 if update:
4102 4102 cmdutil.checkunfinished(repo)
4103 4103 if exact or not opts.get(b'force'):
4104 4104 cmdutil.bailifchanged(repo)
4105 4105
4106 4106 if not opts.get(b'no_commit'):
4107 4107 lock = repo.lock
4108 4108 tr = lambda: repo.transaction(b'import')
4109 4109 dsguard = util.nullcontextmanager
4110 4110 else:
4111 4111 lock = util.nullcontextmanager
4112 4112 tr = util.nullcontextmanager
4113 4113 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4114 4114 with lock(), tr(), dsguard():
4115 4115 parents = repo[None].parents()
4116 4116 for patchurl in patches:
4117 4117 if patchurl == b'-':
4118 4118 ui.status(_(b'applying patch from stdin\n'))
4119 4119 patchfile = ui.fin
4120 4120 patchurl = b'stdin' # for error message
4121 4121 else:
4122 4122 patchurl = os.path.join(base, patchurl)
4123 4123 ui.status(_(b'applying %s\n') % patchurl)
4124 4124 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4125 4125
4126 4126 haspatch = False
4127 4127 for hunk in patch.split(patchfile):
4128 4128 with patch.extract(ui, hunk) as patchdata:
4129 4129 msg, node, rej = cmdutil.tryimportone(
4130 4130 ui, repo, patchdata, parents, opts, msgs, hg.clean
4131 4131 )
4132 4132 if msg:
4133 4133 haspatch = True
4134 4134 ui.note(msg + b'\n')
4135 4135 if update or exact:
4136 4136 parents = repo[None].parents()
4137 4137 else:
4138 4138 parents = [repo[node]]
4139 4139 if rej:
4140 4140 ui.write_err(_(b"patch applied partially\n"))
4141 4141 ui.write_err(
4142 4142 _(
4143 4143 b"(fix the .rej files and run "
4144 4144 b"`hg commit --amend`)\n"
4145 4145 )
4146 4146 )
4147 4147 ret = 1
4148 4148 break
4149 4149
4150 4150 if not haspatch:
4151 4151 raise error.InputError(_(b'%s: no diffs found') % patchurl)
4152 4152
4153 4153 if msgs:
4154 4154 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4155 4155 return ret
4156 4156
4157 4157
4158 4158 @command(
4159 4159 b'incoming|in',
4160 4160 [
4161 4161 (
4162 4162 b'f',
4163 4163 b'force',
4164 4164 None,
4165 4165 _(b'run even if remote repository is unrelated'),
4166 4166 ),
4167 4167 (b'n', b'newest-first', None, _(b'show newest record first')),
4168 4168 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4169 4169 (
4170 4170 b'r',
4171 4171 b'rev',
4172 4172 [],
4173 4173 _(b'a remote changeset intended to be added'),
4174 4174 _(b'REV'),
4175 4175 ),
4176 4176 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4177 4177 (
4178 4178 b'b',
4179 4179 b'branch',
4180 4180 [],
4181 4181 _(b'a specific branch you would like to pull'),
4182 4182 _(b'BRANCH'),
4183 4183 ),
4184 4184 ]
4185 4185 + logopts
4186 4186 + remoteopts
4187 4187 + subrepoopts,
4188 4188 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4189 4189 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4190 4190 )
4191 4191 def incoming(ui, repo, source=b"default", **opts):
4192 4192 """show new changesets found in source
4193 4193
4194 4194 Show new changesets found in the specified path/URL or the default
4195 4195 pull location. These are the changesets that would have been pulled
4196 4196 by :hg:`pull` at the time you issued this command.
4197 4197
4198 4198 See pull for valid source format details.
4199 4199
4200 4200 .. container:: verbose
4201 4201
4202 4202 With -B/--bookmarks, the result of bookmark comparison between
4203 4203 local and remote repositories is displayed. With -v/--verbose,
4204 4204 status is also displayed for each bookmark like below::
4205 4205
4206 4206 BM1 01234567890a added
4207 4207 BM2 1234567890ab advanced
4208 4208 BM3 234567890abc diverged
4209 4209 BM4 34567890abcd changed
4210 4210
4211 4211 The action taken locally when pulling depends on the
4212 4212 status of each bookmark:
4213 4213
4214 4214 :``added``: pull will create it
4215 4215 :``advanced``: pull will update it
4216 4216 :``diverged``: pull will create a divergent bookmark
4217 4217 :``changed``: result depends on remote changesets
4218 4218
4219 4219 From the point of view of pulling behavior, bookmark
4220 4220 existing only in the remote repository are treated as ``added``,
4221 4221 even if it is in fact locally deleted.
4222 4222
4223 4223 .. container:: verbose
4224 4224
4225 4225 For remote repository, using --bundle avoids downloading the
4226 4226 changesets twice if the incoming is followed by a pull.
4227 4227
4228 4228 Examples:
4229 4229
4230 4230 - show incoming changes with patches and full description::
4231 4231
4232 4232 hg incoming -vp
4233 4233
4234 4234 - show incoming changes excluding merges, store a bundle::
4235 4235
4236 4236 hg in -vpM --bundle incoming.hg
4237 4237 hg pull incoming.hg
4238 4238
4239 4239 - briefly list changes inside a bundle::
4240 4240
4241 4241 hg in changes.hg -T "{desc|firstline}\\n"
4242 4242
4243 4243 Returns 0 if there are incoming changes, 1 otherwise.
4244 4244 """
4245 4245 opts = pycompat.byteskwargs(opts)
4246 4246 if opts.get(b'graph'):
4247 4247 logcmdutil.checkunsupportedgraphflags([], opts)
4248 4248
4249 4249 def display(other, chlist, displayer):
4250 4250 revdag = logcmdutil.graphrevs(other, chlist, opts)
4251 4251 logcmdutil.displaygraph(
4252 4252 ui, repo, revdag, displayer, graphmod.asciiedges
4253 4253 )
4254 4254
4255 4255 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4256 4256 return 0
4257 4257
4258 4258 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4259 4259
4260 4260 if opts.get(b'bookmarks'):
4261 4261 source, branches = hg.parseurl(
4262 4262 ui.expandpath(source), opts.get(b'branch')
4263 4263 )
4264 4264 other = hg.peer(repo, opts, source)
4265 4265 if b'bookmarks' not in other.listkeys(b'namespaces'):
4266 4266 ui.warn(_(b"remote doesn't support bookmarks\n"))
4267 4267 return 0
4268 4268 ui.pager(b'incoming')
4269 4269 ui.status(_(b'comparing with %s\n') % util.hidepassword(source))
4270 4270 return bookmarks.incoming(ui, repo, other)
4271 4271
4272 4272 repo._subtoppath = ui.expandpath(source)
4273 4273 try:
4274 4274 return hg.incoming(ui, repo, source, opts)
4275 4275 finally:
4276 4276 del repo._subtoppath
4277 4277
4278 4278
4279 4279 @command(
4280 4280 b'init',
4281 4281 remoteopts,
4282 4282 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4283 4283 helpcategory=command.CATEGORY_REPO_CREATION,
4284 4284 helpbasic=True,
4285 4285 norepo=True,
4286 4286 )
4287 4287 def init(ui, dest=b".", **opts):
4288 4288 """create a new repository in the given directory
4289 4289
4290 4290 Initialize a new repository in the given directory. If the given
4291 4291 directory does not exist, it will be created.
4292 4292
4293 4293 If no directory is given, the current directory is used.
4294 4294
4295 4295 It is possible to specify an ``ssh://`` URL as the destination.
4296 4296 See :hg:`help urls` for more information.
4297 4297
4298 4298 Returns 0 on success.
4299 4299 """
4300 4300 opts = pycompat.byteskwargs(opts)
4301 4301 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4302 4302
4303 4303
4304 4304 @command(
4305 4305 b'locate',
4306 4306 [
4307 4307 (
4308 4308 b'r',
4309 4309 b'rev',
4310 4310 b'',
4311 4311 _(b'search the repository as it is in REV'),
4312 4312 _(b'REV'),
4313 4313 ),
4314 4314 (
4315 4315 b'0',
4316 4316 b'print0',
4317 4317 None,
4318 4318 _(b'end filenames with NUL, for use with xargs'),
4319 4319 ),
4320 4320 (
4321 4321 b'f',
4322 4322 b'fullpath',
4323 4323 None,
4324 4324 _(b'print complete paths from the filesystem root'),
4325 4325 ),
4326 4326 ]
4327 4327 + walkopts,
4328 4328 _(b'[OPTION]... [PATTERN]...'),
4329 4329 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4330 4330 )
4331 4331 def locate(ui, repo, *pats, **opts):
4332 4332 """locate files matching specific patterns (DEPRECATED)
4333 4333
4334 4334 Print files under Mercurial control in the working directory whose
4335 4335 names match the given patterns.
4336 4336
4337 4337 By default, this command searches all directories in the working
4338 4338 directory. To search just the current directory and its
4339 4339 subdirectories, use "--include .".
4340 4340
4341 4341 If no patterns are given to match, this command prints the names
4342 4342 of all files under Mercurial control in the working directory.
4343 4343
4344 4344 If you want to feed the output of this command into the "xargs"
4345 4345 command, use the -0 option to both this command and "xargs". This
4346 4346 will avoid the problem of "xargs" treating single filenames that
4347 4347 contain whitespace as multiple filenames.
4348 4348
4349 4349 See :hg:`help files` for a more versatile command.
4350 4350
4351 4351 Returns 0 if a match is found, 1 otherwise.
4352 4352 """
4353 4353 opts = pycompat.byteskwargs(opts)
4354 4354 if opts.get(b'print0'):
4355 4355 end = b'\0'
4356 4356 else:
4357 4357 end = b'\n'
4358 4358 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4359 4359
4360 4360 ret = 1
4361 4361 m = scmutil.match(
4362 4362 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4363 4363 )
4364 4364
4365 4365 ui.pager(b'locate')
4366 4366 if ctx.rev() is None:
4367 4367 # When run on the working copy, "locate" includes removed files, so
4368 4368 # we get the list of files from the dirstate.
4369 4369 filesgen = sorted(repo.dirstate.matches(m))
4370 4370 else:
4371 4371 filesgen = ctx.matches(m)
4372 4372 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4373 4373 for abs in filesgen:
4374 4374 if opts.get(b'fullpath'):
4375 4375 ui.write(repo.wjoin(abs), end)
4376 4376 else:
4377 4377 ui.write(uipathfn(abs), end)
4378 4378 ret = 0
4379 4379
4380 4380 return ret
4381 4381
4382 4382
4383 4383 @command(
4384 4384 b'log|history',
4385 4385 [
4386 4386 (
4387 4387 b'f',
4388 4388 b'follow',
4389 4389 None,
4390 4390 _(
4391 4391 b'follow changeset history, or file history across copies and renames'
4392 4392 ),
4393 4393 ),
4394 4394 (
4395 4395 b'',
4396 4396 b'follow-first',
4397 4397 None,
4398 4398 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4399 4399 ),
4400 4400 (
4401 4401 b'd',
4402 4402 b'date',
4403 4403 b'',
4404 4404 _(b'show revisions matching date spec'),
4405 4405 _(b'DATE'),
4406 4406 ),
4407 4407 (b'C', b'copies', None, _(b'show copied files')),
4408 4408 (
4409 4409 b'k',
4410 4410 b'keyword',
4411 4411 [],
4412 4412 _(b'do case-insensitive search for a given text'),
4413 4413 _(b'TEXT'),
4414 4414 ),
4415 4415 (
4416 4416 b'r',
4417 4417 b'rev',
4418 4418 [],
4419 4419 _(b'show the specified revision or revset'),
4420 4420 _(b'REV'),
4421 4421 ),
4422 4422 (
4423 4423 b'L',
4424 4424 b'line-range',
4425 4425 [],
4426 4426 _(b'follow line range of specified file (EXPERIMENTAL)'),
4427 4427 _(b'FILE,RANGE'),
4428 4428 ),
4429 4429 (
4430 4430 b'',
4431 4431 b'removed',
4432 4432 None,
4433 4433 _(b'include revisions where files were removed'),
4434 4434 ),
4435 4435 (
4436 4436 b'm',
4437 4437 b'only-merges',
4438 4438 None,
4439 4439 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4440 4440 ),
4441 4441 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4442 4442 (
4443 4443 b'',
4444 4444 b'only-branch',
4445 4445 [],
4446 4446 _(
4447 4447 b'show only changesets within the given named branch (DEPRECATED)'
4448 4448 ),
4449 4449 _(b'BRANCH'),
4450 4450 ),
4451 4451 (
4452 4452 b'b',
4453 4453 b'branch',
4454 4454 [],
4455 4455 _(b'show changesets within the given named branch'),
4456 4456 _(b'BRANCH'),
4457 4457 ),
4458 4458 (
4459 4459 b'P',
4460 4460 b'prune',
4461 4461 [],
4462 4462 _(b'do not display revision or any of its ancestors'),
4463 4463 _(b'REV'),
4464 4464 ),
4465 4465 ]
4466 4466 + logopts
4467 4467 + walkopts,
4468 4468 _(b'[OPTION]... [FILE]'),
4469 4469 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4470 4470 helpbasic=True,
4471 4471 inferrepo=True,
4472 4472 intents={INTENT_READONLY},
4473 4473 )
4474 4474 def log(ui, repo, *pats, **opts):
4475 4475 """show revision history of entire repository or files
4476 4476
4477 4477 Print the revision history of the specified files or the entire
4478 4478 project.
4479 4479
4480 4480 If no revision range is specified, the default is ``tip:0`` unless
4481 4481 --follow is set, in which case the working directory parent is
4482 4482 used as the starting revision.
4483 4483
4484 4484 File history is shown without following rename or copy history of
4485 4485 files. Use -f/--follow with a filename to follow history across
4486 4486 renames and copies. --follow without a filename will only show
4487 4487 ancestors of the starting revision.
4488 4488
4489 4489 By default this command prints revision number and changeset id,
4490 4490 tags, non-trivial parents, user, date and time, and a summary for
4491 4491 each commit. When the -v/--verbose switch is used, the list of
4492 4492 changed files and full commit message are shown.
4493 4493
4494 4494 With --graph the revisions are shown as an ASCII art DAG with the most
4495 4495 recent changeset at the top.
4496 4496 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4497 4497 involved in an unresolved merge conflict, '_' closes a branch,
4498 4498 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4499 4499 changeset from the lines below is a parent of the 'o' merge on the same
4500 4500 line.
4501 4501 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4502 4502 of a '|' indicates one or more revisions in a path are omitted.
4503 4503
4504 4504 .. container:: verbose
4505 4505
4506 4506 Use -L/--line-range FILE,M:N options to follow the history of lines
4507 4507 from M to N in FILE. With -p/--patch only diff hunks affecting
4508 4508 specified line range will be shown. This option requires --follow;
4509 4509 it can be specified multiple times. Currently, this option is not
4510 4510 compatible with --graph. This option is experimental.
4511 4511
4512 4512 .. note::
4513 4513
4514 4514 :hg:`log --patch` may generate unexpected diff output for merge
4515 4515 changesets, as it will only compare the merge changeset against
4516 4516 its first parent. Also, only files different from BOTH parents
4517 4517 will appear in files:.
4518 4518
4519 4519 .. note::
4520 4520
4521 4521 For performance reasons, :hg:`log FILE` may omit duplicate changes
4522 4522 made on branches and will not show removals or mode changes. To
4523 4523 see all such changes, use the --removed switch.
4524 4524
4525 4525 .. container:: verbose
4526 4526
4527 4527 .. note::
4528 4528
4529 4529 The history resulting from -L/--line-range options depends on diff
4530 4530 options; for instance if white-spaces are ignored, respective changes
4531 4531 with only white-spaces in specified line range will not be listed.
4532 4532
4533 4533 .. container:: verbose
4534 4534
4535 4535 Some examples:
4536 4536
4537 4537 - changesets with full descriptions and file lists::
4538 4538
4539 4539 hg log -v
4540 4540
4541 4541 - changesets ancestral to the working directory::
4542 4542
4543 4543 hg log -f
4544 4544
4545 4545 - last 10 commits on the current branch::
4546 4546
4547 4547 hg log -l 10 -b .
4548 4548
4549 4549 - changesets showing all modifications of a file, including removals::
4550 4550
4551 4551 hg log --removed file.c
4552 4552
4553 4553 - all changesets that touch a directory, with diffs, excluding merges::
4554 4554
4555 4555 hg log -Mp lib/
4556 4556
4557 4557 - all revision numbers that match a keyword::
4558 4558
4559 4559 hg log -k bug --template "{rev}\\n"
4560 4560
4561 4561 - the full hash identifier of the working directory parent::
4562 4562
4563 4563 hg log -r . --template "{node}\\n"
4564 4564
4565 4565 - list available log templates::
4566 4566
4567 4567 hg log -T list
4568 4568
4569 4569 - check if a given changeset is included in a tagged release::
4570 4570
4571 4571 hg log -r "a21ccf and ancestor(1.9)"
4572 4572
4573 4573 - find all changesets by some user in a date range::
4574 4574
4575 4575 hg log -k alice -d "may 2008 to jul 2008"
4576 4576
4577 4577 - summary of all changesets after the last tag::
4578 4578
4579 4579 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4580 4580
4581 4581 - changesets touching lines 13 to 23 for file.c::
4582 4582
4583 4583 hg log -L file.c,13:23
4584 4584
4585 4585 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4586 4586 main.c with patch::
4587 4587
4588 4588 hg log -L file.c,13:23 -L main.c,2:6 -p
4589 4589
4590 4590 See :hg:`help dates` for a list of formats valid for -d/--date.
4591 4591
4592 4592 See :hg:`help revisions` for more about specifying and ordering
4593 4593 revisions.
4594 4594
4595 4595 See :hg:`help templates` for more about pre-packaged styles and
4596 4596 specifying custom templates. The default template used by the log
4597 4597 command can be customized via the ``command-templates.log`` configuration
4598 4598 setting.
4599 4599
4600 4600 Returns 0 on success.
4601 4601
4602 4602 """
4603 4603 opts = pycompat.byteskwargs(opts)
4604 4604 linerange = opts.get(b'line_range')
4605 4605
4606 4606 if linerange and not opts.get(b'follow'):
4607 4607 raise error.InputError(_(b'--line-range requires --follow'))
4608 4608
4609 4609 if linerange and pats:
4610 4610 # TODO: take pats as patterns with no line-range filter
4611 4611 raise error.InputError(
4612 4612 _(b'FILE arguments are not compatible with --line-range option')
4613 4613 )
4614 4614
4615 4615 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4616 4616 revs, differ = logcmdutil.getrevs(
4617 4617 repo, logcmdutil.parseopts(ui, pats, opts)
4618 4618 )
4619 4619 if linerange:
4620 4620 # TODO: should follow file history from logcmdutil._initialrevs(),
4621 4621 # then filter the result by logcmdutil._makerevset() and --limit
4622 4622 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4623 4623
4624 4624 getcopies = None
4625 4625 if opts.get(b'copies'):
4626 4626 endrev = None
4627 4627 if revs:
4628 4628 endrev = revs.max() + 1
4629 4629 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4630 4630
4631 4631 ui.pager(b'log')
4632 4632 displayer = logcmdutil.changesetdisplayer(
4633 4633 ui, repo, opts, differ, buffered=True
4634 4634 )
4635 4635 if opts.get(b'graph'):
4636 4636 displayfn = logcmdutil.displaygraphrevs
4637 4637 else:
4638 4638 displayfn = logcmdutil.displayrevs
4639 4639 displayfn(ui, repo, revs, displayer, getcopies)
4640 4640
4641 4641
4642 4642 @command(
4643 4643 b'manifest',
4644 4644 [
4645 4645 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4646 4646 (b'', b'all', False, _(b"list files from all revisions")),
4647 4647 ]
4648 4648 + formatteropts,
4649 4649 _(b'[-r REV]'),
4650 4650 helpcategory=command.CATEGORY_MAINTENANCE,
4651 4651 intents={INTENT_READONLY},
4652 4652 )
4653 4653 def manifest(ui, repo, node=None, rev=None, **opts):
4654 4654 """output the current or given revision of the project manifest
4655 4655
4656 4656 Print a list of version controlled files for the given revision.
4657 4657 If no revision is given, the first parent of the working directory
4658 4658 is used, or the null revision if no revision is checked out.
4659 4659
4660 4660 With -v, print file permissions, symlink and executable bits.
4661 4661 With --debug, print file revision hashes.
4662 4662
4663 4663 If option --all is specified, the list of all files from all revisions
4664 4664 is printed. This includes deleted and renamed files.
4665 4665
4666 4666 Returns 0 on success.
4667 4667 """
4668 4668 opts = pycompat.byteskwargs(opts)
4669 4669 fm = ui.formatter(b'manifest', opts)
4670 4670
4671 4671 if opts.get(b'all'):
4672 4672 if rev or node:
4673 4673 raise error.InputError(_(b"can't specify a revision with --all"))
4674 4674
4675 4675 res = set()
4676 4676 for rev in repo:
4677 4677 ctx = repo[rev]
4678 4678 res |= set(ctx.files())
4679 4679
4680 4680 ui.pager(b'manifest')
4681 4681 for f in sorted(res):
4682 4682 fm.startitem()
4683 4683 fm.write(b"path", b'%s\n', f)
4684 4684 fm.end()
4685 4685 return
4686 4686
4687 4687 if rev and node:
4688 4688 raise error.InputError(_(b"please specify just one revision"))
4689 4689
4690 4690 if not node:
4691 4691 node = rev
4692 4692
4693 4693 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4694 4694 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4695 4695 if node:
4696 4696 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4697 4697 ctx = scmutil.revsingle(repo, node)
4698 4698 mf = ctx.manifest()
4699 4699 ui.pager(b'manifest')
4700 4700 for f in ctx:
4701 4701 fm.startitem()
4702 4702 fm.context(ctx=ctx)
4703 4703 fl = ctx[f].flags()
4704 4704 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4705 4705 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4706 4706 fm.write(b'path', b'%s\n', f)
4707 4707 fm.end()
4708 4708
4709 4709
4710 4710 @command(
4711 4711 b'merge',
4712 4712 [
4713 4713 (
4714 4714 b'f',
4715 4715 b'force',
4716 4716 None,
4717 4717 _(b'force a merge including outstanding changes (DEPRECATED)'),
4718 4718 ),
4719 4719 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4720 4720 (
4721 4721 b'P',
4722 4722 b'preview',
4723 4723 None,
4724 4724 _(b'review revisions to merge (no merge is performed)'),
4725 4725 ),
4726 4726 (b'', b'abort', None, _(b'abort the ongoing merge')),
4727 4727 ]
4728 4728 + mergetoolopts,
4729 4729 _(b'[-P] [[-r] REV]'),
4730 4730 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4731 4731 helpbasic=True,
4732 4732 )
4733 4733 def merge(ui, repo, node=None, **opts):
4734 4734 """merge another revision into working directory
4735 4735
4736 4736 The current working directory is updated with all changes made in
4737 4737 the requested revision since the last common predecessor revision.
4738 4738
4739 4739 Files that changed between either parent are marked as changed for
4740 4740 the next commit and a commit must be performed before any further
4741 4741 updates to the repository are allowed. The next commit will have
4742 4742 two parents.
4743 4743
4744 4744 ``--tool`` can be used to specify the merge tool used for file
4745 4745 merges. It overrides the HGMERGE environment variable and your
4746 4746 configuration files. See :hg:`help merge-tools` for options.
4747 4747
4748 4748 If no revision is specified, the working directory's parent is a
4749 4749 head revision, and the current branch contains exactly one other
4750 4750 head, the other head is merged with by default. Otherwise, an
4751 4751 explicit revision with which to merge must be provided.
4752 4752
4753 4753 See :hg:`help resolve` for information on handling file conflicts.
4754 4754
4755 4755 To undo an uncommitted merge, use :hg:`merge --abort` which
4756 4756 will check out a clean copy of the original merge parent, losing
4757 4757 all changes.
4758 4758
4759 4759 Returns 0 on success, 1 if there are unresolved files.
4760 4760 """
4761 4761
4762 4762 opts = pycompat.byteskwargs(opts)
4763 4763 abort = opts.get(b'abort')
4764 4764 if abort and repo.dirstate.p2() == nullid:
4765 4765 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4766 4766 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4767 4767 if abort:
4768 4768 state = cmdutil.getunfinishedstate(repo)
4769 4769 if state and state._opname != b'merge':
4770 raise error.Abort(
4770 raise error.StateError(
4771 4771 _(b'cannot abort merge with %s in progress') % (state._opname),
4772 4772 hint=state.hint(),
4773 4773 )
4774 4774 if node:
4775 4775 raise error.InputError(_(b"cannot specify a node with --abort"))
4776 4776 return hg.abortmerge(repo.ui, repo)
4777 4777
4778 4778 if opts.get(b'rev') and node:
4779 4779 raise error.InputError(_(b"please specify just one revision"))
4780 4780 if not node:
4781 4781 node = opts.get(b'rev')
4782 4782
4783 4783 if node:
4784 4784 ctx = scmutil.revsingle(repo, node)
4785 4785 else:
4786 4786 if ui.configbool(b'commands', b'merge.require-rev'):
4787 4787 raise error.InputError(
4788 4788 _(
4789 4789 b'configuration requires specifying revision to merge '
4790 4790 b'with'
4791 4791 )
4792 4792 )
4793 4793 ctx = repo[destutil.destmerge(repo)]
4794 4794
4795 4795 if ctx.node() is None:
4796 4796 raise error.InputError(
4797 4797 _(b'merging with the working copy has no effect')
4798 4798 )
4799 4799
4800 4800 if opts.get(b'preview'):
4801 4801 # find nodes that are ancestors of p2 but not of p1
4802 4802 p1 = repo[b'.'].node()
4803 4803 p2 = ctx.node()
4804 4804 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4805 4805
4806 4806 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4807 4807 for node in nodes:
4808 4808 displayer.show(repo[node])
4809 4809 displayer.close()
4810 4810 return 0
4811 4811
4812 4812 # ui.forcemerge is an internal variable, do not document
4813 4813 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4814 4814 with ui.configoverride(overrides, b'merge'):
4815 4815 force = opts.get(b'force')
4816 4816 labels = [b'working copy', b'merge rev']
4817 4817 return hg.merge(ctx, force=force, labels=labels)
4818 4818
4819 4819
4820 4820 statemod.addunfinished(
4821 4821 b'merge',
4822 4822 fname=None,
4823 4823 clearable=True,
4824 4824 allowcommit=True,
4825 4825 cmdmsg=_(b'outstanding uncommitted merge'),
4826 4826 abortfunc=hg.abortmerge,
4827 4827 statushint=_(
4828 4828 b'To continue: hg commit\nTo abort: hg merge --abort'
4829 4829 ),
4830 4830 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4831 4831 )
4832 4832
4833 4833
4834 4834 @command(
4835 4835 b'outgoing|out',
4836 4836 [
4837 4837 (
4838 4838 b'f',
4839 4839 b'force',
4840 4840 None,
4841 4841 _(b'run even when the destination is unrelated'),
4842 4842 ),
4843 4843 (
4844 4844 b'r',
4845 4845 b'rev',
4846 4846 [],
4847 4847 _(b'a changeset intended to be included in the destination'),
4848 4848 _(b'REV'),
4849 4849 ),
4850 4850 (b'n', b'newest-first', None, _(b'show newest record first')),
4851 4851 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4852 4852 (
4853 4853 b'b',
4854 4854 b'branch',
4855 4855 [],
4856 4856 _(b'a specific branch you would like to push'),
4857 4857 _(b'BRANCH'),
4858 4858 ),
4859 4859 ]
4860 4860 + logopts
4861 4861 + remoteopts
4862 4862 + subrepoopts,
4863 4863 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4864 4864 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4865 4865 )
4866 4866 def outgoing(ui, repo, dest=None, **opts):
4867 4867 """show changesets not found in the destination
4868 4868
4869 4869 Show changesets not found in the specified destination repository
4870 4870 or the default push location. These are the changesets that would
4871 4871 be pushed if a push was requested.
4872 4872
4873 4873 See pull for details of valid destination formats.
4874 4874
4875 4875 .. container:: verbose
4876 4876
4877 4877 With -B/--bookmarks, the result of bookmark comparison between
4878 4878 local and remote repositories is displayed. With -v/--verbose,
4879 4879 status is also displayed for each bookmark like below::
4880 4880
4881 4881 BM1 01234567890a added
4882 4882 BM2 deleted
4883 4883 BM3 234567890abc advanced
4884 4884 BM4 34567890abcd diverged
4885 4885 BM5 4567890abcde changed
4886 4886
4887 4887 The action taken when pushing depends on the
4888 4888 status of each bookmark:
4889 4889
4890 4890 :``added``: push with ``-B`` will create it
4891 4891 :``deleted``: push with ``-B`` will delete it
4892 4892 :``advanced``: push will update it
4893 4893 :``diverged``: push with ``-B`` will update it
4894 4894 :``changed``: push with ``-B`` will update it
4895 4895
4896 4896 From the point of view of pushing behavior, bookmarks
4897 4897 existing only in the remote repository are treated as
4898 4898 ``deleted``, even if it is in fact added remotely.
4899 4899
4900 4900 Returns 0 if there are outgoing changes, 1 otherwise.
4901 4901 """
4902 4902 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4903 4903 # style URLs, so don't overwrite dest.
4904 4904 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
4905 4905 if not path:
4906 4906 raise error.Abort(
4907 4907 _(b'default repository not configured!'),
4908 4908 hint=_(b"see 'hg help config.paths'"),
4909 4909 )
4910 4910
4911 4911 opts = pycompat.byteskwargs(opts)
4912 4912 if opts.get(b'graph'):
4913 4913 logcmdutil.checkunsupportedgraphflags([], opts)
4914 4914 o, other = hg._outgoing(ui, repo, dest, opts)
4915 4915 if not o:
4916 4916 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4917 4917 return
4918 4918
4919 4919 revdag = logcmdutil.graphrevs(repo, o, opts)
4920 4920 ui.pager(b'outgoing')
4921 4921 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4922 4922 logcmdutil.displaygraph(
4923 4923 ui, repo, revdag, displayer, graphmod.asciiedges
4924 4924 )
4925 4925 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4926 4926 return 0
4927 4927
4928 4928 if opts.get(b'bookmarks'):
4929 4929 dest = path.pushloc or path.loc
4930 4930 other = hg.peer(repo, opts, dest)
4931 4931 if b'bookmarks' not in other.listkeys(b'namespaces'):
4932 4932 ui.warn(_(b"remote doesn't support bookmarks\n"))
4933 4933 return 0
4934 4934 ui.status(_(b'comparing with %s\n') % util.hidepassword(dest))
4935 4935 ui.pager(b'outgoing')
4936 4936 return bookmarks.outgoing(ui, repo, other)
4937 4937
4938 4938 repo._subtoppath = path.pushloc or path.loc
4939 4939 try:
4940 4940 return hg.outgoing(ui, repo, dest, opts)
4941 4941 finally:
4942 4942 del repo._subtoppath
4943 4943
4944 4944
4945 4945 @command(
4946 4946 b'parents',
4947 4947 [
4948 4948 (
4949 4949 b'r',
4950 4950 b'rev',
4951 4951 b'',
4952 4952 _(b'show parents of the specified revision'),
4953 4953 _(b'REV'),
4954 4954 ),
4955 4955 ]
4956 4956 + templateopts,
4957 4957 _(b'[-r REV] [FILE]'),
4958 4958 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4959 4959 inferrepo=True,
4960 4960 )
4961 4961 def parents(ui, repo, file_=None, **opts):
4962 4962 """show the parents of the working directory or revision (DEPRECATED)
4963 4963
4964 4964 Print the working directory's parent revisions. If a revision is
4965 4965 given via -r/--rev, the parent of that revision will be printed.
4966 4966 If a file argument is given, the revision in which the file was
4967 4967 last changed (before the working directory revision or the
4968 4968 argument to --rev if given) is printed.
4969 4969
4970 4970 This command is equivalent to::
4971 4971
4972 4972 hg log -r "p1()+p2()" or
4973 4973 hg log -r "p1(REV)+p2(REV)" or
4974 4974 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4975 4975 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4976 4976
4977 4977 See :hg:`summary` and :hg:`help revsets` for related information.
4978 4978
4979 4979 Returns 0 on success.
4980 4980 """
4981 4981
4982 4982 opts = pycompat.byteskwargs(opts)
4983 4983 rev = opts.get(b'rev')
4984 4984 if rev:
4985 4985 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
4986 4986 ctx = scmutil.revsingle(repo, rev, None)
4987 4987
4988 4988 if file_:
4989 4989 m = scmutil.match(ctx, (file_,), opts)
4990 4990 if m.anypats() or len(m.files()) != 1:
4991 4991 raise error.InputError(_(b'can only specify an explicit filename'))
4992 4992 file_ = m.files()[0]
4993 4993 filenodes = []
4994 4994 for cp in ctx.parents():
4995 4995 if not cp:
4996 4996 continue
4997 4997 try:
4998 4998 filenodes.append(cp.filenode(file_))
4999 4999 except error.LookupError:
5000 5000 pass
5001 5001 if not filenodes:
5002 5002 raise error.InputError(_(b"'%s' not found in manifest!") % file_)
5003 5003 p = []
5004 5004 for fn in filenodes:
5005 5005 fctx = repo.filectx(file_, fileid=fn)
5006 5006 p.append(fctx.node())
5007 5007 else:
5008 5008 p = [cp.node() for cp in ctx.parents()]
5009 5009
5010 5010 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5011 5011 for n in p:
5012 5012 if n != nullid:
5013 5013 displayer.show(repo[n])
5014 5014 displayer.close()
5015 5015
5016 5016
5017 5017 @command(
5018 5018 b'paths',
5019 5019 formatteropts,
5020 5020 _(b'[NAME]'),
5021 5021 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5022 5022 optionalrepo=True,
5023 5023 intents={INTENT_READONLY},
5024 5024 )
5025 5025 def paths(ui, repo, search=None, **opts):
5026 5026 """show aliases for remote repositories
5027 5027
5028 5028 Show definition of symbolic path name NAME. If no name is given,
5029 5029 show definition of all available names.
5030 5030
5031 5031 Option -q/--quiet suppresses all output when searching for NAME
5032 5032 and shows only the path names when listing all definitions.
5033 5033
5034 5034 Path names are defined in the [paths] section of your
5035 5035 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5036 5036 repository, ``.hg/hgrc`` is used, too.
5037 5037
5038 5038 The path names ``default`` and ``default-push`` have a special
5039 5039 meaning. When performing a push or pull operation, they are used
5040 5040 as fallbacks if no location is specified on the command-line.
5041 5041 When ``default-push`` is set, it will be used for push and
5042 5042 ``default`` will be used for pull; otherwise ``default`` is used
5043 5043 as the fallback for both. When cloning a repository, the clone
5044 5044 source is written as ``default`` in ``.hg/hgrc``.
5045 5045
5046 5046 .. note::
5047 5047
5048 5048 ``default`` and ``default-push`` apply to all inbound (e.g.
5049 5049 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5050 5050 and :hg:`bundle`) operations.
5051 5051
5052 5052 See :hg:`help urls` for more information.
5053 5053
5054 5054 .. container:: verbose
5055 5055
5056 5056 Template:
5057 5057
5058 5058 The following keywords are supported. See also :hg:`help templates`.
5059 5059
5060 5060 :name: String. Symbolic name of the path alias.
5061 5061 :pushurl: String. URL for push operations.
5062 5062 :url: String. URL or directory path for the other operations.
5063 5063
5064 5064 Returns 0 on success.
5065 5065 """
5066 5066
5067 5067 opts = pycompat.byteskwargs(opts)
5068 5068 ui.pager(b'paths')
5069 5069 if search:
5070 5070 pathitems = [
5071 5071 (name, path)
5072 5072 for name, path in pycompat.iteritems(ui.paths)
5073 5073 if name == search
5074 5074 ]
5075 5075 else:
5076 5076 pathitems = sorted(pycompat.iteritems(ui.paths))
5077 5077
5078 5078 fm = ui.formatter(b'paths', opts)
5079 5079 if fm.isplain():
5080 5080 hidepassword = util.hidepassword
5081 5081 else:
5082 5082 hidepassword = bytes
5083 5083 if ui.quiet:
5084 5084 namefmt = b'%s\n'
5085 5085 else:
5086 5086 namefmt = b'%s = '
5087 5087 showsubopts = not search and not ui.quiet
5088 5088
5089 5089 for name, path in pathitems:
5090 5090 fm.startitem()
5091 5091 fm.condwrite(not search, b'name', namefmt, name)
5092 5092 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5093 5093 for subopt, value in sorted(path.suboptions.items()):
5094 5094 assert subopt not in (b'name', b'url')
5095 5095 if showsubopts:
5096 5096 fm.plain(b'%s:%s = ' % (name, subopt))
5097 5097 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5098 5098
5099 5099 fm.end()
5100 5100
5101 5101 if search and not pathitems:
5102 5102 if not ui.quiet:
5103 5103 ui.warn(_(b"not found!\n"))
5104 5104 return 1
5105 5105 else:
5106 5106 return 0
5107 5107
5108 5108
5109 5109 @command(
5110 5110 b'phase',
5111 5111 [
5112 5112 (b'p', b'public', False, _(b'set changeset phase to public')),
5113 5113 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5114 5114 (b's', b'secret', False, _(b'set changeset phase to secret')),
5115 5115 (b'f', b'force', False, _(b'allow to move boundary backward')),
5116 5116 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5117 5117 ],
5118 5118 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5119 5119 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5120 5120 )
5121 5121 def phase(ui, repo, *revs, **opts):
5122 5122 """set or show the current phase name
5123 5123
5124 5124 With no argument, show the phase name of the current revision(s).
5125 5125
5126 5126 With one of -p/--public, -d/--draft or -s/--secret, change the
5127 5127 phase value of the specified revisions.
5128 5128
5129 5129 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5130 5130 lower phase to a higher phase. Phases are ordered as follows::
5131 5131
5132 5132 public < draft < secret
5133 5133
5134 5134 Returns 0 on success, 1 if some phases could not be changed.
5135 5135
5136 5136 (For more information about the phases concept, see :hg:`help phases`.)
5137 5137 """
5138 5138 opts = pycompat.byteskwargs(opts)
5139 5139 # search for a unique phase argument
5140 5140 targetphase = None
5141 5141 for idx, name in enumerate(phases.cmdphasenames):
5142 5142 if opts[name]:
5143 5143 if targetphase is not None:
5144 5144 raise error.InputError(_(b'only one phase can be specified'))
5145 5145 targetphase = idx
5146 5146
5147 5147 # look for specified revision
5148 5148 revs = list(revs)
5149 5149 revs.extend(opts[b'rev'])
5150 5150 if not revs:
5151 5151 # display both parents as the second parent phase can influence
5152 5152 # the phase of a merge commit
5153 5153 revs = [c.rev() for c in repo[None].parents()]
5154 5154
5155 5155 revs = scmutil.revrange(repo, revs)
5156 5156
5157 5157 ret = 0
5158 5158 if targetphase is None:
5159 5159 # display
5160 5160 for r in revs:
5161 5161 ctx = repo[r]
5162 5162 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5163 5163 else:
5164 5164 with repo.lock(), repo.transaction(b"phase") as tr:
5165 5165 # set phase
5166 5166 if not revs:
5167 5167 raise error.InputError(_(b'empty revision set'))
5168 5168 nodes = [repo[r].node() for r in revs]
5169 5169 # moving revision from public to draft may hide them
5170 5170 # We have to check result on an unfiltered repository
5171 5171 unfi = repo.unfiltered()
5172 5172 getphase = unfi._phasecache.phase
5173 5173 olddata = [getphase(unfi, r) for r in unfi]
5174 5174 phases.advanceboundary(repo, tr, targetphase, nodes)
5175 5175 if opts[b'force']:
5176 5176 phases.retractboundary(repo, tr, targetphase, nodes)
5177 5177 getphase = unfi._phasecache.phase
5178 5178 newdata = [getphase(unfi, r) for r in unfi]
5179 5179 changes = sum(newdata[r] != olddata[r] for r in unfi)
5180 5180 cl = unfi.changelog
5181 5181 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5182 5182 if rejected:
5183 5183 ui.warn(
5184 5184 _(
5185 5185 b'cannot move %i changesets to a higher '
5186 5186 b'phase, use --force\n'
5187 5187 )
5188 5188 % len(rejected)
5189 5189 )
5190 5190 ret = 1
5191 5191 if changes:
5192 5192 msg = _(b'phase changed for %i changesets\n') % changes
5193 5193 if ret:
5194 5194 ui.status(msg)
5195 5195 else:
5196 5196 ui.note(msg)
5197 5197 else:
5198 5198 ui.warn(_(b'no phases changed\n'))
5199 5199 return ret
5200 5200
5201 5201
5202 5202 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5203 5203 """Run after a changegroup has been added via pull/unbundle
5204 5204
5205 5205 This takes arguments below:
5206 5206
5207 5207 :modheads: change of heads by pull/unbundle
5208 5208 :optupdate: updating working directory is needed or not
5209 5209 :checkout: update destination revision (or None to default destination)
5210 5210 :brev: a name, which might be a bookmark to be activated after updating
5211 5211 """
5212 5212 if modheads == 0:
5213 5213 return
5214 5214 if optupdate:
5215 5215 try:
5216 5216 return hg.updatetotally(ui, repo, checkout, brev)
5217 5217 except error.UpdateAbort as inst:
5218 5218 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5219 5219 hint = inst.hint
5220 5220 raise error.UpdateAbort(msg, hint=hint)
5221 5221 if modheads is not None and modheads > 1:
5222 5222 currentbranchheads = len(repo.branchheads())
5223 5223 if currentbranchheads == modheads:
5224 5224 ui.status(
5225 5225 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5226 5226 )
5227 5227 elif currentbranchheads > 1:
5228 5228 ui.status(
5229 5229 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5230 5230 )
5231 5231 else:
5232 5232 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5233 5233 elif not ui.configbool(b'commands', b'update.requiredest'):
5234 5234 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5235 5235
5236 5236
5237 5237 @command(
5238 5238 b'pull',
5239 5239 [
5240 5240 (
5241 5241 b'u',
5242 5242 b'update',
5243 5243 None,
5244 5244 _(b'update to new branch head if new descendants were pulled'),
5245 5245 ),
5246 5246 (
5247 5247 b'f',
5248 5248 b'force',
5249 5249 None,
5250 5250 _(b'run even when remote repository is unrelated'),
5251 5251 ),
5252 5252 (b'', b'confirm', None, _(b'confirm pull before applying changes'),),
5253 5253 (
5254 5254 b'r',
5255 5255 b'rev',
5256 5256 [],
5257 5257 _(b'a remote changeset intended to be added'),
5258 5258 _(b'REV'),
5259 5259 ),
5260 5260 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5261 5261 (
5262 5262 b'b',
5263 5263 b'branch',
5264 5264 [],
5265 5265 _(b'a specific branch you would like to pull'),
5266 5266 _(b'BRANCH'),
5267 5267 ),
5268 5268 ]
5269 5269 + remoteopts,
5270 5270 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
5271 5271 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5272 5272 helpbasic=True,
5273 5273 )
5274 5274 def pull(ui, repo, source=b"default", **opts):
5275 5275 """pull changes from the specified source
5276 5276
5277 5277 Pull changes from a remote repository to a local one.
5278 5278
5279 5279 This finds all changes from the repository at the specified path
5280 5280 or URL and adds them to a local repository (the current one unless
5281 5281 -R is specified). By default, this does not update the copy of the
5282 5282 project in the working directory.
5283 5283
5284 5284 When cloning from servers that support it, Mercurial may fetch
5285 5285 pre-generated data. When this is done, hooks operating on incoming
5286 5286 changesets and changegroups may fire more than once, once for each
5287 5287 pre-generated bundle and as well as for any additional remaining
5288 5288 data. See :hg:`help -e clonebundles` for more.
5289 5289
5290 5290 Use :hg:`incoming` if you want to see what would have been added
5291 5291 by a pull at the time you issued this command. If you then decide
5292 5292 to add those changes to the repository, you should use :hg:`pull
5293 5293 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5294 5294
5295 5295 If SOURCE is omitted, the 'default' path will be used.
5296 5296 See :hg:`help urls` for more information.
5297 5297
5298 5298 Specifying bookmark as ``.`` is equivalent to specifying the active
5299 5299 bookmark's name.
5300 5300
5301 5301 Returns 0 on success, 1 if an update had unresolved files.
5302 5302 """
5303 5303
5304 5304 opts = pycompat.byteskwargs(opts)
5305 5305 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5306 5306 b'update'
5307 5307 ):
5308 5308 msg = _(b'update destination required by configuration')
5309 5309 hint = _(b'use hg pull followed by hg update DEST')
5310 5310 raise error.InputError(msg, hint=hint)
5311 5311
5312 5312 source, branches = hg.parseurl(ui.expandpath(source), opts.get(b'branch'))
5313 5313 ui.status(_(b'pulling from %s\n') % util.hidepassword(source))
5314 5314 other = hg.peer(repo, opts, source)
5315 5315 try:
5316 5316 revs, checkout = hg.addbranchrevs(
5317 5317 repo, other, branches, opts.get(b'rev')
5318 5318 )
5319 5319
5320 5320 pullopargs = {}
5321 5321
5322 5322 nodes = None
5323 5323 if opts.get(b'bookmark') or revs:
5324 5324 # The list of bookmark used here is the same used to actually update
5325 5325 # the bookmark names, to avoid the race from issue 4689 and we do
5326 5326 # all lookup and bookmark queries in one go so they see the same
5327 5327 # version of the server state (issue 4700).
5328 5328 nodes = []
5329 5329 fnodes = []
5330 5330 revs = revs or []
5331 5331 if revs and not other.capable(b'lookup'):
5332 5332 err = _(
5333 5333 b"other repository doesn't support revision lookup, "
5334 5334 b"so a rev cannot be specified."
5335 5335 )
5336 5336 raise error.Abort(err)
5337 5337 with other.commandexecutor() as e:
5338 5338 fremotebookmarks = e.callcommand(
5339 5339 b'listkeys', {b'namespace': b'bookmarks'}
5340 5340 )
5341 5341 for r in revs:
5342 5342 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5343 5343 remotebookmarks = fremotebookmarks.result()
5344 5344 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5345 5345 pullopargs[b'remotebookmarks'] = remotebookmarks
5346 5346 for b in opts.get(b'bookmark', []):
5347 5347 b = repo._bookmarks.expandname(b)
5348 5348 if b not in remotebookmarks:
5349 5349 raise error.InputError(
5350 5350 _(b'remote bookmark %s not found!') % b
5351 5351 )
5352 5352 nodes.append(remotebookmarks[b])
5353 5353 for i, rev in enumerate(revs):
5354 5354 node = fnodes[i].result()
5355 5355 nodes.append(node)
5356 5356 if rev == checkout:
5357 5357 checkout = node
5358 5358
5359 5359 wlock = util.nullcontextmanager()
5360 5360 if opts.get(b'update'):
5361 5361 wlock = repo.wlock()
5362 5362 with wlock:
5363 5363 pullopargs.update(opts.get(b'opargs', {}))
5364 5364 modheads = exchange.pull(
5365 5365 repo,
5366 5366 other,
5367 5367 heads=nodes,
5368 5368 force=opts.get(b'force'),
5369 5369 bookmarks=opts.get(b'bookmark', ()),
5370 5370 opargs=pullopargs,
5371 5371 confirm=opts.get(b'confirm'),
5372 5372 ).cgresult
5373 5373
5374 5374 # brev is a name, which might be a bookmark to be activated at
5375 5375 # the end of the update. In other words, it is an explicit
5376 5376 # destination of the update
5377 5377 brev = None
5378 5378
5379 5379 if checkout:
5380 5380 checkout = repo.unfiltered().changelog.rev(checkout)
5381 5381
5382 5382 # order below depends on implementation of
5383 5383 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5384 5384 # because 'checkout' is determined without it.
5385 5385 if opts.get(b'rev'):
5386 5386 brev = opts[b'rev'][0]
5387 5387 elif opts.get(b'branch'):
5388 5388 brev = opts[b'branch'][0]
5389 5389 else:
5390 5390 brev = branches[0]
5391 5391 repo._subtoppath = source
5392 5392 try:
5393 5393 ret = postincoming(
5394 5394 ui, repo, modheads, opts.get(b'update'), checkout, brev
5395 5395 )
5396 5396 except error.FilteredRepoLookupError as exc:
5397 5397 msg = _(b'cannot update to target: %s') % exc.args[0]
5398 5398 exc.args = (msg,) + exc.args[1:]
5399 5399 raise
5400 5400 finally:
5401 5401 del repo._subtoppath
5402 5402
5403 5403 finally:
5404 5404 other.close()
5405 5405 return ret
5406 5406
5407 5407
5408 5408 @command(
5409 5409 b'push',
5410 5410 [
5411 5411 (b'f', b'force', None, _(b'force push')),
5412 5412 (
5413 5413 b'r',
5414 5414 b'rev',
5415 5415 [],
5416 5416 _(b'a changeset intended to be included in the destination'),
5417 5417 _(b'REV'),
5418 5418 ),
5419 5419 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5420 5420 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5421 5421 (
5422 5422 b'b',
5423 5423 b'branch',
5424 5424 [],
5425 5425 _(b'a specific branch you would like to push'),
5426 5426 _(b'BRANCH'),
5427 5427 ),
5428 5428 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5429 5429 (
5430 5430 b'',
5431 5431 b'pushvars',
5432 5432 [],
5433 5433 _(b'variables that can be sent to server (ADVANCED)'),
5434 5434 ),
5435 5435 (
5436 5436 b'',
5437 5437 b'publish',
5438 5438 False,
5439 5439 _(b'push the changeset as public (EXPERIMENTAL)'),
5440 5440 ),
5441 5441 ]
5442 5442 + remoteopts,
5443 5443 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
5444 5444 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5445 5445 helpbasic=True,
5446 5446 )
5447 5447 def push(ui, repo, dest=None, **opts):
5448 5448 """push changes to the specified destination
5449 5449
5450 5450 Push changesets from the local repository to the specified
5451 5451 destination.
5452 5452
5453 5453 This operation is symmetrical to pull: it is identical to a pull
5454 5454 in the destination repository from the current one.
5455 5455
5456 5456 By default, push will not allow creation of new heads at the
5457 5457 destination, since multiple heads would make it unclear which head
5458 5458 to use. In this situation, it is recommended to pull and merge
5459 5459 before pushing.
5460 5460
5461 5461 Use --new-branch if you want to allow push to create a new named
5462 5462 branch that is not present at the destination. This allows you to
5463 5463 only create a new branch without forcing other changes.
5464 5464
5465 5465 .. note::
5466 5466
5467 5467 Extra care should be taken with the -f/--force option,
5468 5468 which will push all new heads on all branches, an action which will
5469 5469 almost always cause confusion for collaborators.
5470 5470
5471 5471 If -r/--rev is used, the specified revision and all its ancestors
5472 5472 will be pushed to the remote repository.
5473 5473
5474 5474 If -B/--bookmark is used, the specified bookmarked revision, its
5475 5475 ancestors, and the bookmark will be pushed to the remote
5476 5476 repository. Specifying ``.`` is equivalent to specifying the active
5477 5477 bookmark's name. Use the --all-bookmarks option for pushing all
5478 5478 current bookmarks.
5479 5479
5480 5480 Please see :hg:`help urls` for important details about ``ssh://``
5481 5481 URLs. If DESTINATION is omitted, a default path will be used.
5482 5482
5483 5483 .. container:: verbose
5484 5484
5485 5485 The --pushvars option sends strings to the server that become
5486 5486 environment variables prepended with ``HG_USERVAR_``. For example,
5487 5487 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5488 5488 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5489 5489
5490 5490 pushvars can provide for user-overridable hooks as well as set debug
5491 5491 levels. One example is having a hook that blocks commits containing
5492 5492 conflict markers, but enables the user to override the hook if the file
5493 5493 is using conflict markers for testing purposes or the file format has
5494 5494 strings that look like conflict markers.
5495 5495
5496 5496 By default, servers will ignore `--pushvars`. To enable it add the
5497 5497 following to your configuration file::
5498 5498
5499 5499 [push]
5500 5500 pushvars.server = true
5501 5501
5502 5502 Returns 0 if push was successful, 1 if nothing to push.
5503 5503 """
5504 5504
5505 5505 opts = pycompat.byteskwargs(opts)
5506 5506
5507 5507 if opts.get(b'all_bookmarks'):
5508 5508 cmdutil.check_incompatible_arguments(
5509 5509 opts, b'all_bookmarks', [b'bookmark', b'rev'],
5510 5510 )
5511 5511 opts[b'bookmark'] = list(repo._bookmarks)
5512 5512
5513 5513 if opts.get(b'bookmark'):
5514 5514 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5515 5515 for b in opts[b'bookmark']:
5516 5516 # translate -B options to -r so changesets get pushed
5517 5517 b = repo._bookmarks.expandname(b)
5518 5518 if b in repo._bookmarks:
5519 5519 opts.setdefault(b'rev', []).append(b)
5520 5520 else:
5521 5521 # if we try to push a deleted bookmark, translate it to null
5522 5522 # this lets simultaneous -r, -b options continue working
5523 5523 opts.setdefault(b'rev', []).append(b"null")
5524 5524
5525 5525 path = ui.paths.getpath(dest, default=(b'default-push', b'default'))
5526 5526 if not path:
5527 5527 raise error.Abort(
5528 5528 _(b'default repository not configured!'),
5529 5529 hint=_(b"see 'hg help config.paths'"),
5530 5530 )
5531 5531 dest = path.pushloc or path.loc
5532 5532 branches = (path.branch, opts.get(b'branch') or [])
5533 5533 ui.status(_(b'pushing to %s\n') % util.hidepassword(dest))
5534 5534 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev'))
5535 5535 other = hg.peer(repo, opts, dest)
5536 5536
5537 5537 if revs:
5538 5538 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5539 5539 if not revs:
5540 5540 raise error.InputError(
5541 5541 _(b"specified revisions evaluate to an empty set"),
5542 5542 hint=_(b"use different revision arguments"),
5543 5543 )
5544 5544 elif path.pushrev:
5545 5545 # It doesn't make any sense to specify ancestor revisions. So limit
5546 5546 # to DAG heads to make discovery simpler.
5547 5547 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5548 5548 revs = scmutil.revrange(repo, [expr])
5549 5549 revs = [repo[rev].node() for rev in revs]
5550 5550 if not revs:
5551 5551 raise error.InputError(
5552 5552 _(b'default push revset for path evaluates to an empty set')
5553 5553 )
5554 5554 elif ui.configbool(b'commands', b'push.require-revs'):
5555 5555 raise error.InputError(
5556 5556 _(b'no revisions specified to push'),
5557 5557 hint=_(b'did you mean "hg push -r ."?'),
5558 5558 )
5559 5559
5560 5560 repo._subtoppath = dest
5561 5561 try:
5562 5562 # push subrepos depth-first for coherent ordering
5563 5563 c = repo[b'.']
5564 5564 subs = c.substate # only repos that are committed
5565 5565 for s in sorted(subs):
5566 5566 result = c.sub(s).push(opts)
5567 5567 if result == 0:
5568 5568 return not result
5569 5569 finally:
5570 5570 del repo._subtoppath
5571 5571
5572 5572 opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it
5573 5573 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5574 5574
5575 5575 pushop = exchange.push(
5576 5576 repo,
5577 5577 other,
5578 5578 opts.get(b'force'),
5579 5579 revs=revs,
5580 5580 newbranch=opts.get(b'new_branch'),
5581 5581 bookmarks=opts.get(b'bookmark', ()),
5582 5582 publish=opts.get(b'publish'),
5583 5583 opargs=opargs,
5584 5584 )
5585 5585
5586 5586 result = not pushop.cgresult
5587 5587
5588 5588 if pushop.bkresult is not None:
5589 5589 if pushop.bkresult == 2:
5590 5590 result = 2
5591 5591 elif not result and pushop.bkresult:
5592 5592 result = 2
5593 5593
5594 5594 return result
5595 5595
5596 5596
5597 5597 @command(
5598 5598 b'recover',
5599 5599 [(b'', b'verify', False, b"run `hg verify` after successful recover"),],
5600 5600 helpcategory=command.CATEGORY_MAINTENANCE,
5601 5601 )
5602 5602 def recover(ui, repo, **opts):
5603 5603 """roll back an interrupted transaction
5604 5604
5605 5605 Recover from an interrupted commit or pull.
5606 5606
5607 5607 This command tries to fix the repository status after an
5608 5608 interrupted operation. It should only be necessary when Mercurial
5609 5609 suggests it.
5610 5610
5611 5611 Returns 0 if successful, 1 if nothing to recover or verify fails.
5612 5612 """
5613 5613 ret = repo.recover()
5614 5614 if ret:
5615 5615 if opts['verify']:
5616 5616 return hg.verify(repo)
5617 5617 else:
5618 5618 msg = _(
5619 5619 b"(verify step skipped, run `hg verify` to check your "
5620 5620 b"repository content)\n"
5621 5621 )
5622 5622 ui.warn(msg)
5623 5623 return 0
5624 5624 return 1
5625 5625
5626 5626
5627 5627 @command(
5628 5628 b'remove|rm',
5629 5629 [
5630 5630 (b'A', b'after', None, _(b'record delete for missing files')),
5631 5631 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5632 5632 ]
5633 5633 + subrepoopts
5634 5634 + walkopts
5635 5635 + dryrunopts,
5636 5636 _(b'[OPTION]... FILE...'),
5637 5637 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5638 5638 helpbasic=True,
5639 5639 inferrepo=True,
5640 5640 )
5641 5641 def remove(ui, repo, *pats, **opts):
5642 5642 """remove the specified files on the next commit
5643 5643
5644 5644 Schedule the indicated files for removal from the current branch.
5645 5645
5646 5646 This command schedules the files to be removed at the next commit.
5647 5647 To undo a remove before that, see :hg:`revert`. To undo added
5648 5648 files, see :hg:`forget`.
5649 5649
5650 5650 .. container:: verbose
5651 5651
5652 5652 -A/--after can be used to remove only files that have already
5653 5653 been deleted, -f/--force can be used to force deletion, and -Af
5654 5654 can be used to remove files from the next revision without
5655 5655 deleting them from the working directory.
5656 5656
5657 5657 The following table details the behavior of remove for different
5658 5658 file states (columns) and option combinations (rows). The file
5659 5659 states are Added [A], Clean [C], Modified [M] and Missing [!]
5660 5660 (as reported by :hg:`status`). The actions are Warn, Remove
5661 5661 (from branch) and Delete (from disk):
5662 5662
5663 5663 ========= == == == ==
5664 5664 opt/state A C M !
5665 5665 ========= == == == ==
5666 5666 none W RD W R
5667 5667 -f R RD RD R
5668 5668 -A W W W R
5669 5669 -Af R R R R
5670 5670 ========= == == == ==
5671 5671
5672 5672 .. note::
5673 5673
5674 5674 :hg:`remove` never deletes files in Added [A] state from the
5675 5675 working directory, not even if ``--force`` is specified.
5676 5676
5677 5677 Returns 0 on success, 1 if any warnings encountered.
5678 5678 """
5679 5679
5680 5680 opts = pycompat.byteskwargs(opts)
5681 5681 after, force = opts.get(b'after'), opts.get(b'force')
5682 5682 dryrun = opts.get(b'dry_run')
5683 5683 if not pats and not after:
5684 5684 raise error.InputError(_(b'no files specified'))
5685 5685
5686 5686 m = scmutil.match(repo[None], pats, opts)
5687 5687 subrepos = opts.get(b'subrepos')
5688 5688 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5689 5689 return cmdutil.remove(
5690 5690 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5691 5691 )
5692 5692
5693 5693
5694 5694 @command(
5695 5695 b'rename|move|mv',
5696 5696 [
5697 5697 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5698 5698 (
5699 5699 b'',
5700 5700 b'at-rev',
5701 5701 b'',
5702 5702 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5703 5703 _(b'REV'),
5704 5704 ),
5705 5705 (
5706 5706 b'f',
5707 5707 b'force',
5708 5708 None,
5709 5709 _(b'forcibly move over an existing managed file'),
5710 5710 ),
5711 5711 ]
5712 5712 + walkopts
5713 5713 + dryrunopts,
5714 5714 _(b'[OPTION]... SOURCE... DEST'),
5715 5715 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5716 5716 )
5717 5717 def rename(ui, repo, *pats, **opts):
5718 5718 """rename files; equivalent of copy + remove
5719 5719
5720 5720 Mark dest as copies of sources; mark sources for deletion. If dest
5721 5721 is a directory, copies are put in that directory. If dest is a
5722 5722 file, there can only be one source.
5723 5723
5724 5724 By default, this command copies the contents of files as they
5725 5725 exist in the working directory. If invoked with -A/--after, the
5726 5726 operation is recorded, but no copying is performed.
5727 5727
5728 5728 This command takes effect at the next commit. To undo a rename
5729 5729 before that, see :hg:`revert`.
5730 5730
5731 5731 Returns 0 on success, 1 if errors are encountered.
5732 5732 """
5733 5733 opts = pycompat.byteskwargs(opts)
5734 5734 with repo.wlock():
5735 5735 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5736 5736
5737 5737
5738 5738 @command(
5739 5739 b'resolve',
5740 5740 [
5741 5741 (b'a', b'all', None, _(b'select all unresolved files')),
5742 5742 (b'l', b'list', None, _(b'list state of files needing merge')),
5743 5743 (b'm', b'mark', None, _(b'mark files as resolved')),
5744 5744 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5745 5745 (b'n', b'no-status', None, _(b'hide status prefix')),
5746 5746 (b'', b're-merge', None, _(b're-merge files')),
5747 5747 ]
5748 5748 + mergetoolopts
5749 5749 + walkopts
5750 5750 + formatteropts,
5751 5751 _(b'[OPTION]... [FILE]...'),
5752 5752 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5753 5753 inferrepo=True,
5754 5754 )
5755 5755 def resolve(ui, repo, *pats, **opts):
5756 5756 """redo merges or set/view the merge status of files
5757 5757
5758 5758 Merges with unresolved conflicts are often the result of
5759 5759 non-interactive merging using the ``internal:merge`` configuration
5760 5760 setting, or a command-line merge tool like ``diff3``. The resolve
5761 5761 command is used to manage the files involved in a merge, after
5762 5762 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5763 5763 working directory must have two parents). See :hg:`help
5764 5764 merge-tools` for information on configuring merge tools.
5765 5765
5766 5766 The resolve command can be used in the following ways:
5767 5767
5768 5768 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5769 5769 the specified files, discarding any previous merge attempts. Re-merging
5770 5770 is not performed for files already marked as resolved. Use ``--all/-a``
5771 5771 to select all unresolved files. ``--tool`` can be used to specify
5772 5772 the merge tool used for the given files. It overrides the HGMERGE
5773 5773 environment variable and your configuration files. Previous file
5774 5774 contents are saved with a ``.orig`` suffix.
5775 5775
5776 5776 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5777 5777 (e.g. after having manually fixed-up the files). The default is
5778 5778 to mark all unresolved files.
5779 5779
5780 5780 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5781 5781 default is to mark all resolved files.
5782 5782
5783 5783 - :hg:`resolve -l`: list files which had or still have conflicts.
5784 5784 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5785 5785 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5786 5786 the list. See :hg:`help filesets` for details.
5787 5787
5788 5788 .. note::
5789 5789
5790 5790 Mercurial will not let you commit files with unresolved merge
5791 5791 conflicts. You must use :hg:`resolve -m ...` before you can
5792 5792 commit after a conflicting merge.
5793 5793
5794 5794 .. container:: verbose
5795 5795
5796 5796 Template:
5797 5797
5798 5798 The following keywords are supported in addition to the common template
5799 5799 keywords and functions. See also :hg:`help templates`.
5800 5800
5801 5801 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5802 5802 :path: String. Repository-absolute path of the file.
5803 5803
5804 5804 Returns 0 on success, 1 if any files fail a resolve attempt.
5805 5805 """
5806 5806
5807 5807 opts = pycompat.byteskwargs(opts)
5808 5808 confirm = ui.configbool(b'commands', b'resolve.confirm')
5809 5809 flaglist = b'all mark unmark list no_status re_merge'.split()
5810 5810 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5811 5811
5812 5812 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5813 5813 if actioncount > 1:
5814 5814 raise error.InputError(_(b"too many actions specified"))
5815 5815 elif actioncount == 0 and ui.configbool(
5816 5816 b'commands', b'resolve.explicit-re-merge'
5817 5817 ):
5818 5818 hint = _(b'use --mark, --unmark, --list or --re-merge')
5819 5819 raise error.InputError(_(b'no action specified'), hint=hint)
5820 5820 if pats and all:
5821 5821 raise error.InputError(_(b"can't specify --all and patterns"))
5822 5822 if not (all or pats or show or mark or unmark):
5823 5823 raise error.InputError(
5824 5824 _(b'no files or directories specified'),
5825 5825 hint=b'use --all to re-merge all unresolved files',
5826 5826 )
5827 5827
5828 5828 if confirm:
5829 5829 if all:
5830 5830 if ui.promptchoice(
5831 5831 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5832 5832 ):
5833 5833 raise error.Abort(_(b'user quit'))
5834 5834 if mark and not pats:
5835 5835 if ui.promptchoice(
5836 5836 _(
5837 5837 b'mark all unresolved files as resolved (yn)?'
5838 5838 b'$$ &Yes $$ &No'
5839 5839 )
5840 5840 ):
5841 5841 raise error.Abort(_(b'user quit'))
5842 5842 if unmark and not pats:
5843 5843 if ui.promptchoice(
5844 5844 _(
5845 5845 b'mark all resolved files as unresolved (yn)?'
5846 5846 b'$$ &Yes $$ &No'
5847 5847 )
5848 5848 ):
5849 5849 raise error.Abort(_(b'user quit'))
5850 5850
5851 5851 uipathfn = scmutil.getuipathfn(repo)
5852 5852
5853 5853 if show:
5854 5854 ui.pager(b'resolve')
5855 5855 fm = ui.formatter(b'resolve', opts)
5856 5856 ms = mergestatemod.mergestate.read(repo)
5857 5857 wctx = repo[None]
5858 5858 m = scmutil.match(wctx, pats, opts)
5859 5859
5860 5860 # Labels and keys based on merge state. Unresolved path conflicts show
5861 5861 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5862 5862 # resolved conflicts.
5863 5863 mergestateinfo = {
5864 5864 mergestatemod.MERGE_RECORD_UNRESOLVED: (
5865 5865 b'resolve.unresolved',
5866 5866 b'U',
5867 5867 ),
5868 5868 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5869 5869 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
5870 5870 b'resolve.unresolved',
5871 5871 b'P',
5872 5872 ),
5873 5873 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
5874 5874 b'resolve.resolved',
5875 5875 b'R',
5876 5876 ),
5877 5877 }
5878 5878
5879 5879 for f in ms:
5880 5880 if not m(f):
5881 5881 continue
5882 5882
5883 5883 label, key = mergestateinfo[ms[f]]
5884 5884 fm.startitem()
5885 5885 fm.context(ctx=wctx)
5886 5886 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5887 5887 fm.data(path=f)
5888 5888 fm.plain(b'%s\n' % uipathfn(f), label=label)
5889 5889 fm.end()
5890 5890 return 0
5891 5891
5892 5892 with repo.wlock():
5893 5893 ms = mergestatemod.mergestate.read(repo)
5894 5894
5895 5895 if not (ms.active() or repo.dirstate.p2() != nullid):
5896 raise error.Abort(
5896 raise error.StateError(
5897 5897 _(b'resolve command not applicable when not merging')
5898 5898 )
5899 5899
5900 5900 wctx = repo[None]
5901 5901 m = scmutil.match(wctx, pats, opts)
5902 5902 ret = 0
5903 5903 didwork = False
5904 5904
5905 5905 tocomplete = []
5906 5906 hasconflictmarkers = []
5907 5907 if mark:
5908 5908 markcheck = ui.config(b'commands', b'resolve.mark-check')
5909 5909 if markcheck not in [b'warn', b'abort']:
5910 5910 # Treat all invalid / unrecognized values as 'none'.
5911 5911 markcheck = False
5912 5912 for f in ms:
5913 5913 if not m(f):
5914 5914 continue
5915 5915
5916 5916 didwork = True
5917 5917
5918 5918 # path conflicts must be resolved manually
5919 5919 if ms[f] in (
5920 5920 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
5921 5921 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
5922 5922 ):
5923 5923 if mark:
5924 5924 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
5925 5925 elif unmark:
5926 5926 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
5927 5927 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
5928 5928 ui.warn(
5929 5929 _(b'%s: path conflict must be resolved manually\n')
5930 5930 % uipathfn(f)
5931 5931 )
5932 5932 continue
5933 5933
5934 5934 if mark:
5935 5935 if markcheck:
5936 5936 fdata = repo.wvfs.tryread(f)
5937 5937 if (
5938 5938 filemerge.hasconflictmarkers(fdata)
5939 5939 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
5940 5940 ):
5941 5941 hasconflictmarkers.append(f)
5942 5942 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
5943 5943 elif unmark:
5944 5944 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
5945 5945 else:
5946 5946 # backup pre-resolve (merge uses .orig for its own purposes)
5947 5947 a = repo.wjoin(f)
5948 5948 try:
5949 5949 util.copyfile(a, a + b".resolve")
5950 5950 except (IOError, OSError) as inst:
5951 5951 if inst.errno != errno.ENOENT:
5952 5952 raise
5953 5953
5954 5954 try:
5955 5955 # preresolve file
5956 5956 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
5957 5957 with ui.configoverride(overrides, b'resolve'):
5958 5958 complete, r = ms.preresolve(f, wctx)
5959 5959 if not complete:
5960 5960 tocomplete.append(f)
5961 5961 elif r:
5962 5962 ret = 1
5963 5963 finally:
5964 5964 ms.commit()
5965 5965
5966 5966 # replace filemerge's .orig file with our resolve file, but only
5967 5967 # for merges that are complete
5968 5968 if complete:
5969 5969 try:
5970 5970 util.rename(
5971 5971 a + b".resolve", scmutil.backuppath(ui, repo, f)
5972 5972 )
5973 5973 except OSError as inst:
5974 5974 if inst.errno != errno.ENOENT:
5975 5975 raise
5976 5976
5977 5977 if hasconflictmarkers:
5978 5978 ui.warn(
5979 5979 _(
5980 5980 b'warning: the following files still have conflict '
5981 5981 b'markers:\n'
5982 5982 )
5983 5983 + b''.join(
5984 5984 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
5985 5985 )
5986 5986 )
5987 5987 if markcheck == b'abort' and not all and not pats:
5988 raise error.Abort(
5988 raise error.StateError(
5989 5989 _(b'conflict markers detected'),
5990 5990 hint=_(b'use --all to mark anyway'),
5991 5991 )
5992 5992
5993 5993 for f in tocomplete:
5994 5994 try:
5995 5995 # resolve file
5996 5996 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
5997 5997 with ui.configoverride(overrides, b'resolve'):
5998 5998 r = ms.resolve(f, wctx)
5999 5999 if r:
6000 6000 ret = 1
6001 6001 finally:
6002 6002 ms.commit()
6003 6003
6004 6004 # replace filemerge's .orig file with our resolve file
6005 6005 a = repo.wjoin(f)
6006 6006 try:
6007 6007 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6008 6008 except OSError as inst:
6009 6009 if inst.errno != errno.ENOENT:
6010 6010 raise
6011 6011
6012 6012 ms.commit()
6013 6013 branchmerge = repo.dirstate.p2() != nullid
6014 6014 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6015 6015
6016 6016 if not didwork and pats:
6017 6017 hint = None
6018 6018 if not any([p for p in pats if p.find(b':') >= 0]):
6019 6019 pats = [b'path:%s' % p for p in pats]
6020 6020 m = scmutil.match(wctx, pats, opts)
6021 6021 for f in ms:
6022 6022 if not m(f):
6023 6023 continue
6024 6024
6025 6025 def flag(o):
6026 6026 if o == b're_merge':
6027 6027 return b'--re-merge '
6028 6028 return b'-%s ' % o[0:1]
6029 6029
6030 6030 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6031 6031 hint = _(b"(try: hg resolve %s%s)\n") % (
6032 6032 flags,
6033 6033 b' '.join(pats),
6034 6034 )
6035 6035 break
6036 6036 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6037 6037 if hint:
6038 6038 ui.warn(hint)
6039 6039
6040 6040 unresolvedf = list(ms.unresolved())
6041 6041 if not unresolvedf:
6042 6042 ui.status(_(b'(no more unresolved files)\n'))
6043 6043 cmdutil.checkafterresolved(repo)
6044 6044
6045 6045 return ret
6046 6046
6047 6047
6048 6048 @command(
6049 6049 b'revert',
6050 6050 [
6051 6051 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6052 6052 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6053 6053 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6054 6054 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6055 6055 (b'i', b'interactive', None, _(b'interactively select the changes')),
6056 6056 ]
6057 6057 + walkopts
6058 6058 + dryrunopts,
6059 6059 _(b'[OPTION]... [-r REV] [NAME]...'),
6060 6060 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6061 6061 )
6062 6062 def revert(ui, repo, *pats, **opts):
6063 6063 """restore files to their checkout state
6064 6064
6065 6065 .. note::
6066 6066
6067 6067 To check out earlier revisions, you should use :hg:`update REV`.
6068 6068 To cancel an uncommitted merge (and lose your changes),
6069 6069 use :hg:`merge --abort`.
6070 6070
6071 6071 With no revision specified, revert the specified files or directories
6072 6072 to the contents they had in the parent of the working directory.
6073 6073 This restores the contents of files to an unmodified
6074 6074 state and unschedules adds, removes, copies, and renames. If the
6075 6075 working directory has two parents, you must explicitly specify a
6076 6076 revision.
6077 6077
6078 6078 Using the -r/--rev or -d/--date options, revert the given files or
6079 6079 directories to their states as of a specific revision. Because
6080 6080 revert does not change the working directory parents, this will
6081 6081 cause these files to appear modified. This can be helpful to "back
6082 6082 out" some or all of an earlier change. See :hg:`backout` for a
6083 6083 related method.
6084 6084
6085 6085 Modified files are saved with a .orig suffix before reverting.
6086 6086 To disable these backups, use --no-backup. It is possible to store
6087 6087 the backup files in a custom directory relative to the root of the
6088 6088 repository by setting the ``ui.origbackuppath`` configuration
6089 6089 option.
6090 6090
6091 6091 See :hg:`help dates` for a list of formats valid for -d/--date.
6092 6092
6093 6093 See :hg:`help backout` for a way to reverse the effect of an
6094 6094 earlier changeset.
6095 6095
6096 6096 Returns 0 on success.
6097 6097 """
6098 6098
6099 6099 opts = pycompat.byteskwargs(opts)
6100 6100 if opts.get(b"date"):
6101 6101 cmdutil.check_incompatible_arguments(opts, b'date', [b'rev'])
6102 6102 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6103 6103
6104 6104 parent, p2 = repo.dirstate.parents()
6105 6105 if not opts.get(b'rev') and p2 != nullid:
6106 6106 # revert after merge is a trap for new users (issue2915)
6107 6107 raise error.InputError(
6108 6108 _(b'uncommitted merge with no revision specified'),
6109 6109 hint=_(b"use 'hg update' or see 'hg help revert'"),
6110 6110 )
6111 6111
6112 6112 rev = opts.get(b'rev')
6113 6113 if rev:
6114 6114 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6115 6115 ctx = scmutil.revsingle(repo, rev)
6116 6116
6117 6117 if not (
6118 6118 pats
6119 6119 or opts.get(b'include')
6120 6120 or opts.get(b'exclude')
6121 6121 or opts.get(b'all')
6122 6122 or opts.get(b'interactive')
6123 6123 ):
6124 6124 msg = _(b"no files or directories specified")
6125 6125 if p2 != nullid:
6126 6126 hint = _(
6127 6127 b"uncommitted merge, use --all to discard all changes,"
6128 6128 b" or 'hg update -C .' to abort the merge"
6129 6129 )
6130 6130 raise error.InputError(msg, hint=hint)
6131 6131 dirty = any(repo.status())
6132 6132 node = ctx.node()
6133 6133 if node != parent:
6134 6134 if dirty:
6135 6135 hint = (
6136 6136 _(
6137 6137 b"uncommitted changes, use --all to discard all"
6138 6138 b" changes, or 'hg update %d' to update"
6139 6139 )
6140 6140 % ctx.rev()
6141 6141 )
6142 6142 else:
6143 6143 hint = (
6144 6144 _(
6145 6145 b"use --all to revert all files,"
6146 6146 b" or 'hg update %d' to update"
6147 6147 )
6148 6148 % ctx.rev()
6149 6149 )
6150 6150 elif dirty:
6151 6151 hint = _(b"uncommitted changes, use --all to discard all changes")
6152 6152 else:
6153 6153 hint = _(b"use --all to revert all files")
6154 6154 raise error.InputError(msg, hint=hint)
6155 6155
6156 6156 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6157 6157
6158 6158
6159 6159 @command(
6160 6160 b'rollback',
6161 6161 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6162 6162 helpcategory=command.CATEGORY_MAINTENANCE,
6163 6163 )
6164 6164 def rollback(ui, repo, **opts):
6165 6165 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6166 6166
6167 6167 Please use :hg:`commit --amend` instead of rollback to correct
6168 6168 mistakes in the last commit.
6169 6169
6170 6170 This command should be used with care. There is only one level of
6171 6171 rollback, and there is no way to undo a rollback. It will also
6172 6172 restore the dirstate at the time of the last transaction, losing
6173 6173 any dirstate changes since that time. This command does not alter
6174 6174 the working directory.
6175 6175
6176 6176 Transactions are used to encapsulate the effects of all commands
6177 6177 that create new changesets or propagate existing changesets into a
6178 6178 repository.
6179 6179
6180 6180 .. container:: verbose
6181 6181
6182 6182 For example, the following commands are transactional, and their
6183 6183 effects can be rolled back:
6184 6184
6185 6185 - commit
6186 6186 - import
6187 6187 - pull
6188 6188 - push (with this repository as the destination)
6189 6189 - unbundle
6190 6190
6191 6191 To avoid permanent data loss, rollback will refuse to rollback a
6192 6192 commit transaction if it isn't checked out. Use --force to
6193 6193 override this protection.
6194 6194
6195 6195 The rollback command can be entirely disabled by setting the
6196 6196 ``ui.rollback`` configuration setting to false. If you're here
6197 6197 because you want to use rollback and it's disabled, you can
6198 6198 re-enable the command by setting ``ui.rollback`` to true.
6199 6199
6200 6200 This command is not intended for use on public repositories. Once
6201 6201 changes are visible for pull by other users, rolling a transaction
6202 6202 back locally is ineffective (someone else may already have pulled
6203 6203 the changes). Furthermore, a race is possible with readers of the
6204 6204 repository; for example an in-progress pull from the repository
6205 6205 may fail if a rollback is performed.
6206 6206
6207 6207 Returns 0 on success, 1 if no rollback data is available.
6208 6208 """
6209 6209 if not ui.configbool(b'ui', b'rollback'):
6210 6210 raise error.Abort(
6211 6211 _(b'rollback is disabled because it is unsafe'),
6212 6212 hint=b'see `hg help -v rollback` for information',
6213 6213 )
6214 6214 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6215 6215
6216 6216
6217 6217 @command(
6218 6218 b'root',
6219 6219 [] + formatteropts,
6220 6220 intents={INTENT_READONLY},
6221 6221 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6222 6222 )
6223 6223 def root(ui, repo, **opts):
6224 6224 """print the root (top) of the current working directory
6225 6225
6226 6226 Print the root directory of the current repository.
6227 6227
6228 6228 .. container:: verbose
6229 6229
6230 6230 Template:
6231 6231
6232 6232 The following keywords are supported in addition to the common template
6233 6233 keywords and functions. See also :hg:`help templates`.
6234 6234
6235 6235 :hgpath: String. Path to the .hg directory.
6236 6236 :storepath: String. Path to the directory holding versioned data.
6237 6237
6238 6238 Returns 0 on success.
6239 6239 """
6240 6240 opts = pycompat.byteskwargs(opts)
6241 6241 with ui.formatter(b'root', opts) as fm:
6242 6242 fm.startitem()
6243 6243 fm.write(b'reporoot', b'%s\n', repo.root)
6244 6244 fm.data(hgpath=repo.path, storepath=repo.spath)
6245 6245
6246 6246
6247 6247 @command(
6248 6248 b'serve',
6249 6249 [
6250 6250 (
6251 6251 b'A',
6252 6252 b'accesslog',
6253 6253 b'',
6254 6254 _(b'name of access log file to write to'),
6255 6255 _(b'FILE'),
6256 6256 ),
6257 6257 (b'd', b'daemon', None, _(b'run server in background')),
6258 6258 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6259 6259 (
6260 6260 b'E',
6261 6261 b'errorlog',
6262 6262 b'',
6263 6263 _(b'name of error log file to write to'),
6264 6264 _(b'FILE'),
6265 6265 ),
6266 6266 # use string type, then we can check if something was passed
6267 6267 (
6268 6268 b'p',
6269 6269 b'port',
6270 6270 b'',
6271 6271 _(b'port to listen on (default: 8000)'),
6272 6272 _(b'PORT'),
6273 6273 ),
6274 6274 (
6275 6275 b'a',
6276 6276 b'address',
6277 6277 b'',
6278 6278 _(b'address to listen on (default: all interfaces)'),
6279 6279 _(b'ADDR'),
6280 6280 ),
6281 6281 (
6282 6282 b'',
6283 6283 b'prefix',
6284 6284 b'',
6285 6285 _(b'prefix path to serve from (default: server root)'),
6286 6286 _(b'PREFIX'),
6287 6287 ),
6288 6288 (
6289 6289 b'n',
6290 6290 b'name',
6291 6291 b'',
6292 6292 _(b'name to show in web pages (default: working directory)'),
6293 6293 _(b'NAME'),
6294 6294 ),
6295 6295 (
6296 6296 b'',
6297 6297 b'web-conf',
6298 6298 b'',
6299 6299 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6300 6300 _(b'FILE'),
6301 6301 ),
6302 6302 (
6303 6303 b'',
6304 6304 b'webdir-conf',
6305 6305 b'',
6306 6306 _(b'name of the hgweb config file (DEPRECATED)'),
6307 6307 _(b'FILE'),
6308 6308 ),
6309 6309 (
6310 6310 b'',
6311 6311 b'pid-file',
6312 6312 b'',
6313 6313 _(b'name of file to write process ID to'),
6314 6314 _(b'FILE'),
6315 6315 ),
6316 6316 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6317 6317 (
6318 6318 b'',
6319 6319 b'cmdserver',
6320 6320 b'',
6321 6321 _(b'for remote clients (ADVANCED)'),
6322 6322 _(b'MODE'),
6323 6323 ),
6324 6324 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6325 6325 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6326 6326 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6327 6327 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6328 6328 (b'', b'print-url', None, _(b'start and print only the URL')),
6329 6329 ]
6330 6330 + subrepoopts,
6331 6331 _(b'[OPTION]...'),
6332 6332 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6333 6333 helpbasic=True,
6334 6334 optionalrepo=True,
6335 6335 )
6336 6336 def serve(ui, repo, **opts):
6337 6337 """start stand-alone webserver
6338 6338
6339 6339 Start a local HTTP repository browser and pull server. You can use
6340 6340 this for ad-hoc sharing and browsing of repositories. It is
6341 6341 recommended to use a real web server to serve a repository for
6342 6342 longer periods of time.
6343 6343
6344 6344 Please note that the server does not implement access control.
6345 6345 This means that, by default, anybody can read from the server and
6346 6346 nobody can write to it by default. Set the ``web.allow-push``
6347 6347 option to ``*`` to allow everybody to push to the server. You
6348 6348 should use a real web server if you need to authenticate users.
6349 6349
6350 6350 By default, the server logs accesses to stdout and errors to
6351 6351 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6352 6352 files.
6353 6353
6354 6354 To have the server choose a free port number to listen on, specify
6355 6355 a port number of 0; in this case, the server will print the port
6356 6356 number it uses.
6357 6357
6358 6358 Returns 0 on success.
6359 6359 """
6360 6360
6361 6361 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6362 6362 opts = pycompat.byteskwargs(opts)
6363 6363 if opts[b"print_url"] and ui.verbose:
6364 6364 raise error.InputError(_(b"cannot use --print-url with --verbose"))
6365 6365
6366 6366 if opts[b"stdio"]:
6367 6367 if repo is None:
6368 6368 raise error.RepoError(
6369 6369 _(b"there is no Mercurial repository here (.hg not found)")
6370 6370 )
6371 6371 s = wireprotoserver.sshserver(ui, repo)
6372 6372 s.serve_forever()
6373 6373 return
6374 6374
6375 6375 service = server.createservice(ui, repo, opts)
6376 6376 return server.runservice(opts, initfn=service.init, runfn=service.run)
6377 6377
6378 6378
6379 6379 @command(
6380 6380 b'shelve',
6381 6381 [
6382 6382 (
6383 6383 b'A',
6384 6384 b'addremove',
6385 6385 None,
6386 6386 _(b'mark new/missing files as added/removed before shelving'),
6387 6387 ),
6388 6388 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6389 6389 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6390 6390 (
6391 6391 b'',
6392 6392 b'date',
6393 6393 b'',
6394 6394 _(b'shelve with the specified commit date'),
6395 6395 _(b'DATE'),
6396 6396 ),
6397 6397 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6398 6398 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6399 6399 (
6400 6400 b'k',
6401 6401 b'keep',
6402 6402 False,
6403 6403 _(b'shelve, but keep changes in the working directory'),
6404 6404 ),
6405 6405 (b'l', b'list', None, _(b'list current shelves')),
6406 6406 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6407 6407 (
6408 6408 b'n',
6409 6409 b'name',
6410 6410 b'',
6411 6411 _(b'use the given name for the shelved commit'),
6412 6412 _(b'NAME'),
6413 6413 ),
6414 6414 (
6415 6415 b'p',
6416 6416 b'patch',
6417 6417 None,
6418 6418 _(
6419 6419 b'output patches for changes (provide the names of the shelved '
6420 6420 b'changes as positional arguments)'
6421 6421 ),
6422 6422 ),
6423 6423 (b'i', b'interactive', None, _(b'interactive mode')),
6424 6424 (
6425 6425 b'',
6426 6426 b'stat',
6427 6427 None,
6428 6428 _(
6429 6429 b'output diffstat-style summary of changes (provide the names of '
6430 6430 b'the shelved changes as positional arguments)'
6431 6431 ),
6432 6432 ),
6433 6433 ]
6434 6434 + cmdutil.walkopts,
6435 6435 _(b'hg shelve [OPTION]... [FILE]...'),
6436 6436 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6437 6437 )
6438 6438 def shelve(ui, repo, *pats, **opts):
6439 6439 '''save and set aside changes from the working directory
6440 6440
6441 6441 Shelving takes files that "hg status" reports as not clean, saves
6442 6442 the modifications to a bundle (a shelved change), and reverts the
6443 6443 files so that their state in the working directory becomes clean.
6444 6444
6445 6445 To restore these changes to the working directory, using "hg
6446 6446 unshelve"; this will work even if you switch to a different
6447 6447 commit.
6448 6448
6449 6449 When no files are specified, "hg shelve" saves all not-clean
6450 6450 files. If specific files or directories are named, only changes to
6451 6451 those files are shelved.
6452 6452
6453 6453 In bare shelve (when no files are specified, without interactive,
6454 6454 include and exclude option), shelving remembers information if the
6455 6455 working directory was on newly created branch, in other words working
6456 6456 directory was on different branch than its first parent. In this
6457 6457 situation unshelving restores branch information to the working directory.
6458 6458
6459 6459 Each shelved change has a name that makes it easier to find later.
6460 6460 The name of a shelved change defaults to being based on the active
6461 6461 bookmark, or if there is no active bookmark, the current named
6462 6462 branch. To specify a different name, use ``--name``.
6463 6463
6464 6464 To see a list of existing shelved changes, use the ``--list``
6465 6465 option. For each shelved change, this will print its name, age,
6466 6466 and description; use ``--patch`` or ``--stat`` for more details.
6467 6467
6468 6468 To delete specific shelved changes, use ``--delete``. To delete
6469 6469 all shelved changes, use ``--cleanup``.
6470 6470 '''
6471 6471 opts = pycompat.byteskwargs(opts)
6472 6472 allowables = [
6473 6473 (b'addremove', {b'create'}), # 'create' is pseudo action
6474 6474 (b'unknown', {b'create'}),
6475 6475 (b'cleanup', {b'cleanup'}),
6476 6476 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6477 6477 (b'delete', {b'delete'}),
6478 6478 (b'edit', {b'create'}),
6479 6479 (b'keep', {b'create'}),
6480 6480 (b'list', {b'list'}),
6481 6481 (b'message', {b'create'}),
6482 6482 (b'name', {b'create'}),
6483 6483 (b'patch', {b'patch', b'list'}),
6484 6484 (b'stat', {b'stat', b'list'}),
6485 6485 ]
6486 6486
6487 6487 def checkopt(opt):
6488 6488 if opts.get(opt):
6489 6489 for i, allowable in allowables:
6490 6490 if opts[i] and opt not in allowable:
6491 6491 raise error.InputError(
6492 6492 _(
6493 6493 b"options '--%s' and '--%s' may not be "
6494 6494 b"used together"
6495 6495 )
6496 6496 % (opt, i)
6497 6497 )
6498 6498 return True
6499 6499
6500 6500 if checkopt(b'cleanup'):
6501 6501 if pats:
6502 6502 raise error.InputError(
6503 6503 _(b"cannot specify names when using '--cleanup'")
6504 6504 )
6505 6505 return shelvemod.cleanupcmd(ui, repo)
6506 6506 elif checkopt(b'delete'):
6507 6507 return shelvemod.deletecmd(ui, repo, pats)
6508 6508 elif checkopt(b'list'):
6509 6509 return shelvemod.listcmd(ui, repo, pats, opts)
6510 6510 elif checkopt(b'patch') or checkopt(b'stat'):
6511 6511 return shelvemod.patchcmds(ui, repo, pats, opts)
6512 6512 else:
6513 6513 return shelvemod.createcmd(ui, repo, pats, opts)
6514 6514
6515 6515
6516 6516 _NOTTERSE = b'nothing'
6517 6517
6518 6518
6519 6519 @command(
6520 6520 b'status|st',
6521 6521 [
6522 6522 (b'A', b'all', None, _(b'show status of all files')),
6523 6523 (b'm', b'modified', None, _(b'show only modified files')),
6524 6524 (b'a', b'added', None, _(b'show only added files')),
6525 6525 (b'r', b'removed', None, _(b'show only removed files')),
6526 6526 (b'd', b'deleted', None, _(b'show only missing files')),
6527 6527 (b'c', b'clean', None, _(b'show only files without changes')),
6528 6528 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6529 6529 (b'i', b'ignored', None, _(b'show only ignored files')),
6530 6530 (b'n', b'no-status', None, _(b'hide status prefix')),
6531 6531 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6532 6532 (
6533 6533 b'C',
6534 6534 b'copies',
6535 6535 None,
6536 6536 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6537 6537 ),
6538 6538 (
6539 6539 b'0',
6540 6540 b'print0',
6541 6541 None,
6542 6542 _(b'end filenames with NUL, for use with xargs'),
6543 6543 ),
6544 6544 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6545 6545 (
6546 6546 b'',
6547 6547 b'change',
6548 6548 b'',
6549 6549 _(b'list the changed files of a revision'),
6550 6550 _(b'REV'),
6551 6551 ),
6552 6552 ]
6553 6553 + walkopts
6554 6554 + subrepoopts
6555 6555 + formatteropts,
6556 6556 _(b'[OPTION]... [FILE]...'),
6557 6557 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6558 6558 helpbasic=True,
6559 6559 inferrepo=True,
6560 6560 intents={INTENT_READONLY},
6561 6561 )
6562 6562 def status(ui, repo, *pats, **opts):
6563 6563 """show changed files in the working directory
6564 6564
6565 6565 Show status of files in the repository. If names are given, only
6566 6566 files that match are shown. Files that are clean or ignored or
6567 6567 the source of a copy/move operation, are not listed unless
6568 6568 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6569 6569 Unless options described with "show only ..." are given, the
6570 6570 options -mardu are used.
6571 6571
6572 6572 Option -q/--quiet hides untracked (unknown and ignored) files
6573 6573 unless explicitly requested with -u/--unknown or -i/--ignored.
6574 6574
6575 6575 .. note::
6576 6576
6577 6577 :hg:`status` may appear to disagree with diff if permissions have
6578 6578 changed or a merge has occurred. The standard diff format does
6579 6579 not report permission changes and diff only reports changes
6580 6580 relative to one merge parent.
6581 6581
6582 6582 If one revision is given, it is used as the base revision.
6583 6583 If two revisions are given, the differences between them are
6584 6584 shown. The --change option can also be used as a shortcut to list
6585 6585 the changed files of a revision from its first parent.
6586 6586
6587 6587 The codes used to show the status of files are::
6588 6588
6589 6589 M = modified
6590 6590 A = added
6591 6591 R = removed
6592 6592 C = clean
6593 6593 ! = missing (deleted by non-hg command, but still tracked)
6594 6594 ? = not tracked
6595 6595 I = ignored
6596 6596 = origin of the previous file (with --copies)
6597 6597
6598 6598 .. container:: verbose
6599 6599
6600 6600 The -t/--terse option abbreviates the output by showing only the directory
6601 6601 name if all the files in it share the same status. The option takes an
6602 6602 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6603 6603 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6604 6604 for 'ignored' and 'c' for clean.
6605 6605
6606 6606 It abbreviates only those statuses which are passed. Note that clean and
6607 6607 ignored files are not displayed with '--terse ic' unless the -c/--clean
6608 6608 and -i/--ignored options are also used.
6609 6609
6610 6610 The -v/--verbose option shows information when the repository is in an
6611 6611 unfinished merge, shelve, rebase state etc. You can have this behavior
6612 6612 turned on by default by enabling the ``commands.status.verbose`` option.
6613 6613
6614 6614 You can skip displaying some of these states by setting
6615 6615 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6616 6616 'histedit', 'merge', 'rebase', or 'unshelve'.
6617 6617
6618 6618 Template:
6619 6619
6620 6620 The following keywords are supported in addition to the common template
6621 6621 keywords and functions. See also :hg:`help templates`.
6622 6622
6623 6623 :path: String. Repository-absolute path of the file.
6624 6624 :source: String. Repository-absolute path of the file originated from.
6625 6625 Available if ``--copies`` is specified.
6626 6626 :status: String. Character denoting file's status.
6627 6627
6628 6628 Examples:
6629 6629
6630 6630 - show changes in the working directory relative to a
6631 6631 changeset::
6632 6632
6633 6633 hg status --rev 9353
6634 6634
6635 6635 - show changes in the working directory relative to the
6636 6636 current directory (see :hg:`help patterns` for more information)::
6637 6637
6638 6638 hg status re:
6639 6639
6640 6640 - show all changes including copies in an existing changeset::
6641 6641
6642 6642 hg status --copies --change 9353
6643 6643
6644 6644 - get a NUL separated list of added files, suitable for xargs::
6645 6645
6646 6646 hg status -an0
6647 6647
6648 6648 - show more information about the repository status, abbreviating
6649 6649 added, removed, modified, deleted, and untracked paths::
6650 6650
6651 6651 hg status -v -t mardu
6652 6652
6653 6653 Returns 0 on success.
6654 6654
6655 6655 """
6656 6656
6657 6657 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6658 6658 opts = pycompat.byteskwargs(opts)
6659 6659 revs = opts.get(b'rev')
6660 6660 change = opts.get(b'change')
6661 6661 terse = opts.get(b'terse')
6662 6662 if terse is _NOTTERSE:
6663 6663 if revs:
6664 6664 terse = b''
6665 6665 else:
6666 6666 terse = ui.config(b'commands', b'status.terse')
6667 6667
6668 6668 if revs and terse:
6669 6669 msg = _(b'cannot use --terse with --rev')
6670 6670 raise error.InputError(msg)
6671 6671 elif change:
6672 6672 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6673 6673 ctx2 = scmutil.revsingle(repo, change, None)
6674 6674 ctx1 = ctx2.p1()
6675 6675 else:
6676 6676 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6677 6677 ctx1, ctx2 = scmutil.revpair(repo, revs)
6678 6678
6679 6679 forcerelativevalue = None
6680 6680 if ui.hasconfig(b'commands', b'status.relative'):
6681 6681 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6682 6682 uipathfn = scmutil.getuipathfn(
6683 6683 repo,
6684 6684 legacyrelativevalue=bool(pats),
6685 6685 forcerelativevalue=forcerelativevalue,
6686 6686 )
6687 6687
6688 6688 if opts.get(b'print0'):
6689 6689 end = b'\0'
6690 6690 else:
6691 6691 end = b'\n'
6692 6692 states = b'modified added removed deleted unknown ignored clean'.split()
6693 6693 show = [k for k in states if opts.get(k)]
6694 6694 if opts.get(b'all'):
6695 6695 show += ui.quiet and (states[:4] + [b'clean']) or states
6696 6696
6697 6697 if not show:
6698 6698 if ui.quiet:
6699 6699 show = states[:4]
6700 6700 else:
6701 6701 show = states[:5]
6702 6702
6703 6703 m = scmutil.match(ctx2, pats, opts)
6704 6704 if terse:
6705 6705 # we need to compute clean and unknown to terse
6706 6706 stat = repo.status(
6707 6707 ctx1.node(),
6708 6708 ctx2.node(),
6709 6709 m,
6710 6710 b'ignored' in show or b'i' in terse,
6711 6711 clean=True,
6712 6712 unknown=True,
6713 6713 listsubrepos=opts.get(b'subrepos'),
6714 6714 )
6715 6715
6716 6716 stat = cmdutil.tersedir(stat, terse)
6717 6717 else:
6718 6718 stat = repo.status(
6719 6719 ctx1.node(),
6720 6720 ctx2.node(),
6721 6721 m,
6722 6722 b'ignored' in show,
6723 6723 b'clean' in show,
6724 6724 b'unknown' in show,
6725 6725 opts.get(b'subrepos'),
6726 6726 )
6727 6727
6728 6728 changestates = zip(
6729 6729 states,
6730 6730 pycompat.iterbytestr(b'MAR!?IC'),
6731 6731 [getattr(stat, s.decode('utf8')) for s in states],
6732 6732 )
6733 6733
6734 6734 copy = {}
6735 6735 if (
6736 6736 opts.get(b'all')
6737 6737 or opts.get(b'copies')
6738 6738 or ui.configbool(b'ui', b'statuscopies')
6739 6739 ) and not opts.get(b'no_status'):
6740 6740 copy = copies.pathcopies(ctx1, ctx2, m)
6741 6741
6742 6742 morestatus = None
6743 6743 if (
6744 6744 ui.verbose or ui.configbool(b'commands', b'status.verbose')
6745 6745 ) and not ui.plain():
6746 6746 morestatus = cmdutil.readmorestatus(repo)
6747 6747
6748 6748 ui.pager(b'status')
6749 6749 fm = ui.formatter(b'status', opts)
6750 6750 fmt = b'%s' + end
6751 6751 showchar = not opts.get(b'no_status')
6752 6752
6753 6753 for state, char, files in changestates:
6754 6754 if state in show:
6755 6755 label = b'status.' + state
6756 6756 for f in files:
6757 6757 fm.startitem()
6758 6758 fm.context(ctx=ctx2)
6759 6759 fm.data(itemtype=b'file', path=f)
6760 6760 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6761 6761 fm.plain(fmt % uipathfn(f), label=label)
6762 6762 if f in copy:
6763 6763 fm.data(source=copy[f])
6764 6764 fm.plain(
6765 6765 (b' %s' + end) % uipathfn(copy[f]),
6766 6766 label=b'status.copied',
6767 6767 )
6768 6768 if morestatus:
6769 6769 morestatus.formatfile(f, fm)
6770 6770
6771 6771 if morestatus:
6772 6772 morestatus.formatfooter(fm)
6773 6773 fm.end()
6774 6774
6775 6775
6776 6776 @command(
6777 6777 b'summary|sum',
6778 6778 [(b'', b'remote', None, _(b'check for push and pull'))],
6779 6779 b'[--remote]',
6780 6780 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6781 6781 helpbasic=True,
6782 6782 intents={INTENT_READONLY},
6783 6783 )
6784 6784 def summary(ui, repo, **opts):
6785 6785 """summarize working directory state
6786 6786
6787 6787 This generates a brief summary of the working directory state,
6788 6788 including parents, branch, commit status, phase and available updates.
6789 6789
6790 6790 With the --remote option, this will check the default paths for
6791 6791 incoming and outgoing changes. This can be time-consuming.
6792 6792
6793 6793 Returns 0 on success.
6794 6794 """
6795 6795
6796 6796 opts = pycompat.byteskwargs(opts)
6797 6797 ui.pager(b'summary')
6798 6798 ctx = repo[None]
6799 6799 parents = ctx.parents()
6800 6800 pnode = parents[0].node()
6801 6801 marks = []
6802 6802
6803 6803 try:
6804 6804 ms = mergestatemod.mergestate.read(repo)
6805 6805 except error.UnsupportedMergeRecords as e:
6806 6806 s = b' '.join(e.recordtypes)
6807 6807 ui.warn(
6808 6808 _(b'warning: merge state has unsupported record types: %s\n') % s
6809 6809 )
6810 6810 unresolved = []
6811 6811 else:
6812 6812 unresolved = list(ms.unresolved())
6813 6813
6814 6814 for p in parents:
6815 6815 # label with log.changeset (instead of log.parent) since this
6816 6816 # shows a working directory parent *changeset*:
6817 6817 # i18n: column positioning for "hg summary"
6818 6818 ui.write(
6819 6819 _(b'parent: %d:%s ') % (p.rev(), p),
6820 6820 label=logcmdutil.changesetlabels(p),
6821 6821 )
6822 6822 ui.write(b' '.join(p.tags()), label=b'log.tag')
6823 6823 if p.bookmarks():
6824 6824 marks.extend(p.bookmarks())
6825 6825 if p.rev() == -1:
6826 6826 if not len(repo):
6827 6827 ui.write(_(b' (empty repository)'))
6828 6828 else:
6829 6829 ui.write(_(b' (no revision checked out)'))
6830 6830 if p.obsolete():
6831 6831 ui.write(_(b' (obsolete)'))
6832 6832 if p.isunstable():
6833 6833 instabilities = (
6834 6834 ui.label(instability, b'trouble.%s' % instability)
6835 6835 for instability in p.instabilities()
6836 6836 )
6837 6837 ui.write(b' (' + b', '.join(instabilities) + b')')
6838 6838 ui.write(b'\n')
6839 6839 if p.description():
6840 6840 ui.status(
6841 6841 b' ' + p.description().splitlines()[0].strip() + b'\n',
6842 6842 label=b'log.summary',
6843 6843 )
6844 6844
6845 6845 branch = ctx.branch()
6846 6846 bheads = repo.branchheads(branch)
6847 6847 # i18n: column positioning for "hg summary"
6848 6848 m = _(b'branch: %s\n') % branch
6849 6849 if branch != b'default':
6850 6850 ui.write(m, label=b'log.branch')
6851 6851 else:
6852 6852 ui.status(m, label=b'log.branch')
6853 6853
6854 6854 if marks:
6855 6855 active = repo._activebookmark
6856 6856 # i18n: column positioning for "hg summary"
6857 6857 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6858 6858 if active is not None:
6859 6859 if active in marks:
6860 6860 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6861 6861 marks.remove(active)
6862 6862 else:
6863 6863 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6864 6864 for m in marks:
6865 6865 ui.write(b' ' + m, label=b'log.bookmark')
6866 6866 ui.write(b'\n', label=b'log.bookmark')
6867 6867
6868 6868 status = repo.status(unknown=True)
6869 6869
6870 6870 c = repo.dirstate.copies()
6871 6871 copied, renamed = [], []
6872 6872 for d, s in pycompat.iteritems(c):
6873 6873 if s in status.removed:
6874 6874 status.removed.remove(s)
6875 6875 renamed.append(d)
6876 6876 else:
6877 6877 copied.append(d)
6878 6878 if d in status.added:
6879 6879 status.added.remove(d)
6880 6880
6881 6881 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6882 6882
6883 6883 labels = [
6884 6884 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
6885 6885 (ui.label(_(b'%d added'), b'status.added'), status.added),
6886 6886 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
6887 6887 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
6888 6888 (ui.label(_(b'%d copied'), b'status.copied'), copied),
6889 6889 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
6890 6890 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
6891 6891 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
6892 6892 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
6893 6893 ]
6894 6894 t = []
6895 6895 for l, s in labels:
6896 6896 if s:
6897 6897 t.append(l % len(s))
6898 6898
6899 6899 t = b', '.join(t)
6900 6900 cleanworkdir = False
6901 6901
6902 6902 if repo.vfs.exists(b'graftstate'):
6903 6903 t += _(b' (graft in progress)')
6904 6904 if repo.vfs.exists(b'updatestate'):
6905 6905 t += _(b' (interrupted update)')
6906 6906 elif len(parents) > 1:
6907 6907 t += _(b' (merge)')
6908 6908 elif branch != parents[0].branch():
6909 6909 t += _(b' (new branch)')
6910 6910 elif parents[0].closesbranch() and pnode in repo.branchheads(
6911 6911 branch, closed=True
6912 6912 ):
6913 6913 t += _(b' (head closed)')
6914 6914 elif not (
6915 6915 status.modified
6916 6916 or status.added
6917 6917 or status.removed
6918 6918 or renamed
6919 6919 or copied
6920 6920 or subs
6921 6921 ):
6922 6922 t += _(b' (clean)')
6923 6923 cleanworkdir = True
6924 6924 elif pnode not in bheads:
6925 6925 t += _(b' (new branch head)')
6926 6926
6927 6927 if parents:
6928 6928 pendingphase = max(p.phase() for p in parents)
6929 6929 else:
6930 6930 pendingphase = phases.public
6931 6931
6932 6932 if pendingphase > phases.newcommitphase(ui):
6933 6933 t += b' (%s)' % phases.phasenames[pendingphase]
6934 6934
6935 6935 if cleanworkdir:
6936 6936 # i18n: column positioning for "hg summary"
6937 6937 ui.status(_(b'commit: %s\n') % t.strip())
6938 6938 else:
6939 6939 # i18n: column positioning for "hg summary"
6940 6940 ui.write(_(b'commit: %s\n') % t.strip())
6941 6941
6942 6942 # all ancestors of branch heads - all ancestors of parent = new csets
6943 6943 new = len(
6944 6944 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
6945 6945 )
6946 6946
6947 6947 if new == 0:
6948 6948 # i18n: column positioning for "hg summary"
6949 6949 ui.status(_(b'update: (current)\n'))
6950 6950 elif pnode not in bheads:
6951 6951 # i18n: column positioning for "hg summary"
6952 6952 ui.write(_(b'update: %d new changesets (update)\n') % new)
6953 6953 else:
6954 6954 # i18n: column positioning for "hg summary"
6955 6955 ui.write(
6956 6956 _(b'update: %d new changesets, %d branch heads (merge)\n')
6957 6957 % (new, len(bheads))
6958 6958 )
6959 6959
6960 6960 t = []
6961 6961 draft = len(repo.revs(b'draft()'))
6962 6962 if draft:
6963 6963 t.append(_(b'%d draft') % draft)
6964 6964 secret = len(repo.revs(b'secret()'))
6965 6965 if secret:
6966 6966 t.append(_(b'%d secret') % secret)
6967 6967
6968 6968 if draft or secret:
6969 6969 ui.status(_(b'phases: %s\n') % b', '.join(t))
6970 6970
6971 6971 if obsolete.isenabled(repo, obsolete.createmarkersopt):
6972 6972 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
6973 6973 numtrouble = len(repo.revs(trouble + b"()"))
6974 6974 # We write all the possibilities to ease translation
6975 6975 troublemsg = {
6976 6976 b"orphan": _(b"orphan: %d changesets"),
6977 6977 b"contentdivergent": _(b"content-divergent: %d changesets"),
6978 6978 b"phasedivergent": _(b"phase-divergent: %d changesets"),
6979 6979 }
6980 6980 if numtrouble > 0:
6981 6981 ui.status(troublemsg[trouble] % numtrouble + b"\n")
6982 6982
6983 6983 cmdutil.summaryhooks(ui, repo)
6984 6984
6985 6985 if opts.get(b'remote'):
6986 6986 needsincoming, needsoutgoing = True, True
6987 6987 else:
6988 6988 needsincoming, needsoutgoing = False, False
6989 6989 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
6990 6990 if i:
6991 6991 needsincoming = True
6992 6992 if o:
6993 6993 needsoutgoing = True
6994 6994 if not needsincoming and not needsoutgoing:
6995 6995 return
6996 6996
6997 6997 def getincoming():
6998 6998 source, branches = hg.parseurl(ui.expandpath(b'default'))
6999 6999 sbranch = branches[0]
7000 7000 try:
7001 7001 other = hg.peer(repo, {}, source)
7002 7002 except error.RepoError:
7003 7003 if opts.get(b'remote'):
7004 7004 raise
7005 7005 return source, sbranch, None, None, None
7006 7006 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7007 7007 if revs:
7008 7008 revs = [other.lookup(rev) for rev in revs]
7009 7009 ui.debug(b'comparing with %s\n' % util.hidepassword(source))
7010 7010 repo.ui.pushbuffer()
7011 7011 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7012 7012 repo.ui.popbuffer()
7013 7013 return source, sbranch, other, commoninc, commoninc[1]
7014 7014
7015 7015 if needsincoming:
7016 7016 source, sbranch, sother, commoninc, incoming = getincoming()
7017 7017 else:
7018 7018 source = sbranch = sother = commoninc = incoming = None
7019 7019
7020 7020 def getoutgoing():
7021 7021 dest, branches = hg.parseurl(ui.expandpath(b'default-push', b'default'))
7022 7022 dbranch = branches[0]
7023 7023 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
7024 7024 if source != dest:
7025 7025 try:
7026 7026 dother = hg.peer(repo, {}, dest)
7027 7027 except error.RepoError:
7028 7028 if opts.get(b'remote'):
7029 7029 raise
7030 7030 return dest, dbranch, None, None
7031 7031 ui.debug(b'comparing with %s\n' % util.hidepassword(dest))
7032 7032 elif sother is None:
7033 7033 # there is no explicit destination peer, but source one is invalid
7034 7034 return dest, dbranch, None, None
7035 7035 else:
7036 7036 dother = sother
7037 7037 if source != dest or (sbranch is not None and sbranch != dbranch):
7038 7038 common = None
7039 7039 else:
7040 7040 common = commoninc
7041 7041 if revs:
7042 7042 revs = [repo.lookup(rev) for rev in revs]
7043 7043 repo.ui.pushbuffer()
7044 7044 outgoing = discovery.findcommonoutgoing(
7045 7045 repo, dother, onlyheads=revs, commoninc=common
7046 7046 )
7047 7047 repo.ui.popbuffer()
7048 7048 return dest, dbranch, dother, outgoing
7049 7049
7050 7050 if needsoutgoing:
7051 7051 dest, dbranch, dother, outgoing = getoutgoing()
7052 7052 else:
7053 7053 dest = dbranch = dother = outgoing = None
7054 7054
7055 7055 if opts.get(b'remote'):
7056 7056 t = []
7057 7057 if incoming:
7058 7058 t.append(_(b'1 or more incoming'))
7059 7059 o = outgoing.missing
7060 7060 if o:
7061 7061 t.append(_(b'%d outgoing') % len(o))
7062 7062 other = dother or sother
7063 7063 if b'bookmarks' in other.listkeys(b'namespaces'):
7064 7064 counts = bookmarks.summary(repo, other)
7065 7065 if counts[0] > 0:
7066 7066 t.append(_(b'%d incoming bookmarks') % counts[0])
7067 7067 if counts[1] > 0:
7068 7068 t.append(_(b'%d outgoing bookmarks') % counts[1])
7069 7069
7070 7070 if t:
7071 7071 # i18n: column positioning for "hg summary"
7072 7072 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7073 7073 else:
7074 7074 # i18n: column positioning for "hg summary"
7075 7075 ui.status(_(b'remote: (synced)\n'))
7076 7076
7077 7077 cmdutil.summaryremotehooks(
7078 7078 ui,
7079 7079 repo,
7080 7080 opts,
7081 7081 (
7082 7082 (source, sbranch, sother, commoninc),
7083 7083 (dest, dbranch, dother, outgoing),
7084 7084 ),
7085 7085 )
7086 7086
7087 7087
7088 7088 @command(
7089 7089 b'tag',
7090 7090 [
7091 7091 (b'f', b'force', None, _(b'force tag')),
7092 7092 (b'l', b'local', None, _(b'make the tag local')),
7093 7093 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7094 7094 (b'', b'remove', None, _(b'remove a tag')),
7095 7095 # -l/--local is already there, commitopts cannot be used
7096 7096 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7097 7097 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7098 7098 ]
7099 7099 + commitopts2,
7100 7100 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7101 7101 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7102 7102 )
7103 7103 def tag(ui, repo, name1, *names, **opts):
7104 7104 """add one or more tags for the current or given revision
7105 7105
7106 7106 Name a particular revision using <name>.
7107 7107
7108 7108 Tags are used to name particular revisions of the repository and are
7109 7109 very useful to compare different revisions, to go back to significant
7110 7110 earlier versions or to mark branch points as releases, etc. Changing
7111 7111 an existing tag is normally disallowed; use -f/--force to override.
7112 7112
7113 7113 If no revision is given, the parent of the working directory is
7114 7114 used.
7115 7115
7116 7116 To facilitate version control, distribution, and merging of tags,
7117 7117 they are stored as a file named ".hgtags" which is managed similarly
7118 7118 to other project files and can be hand-edited if necessary. This
7119 7119 also means that tagging creates a new commit. The file
7120 7120 ".hg/localtags" is used for local tags (not shared among
7121 7121 repositories).
7122 7122
7123 7123 Tag commits are usually made at the head of a branch. If the parent
7124 7124 of the working directory is not a branch head, :hg:`tag` aborts; use
7125 7125 -f/--force to force the tag commit to be based on a non-head
7126 7126 changeset.
7127 7127
7128 7128 See :hg:`help dates` for a list of formats valid for -d/--date.
7129 7129
7130 7130 Since tag names have priority over branch names during revision
7131 7131 lookup, using an existing branch name as a tag name is discouraged.
7132 7132
7133 7133 Returns 0 on success.
7134 7134 """
7135 7135 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7136 7136 opts = pycompat.byteskwargs(opts)
7137 7137 with repo.wlock(), repo.lock():
7138 7138 rev_ = b"."
7139 7139 names = [t.strip() for t in (name1,) + names]
7140 7140 if len(names) != len(set(names)):
7141 7141 raise error.InputError(_(b'tag names must be unique'))
7142 7142 for n in names:
7143 7143 scmutil.checknewlabel(repo, n, b'tag')
7144 7144 if not n:
7145 7145 raise error.InputError(
7146 7146 _(b'tag names cannot consist entirely of whitespace')
7147 7147 )
7148 7148 if opts.get(b'rev'):
7149 7149 rev_ = opts[b'rev']
7150 7150 message = opts.get(b'message')
7151 7151 if opts.get(b'remove'):
7152 7152 if opts.get(b'local'):
7153 7153 expectedtype = b'local'
7154 7154 else:
7155 7155 expectedtype = b'global'
7156 7156
7157 7157 for n in names:
7158 7158 if repo.tagtype(n) == b'global':
7159 7159 alltags = tagsmod.findglobaltags(ui, repo)
7160 7160 if alltags[n][0] == nullid:
7161 7161 raise error.InputError(
7162 7162 _(b"tag '%s' is already removed") % n
7163 7163 )
7164 7164 if not repo.tagtype(n):
7165 7165 raise error.InputError(_(b"tag '%s' does not exist") % n)
7166 7166 if repo.tagtype(n) != expectedtype:
7167 7167 if expectedtype == b'global':
7168 7168 raise error.InputError(
7169 7169 _(b"tag '%s' is not a global tag") % n
7170 7170 )
7171 7171 else:
7172 7172 raise error.InputError(
7173 7173 _(b"tag '%s' is not a local tag") % n
7174 7174 )
7175 7175 rev_ = b'null'
7176 7176 if not message:
7177 7177 # we don't translate commit messages
7178 7178 message = b'Removed tag %s' % b', '.join(names)
7179 7179 elif not opts.get(b'force'):
7180 7180 for n in names:
7181 7181 if n in repo.tags():
7182 7182 raise error.InputError(
7183 7183 _(b"tag '%s' already exists (use -f to force)") % n
7184 7184 )
7185 7185 if not opts.get(b'local'):
7186 7186 p1, p2 = repo.dirstate.parents()
7187 7187 if p2 != nullid:
7188 raise error.Abort(_(b'uncommitted merge'))
7188 raise error.StateError(_(b'uncommitted merge'))
7189 7189 bheads = repo.branchheads()
7190 7190 if not opts.get(b'force') and bheads and p1 not in bheads:
7191 7191 raise error.InputError(
7192 7192 _(
7193 7193 b'working directory is not at a branch head '
7194 7194 b'(use -f to force)'
7195 7195 )
7196 7196 )
7197 7197 node = scmutil.revsingle(repo, rev_).node()
7198 7198
7199 7199 if not message:
7200 7200 # we don't translate commit messages
7201 7201 message = b'Added tag %s for changeset %s' % (
7202 7202 b', '.join(names),
7203 7203 short(node),
7204 7204 )
7205 7205
7206 7206 date = opts.get(b'date')
7207 7207 if date:
7208 7208 date = dateutil.parsedate(date)
7209 7209
7210 7210 if opts.get(b'remove'):
7211 7211 editform = b'tag.remove'
7212 7212 else:
7213 7213 editform = b'tag.add'
7214 7214 editor = cmdutil.getcommiteditor(
7215 7215 editform=editform, **pycompat.strkwargs(opts)
7216 7216 )
7217 7217
7218 7218 # don't allow tagging the null rev
7219 7219 if (
7220 7220 not opts.get(b'remove')
7221 7221 and scmutil.revsingle(repo, rev_).rev() == nullrev
7222 7222 ):
7223 7223 raise error.InputError(_(b"cannot tag null revision"))
7224 7224
7225 7225 tagsmod.tag(
7226 7226 repo,
7227 7227 names,
7228 7228 node,
7229 7229 message,
7230 7230 opts.get(b'local'),
7231 7231 opts.get(b'user'),
7232 7232 date,
7233 7233 editor=editor,
7234 7234 )
7235 7235
7236 7236
7237 7237 @command(
7238 7238 b'tags',
7239 7239 formatteropts,
7240 7240 b'',
7241 7241 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7242 7242 intents={INTENT_READONLY},
7243 7243 )
7244 7244 def tags(ui, repo, **opts):
7245 7245 """list repository tags
7246 7246
7247 7247 This lists both regular and local tags. When the -v/--verbose
7248 7248 switch is used, a third column "local" is printed for local tags.
7249 7249 When the -q/--quiet switch is used, only the tag name is printed.
7250 7250
7251 7251 .. container:: verbose
7252 7252
7253 7253 Template:
7254 7254
7255 7255 The following keywords are supported in addition to the common template
7256 7256 keywords and functions such as ``{tag}``. See also
7257 7257 :hg:`help templates`.
7258 7258
7259 7259 :type: String. ``local`` for local tags.
7260 7260
7261 7261 Returns 0 on success.
7262 7262 """
7263 7263
7264 7264 opts = pycompat.byteskwargs(opts)
7265 7265 ui.pager(b'tags')
7266 7266 fm = ui.formatter(b'tags', opts)
7267 7267 hexfunc = fm.hexfunc
7268 7268
7269 7269 for t, n in reversed(repo.tagslist()):
7270 7270 hn = hexfunc(n)
7271 7271 label = b'tags.normal'
7272 7272 tagtype = b''
7273 7273 if repo.tagtype(t) == b'local':
7274 7274 label = b'tags.local'
7275 7275 tagtype = b'local'
7276 7276
7277 7277 fm.startitem()
7278 7278 fm.context(repo=repo)
7279 7279 fm.write(b'tag', b'%s', t, label=label)
7280 7280 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7281 7281 fm.condwrite(
7282 7282 not ui.quiet,
7283 7283 b'rev node',
7284 7284 fmt,
7285 7285 repo.changelog.rev(n),
7286 7286 hn,
7287 7287 label=label,
7288 7288 )
7289 7289 fm.condwrite(
7290 7290 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7291 7291 )
7292 7292 fm.plain(b'\n')
7293 7293 fm.end()
7294 7294
7295 7295
7296 7296 @command(
7297 7297 b'tip',
7298 7298 [
7299 7299 (b'p', b'patch', None, _(b'show patch')),
7300 7300 (b'g', b'git', None, _(b'use git extended diff format')),
7301 7301 ]
7302 7302 + templateopts,
7303 7303 _(b'[-p] [-g]'),
7304 7304 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7305 7305 )
7306 7306 def tip(ui, repo, **opts):
7307 7307 """show the tip revision (DEPRECATED)
7308 7308
7309 7309 The tip revision (usually just called the tip) is the changeset
7310 7310 most recently added to the repository (and therefore the most
7311 7311 recently changed head).
7312 7312
7313 7313 If you have just made a commit, that commit will be the tip. If
7314 7314 you have just pulled changes from another repository, the tip of
7315 7315 that repository becomes the current tip. The "tip" tag is special
7316 7316 and cannot be renamed or assigned to a different changeset.
7317 7317
7318 7318 This command is deprecated, please use :hg:`heads` instead.
7319 7319
7320 7320 Returns 0 on success.
7321 7321 """
7322 7322 opts = pycompat.byteskwargs(opts)
7323 7323 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7324 7324 displayer.show(repo[b'tip'])
7325 7325 displayer.close()
7326 7326
7327 7327
7328 7328 @command(
7329 7329 b'unbundle',
7330 7330 [
7331 7331 (
7332 7332 b'u',
7333 7333 b'update',
7334 7334 None,
7335 7335 _(b'update to new branch head if changesets were unbundled'),
7336 7336 )
7337 7337 ],
7338 7338 _(b'[-u] FILE...'),
7339 7339 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7340 7340 )
7341 7341 def unbundle(ui, repo, fname1, *fnames, **opts):
7342 7342 """apply one or more bundle files
7343 7343
7344 7344 Apply one or more bundle files generated by :hg:`bundle`.
7345 7345
7346 7346 Returns 0 on success, 1 if an update has unresolved files.
7347 7347 """
7348 7348 fnames = (fname1,) + fnames
7349 7349
7350 7350 with repo.lock():
7351 7351 for fname in fnames:
7352 7352 f = hg.openpath(ui, fname)
7353 7353 gen = exchange.readbundle(ui, f, fname)
7354 7354 if isinstance(gen, streamclone.streamcloneapplier):
7355 7355 raise error.InputError(
7356 7356 _(
7357 7357 b'packed bundles cannot be applied with '
7358 7358 b'"hg unbundle"'
7359 7359 ),
7360 7360 hint=_(b'use "hg debugapplystreamclonebundle"'),
7361 7361 )
7362 7362 url = b'bundle:' + fname
7363 7363 try:
7364 7364 txnname = b'unbundle'
7365 7365 if not isinstance(gen, bundle2.unbundle20):
7366 7366 txnname = b'unbundle\n%s' % util.hidepassword(url)
7367 7367 with repo.transaction(txnname) as tr:
7368 7368 op = bundle2.applybundle(
7369 7369 repo, gen, tr, source=b'unbundle', url=url
7370 7370 )
7371 7371 except error.BundleUnknownFeatureError as exc:
7372 7372 raise error.Abort(
7373 7373 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7374 7374 hint=_(
7375 7375 b"see https://mercurial-scm.org/"
7376 7376 b"wiki/BundleFeature for more "
7377 7377 b"information"
7378 7378 ),
7379 7379 )
7380 7380 modheads = bundle2.combinechangegroupresults(op)
7381 7381
7382 7382 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7383 7383
7384 7384
7385 7385 @command(
7386 7386 b'unshelve',
7387 7387 [
7388 7388 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7389 7389 (
7390 7390 b'c',
7391 7391 b'continue',
7392 7392 None,
7393 7393 _(b'continue an incomplete unshelve operation'),
7394 7394 ),
7395 7395 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7396 7396 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7397 7397 (
7398 7398 b'n',
7399 7399 b'name',
7400 7400 b'',
7401 7401 _(b'restore shelved change with given name'),
7402 7402 _(b'NAME'),
7403 7403 ),
7404 7404 (b't', b'tool', b'', _(b'specify merge tool')),
7405 7405 (
7406 7406 b'',
7407 7407 b'date',
7408 7408 b'',
7409 7409 _(b'set date for temporary commits (DEPRECATED)'),
7410 7410 _(b'DATE'),
7411 7411 ),
7412 7412 ],
7413 7413 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7414 7414 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7415 7415 )
7416 7416 def unshelve(ui, repo, *shelved, **opts):
7417 7417 """restore a shelved change to the working directory
7418 7418
7419 7419 This command accepts an optional name of a shelved change to
7420 7420 restore. If none is given, the most recent shelved change is used.
7421 7421
7422 7422 If a shelved change is applied successfully, the bundle that
7423 7423 contains the shelved changes is moved to a backup location
7424 7424 (.hg/shelve-backup).
7425 7425
7426 7426 Since you can restore a shelved change on top of an arbitrary
7427 7427 commit, it is possible that unshelving will result in a conflict
7428 7428 between your changes and the commits you are unshelving onto. If
7429 7429 this occurs, you must resolve the conflict, then use
7430 7430 ``--continue`` to complete the unshelve operation. (The bundle
7431 7431 will not be moved until you successfully complete the unshelve.)
7432 7432
7433 7433 (Alternatively, you can use ``--abort`` to abandon an unshelve
7434 7434 that causes a conflict. This reverts the unshelved changes, and
7435 7435 leaves the bundle in place.)
7436 7436
7437 7437 If bare shelved change (without interactive, include and exclude
7438 7438 option) was done on newly created branch it would restore branch
7439 7439 information to the working directory.
7440 7440
7441 7441 After a successful unshelve, the shelved changes are stored in a
7442 7442 backup directory. Only the N most recent backups are kept. N
7443 7443 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7444 7444 configuration option.
7445 7445
7446 7446 .. container:: verbose
7447 7447
7448 7448 Timestamp in seconds is used to decide order of backups. More
7449 7449 than ``maxbackups`` backups are kept, if same timestamp
7450 7450 prevents from deciding exact order of them, for safety.
7451 7451
7452 7452 Selected changes can be unshelved with ``--interactive`` flag.
7453 7453 The working directory is updated with the selected changes, and
7454 7454 only the unselected changes remain shelved.
7455 7455 Note: The whole shelve is applied to working directory first before
7456 7456 running interactively. So, this will bring up all the conflicts between
7457 7457 working directory and the shelve, irrespective of which changes will be
7458 7458 unshelved.
7459 7459 """
7460 7460 with repo.wlock():
7461 7461 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7462 7462
7463 7463
7464 7464 statemod.addunfinished(
7465 7465 b'unshelve',
7466 7466 fname=b'shelvedstate',
7467 7467 continueflag=True,
7468 7468 abortfunc=shelvemod.hgabortunshelve,
7469 7469 continuefunc=shelvemod.hgcontinueunshelve,
7470 7470 cmdmsg=_(b'unshelve already in progress'),
7471 7471 )
7472 7472
7473 7473
7474 7474 @command(
7475 7475 b'update|up|checkout|co',
7476 7476 [
7477 7477 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7478 7478 (b'c', b'check', None, _(b'require clean working directory')),
7479 7479 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7480 7480 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7481 7481 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7482 7482 ]
7483 7483 + mergetoolopts,
7484 7484 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7485 7485 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7486 7486 helpbasic=True,
7487 7487 )
7488 7488 def update(ui, repo, node=None, **opts):
7489 7489 """update working directory (or switch revisions)
7490 7490
7491 7491 Update the repository's working directory to the specified
7492 7492 changeset. If no changeset is specified, update to the tip of the
7493 7493 current named branch and move the active bookmark (see :hg:`help
7494 7494 bookmarks`).
7495 7495
7496 7496 Update sets the working directory's parent revision to the specified
7497 7497 changeset (see :hg:`help parents`).
7498 7498
7499 7499 If the changeset is not a descendant or ancestor of the working
7500 7500 directory's parent and there are uncommitted changes, the update is
7501 7501 aborted. With the -c/--check option, the working directory is checked
7502 7502 for uncommitted changes; if none are found, the working directory is
7503 7503 updated to the specified changeset.
7504 7504
7505 7505 .. container:: verbose
7506 7506
7507 7507 The -C/--clean, -c/--check, and -m/--merge options control what
7508 7508 happens if the working directory contains uncommitted changes.
7509 7509 At most of one of them can be specified.
7510 7510
7511 7511 1. If no option is specified, and if
7512 7512 the requested changeset is an ancestor or descendant of
7513 7513 the working directory's parent, the uncommitted changes
7514 7514 are merged into the requested changeset and the merged
7515 7515 result is left uncommitted. If the requested changeset is
7516 7516 not an ancestor or descendant (that is, it is on another
7517 7517 branch), the update is aborted and the uncommitted changes
7518 7518 are preserved.
7519 7519
7520 7520 2. With the -m/--merge option, the update is allowed even if the
7521 7521 requested changeset is not an ancestor or descendant of
7522 7522 the working directory's parent.
7523 7523
7524 7524 3. With the -c/--check option, the update is aborted and the
7525 7525 uncommitted changes are preserved.
7526 7526
7527 7527 4. With the -C/--clean option, uncommitted changes are discarded and
7528 7528 the working directory is updated to the requested changeset.
7529 7529
7530 7530 To cancel an uncommitted merge (and lose your changes), use
7531 7531 :hg:`merge --abort`.
7532 7532
7533 7533 Use null as the changeset to remove the working directory (like
7534 7534 :hg:`clone -U`).
7535 7535
7536 7536 If you want to revert just one file to an older revision, use
7537 7537 :hg:`revert [-r REV] NAME`.
7538 7538
7539 7539 See :hg:`help dates` for a list of formats valid for -d/--date.
7540 7540
7541 7541 Returns 0 on success, 1 if there are unresolved files.
7542 7542 """
7543 7543 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7544 7544 rev = opts.get('rev')
7545 7545 date = opts.get('date')
7546 7546 clean = opts.get('clean')
7547 7547 check = opts.get('check')
7548 7548 merge = opts.get('merge')
7549 7549 if rev and node:
7550 7550 raise error.InputError(_(b"please specify just one revision"))
7551 7551
7552 7552 if ui.configbool(b'commands', b'update.requiredest'):
7553 7553 if not node and not rev and not date:
7554 7554 raise error.InputError(
7555 7555 _(b'you must specify a destination'),
7556 7556 hint=_(b'for example: hg update ".::"'),
7557 7557 )
7558 7558
7559 7559 if rev is None or rev == b'':
7560 7560 rev = node
7561 7561
7562 7562 if date and rev is not None:
7563 7563 raise error.InputError(_(b"you can't specify a revision and a date"))
7564 7564
7565 7565 updatecheck = None
7566 7566 if check:
7567 7567 updatecheck = b'abort'
7568 7568 elif merge:
7569 7569 updatecheck = b'none'
7570 7570
7571 7571 with repo.wlock():
7572 7572 cmdutil.clearunfinished(repo)
7573 7573 if date:
7574 7574 rev = cmdutil.finddate(ui, repo, date)
7575 7575
7576 7576 # if we defined a bookmark, we have to remember the original name
7577 7577 brev = rev
7578 7578 if rev:
7579 7579 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7580 7580 ctx = scmutil.revsingle(repo, rev, default=None)
7581 7581 rev = ctx.rev()
7582 7582 hidden = ctx.hidden()
7583 7583 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7584 7584 with ui.configoverride(overrides, b'update'):
7585 7585 ret = hg.updatetotally(
7586 7586 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7587 7587 )
7588 7588 if hidden:
7589 7589 ctxstr = ctx.hex()[:12]
7590 7590 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7591 7591
7592 7592 if ctx.obsolete():
7593 7593 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7594 7594 ui.warn(b"(%s)\n" % obsfatemsg)
7595 7595 return ret
7596 7596
7597 7597
7598 7598 @command(
7599 7599 b'verify',
7600 7600 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7601 7601 helpcategory=command.CATEGORY_MAINTENANCE,
7602 7602 )
7603 7603 def verify(ui, repo, **opts):
7604 7604 """verify the integrity of the repository
7605 7605
7606 7606 Verify the integrity of the current repository.
7607 7607
7608 7608 This will perform an extensive check of the repository's
7609 7609 integrity, validating the hashes and checksums of each entry in
7610 7610 the changelog, manifest, and tracked files, as well as the
7611 7611 integrity of their crosslinks and indices.
7612 7612
7613 7613 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7614 7614 for more information about recovery from corruption of the
7615 7615 repository.
7616 7616
7617 7617 Returns 0 on success, 1 if errors are encountered.
7618 7618 """
7619 7619 opts = pycompat.byteskwargs(opts)
7620 7620
7621 7621 level = None
7622 7622 if opts[b'full']:
7623 7623 level = verifymod.VERIFY_FULL
7624 7624 return hg.verify(repo, level)
7625 7625
7626 7626
7627 7627 @command(
7628 7628 b'version',
7629 7629 [] + formatteropts,
7630 7630 helpcategory=command.CATEGORY_HELP,
7631 7631 norepo=True,
7632 7632 intents={INTENT_READONLY},
7633 7633 )
7634 7634 def version_(ui, **opts):
7635 7635 """output version and copyright information
7636 7636
7637 7637 .. container:: verbose
7638 7638
7639 7639 Template:
7640 7640
7641 7641 The following keywords are supported. See also :hg:`help templates`.
7642 7642
7643 7643 :extensions: List of extensions.
7644 7644 :ver: String. Version number.
7645 7645
7646 7646 And each entry of ``{extensions}`` provides the following sub-keywords
7647 7647 in addition to ``{ver}``.
7648 7648
7649 7649 :bundled: Boolean. True if included in the release.
7650 7650 :name: String. Extension name.
7651 7651 """
7652 7652 opts = pycompat.byteskwargs(opts)
7653 7653 if ui.verbose:
7654 7654 ui.pager(b'version')
7655 7655 fm = ui.formatter(b"version", opts)
7656 7656 fm.startitem()
7657 7657 fm.write(
7658 7658 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7659 7659 )
7660 7660 license = _(
7661 7661 b"(see https://mercurial-scm.org for more information)\n"
7662 7662 b"\nCopyright (C) 2005-2020 Matt Mackall and others\n"
7663 7663 b"This is free software; see the source for copying conditions. "
7664 7664 b"There is NO\nwarranty; "
7665 7665 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7666 7666 )
7667 7667 if not ui.quiet:
7668 7668 fm.plain(license)
7669 7669
7670 7670 if ui.verbose:
7671 7671 fm.plain(_(b"\nEnabled extensions:\n\n"))
7672 7672 # format names and versions into columns
7673 7673 names = []
7674 7674 vers = []
7675 7675 isinternals = []
7676 7676 for name, module in sorted(extensions.extensions()):
7677 7677 names.append(name)
7678 7678 vers.append(extensions.moduleversion(module) or None)
7679 7679 isinternals.append(extensions.ismoduleinternal(module))
7680 7680 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7681 7681 if names:
7682 7682 namefmt = b" %%-%ds " % max(len(n) for n in names)
7683 7683 places = [_(b"external"), _(b"internal")]
7684 7684 for n, v, p in zip(names, vers, isinternals):
7685 7685 fn.startitem()
7686 7686 fn.condwrite(ui.verbose, b"name", namefmt, n)
7687 7687 if ui.verbose:
7688 7688 fn.plain(b"%s " % places[p])
7689 7689 fn.data(bundled=p)
7690 7690 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7691 7691 if ui.verbose:
7692 7692 fn.plain(b"\n")
7693 7693 fn.end()
7694 7694 fm.end()
7695 7695
7696 7696
7697 7697 def loadcmdtable(ui, name, cmdtable):
7698 7698 """Load command functions from specified cmdtable
7699 7699 """
7700 7700 overrides = [cmd for cmd in cmdtable if cmd in table]
7701 7701 if overrides:
7702 7702 ui.warn(
7703 7703 _(b"extension '%s' overrides commands: %s\n")
7704 7704 % (name, b" ".join(overrides))
7705 7705 )
7706 7706 table.update(cmdtable)
@@ -1,505 +1,512 b''
1 1 # error.py - Mercurial exceptions
2 2 #
3 3 # Copyright 2005-2008 Matt Mackall <mpm@selenic.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 """Mercurial exceptions.
9 9
10 10 This allows us to catch exceptions at higher levels without forcing
11 11 imports.
12 12 """
13 13
14 14 from __future__ import absolute_import
15 15
16 16 # Do not import anything but pycompat here, please
17 17 from . import pycompat
18 18
19 19
20 20 def _tobytes(exc):
21 21 """Byte-stringify exception in the same way as BaseException_str()"""
22 22 if not exc.args:
23 23 return b''
24 24 if len(exc.args) == 1:
25 25 return pycompat.bytestr(exc.args[0])
26 26 return b'(%s)' % b', '.join(b"'%s'" % pycompat.bytestr(a) for a in exc.args)
27 27
28 28
29 29 class Hint(object):
30 30 """Mix-in to provide a hint of an error
31 31
32 32 This should come first in the inheritance list to consume a hint and
33 33 pass remaining arguments to the exception class.
34 34 """
35 35
36 36 def __init__(self, *args, **kw):
37 37 self.hint = kw.pop('hint', None)
38 38 super(Hint, self).__init__(*args, **kw)
39 39
40 40
41 41 class StorageError(Hint, Exception):
42 42 """Raised when an error occurs in a storage layer.
43 43
44 44 Usually subclassed by a storage-specific exception.
45 45 """
46 46
47 47 __bytes__ = _tobytes
48 48
49 49
50 50 class RevlogError(StorageError):
51 51 pass
52 52
53 53
54 54 class SidedataHashError(RevlogError):
55 55 def __init__(self, key, expected, got):
56 56 self.sidedatakey = key
57 57 self.expecteddigest = expected
58 58 self.actualdigest = got
59 59
60 60
61 61 class FilteredIndexError(IndexError):
62 62 __bytes__ = _tobytes
63 63
64 64
65 65 class LookupError(RevlogError, KeyError):
66 66 def __init__(self, name, index, message):
67 67 self.name = name
68 68 self.index = index
69 69 # this can't be called 'message' because at least some installs of
70 70 # Python 2.6+ complain about the 'message' property being deprecated
71 71 self.lookupmessage = message
72 72 if isinstance(name, bytes) and len(name) == 20:
73 73 from .node import short
74 74
75 75 name = short(name)
76 76 # if name is a binary node, it can be None
77 77 RevlogError.__init__(
78 78 self, b'%s@%s: %s' % (index, pycompat.bytestr(name), message)
79 79 )
80 80
81 81 def __bytes__(self):
82 82 return RevlogError.__bytes__(self)
83 83
84 84 def __str__(self):
85 85 return RevlogError.__str__(self)
86 86
87 87
88 88 class AmbiguousPrefixLookupError(LookupError):
89 89 pass
90 90
91 91
92 92 class FilteredLookupError(LookupError):
93 93 pass
94 94
95 95
96 96 class ManifestLookupError(LookupError):
97 97 pass
98 98
99 99
100 100 class CommandError(Exception):
101 101 """Exception raised on errors in parsing the command line."""
102 102
103 103 def __init__(self, command, message):
104 104 self.command = command
105 105 self.message = message
106 106 super(CommandError, self).__init__()
107 107
108 108 __bytes__ = _tobytes
109 109
110 110
111 111 class UnknownCommand(Exception):
112 112 """Exception raised if command is not in the command table."""
113 113
114 114 def __init__(self, command, all_commands=None):
115 115 self.command = command
116 116 self.all_commands = all_commands
117 117 super(UnknownCommand, self).__init__()
118 118
119 119 __bytes__ = _tobytes
120 120
121 121
122 122 class AmbiguousCommand(Exception):
123 123 """Exception raised if command shortcut matches more than one command."""
124 124
125 125 def __init__(self, prefix, matches):
126 126 self.prefix = prefix
127 127 self.matches = matches
128 128 super(AmbiguousCommand, self).__init__()
129 129
130 130 __bytes__ = _tobytes
131 131
132 132
133 133 class WorkerError(Exception):
134 134 """Exception raised when a worker process dies."""
135 135
136 136 def __init__(self, status_code):
137 137 self.status_code = status_code
138 138
139 139
140 140 class InterventionRequired(Hint, Exception):
141 141 """Exception raised when a command requires human intervention."""
142 142
143 143 __bytes__ = _tobytes
144 144
145 145
146 146 class ConflictResolutionRequired(InterventionRequired):
147 147 """Exception raised when a continuable command required merge conflict resolution."""
148 148
149 149 def __init__(self, opname):
150 150 from .i18n import _
151 151
152 152 self.opname = opname
153 153 InterventionRequired.__init__(
154 154 self,
155 155 _(
156 156 b"unresolved conflicts (see 'hg resolve', then 'hg %s --continue')"
157 157 )
158 158 % opname,
159 159 )
160 160
161 161
162 162 class Abort(Hint, Exception):
163 163 """Raised if a command needs to print an error and exit."""
164 164
165 165 def __init__(self, message, hint=None):
166 166 self.message = message
167 167 self.hint = hint
168 168 # Pass the message into the Exception constructor to help extensions
169 169 # that look for exc.args[0].
170 170 Exception.__init__(self, message)
171 171
172 172 def __bytes__(self):
173 173 return self.message
174 174
175 175 if pycompat.ispy3:
176 176
177 177 def __str__(self):
178 178 # the output would be unreadable if the message was translated,
179 179 # but do not replace it with encoding.strfromlocal(), which
180 180 # may raise another exception.
181 181 return pycompat.sysstr(self.__bytes__())
182 182
183 183
184 184 class InputError(Abort):
185 185 """Indicates that the user made an error in their input.
186 186
187 187 Examples: Invalid command, invalid flags, invalid revision.
188 188 """
189 189
190 190
191 class StateError(Abort):
192 """Indicates that the operation might work if retried in a different state.
193
194 Examples: Unresolved merge conflicts, unfinished operations.
195 """
196
197
191 198 class HookLoadError(Abort):
192 199 """raised when loading a hook fails, aborting an operation
193 200
194 201 Exists to allow more specialized catching."""
195 202
196 203
197 204 class HookAbort(Abort):
198 205 """raised when a validation hook fails, aborting an operation
199 206
200 207 Exists to allow more specialized catching."""
201 208
202 209
203 210 class ConfigError(Abort):
204 211 """Exception raised when parsing config files"""
205 212
206 213
207 214 class UpdateAbort(Abort):
208 215 """Raised when an update is aborted for destination issue"""
209 216
210 217
211 218 class MergeDestAbort(Abort):
212 219 """Raised when an update is aborted for destination issues"""
213 220
214 221
215 222 class NoMergeDestAbort(MergeDestAbort):
216 223 """Raised when an update is aborted because there is nothing to merge"""
217 224
218 225
219 226 class ManyMergeDestAbort(MergeDestAbort):
220 227 """Raised when an update is aborted because destination is ambiguous"""
221 228
222 229
223 230 class ResponseExpected(Abort):
224 231 """Raised when an EOF is received for a prompt"""
225 232
226 233 def __init__(self):
227 234 from .i18n import _
228 235
229 236 Abort.__init__(self, _(b'response expected'))
230 237
231 238
232 239 class OutOfBandError(Hint, Exception):
233 240 """Exception raised when a remote repo reports failure"""
234 241
235 242 __bytes__ = _tobytes
236 243
237 244
238 245 class ParseError(Hint, Exception):
239 246 """Raised when parsing config files and {rev,file}sets (msg[, pos])"""
240 247
241 248 def __init__(self, message, location=None, hint=None):
242 249 self.message = message
243 250 self.location = location
244 251 self.hint = hint
245 252 # Pass the message and possibly location into the Exception constructor
246 253 # to help code that looks for exc.args.
247 254 if location is not None:
248 255 Exception.__init__(self, message, location)
249 256 else:
250 257 Exception.__init__(self, message)
251 258
252 259 __bytes__ = _tobytes
253 260
254 261
255 262 class PatchError(Exception):
256 263 __bytes__ = _tobytes
257 264
258 265
259 266 class UnknownIdentifier(ParseError):
260 267 """Exception raised when a {rev,file}set references an unknown identifier"""
261 268
262 269 def __init__(self, function, symbols):
263 270 from .i18n import _
264 271
265 272 ParseError.__init__(self, _(b"unknown identifier: %s") % function)
266 273 self.function = function
267 274 self.symbols = symbols
268 275
269 276
270 277 class RepoError(Hint, Exception):
271 278 __bytes__ = _tobytes
272 279
273 280
274 281 class RepoLookupError(RepoError):
275 282 pass
276 283
277 284
278 285 class FilteredRepoLookupError(RepoLookupError):
279 286 pass
280 287
281 288
282 289 class CapabilityError(RepoError):
283 290 pass
284 291
285 292
286 293 class RequirementError(RepoError):
287 294 """Exception raised if .hg/requires has an unknown entry."""
288 295
289 296
290 297 class StdioError(IOError):
291 298 """Raised if I/O to stdout or stderr fails"""
292 299
293 300 def __init__(self, err):
294 301 IOError.__init__(self, err.errno, err.strerror)
295 302
296 303 # no __bytes__() because error message is derived from the standard IOError
297 304
298 305
299 306 class UnsupportedMergeRecords(Abort):
300 307 def __init__(self, recordtypes):
301 308 from .i18n import _
302 309
303 310 self.recordtypes = sorted(recordtypes)
304 311 s = b' '.join(self.recordtypes)
305 312 Abort.__init__(
306 313 self,
307 314 _(b'unsupported merge state records: %s') % s,
308 315 hint=_(
309 316 b'see https://mercurial-scm.org/wiki/MergeStateRecords for '
310 317 b'more information'
311 318 ),
312 319 )
313 320
314 321
315 322 class UnknownVersion(Abort):
316 323 """generic exception for aborting from an encounter with an unknown version
317 324 """
318 325
319 326 def __init__(self, msg, hint=None, version=None):
320 327 self.version = version
321 328 super(UnknownVersion, self).__init__(msg, hint=hint)
322 329
323 330
324 331 class LockError(IOError):
325 332 def __init__(self, errno, strerror, filename, desc):
326 333 IOError.__init__(self, errno, strerror, filename)
327 334 self.desc = desc
328 335
329 336 # no __bytes__() because error message is derived from the standard IOError
330 337
331 338
332 339 class LockHeld(LockError):
333 340 def __init__(self, errno, filename, desc, locker):
334 341 LockError.__init__(self, errno, b'Lock held', filename, desc)
335 342 self.locker = locker
336 343
337 344
338 345 class LockUnavailable(LockError):
339 346 pass
340 347
341 348
342 349 # LockError is for errors while acquiring the lock -- this is unrelated
343 350 class LockInheritanceContractViolation(RuntimeError):
344 351 __bytes__ = _tobytes
345 352
346 353
347 354 class ResponseError(Exception):
348 355 """Raised to print an error with part of output and exit."""
349 356
350 357 __bytes__ = _tobytes
351 358
352 359
353 360 # derived from KeyboardInterrupt to simplify some breakout code
354 361 class SignalInterrupt(KeyboardInterrupt):
355 362 """Exception raised on SIGTERM and SIGHUP."""
356 363
357 364
358 365 class SignatureError(Exception):
359 366 __bytes__ = _tobytes
360 367
361 368
362 369 class PushRaced(RuntimeError):
363 370 """An exception raised during unbundling that indicate a push race"""
364 371
365 372 __bytes__ = _tobytes
366 373
367 374
368 375 class ProgrammingError(Hint, RuntimeError):
369 376 """Raised if a mercurial (core or extension) developer made a mistake"""
370 377
371 378 def __init__(self, msg, *args, **kwargs):
372 379 # On Python 3, turn the message back into a string since this is
373 380 # an internal-only error that won't be printed except in a
374 381 # stack traces.
375 382 msg = pycompat.sysstr(msg)
376 383 super(ProgrammingError, self).__init__(msg, *args, **kwargs)
377 384
378 385 __bytes__ = _tobytes
379 386
380 387
381 388 class WdirUnsupported(Exception):
382 389 """An exception which is raised when 'wdir()' is not supported"""
383 390
384 391 __bytes__ = _tobytes
385 392
386 393
387 394 # bundle2 related errors
388 395 class BundleValueError(ValueError):
389 396 """error raised when bundle2 cannot be processed"""
390 397
391 398 __bytes__ = _tobytes
392 399
393 400
394 401 class BundleUnknownFeatureError(BundleValueError):
395 402 def __init__(self, parttype=None, params=(), values=()):
396 403 self.parttype = parttype
397 404 self.params = params
398 405 self.values = values
399 406 if self.parttype is None:
400 407 msg = b'Stream Parameter'
401 408 else:
402 409 msg = parttype
403 410 entries = self.params
404 411 if self.params and self.values:
405 412 assert len(self.params) == len(self.values)
406 413 entries = []
407 414 for idx, par in enumerate(self.params):
408 415 val = self.values[idx]
409 416 if val is None:
410 417 entries.append(val)
411 418 else:
412 419 entries.append(b"%s=%r" % (par, pycompat.maybebytestr(val)))
413 420 if entries:
414 421 msg = b'%s - %s' % (msg, b', '.join(entries))
415 422 ValueError.__init__(self, msg)
416 423
417 424
418 425 class ReadOnlyPartError(RuntimeError):
419 426 """error raised when code tries to alter a part being generated"""
420 427
421 428 __bytes__ = _tobytes
422 429
423 430
424 431 class PushkeyFailed(Abort):
425 432 """error raised when a pushkey part failed to update a value"""
426 433
427 434 def __init__(
428 435 self, partid, namespace=None, key=None, new=None, old=None, ret=None
429 436 ):
430 437 self.partid = partid
431 438 self.namespace = namespace
432 439 self.key = key
433 440 self.new = new
434 441 self.old = old
435 442 self.ret = ret
436 443 # no i18n expected to be processed into a better message
437 444 Abort.__init__(
438 445 self, b'failed to update value for "%s/%s"' % (namespace, key)
439 446 )
440 447
441 448
442 449 class CensoredNodeError(StorageError):
443 450 """error raised when content verification fails on a censored node
444 451
445 452 Also contains the tombstone data substituted for the uncensored data.
446 453 """
447 454
448 455 def __init__(self, filename, node, tombstone):
449 456 from .node import short
450 457
451 458 StorageError.__init__(self, b'%s:%s' % (filename, short(node)))
452 459 self.tombstone = tombstone
453 460
454 461
455 462 class CensoredBaseError(StorageError):
456 463 """error raised when a delta is rejected because its base is censored
457 464
458 465 A delta based on a censored revision must be formed as single patch
459 466 operation which replaces the entire base with new content. This ensures
460 467 the delta may be applied by clones which have not censored the base.
461 468 """
462 469
463 470
464 471 class InvalidBundleSpecification(Exception):
465 472 """error raised when a bundle specification is invalid.
466 473
467 474 This is used for syntax errors as opposed to support errors.
468 475 """
469 476
470 477 __bytes__ = _tobytes
471 478
472 479
473 480 class UnsupportedBundleSpecification(Exception):
474 481 """error raised when a bundle specification is not supported."""
475 482
476 483 __bytes__ = _tobytes
477 484
478 485
479 486 class CorruptedState(Exception):
480 487 """error raised when a command is not able to read its state from file"""
481 488
482 489 __bytes__ = _tobytes
483 490
484 491
485 492 class PeerTransportError(Abort):
486 493 """Transport-level I/O error when communicating with a peer repo."""
487 494
488 495
489 496 class InMemoryMergeConflictsError(Exception):
490 497 """Exception raised when merge conflicts arose during an in-memory merge."""
491 498
492 499 __bytes__ = _tobytes
493 500
494 501
495 502 class WireprotoCommandError(Exception):
496 503 """Represents an error during execution of a wire protocol command.
497 504
498 505 Should only be thrown by wire protocol version 2 commands.
499 506
500 507 The error is a formatter string and an optional iterable of arguments.
501 508 """
502 509
503 510 def __init__(self, message, args=None):
504 511 self.message = message
505 512 self.messageargs = args
@@ -1,2304 +1,2306 b''
1 1 # scmutil.py - Mercurial core utility functions
2 2 #
3 3 # Copyright Matt Mackall <mpm@selenic.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 from __future__ import absolute_import
9 9
10 10 import errno
11 11 import glob
12 12 import os
13 13 import posixpath
14 14 import re
15 15 import subprocess
16 16 import weakref
17 17
18 18 from .i18n import _
19 19 from .node import (
20 20 bin,
21 21 hex,
22 22 nullid,
23 23 nullrev,
24 24 short,
25 25 wdirid,
26 26 wdirrev,
27 27 )
28 28 from .pycompat import getattr
29 29 from .thirdparty import attr
30 30 from . import (
31 31 copies as copiesmod,
32 32 encoding,
33 33 error,
34 34 match as matchmod,
35 35 obsolete,
36 36 obsutil,
37 37 pathutil,
38 38 phases,
39 39 policy,
40 40 pycompat,
41 41 requirements as requirementsmod,
42 42 revsetlang,
43 43 similar,
44 44 smartset,
45 45 url,
46 46 util,
47 47 vfs,
48 48 )
49 49
50 50 from .utils import (
51 51 hashutil,
52 52 procutil,
53 53 stringutil,
54 54 )
55 55
56 56 if pycompat.iswindows:
57 57 from . import scmwindows as scmplatform
58 58 else:
59 59 from . import scmposix as scmplatform
60 60
61 61 parsers = policy.importmod('parsers')
62 62 rustrevlog = policy.importrust('revlog')
63 63
64 64 termsize = scmplatform.termsize
65 65
66 66
67 67 @attr.s(slots=True, repr=False)
68 68 class status(object):
69 69 '''Struct with a list of files per status.
70 70
71 71 The 'deleted', 'unknown' and 'ignored' properties are only
72 72 relevant to the working copy.
73 73 '''
74 74
75 75 modified = attr.ib(default=attr.Factory(list))
76 76 added = attr.ib(default=attr.Factory(list))
77 77 removed = attr.ib(default=attr.Factory(list))
78 78 deleted = attr.ib(default=attr.Factory(list))
79 79 unknown = attr.ib(default=attr.Factory(list))
80 80 ignored = attr.ib(default=attr.Factory(list))
81 81 clean = attr.ib(default=attr.Factory(list))
82 82
83 83 def __iter__(self):
84 84 yield self.modified
85 85 yield self.added
86 86 yield self.removed
87 87 yield self.deleted
88 88 yield self.unknown
89 89 yield self.ignored
90 90 yield self.clean
91 91
92 92 def __repr__(self):
93 93 return (
94 94 r'<status modified=%s, added=%s, removed=%s, deleted=%s, '
95 95 r'unknown=%s, ignored=%s, clean=%s>'
96 96 ) % tuple(pycompat.sysstr(stringutil.pprint(v)) for v in self)
97 97
98 98
99 99 def itersubrepos(ctx1, ctx2):
100 100 """find subrepos in ctx1 or ctx2"""
101 101 # Create a (subpath, ctx) mapping where we prefer subpaths from
102 102 # ctx1. The subpaths from ctx2 are important when the .hgsub file
103 103 # has been modified (in ctx2) but not yet committed (in ctx1).
104 104 subpaths = dict.fromkeys(ctx2.substate, ctx2)
105 105 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
106 106
107 107 missing = set()
108 108
109 109 for subpath in ctx2.substate:
110 110 if subpath not in ctx1.substate:
111 111 del subpaths[subpath]
112 112 missing.add(subpath)
113 113
114 114 for subpath, ctx in sorted(pycompat.iteritems(subpaths)):
115 115 yield subpath, ctx.sub(subpath)
116 116
117 117 # Yield an empty subrepo based on ctx1 for anything only in ctx2. That way,
118 118 # status and diff will have an accurate result when it does
119 119 # 'sub.{status|diff}(rev2)'. Otherwise, the ctx2 subrepo is compared
120 120 # against itself.
121 121 for subpath in missing:
122 122 yield subpath, ctx2.nullsub(subpath, ctx1)
123 123
124 124
125 125 def nochangesfound(ui, repo, excluded=None):
126 126 '''Report no changes for push/pull, excluded is None or a list of
127 127 nodes excluded from the push/pull.
128 128 '''
129 129 secretlist = []
130 130 if excluded:
131 131 for n in excluded:
132 132 ctx = repo[n]
133 133 if ctx.phase() >= phases.secret and not ctx.extinct():
134 134 secretlist.append(n)
135 135
136 136 if secretlist:
137 137 ui.status(
138 138 _(b"no changes found (ignored %d secret changesets)\n")
139 139 % len(secretlist)
140 140 )
141 141 else:
142 142 ui.status(_(b"no changes found\n"))
143 143
144 144
145 145 def callcatch(ui, func):
146 146 """call func() with global exception handling
147 147
148 148 return func() if no exception happens. otherwise do some error handling
149 149 and return an exit code accordingly. does not handle all exceptions.
150 150 """
151 151 coarse_exit_code = -1
152 152 detailed_exit_code = -1
153 153 try:
154 154 try:
155 155 return func()
156 156 except: # re-raises
157 157 ui.traceback()
158 158 raise
159 159 # Global exception handling, alphabetically
160 160 # Mercurial-specific first, followed by built-in and library exceptions
161 161 except error.LockHeld as inst:
162 162 detailed_exit_code = 20
163 163 if inst.errno == errno.ETIMEDOUT:
164 164 reason = _(b'timed out waiting for lock held by %r') % (
165 165 pycompat.bytestr(inst.locker)
166 166 )
167 167 else:
168 168 reason = _(b'lock held by %r') % inst.locker
169 169 ui.error(
170 170 _(b"abort: %s: %s\n")
171 171 % (inst.desc or stringutil.forcebytestr(inst.filename), reason)
172 172 )
173 173 if not inst.locker:
174 174 ui.error(_(b"(lock might be very busy)\n"))
175 175 except error.LockUnavailable as inst:
176 176 detailed_exit_code = 20
177 177 ui.error(
178 178 _(b"abort: could not lock %s: %s\n")
179 179 % (
180 180 inst.desc or stringutil.forcebytestr(inst.filename),
181 181 encoding.strtolocal(inst.strerror),
182 182 )
183 183 )
184 184 except error.OutOfBandError as inst:
185 185 detailed_exit_code = 100
186 186 if inst.args:
187 187 msg = _(b"abort: remote error:\n")
188 188 else:
189 189 msg = _(b"abort: remote error\n")
190 190 ui.error(msg)
191 191 if inst.args:
192 192 ui.error(b''.join(inst.args))
193 193 if inst.hint:
194 194 ui.error(b'(%s)\n' % inst.hint)
195 195 except error.RepoError as inst:
196 196 ui.error(_(b"abort: %s!\n") % inst)
197 197 if inst.hint:
198 198 ui.error(_(b"(%s)\n") % inst.hint)
199 199 except error.ResponseError as inst:
200 200 ui.error(_(b"abort: %s") % inst.args[0])
201 201 msg = inst.args[1]
202 202 if isinstance(msg, type(u'')):
203 203 msg = pycompat.sysbytes(msg)
204 204 if not isinstance(msg, bytes):
205 205 ui.error(b" %r\n" % (msg,))
206 206 elif not msg:
207 207 ui.error(_(b" empty string\n"))
208 208 else:
209 209 ui.error(b"\n%r\n" % pycompat.bytestr(stringutil.ellipsis(msg)))
210 210 except error.CensoredNodeError as inst:
211 211 ui.error(_(b"abort: file censored %s!\n") % inst)
212 212 except error.StorageError as inst:
213 213 ui.error(_(b"abort: %s!\n") % inst)
214 214 if inst.hint:
215 215 ui.error(_(b"(%s)\n") % inst.hint)
216 216 except error.InterventionRequired as inst:
217 217 ui.error(b"%s\n" % inst)
218 218 if inst.hint:
219 219 ui.error(_(b"(%s)\n") % inst.hint)
220 220 detailed_exit_code = 240
221 221 coarse_exit_code = 1
222 222 except error.WdirUnsupported:
223 223 ui.error(_(b"abort: working directory revision cannot be specified\n"))
224 224 except error.Abort as inst:
225 225 if isinstance(inst, error.InputError):
226 226 detailed_exit_code = 10
227 elif isinstance(inst, error.StateError):
228 detailed_exit_code = 20
227 229 ui.error(_(b"abort: %s\n") % inst.message)
228 230 if inst.hint:
229 231 ui.error(_(b"(%s)\n") % inst.hint)
230 232 except error.WorkerError as inst:
231 233 # Don't print a message -- the worker already should have
232 234 return inst.status_code
233 235 except ImportError as inst:
234 236 ui.error(_(b"abort: %s!\n") % stringutil.forcebytestr(inst))
235 237 m = stringutil.forcebytestr(inst).split()[-1]
236 238 if m in b"mpatch bdiff".split():
237 239 ui.error(_(b"(did you forget to compile extensions?)\n"))
238 240 elif m in b"zlib".split():
239 241 ui.error(_(b"(is your Python install correct?)\n"))
240 242 except util.urlerr.httperror as inst:
241 243 detailed_exit_code = 100
242 244 ui.error(_(b"abort: %s\n") % stringutil.forcebytestr(inst))
243 245 except util.urlerr.urlerror as inst:
244 246 detailed_exit_code = 100
245 247 try: # usually it is in the form (errno, strerror)
246 248 reason = inst.reason.args[1]
247 249 except (AttributeError, IndexError):
248 250 # it might be anything, for example a string
249 251 reason = inst.reason
250 252 if isinstance(reason, pycompat.unicode):
251 253 # SSLError of Python 2.7.9 contains a unicode
252 254 reason = encoding.unitolocal(reason)
253 255 ui.error(_(b"abort: error: %s\n") % stringutil.forcebytestr(reason))
254 256 except (IOError, OSError) as inst:
255 257 if (
256 258 util.safehasattr(inst, b"args")
257 259 and inst.args
258 260 and inst.args[0] == errno.EPIPE
259 261 ):
260 262 pass
261 263 elif getattr(inst, "strerror", None): # common IOError or OSError
262 264 if getattr(inst, "filename", None) is not None:
263 265 ui.error(
264 266 _(b"abort: %s: '%s'\n")
265 267 % (
266 268 encoding.strtolocal(inst.strerror),
267 269 stringutil.forcebytestr(inst.filename),
268 270 )
269 271 )
270 272 else:
271 273 ui.error(_(b"abort: %s\n") % encoding.strtolocal(inst.strerror))
272 274 else: # suspicious IOError
273 275 raise
274 276 except MemoryError:
275 277 ui.error(_(b"abort: out of memory\n"))
276 278 except SystemExit as inst:
277 279 # Commands shouldn't sys.exit directly, but give a return code.
278 280 # Just in case catch this and and pass exit code to caller.
279 281 detailed_exit_code = 254
280 282 coarse_exit_code = inst.code
281 283
282 284 if ui.configbool(b'ui', b'detailed-exit-code'):
283 285 return detailed_exit_code
284 286 else:
285 287 return coarse_exit_code
286 288
287 289
288 290 def checknewlabel(repo, lbl, kind):
289 291 # Do not use the "kind" parameter in ui output.
290 292 # It makes strings difficult to translate.
291 293 if lbl in [b'tip', b'.', b'null']:
292 294 raise error.Abort(_(b"the name '%s' is reserved") % lbl)
293 295 for c in (b':', b'\0', b'\n', b'\r'):
294 296 if c in lbl:
295 297 raise error.Abort(
296 298 _(b"%r cannot be used in a name") % pycompat.bytestr(c)
297 299 )
298 300 try:
299 301 int(lbl)
300 302 raise error.Abort(_(b"cannot use an integer as a name"))
301 303 except ValueError:
302 304 pass
303 305 if lbl.strip() != lbl:
304 306 raise error.Abort(_(b"leading or trailing whitespace in name %r") % lbl)
305 307
306 308
307 309 def checkfilename(f):
308 310 '''Check that the filename f is an acceptable filename for a tracked file'''
309 311 if b'\r' in f or b'\n' in f:
310 312 raise error.Abort(
311 313 _(b"'\\n' and '\\r' disallowed in filenames: %r")
312 314 % pycompat.bytestr(f)
313 315 )
314 316
315 317
316 318 def checkportable(ui, f):
317 319 '''Check if filename f is portable and warn or abort depending on config'''
318 320 checkfilename(f)
319 321 abort, warn = checkportabilityalert(ui)
320 322 if abort or warn:
321 323 msg = util.checkwinfilename(f)
322 324 if msg:
323 325 msg = b"%s: %s" % (msg, procutil.shellquote(f))
324 326 if abort:
325 327 raise error.Abort(msg)
326 328 ui.warn(_(b"warning: %s\n") % msg)
327 329
328 330
329 331 def checkportabilityalert(ui):
330 332 '''check if the user's config requests nothing, a warning, or abort for
331 333 non-portable filenames'''
332 334 val = ui.config(b'ui', b'portablefilenames')
333 335 lval = val.lower()
334 336 bval = stringutil.parsebool(val)
335 337 abort = pycompat.iswindows or lval == b'abort'
336 338 warn = bval or lval == b'warn'
337 339 if bval is None and not (warn or abort or lval == b'ignore'):
338 340 raise error.ConfigError(
339 341 _(b"ui.portablefilenames value is invalid ('%s')") % val
340 342 )
341 343 return abort, warn
342 344
343 345
344 346 class casecollisionauditor(object):
345 347 def __init__(self, ui, abort, dirstate):
346 348 self._ui = ui
347 349 self._abort = abort
348 350 allfiles = b'\0'.join(dirstate)
349 351 self._loweredfiles = set(encoding.lower(allfiles).split(b'\0'))
350 352 self._dirstate = dirstate
351 353 # The purpose of _newfiles is so that we don't complain about
352 354 # case collisions if someone were to call this object with the
353 355 # same filename twice.
354 356 self._newfiles = set()
355 357
356 358 def __call__(self, f):
357 359 if f in self._newfiles:
358 360 return
359 361 fl = encoding.lower(f)
360 362 if fl in self._loweredfiles and f not in self._dirstate:
361 363 msg = _(b'possible case-folding collision for %s') % f
362 364 if self._abort:
363 365 raise error.Abort(msg)
364 366 self._ui.warn(_(b"warning: %s\n") % msg)
365 367 self._loweredfiles.add(fl)
366 368 self._newfiles.add(f)
367 369
368 370
369 371 def filteredhash(repo, maxrev):
370 372 """build hash of filtered revisions in the current repoview.
371 373
372 374 Multiple caches perform up-to-date validation by checking that the
373 375 tiprev and tipnode stored in the cache file match the current repository.
374 376 However, this is not sufficient for validating repoviews because the set
375 377 of revisions in the view may change without the repository tiprev and
376 378 tipnode changing.
377 379
378 380 This function hashes all the revs filtered from the view and returns
379 381 that SHA-1 digest.
380 382 """
381 383 cl = repo.changelog
382 384 if not cl.filteredrevs:
383 385 return None
384 386 key = cl._filteredrevs_hashcache.get(maxrev)
385 387 if not key:
386 388 revs = sorted(r for r in cl.filteredrevs if r <= maxrev)
387 389 if revs:
388 390 s = hashutil.sha1()
389 391 for rev in revs:
390 392 s.update(b'%d;' % rev)
391 393 key = s.digest()
392 394 cl._filteredrevs_hashcache[maxrev] = key
393 395 return key
394 396
395 397
396 398 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
397 399 '''yield every hg repository under path, always recursively.
398 400 The recurse flag will only control recursion into repo working dirs'''
399 401
400 402 def errhandler(err):
401 403 if err.filename == path:
402 404 raise err
403 405
404 406 samestat = getattr(os.path, 'samestat', None)
405 407 if followsym and samestat is not None:
406 408
407 409 def adddir(dirlst, dirname):
408 410 dirstat = os.stat(dirname)
409 411 match = any(samestat(dirstat, lstdirstat) for lstdirstat in dirlst)
410 412 if not match:
411 413 dirlst.append(dirstat)
412 414 return not match
413 415
414 416 else:
415 417 followsym = False
416 418
417 419 if (seen_dirs is None) and followsym:
418 420 seen_dirs = []
419 421 adddir(seen_dirs, path)
420 422 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
421 423 dirs.sort()
422 424 if b'.hg' in dirs:
423 425 yield root # found a repository
424 426 qroot = os.path.join(root, b'.hg', b'patches')
425 427 if os.path.isdir(os.path.join(qroot, b'.hg')):
426 428 yield qroot # we have a patch queue repo here
427 429 if recurse:
428 430 # avoid recursing inside the .hg directory
429 431 dirs.remove(b'.hg')
430 432 else:
431 433 dirs[:] = [] # don't descend further
432 434 elif followsym:
433 435 newdirs = []
434 436 for d in dirs:
435 437 fname = os.path.join(root, d)
436 438 if adddir(seen_dirs, fname):
437 439 if os.path.islink(fname):
438 440 for hgname in walkrepos(fname, True, seen_dirs):
439 441 yield hgname
440 442 else:
441 443 newdirs.append(d)
442 444 dirs[:] = newdirs
443 445
444 446
445 447 def binnode(ctx):
446 448 """Return binary node id for a given basectx"""
447 449 node = ctx.node()
448 450 if node is None:
449 451 return wdirid
450 452 return node
451 453
452 454
453 455 def intrev(ctx):
454 456 """Return integer for a given basectx that can be used in comparison or
455 457 arithmetic operation"""
456 458 rev = ctx.rev()
457 459 if rev is None:
458 460 return wdirrev
459 461 return rev
460 462
461 463
462 464 def formatchangeid(ctx):
463 465 """Format changectx as '{rev}:{node|formatnode}', which is the default
464 466 template provided by logcmdutil.changesettemplater"""
465 467 repo = ctx.repo()
466 468 return formatrevnode(repo.ui, intrev(ctx), binnode(ctx))
467 469
468 470
469 471 def formatrevnode(ui, rev, node):
470 472 """Format given revision and node depending on the current verbosity"""
471 473 if ui.debugflag:
472 474 hexfunc = hex
473 475 else:
474 476 hexfunc = short
475 477 return b'%d:%s' % (rev, hexfunc(node))
476 478
477 479
478 480 def resolvehexnodeidprefix(repo, prefix):
479 481 if prefix.startswith(b'x'):
480 482 prefix = prefix[1:]
481 483 try:
482 484 # Uses unfiltered repo because it's faster when prefix is ambiguous/
483 485 # This matches the shortesthexnodeidprefix() function below.
484 486 node = repo.unfiltered().changelog._partialmatch(prefix)
485 487 except error.AmbiguousPrefixLookupError:
486 488 revset = repo.ui.config(
487 489 b'experimental', b'revisions.disambiguatewithin'
488 490 )
489 491 if revset:
490 492 # Clear config to avoid infinite recursion
491 493 configoverrides = {
492 494 (b'experimental', b'revisions.disambiguatewithin'): None
493 495 }
494 496 with repo.ui.configoverride(configoverrides):
495 497 revs = repo.anyrevs([revset], user=True)
496 498 matches = []
497 499 for rev in revs:
498 500 node = repo.changelog.node(rev)
499 501 if hex(node).startswith(prefix):
500 502 matches.append(node)
501 503 if len(matches) == 1:
502 504 return matches[0]
503 505 raise
504 506 if node is None:
505 507 return
506 508 repo.changelog.rev(node) # make sure node isn't filtered
507 509 return node
508 510
509 511
510 512 def mayberevnum(repo, prefix):
511 513 """Checks if the given prefix may be mistaken for a revision number"""
512 514 try:
513 515 i = int(prefix)
514 516 # if we are a pure int, then starting with zero will not be
515 517 # confused as a rev; or, obviously, if the int is larger
516 518 # than the value of the tip rev. We still need to disambiguate if
517 519 # prefix == '0', since that *is* a valid revnum.
518 520 if (prefix != b'0' and prefix[0:1] == b'0') or i >= len(repo):
519 521 return False
520 522 return True
521 523 except ValueError:
522 524 return False
523 525
524 526
525 527 def shortesthexnodeidprefix(repo, node, minlength=1, cache=None):
526 528 """Find the shortest unambiguous prefix that matches hexnode.
527 529
528 530 If "cache" is not None, it must be a dictionary that can be used for
529 531 caching between calls to this method.
530 532 """
531 533 # _partialmatch() of filtered changelog could take O(len(repo)) time,
532 534 # which would be unacceptably slow. so we look for hash collision in
533 535 # unfiltered space, which means some hashes may be slightly longer.
534 536
535 537 minlength = max(minlength, 1)
536 538
537 539 def disambiguate(prefix):
538 540 """Disambiguate against revnums."""
539 541 if repo.ui.configbool(b'experimental', b'revisions.prefixhexnode'):
540 542 if mayberevnum(repo, prefix):
541 543 return b'x' + prefix
542 544 else:
543 545 return prefix
544 546
545 547 hexnode = hex(node)
546 548 for length in range(len(prefix), len(hexnode) + 1):
547 549 prefix = hexnode[:length]
548 550 if not mayberevnum(repo, prefix):
549 551 return prefix
550 552
551 553 cl = repo.unfiltered().changelog
552 554 revset = repo.ui.config(b'experimental', b'revisions.disambiguatewithin')
553 555 if revset:
554 556 revs = None
555 557 if cache is not None:
556 558 revs = cache.get(b'disambiguationrevset')
557 559 if revs is None:
558 560 revs = repo.anyrevs([revset], user=True)
559 561 if cache is not None:
560 562 cache[b'disambiguationrevset'] = revs
561 563 if cl.rev(node) in revs:
562 564 hexnode = hex(node)
563 565 nodetree = None
564 566 if cache is not None:
565 567 nodetree = cache.get(b'disambiguationnodetree')
566 568 if not nodetree:
567 569 if util.safehasattr(parsers, 'nodetree'):
568 570 # The CExt is the only implementation to provide a nodetree
569 571 # class so far.
570 572 index = cl.index
571 573 if util.safehasattr(index, 'get_cindex'):
572 574 # the rust wrapped need to give access to its internal index
573 575 index = index.get_cindex()
574 576 nodetree = parsers.nodetree(index, len(revs))
575 577 for r in revs:
576 578 nodetree.insert(r)
577 579 if cache is not None:
578 580 cache[b'disambiguationnodetree'] = nodetree
579 581 if nodetree is not None:
580 582 length = max(nodetree.shortest(node), minlength)
581 583 prefix = hexnode[:length]
582 584 return disambiguate(prefix)
583 585 for length in range(minlength, len(hexnode) + 1):
584 586 matches = []
585 587 prefix = hexnode[:length]
586 588 for rev in revs:
587 589 otherhexnode = repo[rev].hex()
588 590 if prefix == otherhexnode[:length]:
589 591 matches.append(otherhexnode)
590 592 if len(matches) == 1:
591 593 return disambiguate(prefix)
592 594
593 595 try:
594 596 return disambiguate(cl.shortest(node, minlength))
595 597 except error.LookupError:
596 598 raise error.RepoLookupError()
597 599
598 600
599 601 def isrevsymbol(repo, symbol):
600 602 """Checks if a symbol exists in the repo.
601 603
602 604 See revsymbol() for details. Raises error.AmbiguousPrefixLookupError if the
603 605 symbol is an ambiguous nodeid prefix.
604 606 """
605 607 try:
606 608 revsymbol(repo, symbol)
607 609 return True
608 610 except error.RepoLookupError:
609 611 return False
610 612
611 613
612 614 def revsymbol(repo, symbol):
613 615 """Returns a context given a single revision symbol (as string).
614 616
615 617 This is similar to revsingle(), but accepts only a single revision symbol,
616 618 i.e. things like ".", "tip", "1234", "deadbeef", "my-bookmark" work, but
617 619 not "max(public())".
618 620 """
619 621 if not isinstance(symbol, bytes):
620 622 msg = (
621 623 b"symbol (%s of type %s) was not a string, did you mean "
622 624 b"repo[symbol]?" % (symbol, type(symbol))
623 625 )
624 626 raise error.ProgrammingError(msg)
625 627 try:
626 628 if symbol in (b'.', b'tip', b'null'):
627 629 return repo[symbol]
628 630
629 631 try:
630 632 r = int(symbol)
631 633 if b'%d' % r != symbol:
632 634 raise ValueError
633 635 l = len(repo.changelog)
634 636 if r < 0:
635 637 r += l
636 638 if r < 0 or r >= l and r != wdirrev:
637 639 raise ValueError
638 640 return repo[r]
639 641 except error.FilteredIndexError:
640 642 raise
641 643 except (ValueError, OverflowError, IndexError):
642 644 pass
643 645
644 646 if len(symbol) == 40:
645 647 try:
646 648 node = bin(symbol)
647 649 rev = repo.changelog.rev(node)
648 650 return repo[rev]
649 651 except error.FilteredLookupError:
650 652 raise
651 653 except (TypeError, LookupError):
652 654 pass
653 655
654 656 # look up bookmarks through the name interface
655 657 try:
656 658 node = repo.names.singlenode(repo, symbol)
657 659 rev = repo.changelog.rev(node)
658 660 return repo[rev]
659 661 except KeyError:
660 662 pass
661 663
662 664 node = resolvehexnodeidprefix(repo, symbol)
663 665 if node is not None:
664 666 rev = repo.changelog.rev(node)
665 667 return repo[rev]
666 668
667 669 raise error.RepoLookupError(_(b"unknown revision '%s'") % symbol)
668 670
669 671 except error.WdirUnsupported:
670 672 return repo[None]
671 673 except (
672 674 error.FilteredIndexError,
673 675 error.FilteredLookupError,
674 676 error.FilteredRepoLookupError,
675 677 ):
676 678 raise _filterederror(repo, symbol)
677 679
678 680
679 681 def _filterederror(repo, changeid):
680 682 """build an exception to be raised about a filtered changeid
681 683
682 684 This is extracted in a function to help extensions (eg: evolve) to
683 685 experiment with various message variants."""
684 686 if repo.filtername.startswith(b'visible'):
685 687
686 688 # Check if the changeset is obsolete
687 689 unfilteredrepo = repo.unfiltered()
688 690 ctx = revsymbol(unfilteredrepo, changeid)
689 691
690 692 # If the changeset is obsolete, enrich the message with the reason
691 693 # that made this changeset not visible
692 694 if ctx.obsolete():
693 695 msg = obsutil._getfilteredreason(repo, changeid, ctx)
694 696 else:
695 697 msg = _(b"hidden revision '%s'") % changeid
696 698
697 699 hint = _(b'use --hidden to access hidden revisions')
698 700
699 701 return error.FilteredRepoLookupError(msg, hint=hint)
700 702 msg = _(b"filtered revision '%s' (not in '%s' subset)")
701 703 msg %= (changeid, repo.filtername)
702 704 return error.FilteredRepoLookupError(msg)
703 705
704 706
705 707 def revsingle(repo, revspec, default=b'.', localalias=None):
706 708 if not revspec and revspec != 0:
707 709 return repo[default]
708 710
709 711 l = revrange(repo, [revspec], localalias=localalias)
710 712 if not l:
711 713 raise error.Abort(_(b'empty revision set'))
712 714 return repo[l.last()]
713 715
714 716
715 717 def _pairspec(revspec):
716 718 tree = revsetlang.parse(revspec)
717 719 return tree and tree[0] in (
718 720 b'range',
719 721 b'rangepre',
720 722 b'rangepost',
721 723 b'rangeall',
722 724 )
723 725
724 726
725 727 def revpair(repo, revs):
726 728 if not revs:
727 729 return repo[b'.'], repo[None]
728 730
729 731 l = revrange(repo, revs)
730 732
731 733 if not l:
732 734 raise error.Abort(_(b'empty revision range'))
733 735
734 736 first = l.first()
735 737 second = l.last()
736 738
737 739 if (
738 740 first == second
739 741 and len(revs) >= 2
740 742 and not all(revrange(repo, [r]) for r in revs)
741 743 ):
742 744 raise error.Abort(_(b'empty revision on one side of range'))
743 745
744 746 # if top-level is range expression, the result must always be a pair
745 747 if first == second and len(revs) == 1 and not _pairspec(revs[0]):
746 748 return repo[first], repo[None]
747 749
748 750 return repo[first], repo[second]
749 751
750 752
751 753 def revrange(repo, specs, localalias=None):
752 754 """Execute 1 to many revsets and return the union.
753 755
754 756 This is the preferred mechanism for executing revsets using user-specified
755 757 config options, such as revset aliases.
756 758
757 759 The revsets specified by ``specs`` will be executed via a chained ``OR``
758 760 expression. If ``specs`` is empty, an empty result is returned.
759 761
760 762 ``specs`` can contain integers, in which case they are assumed to be
761 763 revision numbers.
762 764
763 765 It is assumed the revsets are already formatted. If you have arguments
764 766 that need to be expanded in the revset, call ``revsetlang.formatspec()``
765 767 and pass the result as an element of ``specs``.
766 768
767 769 Specifying a single revset is allowed.
768 770
769 771 Returns a ``smartset.abstractsmartset`` which is a list-like interface over
770 772 integer revisions.
771 773 """
772 774 allspecs = []
773 775 for spec in specs:
774 776 if isinstance(spec, int):
775 777 spec = revsetlang.formatspec(b'%d', spec)
776 778 allspecs.append(spec)
777 779 return repo.anyrevs(allspecs, user=True, localalias=localalias)
778 780
779 781
780 782 def increasingwindows(windowsize=8, sizelimit=512):
781 783 while True:
782 784 yield windowsize
783 785 if windowsize < sizelimit:
784 786 windowsize *= 2
785 787
786 788
787 789 def walkchangerevs(repo, revs, makefilematcher, prepare):
788 790 '''Iterate over files and the revs in a "windowed" way.
789 791
790 792 Callers most commonly need to iterate backwards over the history
791 793 in which they are interested. Doing so has awful (quadratic-looking)
792 794 performance, so we use iterators in a "windowed" way.
793 795
794 796 We walk a window of revisions in the desired order. Within the
795 797 window, we first walk forwards to gather data, then in the desired
796 798 order (usually backwards) to display it.
797 799
798 800 This function returns an iterator yielding contexts. Before
799 801 yielding each context, the iterator will first call the prepare
800 802 function on each context in the window in forward order.'''
801 803
802 804 if not revs:
803 805 return []
804 806 change = repo.__getitem__
805 807
806 808 def iterate():
807 809 it = iter(revs)
808 810 stopiteration = False
809 811 for windowsize in increasingwindows():
810 812 nrevs = []
811 813 for i in pycompat.xrange(windowsize):
812 814 rev = next(it, None)
813 815 if rev is None:
814 816 stopiteration = True
815 817 break
816 818 nrevs.append(rev)
817 819 for rev in sorted(nrevs):
818 820 ctx = change(rev)
819 821 prepare(ctx, makefilematcher(ctx))
820 822 for rev in nrevs:
821 823 yield change(rev)
822 824
823 825 if stopiteration:
824 826 break
825 827
826 828 return iterate()
827 829
828 830
829 831 def meaningfulparents(repo, ctx):
830 832 """Return list of meaningful (or all if debug) parentrevs for rev.
831 833
832 834 For merges (two non-nullrev revisions) both parents are meaningful.
833 835 Otherwise the first parent revision is considered meaningful if it
834 836 is not the preceding revision.
835 837 """
836 838 parents = ctx.parents()
837 839 if len(parents) > 1:
838 840 return parents
839 841 if repo.ui.debugflag:
840 842 return [parents[0], repo[nullrev]]
841 843 if parents[0].rev() >= intrev(ctx) - 1:
842 844 return []
843 845 return parents
844 846
845 847
846 848 def getuipathfn(repo, legacyrelativevalue=False, forcerelativevalue=None):
847 849 """Return a function that produced paths for presenting to the user.
848 850
849 851 The returned function takes a repo-relative path and produces a path
850 852 that can be presented in the UI.
851 853
852 854 Depending on the value of ui.relative-paths, either a repo-relative or
853 855 cwd-relative path will be produced.
854 856
855 857 legacyrelativevalue is the value to use if ui.relative-paths=legacy
856 858
857 859 If forcerelativevalue is not None, then that value will be used regardless
858 860 of what ui.relative-paths is set to.
859 861 """
860 862 if forcerelativevalue is not None:
861 863 relative = forcerelativevalue
862 864 else:
863 865 config = repo.ui.config(b'ui', b'relative-paths')
864 866 if config == b'legacy':
865 867 relative = legacyrelativevalue
866 868 else:
867 869 relative = stringutil.parsebool(config)
868 870 if relative is None:
869 871 raise error.ConfigError(
870 872 _(b"ui.relative-paths is not a boolean ('%s')") % config
871 873 )
872 874
873 875 if relative:
874 876 cwd = repo.getcwd()
875 877 if cwd != b'':
876 878 # this branch would work even if cwd == b'' (ie cwd = repo
877 879 # root), but its generality makes the returned function slower
878 880 pathto = repo.pathto
879 881 return lambda f: pathto(f, cwd)
880 882 if repo.ui.configbool(b'ui', b'slash'):
881 883 return lambda f: f
882 884 else:
883 885 return util.localpath
884 886
885 887
886 888 def subdiruipathfn(subpath, uipathfn):
887 889 '''Create a new uipathfn that treats the file as relative to subpath.'''
888 890 return lambda f: uipathfn(posixpath.join(subpath, f))
889 891
890 892
891 893 def anypats(pats, opts):
892 894 '''Checks if any patterns, including --include and --exclude were given.
893 895
894 896 Some commands (e.g. addremove) use this condition for deciding whether to
895 897 print absolute or relative paths.
896 898 '''
897 899 return bool(pats or opts.get(b'include') or opts.get(b'exclude'))
898 900
899 901
900 902 def expandpats(pats):
901 903 '''Expand bare globs when running on windows.
902 904 On posix we assume it already has already been done by sh.'''
903 905 if not util.expandglobs:
904 906 return list(pats)
905 907 ret = []
906 908 for kindpat in pats:
907 909 kind, pat = matchmod._patsplit(kindpat, None)
908 910 if kind is None:
909 911 try:
910 912 globbed = glob.glob(pat)
911 913 except re.error:
912 914 globbed = [pat]
913 915 if globbed:
914 916 ret.extend(globbed)
915 917 continue
916 918 ret.append(kindpat)
917 919 return ret
918 920
919 921
920 922 def matchandpats(
921 923 ctx, pats=(), opts=None, globbed=False, default=b'relpath', badfn=None
922 924 ):
923 925 '''Return a matcher and the patterns that were used.
924 926 The matcher will warn about bad matches, unless an alternate badfn callback
925 927 is provided.'''
926 928 if opts is None:
927 929 opts = {}
928 930 if not globbed and default == b'relpath':
929 931 pats = expandpats(pats or [])
930 932
931 933 uipathfn = getuipathfn(ctx.repo(), legacyrelativevalue=True)
932 934
933 935 def bad(f, msg):
934 936 ctx.repo().ui.warn(b"%s: %s\n" % (uipathfn(f), msg))
935 937
936 938 if badfn is None:
937 939 badfn = bad
938 940
939 941 m = ctx.match(
940 942 pats,
941 943 opts.get(b'include'),
942 944 opts.get(b'exclude'),
943 945 default,
944 946 listsubrepos=opts.get(b'subrepos'),
945 947 badfn=badfn,
946 948 )
947 949
948 950 if m.always():
949 951 pats = []
950 952 return m, pats
951 953
952 954
953 955 def match(
954 956 ctx, pats=(), opts=None, globbed=False, default=b'relpath', badfn=None
955 957 ):
956 958 '''Return a matcher that will warn about bad matches.'''
957 959 return matchandpats(ctx, pats, opts, globbed, default, badfn=badfn)[0]
958 960
959 961
960 962 def matchall(repo):
961 963 '''Return a matcher that will efficiently match everything.'''
962 964 return matchmod.always()
963 965
964 966
965 967 def matchfiles(repo, files, badfn=None):
966 968 '''Return a matcher that will efficiently match exactly these files.'''
967 969 return matchmod.exact(files, badfn=badfn)
968 970
969 971
970 972 def parsefollowlinespattern(repo, rev, pat, msg):
971 973 """Return a file name from `pat` pattern suitable for usage in followlines
972 974 logic.
973 975 """
974 976 if not matchmod.patkind(pat):
975 977 return pathutil.canonpath(repo.root, repo.getcwd(), pat)
976 978 else:
977 979 ctx = repo[rev]
978 980 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=ctx)
979 981 files = [f for f in ctx if m(f)]
980 982 if len(files) != 1:
981 983 raise error.ParseError(msg)
982 984 return files[0]
983 985
984 986
985 987 def getorigvfs(ui, repo):
986 988 """return a vfs suitable to save 'orig' file
987 989
988 990 return None if no special directory is configured"""
989 991 origbackuppath = ui.config(b'ui', b'origbackuppath')
990 992 if not origbackuppath:
991 993 return None
992 994 return vfs.vfs(repo.wvfs.join(origbackuppath))
993 995
994 996
995 997 def backuppath(ui, repo, filepath):
996 998 '''customize where working copy backup files (.orig files) are created
997 999
998 1000 Fetch user defined path from config file: [ui] origbackuppath = <path>
999 1001 Fall back to default (filepath with .orig suffix) if not specified
1000 1002
1001 1003 filepath is repo-relative
1002 1004
1003 1005 Returns an absolute path
1004 1006 '''
1005 1007 origvfs = getorigvfs(ui, repo)
1006 1008 if origvfs is None:
1007 1009 return repo.wjoin(filepath + b".orig")
1008 1010
1009 1011 origbackupdir = origvfs.dirname(filepath)
1010 1012 if not origvfs.isdir(origbackupdir) or origvfs.islink(origbackupdir):
1011 1013 ui.note(_(b'creating directory: %s\n') % origvfs.join(origbackupdir))
1012 1014
1013 1015 # Remove any files that conflict with the backup file's path
1014 1016 for f in reversed(list(pathutil.finddirs(filepath))):
1015 1017 if origvfs.isfileorlink(f):
1016 1018 ui.note(_(b'removing conflicting file: %s\n') % origvfs.join(f))
1017 1019 origvfs.unlink(f)
1018 1020 break
1019 1021
1020 1022 origvfs.makedirs(origbackupdir)
1021 1023
1022 1024 if origvfs.isdir(filepath) and not origvfs.islink(filepath):
1023 1025 ui.note(
1024 1026 _(b'removing conflicting directory: %s\n') % origvfs.join(filepath)
1025 1027 )
1026 1028 origvfs.rmtree(filepath, forcibly=True)
1027 1029
1028 1030 return origvfs.join(filepath)
1029 1031
1030 1032
1031 1033 class _containsnode(object):
1032 1034 """proxy __contains__(node) to container.__contains__ which accepts revs"""
1033 1035
1034 1036 def __init__(self, repo, revcontainer):
1035 1037 self._torev = repo.changelog.rev
1036 1038 self._revcontains = revcontainer.__contains__
1037 1039
1038 1040 def __contains__(self, node):
1039 1041 return self._revcontains(self._torev(node))
1040 1042
1041 1043
1042 1044 def cleanupnodes(
1043 1045 repo,
1044 1046 replacements,
1045 1047 operation,
1046 1048 moves=None,
1047 1049 metadata=None,
1048 1050 fixphase=False,
1049 1051 targetphase=None,
1050 1052 backup=True,
1051 1053 ):
1052 1054 """do common cleanups when old nodes are replaced by new nodes
1053 1055
1054 1056 That includes writing obsmarkers or stripping nodes, and moving bookmarks.
1055 1057 (we might also want to move working directory parent in the future)
1056 1058
1057 1059 By default, bookmark moves are calculated automatically from 'replacements',
1058 1060 but 'moves' can be used to override that. Also, 'moves' may include
1059 1061 additional bookmark moves that should not have associated obsmarkers.
1060 1062
1061 1063 replacements is {oldnode: [newnode]} or a iterable of nodes if they do not
1062 1064 have replacements. operation is a string, like "rebase".
1063 1065
1064 1066 metadata is dictionary containing metadata to be stored in obsmarker if
1065 1067 obsolescence is enabled.
1066 1068 """
1067 1069 assert fixphase or targetphase is None
1068 1070 if not replacements and not moves:
1069 1071 return
1070 1072
1071 1073 # translate mapping's other forms
1072 1074 if not util.safehasattr(replacements, b'items'):
1073 1075 replacements = {(n,): () for n in replacements}
1074 1076 else:
1075 1077 # upgrading non tuple "source" to tuple ones for BC
1076 1078 repls = {}
1077 1079 for key, value in replacements.items():
1078 1080 if not isinstance(key, tuple):
1079 1081 key = (key,)
1080 1082 repls[key] = value
1081 1083 replacements = repls
1082 1084
1083 1085 # Unfiltered repo is needed since nodes in replacements might be hidden.
1084 1086 unfi = repo.unfiltered()
1085 1087
1086 1088 # Calculate bookmark movements
1087 1089 if moves is None:
1088 1090 moves = {}
1089 1091 for oldnodes, newnodes in replacements.items():
1090 1092 for oldnode in oldnodes:
1091 1093 if oldnode in moves:
1092 1094 continue
1093 1095 if len(newnodes) > 1:
1094 1096 # usually a split, take the one with biggest rev number
1095 1097 newnode = next(unfi.set(b'max(%ln)', newnodes)).node()
1096 1098 elif len(newnodes) == 0:
1097 1099 # move bookmark backwards
1098 1100 allreplaced = []
1099 1101 for rep in replacements:
1100 1102 allreplaced.extend(rep)
1101 1103 roots = list(
1102 1104 unfi.set(b'max((::%n) - %ln)', oldnode, allreplaced)
1103 1105 )
1104 1106 if roots:
1105 1107 newnode = roots[0].node()
1106 1108 else:
1107 1109 newnode = nullid
1108 1110 else:
1109 1111 newnode = newnodes[0]
1110 1112 moves[oldnode] = newnode
1111 1113
1112 1114 allnewnodes = [n for ns in replacements.values() for n in ns]
1113 1115 toretract = {}
1114 1116 toadvance = {}
1115 1117 if fixphase:
1116 1118 precursors = {}
1117 1119 for oldnodes, newnodes in replacements.items():
1118 1120 for oldnode in oldnodes:
1119 1121 for newnode in newnodes:
1120 1122 precursors.setdefault(newnode, []).append(oldnode)
1121 1123
1122 1124 allnewnodes.sort(key=lambda n: unfi[n].rev())
1123 1125 newphases = {}
1124 1126
1125 1127 def phase(ctx):
1126 1128 return newphases.get(ctx.node(), ctx.phase())
1127 1129
1128 1130 for newnode in allnewnodes:
1129 1131 ctx = unfi[newnode]
1130 1132 parentphase = max(phase(p) for p in ctx.parents())
1131 1133 if targetphase is None:
1132 1134 oldphase = max(
1133 1135 unfi[oldnode].phase() for oldnode in precursors[newnode]
1134 1136 )
1135 1137 newphase = max(oldphase, parentphase)
1136 1138 else:
1137 1139 newphase = max(targetphase, parentphase)
1138 1140 newphases[newnode] = newphase
1139 1141 if newphase > ctx.phase():
1140 1142 toretract.setdefault(newphase, []).append(newnode)
1141 1143 elif newphase < ctx.phase():
1142 1144 toadvance.setdefault(newphase, []).append(newnode)
1143 1145
1144 1146 with repo.transaction(b'cleanup') as tr:
1145 1147 # Move bookmarks
1146 1148 bmarks = repo._bookmarks
1147 1149 bmarkchanges = []
1148 1150 for oldnode, newnode in moves.items():
1149 1151 oldbmarks = repo.nodebookmarks(oldnode)
1150 1152 if not oldbmarks:
1151 1153 continue
1152 1154 from . import bookmarks # avoid import cycle
1153 1155
1154 1156 repo.ui.debug(
1155 1157 b'moving bookmarks %r from %s to %s\n'
1156 1158 % (
1157 1159 pycompat.rapply(pycompat.maybebytestr, oldbmarks),
1158 1160 hex(oldnode),
1159 1161 hex(newnode),
1160 1162 )
1161 1163 )
1162 1164 # Delete divergent bookmarks being parents of related newnodes
1163 1165 deleterevs = repo.revs(
1164 1166 b'parents(roots(%ln & (::%n))) - parents(%n)',
1165 1167 allnewnodes,
1166 1168 newnode,
1167 1169 oldnode,
1168 1170 )
1169 1171 deletenodes = _containsnode(repo, deleterevs)
1170 1172 for name in oldbmarks:
1171 1173 bmarkchanges.append((name, newnode))
1172 1174 for b in bookmarks.divergent2delete(repo, deletenodes, name):
1173 1175 bmarkchanges.append((b, None))
1174 1176
1175 1177 if bmarkchanges:
1176 1178 bmarks.applychanges(repo, tr, bmarkchanges)
1177 1179
1178 1180 for phase, nodes in toretract.items():
1179 1181 phases.retractboundary(repo, tr, phase, nodes)
1180 1182 for phase, nodes in toadvance.items():
1181 1183 phases.advanceboundary(repo, tr, phase, nodes)
1182 1184
1183 1185 mayusearchived = repo.ui.config(b'experimental', b'cleanup-as-archived')
1184 1186 # Obsolete or strip nodes
1185 1187 if obsolete.isenabled(repo, obsolete.createmarkersopt):
1186 1188 # If a node is already obsoleted, and we want to obsolete it
1187 1189 # without a successor, skip that obssolete request since it's
1188 1190 # unnecessary. That's the "if s or not isobs(n)" check below.
1189 1191 # Also sort the node in topology order, that might be useful for
1190 1192 # some obsstore logic.
1191 1193 # NOTE: the sorting might belong to createmarkers.
1192 1194 torev = unfi.changelog.rev
1193 1195 sortfunc = lambda ns: torev(ns[0][0])
1194 1196 rels = []
1195 1197 for ns, s in sorted(replacements.items(), key=sortfunc):
1196 1198 rel = (tuple(unfi[n] for n in ns), tuple(unfi[m] for m in s))
1197 1199 rels.append(rel)
1198 1200 if rels:
1199 1201 obsolete.createmarkers(
1200 1202 repo, rels, operation=operation, metadata=metadata
1201 1203 )
1202 1204 elif phases.supportinternal(repo) and mayusearchived:
1203 1205 # this assume we do not have "unstable" nodes above the cleaned ones
1204 1206 allreplaced = set()
1205 1207 for ns in replacements.keys():
1206 1208 allreplaced.update(ns)
1207 1209 if backup:
1208 1210 from . import repair # avoid import cycle
1209 1211
1210 1212 node = min(allreplaced, key=repo.changelog.rev)
1211 1213 repair.backupbundle(
1212 1214 repo, allreplaced, allreplaced, node, operation
1213 1215 )
1214 1216 phases.retractboundary(repo, tr, phases.archived, allreplaced)
1215 1217 else:
1216 1218 from . import repair # avoid import cycle
1217 1219
1218 1220 tostrip = list(n for ns in replacements for n in ns)
1219 1221 if tostrip:
1220 1222 repair.delayedstrip(
1221 1223 repo.ui, repo, tostrip, operation, backup=backup
1222 1224 )
1223 1225
1224 1226
1225 1227 def addremove(repo, matcher, prefix, uipathfn, opts=None):
1226 1228 if opts is None:
1227 1229 opts = {}
1228 1230 m = matcher
1229 1231 dry_run = opts.get(b'dry_run')
1230 1232 try:
1231 1233 similarity = float(opts.get(b'similarity') or 0)
1232 1234 except ValueError:
1233 1235 raise error.Abort(_(b'similarity must be a number'))
1234 1236 if similarity < 0 or similarity > 100:
1235 1237 raise error.Abort(_(b'similarity must be between 0 and 100'))
1236 1238 similarity /= 100.0
1237 1239
1238 1240 ret = 0
1239 1241
1240 1242 wctx = repo[None]
1241 1243 for subpath in sorted(wctx.substate):
1242 1244 submatch = matchmod.subdirmatcher(subpath, m)
1243 1245 if opts.get(b'subrepos') or m.exact(subpath) or any(submatch.files()):
1244 1246 sub = wctx.sub(subpath)
1245 1247 subprefix = repo.wvfs.reljoin(prefix, subpath)
1246 1248 subuipathfn = subdiruipathfn(subpath, uipathfn)
1247 1249 try:
1248 1250 if sub.addremove(submatch, subprefix, subuipathfn, opts):
1249 1251 ret = 1
1250 1252 except error.LookupError:
1251 1253 repo.ui.status(
1252 1254 _(b"skipping missing subrepository: %s\n")
1253 1255 % uipathfn(subpath)
1254 1256 )
1255 1257
1256 1258 rejected = []
1257 1259
1258 1260 def badfn(f, msg):
1259 1261 if f in m.files():
1260 1262 m.bad(f, msg)
1261 1263 rejected.append(f)
1262 1264
1263 1265 badmatch = matchmod.badmatch(m, badfn)
1264 1266 added, unknown, deleted, removed, forgotten = _interestingfiles(
1265 1267 repo, badmatch
1266 1268 )
1267 1269
1268 1270 unknownset = set(unknown + forgotten)
1269 1271 toprint = unknownset.copy()
1270 1272 toprint.update(deleted)
1271 1273 for abs in sorted(toprint):
1272 1274 if repo.ui.verbose or not m.exact(abs):
1273 1275 if abs in unknownset:
1274 1276 status = _(b'adding %s\n') % uipathfn(abs)
1275 1277 label = b'ui.addremove.added'
1276 1278 else:
1277 1279 status = _(b'removing %s\n') % uipathfn(abs)
1278 1280 label = b'ui.addremove.removed'
1279 1281 repo.ui.status(status, label=label)
1280 1282
1281 1283 renames = _findrenames(
1282 1284 repo, m, added + unknown, removed + deleted, similarity, uipathfn
1283 1285 )
1284 1286
1285 1287 if not dry_run:
1286 1288 _markchanges(repo, unknown + forgotten, deleted, renames)
1287 1289
1288 1290 for f in rejected:
1289 1291 if f in m.files():
1290 1292 return 1
1291 1293 return ret
1292 1294
1293 1295
1294 1296 def marktouched(repo, files, similarity=0.0):
1295 1297 '''Assert that files have somehow been operated upon. files are relative to
1296 1298 the repo root.'''
1297 1299 m = matchfiles(repo, files, badfn=lambda x, y: rejected.append(x))
1298 1300 rejected = []
1299 1301
1300 1302 added, unknown, deleted, removed, forgotten = _interestingfiles(repo, m)
1301 1303
1302 1304 if repo.ui.verbose:
1303 1305 unknownset = set(unknown + forgotten)
1304 1306 toprint = unknownset.copy()
1305 1307 toprint.update(deleted)
1306 1308 for abs in sorted(toprint):
1307 1309 if abs in unknownset:
1308 1310 status = _(b'adding %s\n') % abs
1309 1311 else:
1310 1312 status = _(b'removing %s\n') % abs
1311 1313 repo.ui.status(status)
1312 1314
1313 1315 # TODO: We should probably have the caller pass in uipathfn and apply it to
1314 1316 # the messages above too. legacyrelativevalue=True is consistent with how
1315 1317 # it used to work.
1316 1318 uipathfn = getuipathfn(repo, legacyrelativevalue=True)
1317 1319 renames = _findrenames(
1318 1320 repo, m, added + unknown, removed + deleted, similarity, uipathfn
1319 1321 )
1320 1322
1321 1323 _markchanges(repo, unknown + forgotten, deleted, renames)
1322 1324
1323 1325 for f in rejected:
1324 1326 if f in m.files():
1325 1327 return 1
1326 1328 return 0
1327 1329
1328 1330
1329 1331 def _interestingfiles(repo, matcher):
1330 1332 '''Walk dirstate with matcher, looking for files that addremove would care
1331 1333 about.
1332 1334
1333 1335 This is different from dirstate.status because it doesn't care about
1334 1336 whether files are modified or clean.'''
1335 1337 added, unknown, deleted, removed, forgotten = [], [], [], [], []
1336 1338 audit_path = pathutil.pathauditor(repo.root, cached=True)
1337 1339
1338 1340 ctx = repo[None]
1339 1341 dirstate = repo.dirstate
1340 1342 matcher = repo.narrowmatch(matcher, includeexact=True)
1341 1343 walkresults = dirstate.walk(
1342 1344 matcher,
1343 1345 subrepos=sorted(ctx.substate),
1344 1346 unknown=True,
1345 1347 ignored=False,
1346 1348 full=False,
1347 1349 )
1348 1350 for abs, st in pycompat.iteritems(walkresults):
1349 1351 dstate = dirstate[abs]
1350 1352 if dstate == b'?' and audit_path.check(abs):
1351 1353 unknown.append(abs)
1352 1354 elif dstate != b'r' and not st:
1353 1355 deleted.append(abs)
1354 1356 elif dstate == b'r' and st:
1355 1357 forgotten.append(abs)
1356 1358 # for finding renames
1357 1359 elif dstate == b'r' and not st:
1358 1360 removed.append(abs)
1359 1361 elif dstate == b'a':
1360 1362 added.append(abs)
1361 1363
1362 1364 return added, unknown, deleted, removed, forgotten
1363 1365
1364 1366
1365 1367 def _findrenames(repo, matcher, added, removed, similarity, uipathfn):
1366 1368 '''Find renames from removed files to added ones.'''
1367 1369 renames = {}
1368 1370 if similarity > 0:
1369 1371 for old, new, score in similar.findrenames(
1370 1372 repo, added, removed, similarity
1371 1373 ):
1372 1374 if (
1373 1375 repo.ui.verbose
1374 1376 or not matcher.exact(old)
1375 1377 or not matcher.exact(new)
1376 1378 ):
1377 1379 repo.ui.status(
1378 1380 _(
1379 1381 b'recording removal of %s as rename to %s '
1380 1382 b'(%d%% similar)\n'
1381 1383 )
1382 1384 % (uipathfn(old), uipathfn(new), score * 100)
1383 1385 )
1384 1386 renames[new] = old
1385 1387 return renames
1386 1388
1387 1389
1388 1390 def _markchanges(repo, unknown, deleted, renames):
1389 1391 '''Marks the files in unknown as added, the files in deleted as removed,
1390 1392 and the files in renames as copied.'''
1391 1393 wctx = repo[None]
1392 1394 with repo.wlock():
1393 1395 wctx.forget(deleted)
1394 1396 wctx.add(unknown)
1395 1397 for new, old in pycompat.iteritems(renames):
1396 1398 wctx.copy(old, new)
1397 1399
1398 1400
1399 1401 def getrenamedfn(repo, endrev=None):
1400 1402 if copiesmod.usechangesetcentricalgo(repo):
1401 1403
1402 1404 def getrenamed(fn, rev):
1403 1405 ctx = repo[rev]
1404 1406 p1copies = ctx.p1copies()
1405 1407 if fn in p1copies:
1406 1408 return p1copies[fn]
1407 1409 p2copies = ctx.p2copies()
1408 1410 if fn in p2copies:
1409 1411 return p2copies[fn]
1410 1412 return None
1411 1413
1412 1414 return getrenamed
1413 1415
1414 1416 rcache = {}
1415 1417 if endrev is None:
1416 1418 endrev = len(repo)
1417 1419
1418 1420 def getrenamed(fn, rev):
1419 1421 '''looks up all renames for a file (up to endrev) the first
1420 1422 time the file is given. It indexes on the changerev and only
1421 1423 parses the manifest if linkrev != changerev.
1422 1424 Returns rename info for fn at changerev rev.'''
1423 1425 if fn not in rcache:
1424 1426 rcache[fn] = {}
1425 1427 fl = repo.file(fn)
1426 1428 for i in fl:
1427 1429 lr = fl.linkrev(i)
1428 1430 renamed = fl.renamed(fl.node(i))
1429 1431 rcache[fn][lr] = renamed and renamed[0]
1430 1432 if lr >= endrev:
1431 1433 break
1432 1434 if rev in rcache[fn]:
1433 1435 return rcache[fn][rev]
1434 1436
1435 1437 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1436 1438 # filectx logic.
1437 1439 try:
1438 1440 return repo[rev][fn].copysource()
1439 1441 except error.LookupError:
1440 1442 return None
1441 1443
1442 1444 return getrenamed
1443 1445
1444 1446
1445 1447 def getcopiesfn(repo, endrev=None):
1446 1448 if copiesmod.usechangesetcentricalgo(repo):
1447 1449
1448 1450 def copiesfn(ctx):
1449 1451 if ctx.p2copies():
1450 1452 allcopies = ctx.p1copies().copy()
1451 1453 # There should be no overlap
1452 1454 allcopies.update(ctx.p2copies())
1453 1455 return sorted(allcopies.items())
1454 1456 else:
1455 1457 return sorted(ctx.p1copies().items())
1456 1458
1457 1459 else:
1458 1460 getrenamed = getrenamedfn(repo, endrev)
1459 1461
1460 1462 def copiesfn(ctx):
1461 1463 copies = []
1462 1464 for fn in ctx.files():
1463 1465 rename = getrenamed(fn, ctx.rev())
1464 1466 if rename:
1465 1467 copies.append((fn, rename))
1466 1468 return copies
1467 1469
1468 1470 return copiesfn
1469 1471
1470 1472
1471 1473 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
1472 1474 """Update the dirstate to reflect the intent of copying src to dst. For
1473 1475 different reasons it might not end with dst being marked as copied from src.
1474 1476 """
1475 1477 origsrc = repo.dirstate.copied(src) or src
1476 1478 if dst == origsrc: # copying back a copy?
1477 1479 if repo.dirstate[dst] not in b'mn' and not dryrun:
1478 1480 repo.dirstate.normallookup(dst)
1479 1481 else:
1480 1482 if repo.dirstate[origsrc] == b'a' and origsrc == src:
1481 1483 if not ui.quiet:
1482 1484 ui.warn(
1483 1485 _(
1484 1486 b"%s has not been committed yet, so no copy "
1485 1487 b"data will be stored for %s.\n"
1486 1488 )
1487 1489 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd))
1488 1490 )
1489 1491 if repo.dirstate[dst] in b'?r' and not dryrun:
1490 1492 wctx.add([dst])
1491 1493 elif not dryrun:
1492 1494 wctx.copy(origsrc, dst)
1493 1495
1494 1496
1495 1497 def movedirstate(repo, newctx, match=None):
1496 1498 """Move the dirstate to newctx and adjust it as necessary.
1497 1499
1498 1500 A matcher can be provided as an optimization. It is probably a bug to pass
1499 1501 a matcher that doesn't match all the differences between the parent of the
1500 1502 working copy and newctx.
1501 1503 """
1502 1504 oldctx = repo[b'.']
1503 1505 ds = repo.dirstate
1504 1506 copies = dict(ds.copies())
1505 1507 ds.setparents(newctx.node(), nullid)
1506 1508 s = newctx.status(oldctx, match=match)
1507 1509 for f in s.modified:
1508 1510 if ds[f] == b'r':
1509 1511 # modified + removed -> removed
1510 1512 continue
1511 1513 ds.normallookup(f)
1512 1514
1513 1515 for f in s.added:
1514 1516 if ds[f] == b'r':
1515 1517 # added + removed -> unknown
1516 1518 ds.drop(f)
1517 1519 elif ds[f] != b'a':
1518 1520 ds.add(f)
1519 1521
1520 1522 for f in s.removed:
1521 1523 if ds[f] == b'a':
1522 1524 # removed + added -> normal
1523 1525 ds.normallookup(f)
1524 1526 elif ds[f] != b'r':
1525 1527 ds.remove(f)
1526 1528
1527 1529 # Merge old parent and old working dir copies
1528 1530 oldcopies = copiesmod.pathcopies(newctx, oldctx, match)
1529 1531 oldcopies.update(copies)
1530 1532 copies = {
1531 1533 dst: oldcopies.get(src, src)
1532 1534 for dst, src in pycompat.iteritems(oldcopies)
1533 1535 }
1534 1536 # Adjust the dirstate copies
1535 1537 for dst, src in pycompat.iteritems(copies):
1536 1538 if src not in newctx or dst in newctx or ds[dst] != b'a':
1537 1539 src = None
1538 1540 ds.copy(src, dst)
1539 1541 repo._quick_access_changeid_invalidate()
1540 1542
1541 1543
1542 1544 def filterrequirements(requirements):
1543 1545 """ filters the requirements into two sets:
1544 1546
1545 1547 wcreq: requirements which should be written in .hg/requires
1546 1548 storereq: which should be written in .hg/store/requires
1547 1549
1548 1550 Returns (wcreq, storereq)
1549 1551 """
1550 1552 if requirementsmod.SHARESAFE_REQUIREMENT in requirements:
1551 1553 wc, store = set(), set()
1552 1554 for r in requirements:
1553 1555 if r in requirementsmod.WORKING_DIR_REQUIREMENTS:
1554 1556 wc.add(r)
1555 1557 else:
1556 1558 store.add(r)
1557 1559 return wc, store
1558 1560 return requirements, None
1559 1561
1560 1562
1561 1563 def istreemanifest(repo):
1562 1564 """ returns whether the repository is using treemanifest or not """
1563 1565 return requirementsmod.TREEMANIFEST_REQUIREMENT in repo.requirements
1564 1566
1565 1567
1566 1568 def writereporequirements(repo, requirements=None):
1567 1569 """ writes requirements for the repo to .hg/requires """
1568 1570 if requirements:
1569 1571 repo.requirements = requirements
1570 1572 wcreq, storereq = filterrequirements(repo.requirements)
1571 1573 if wcreq is not None:
1572 1574 writerequires(repo.vfs, wcreq)
1573 1575 if storereq is not None:
1574 1576 writerequires(repo.svfs, storereq)
1575 1577
1576 1578
1577 1579 def writerequires(opener, requirements):
1578 1580 with opener(b'requires', b'w', atomictemp=True) as fp:
1579 1581 for r in sorted(requirements):
1580 1582 fp.write(b"%s\n" % r)
1581 1583
1582 1584
1583 1585 class filecachesubentry(object):
1584 1586 def __init__(self, path, stat):
1585 1587 self.path = path
1586 1588 self.cachestat = None
1587 1589 self._cacheable = None
1588 1590
1589 1591 if stat:
1590 1592 self.cachestat = filecachesubentry.stat(self.path)
1591 1593
1592 1594 if self.cachestat:
1593 1595 self._cacheable = self.cachestat.cacheable()
1594 1596 else:
1595 1597 # None means we don't know yet
1596 1598 self._cacheable = None
1597 1599
1598 1600 def refresh(self):
1599 1601 if self.cacheable():
1600 1602 self.cachestat = filecachesubentry.stat(self.path)
1601 1603
1602 1604 def cacheable(self):
1603 1605 if self._cacheable is not None:
1604 1606 return self._cacheable
1605 1607
1606 1608 # we don't know yet, assume it is for now
1607 1609 return True
1608 1610
1609 1611 def changed(self):
1610 1612 # no point in going further if we can't cache it
1611 1613 if not self.cacheable():
1612 1614 return True
1613 1615
1614 1616 newstat = filecachesubentry.stat(self.path)
1615 1617
1616 1618 # we may not know if it's cacheable yet, check again now
1617 1619 if newstat and self._cacheable is None:
1618 1620 self._cacheable = newstat.cacheable()
1619 1621
1620 1622 # check again
1621 1623 if not self._cacheable:
1622 1624 return True
1623 1625
1624 1626 if self.cachestat != newstat:
1625 1627 self.cachestat = newstat
1626 1628 return True
1627 1629 else:
1628 1630 return False
1629 1631
1630 1632 @staticmethod
1631 1633 def stat(path):
1632 1634 try:
1633 1635 return util.cachestat(path)
1634 1636 except OSError as e:
1635 1637 if e.errno != errno.ENOENT:
1636 1638 raise
1637 1639
1638 1640
1639 1641 class filecacheentry(object):
1640 1642 def __init__(self, paths, stat=True):
1641 1643 self._entries = []
1642 1644 for path in paths:
1643 1645 self._entries.append(filecachesubentry(path, stat))
1644 1646
1645 1647 def changed(self):
1646 1648 '''true if any entry has changed'''
1647 1649 for entry in self._entries:
1648 1650 if entry.changed():
1649 1651 return True
1650 1652 return False
1651 1653
1652 1654 def refresh(self):
1653 1655 for entry in self._entries:
1654 1656 entry.refresh()
1655 1657
1656 1658
1657 1659 class filecache(object):
1658 1660 """A property like decorator that tracks files under .hg/ for updates.
1659 1661
1660 1662 On first access, the files defined as arguments are stat()ed and the
1661 1663 results cached. The decorated function is called. The results are stashed
1662 1664 away in a ``_filecache`` dict on the object whose method is decorated.
1663 1665
1664 1666 On subsequent access, the cached result is used as it is set to the
1665 1667 instance dictionary.
1666 1668
1667 1669 On external property set/delete operations, the caller must update the
1668 1670 corresponding _filecache entry appropriately. Use __class__.<attr>.set()
1669 1671 instead of directly setting <attr>.
1670 1672
1671 1673 When using the property API, the cached data is always used if available.
1672 1674 No stat() is performed to check if the file has changed.
1673 1675
1674 1676 Others can muck about with the state of the ``_filecache`` dict. e.g. they
1675 1677 can populate an entry before the property's getter is called. In this case,
1676 1678 entries in ``_filecache`` will be used during property operations,
1677 1679 if available. If the underlying file changes, it is up to external callers
1678 1680 to reflect this by e.g. calling ``delattr(obj, attr)`` to remove the cached
1679 1681 method result as well as possibly calling ``del obj._filecache[attr]`` to
1680 1682 remove the ``filecacheentry``.
1681 1683 """
1682 1684
1683 1685 def __init__(self, *paths):
1684 1686 self.paths = paths
1685 1687
1686 1688 def join(self, obj, fname):
1687 1689 """Used to compute the runtime path of a cached file.
1688 1690
1689 1691 Users should subclass filecache and provide their own version of this
1690 1692 function to call the appropriate join function on 'obj' (an instance
1691 1693 of the class that its member function was decorated).
1692 1694 """
1693 1695 raise NotImplementedError
1694 1696
1695 1697 def __call__(self, func):
1696 1698 self.func = func
1697 1699 self.sname = func.__name__
1698 1700 self.name = pycompat.sysbytes(self.sname)
1699 1701 return self
1700 1702
1701 1703 def __get__(self, obj, type=None):
1702 1704 # if accessed on the class, return the descriptor itself.
1703 1705 if obj is None:
1704 1706 return self
1705 1707
1706 1708 assert self.sname not in obj.__dict__
1707 1709
1708 1710 entry = obj._filecache.get(self.name)
1709 1711
1710 1712 if entry:
1711 1713 if entry.changed():
1712 1714 entry.obj = self.func(obj)
1713 1715 else:
1714 1716 paths = [self.join(obj, path) for path in self.paths]
1715 1717
1716 1718 # We stat -before- creating the object so our cache doesn't lie if
1717 1719 # a writer modified between the time we read and stat
1718 1720 entry = filecacheentry(paths, True)
1719 1721 entry.obj = self.func(obj)
1720 1722
1721 1723 obj._filecache[self.name] = entry
1722 1724
1723 1725 obj.__dict__[self.sname] = entry.obj
1724 1726 return entry.obj
1725 1727
1726 1728 # don't implement __set__(), which would make __dict__ lookup as slow as
1727 1729 # function call.
1728 1730
1729 1731 def set(self, obj, value):
1730 1732 if self.name not in obj._filecache:
1731 1733 # we add an entry for the missing value because X in __dict__
1732 1734 # implies X in _filecache
1733 1735 paths = [self.join(obj, path) for path in self.paths]
1734 1736 ce = filecacheentry(paths, False)
1735 1737 obj._filecache[self.name] = ce
1736 1738 else:
1737 1739 ce = obj._filecache[self.name]
1738 1740
1739 1741 ce.obj = value # update cached copy
1740 1742 obj.__dict__[self.sname] = value # update copy returned by obj.x
1741 1743
1742 1744
1743 1745 def extdatasource(repo, source):
1744 1746 """Gather a map of rev -> value dict from the specified source
1745 1747
1746 1748 A source spec is treated as a URL, with a special case shell: type
1747 1749 for parsing the output from a shell command.
1748 1750
1749 1751 The data is parsed as a series of newline-separated records where
1750 1752 each record is a revision specifier optionally followed by a space
1751 1753 and a freeform string value. If the revision is known locally, it
1752 1754 is converted to a rev, otherwise the record is skipped.
1753 1755
1754 1756 Note that both key and value are treated as UTF-8 and converted to
1755 1757 the local encoding. This allows uniformity between local and
1756 1758 remote data sources.
1757 1759 """
1758 1760
1759 1761 spec = repo.ui.config(b"extdata", source)
1760 1762 if not spec:
1761 1763 raise error.Abort(_(b"unknown extdata source '%s'") % source)
1762 1764
1763 1765 data = {}
1764 1766 src = proc = None
1765 1767 try:
1766 1768 if spec.startswith(b"shell:"):
1767 1769 # external commands should be run relative to the repo root
1768 1770 cmd = spec[6:]
1769 1771 proc = subprocess.Popen(
1770 1772 procutil.tonativestr(cmd),
1771 1773 shell=True,
1772 1774 bufsize=-1,
1773 1775 close_fds=procutil.closefds,
1774 1776 stdout=subprocess.PIPE,
1775 1777 cwd=procutil.tonativestr(repo.root),
1776 1778 )
1777 1779 src = proc.stdout
1778 1780 else:
1779 1781 # treat as a URL or file
1780 1782 src = url.open(repo.ui, spec)
1781 1783 for l in src:
1782 1784 if b" " in l:
1783 1785 k, v = l.strip().split(b" ", 1)
1784 1786 else:
1785 1787 k, v = l.strip(), b""
1786 1788
1787 1789 k = encoding.tolocal(k)
1788 1790 try:
1789 1791 data[revsingle(repo, k).rev()] = encoding.tolocal(v)
1790 1792 except (error.LookupError, error.RepoLookupError):
1791 1793 pass # we ignore data for nodes that don't exist locally
1792 1794 finally:
1793 1795 if proc:
1794 1796 try:
1795 1797 proc.communicate()
1796 1798 except ValueError:
1797 1799 # This happens if we started iterating src and then
1798 1800 # get a parse error on a line. It should be safe to ignore.
1799 1801 pass
1800 1802 if src:
1801 1803 src.close()
1802 1804 if proc and proc.returncode != 0:
1803 1805 raise error.Abort(
1804 1806 _(b"extdata command '%s' failed: %s")
1805 1807 % (cmd, procutil.explainexit(proc.returncode))
1806 1808 )
1807 1809
1808 1810 return data
1809 1811
1810 1812
1811 1813 class progress(object):
1812 1814 def __init__(self, ui, updatebar, topic, unit=b"", total=None):
1813 1815 self.ui = ui
1814 1816 self.pos = 0
1815 1817 self.topic = topic
1816 1818 self.unit = unit
1817 1819 self.total = total
1818 1820 self.debug = ui.configbool(b'progress', b'debug')
1819 1821 self._updatebar = updatebar
1820 1822
1821 1823 def __enter__(self):
1822 1824 return self
1823 1825
1824 1826 def __exit__(self, exc_type, exc_value, exc_tb):
1825 1827 self.complete()
1826 1828
1827 1829 def update(self, pos, item=b"", total=None):
1828 1830 assert pos is not None
1829 1831 if total:
1830 1832 self.total = total
1831 1833 self.pos = pos
1832 1834 self._updatebar(self.topic, self.pos, item, self.unit, self.total)
1833 1835 if self.debug:
1834 1836 self._printdebug(item)
1835 1837
1836 1838 def increment(self, step=1, item=b"", total=None):
1837 1839 self.update(self.pos + step, item, total)
1838 1840
1839 1841 def complete(self):
1840 1842 self.pos = None
1841 1843 self.unit = b""
1842 1844 self.total = None
1843 1845 self._updatebar(self.topic, self.pos, b"", self.unit, self.total)
1844 1846
1845 1847 def _printdebug(self, item):
1846 1848 unit = b''
1847 1849 if self.unit:
1848 1850 unit = b' ' + self.unit
1849 1851 if item:
1850 1852 item = b' ' + item
1851 1853
1852 1854 if self.total:
1853 1855 pct = 100.0 * self.pos / self.total
1854 1856 self.ui.debug(
1855 1857 b'%s:%s %d/%d%s (%4.2f%%)\n'
1856 1858 % (self.topic, item, self.pos, self.total, unit, pct)
1857 1859 )
1858 1860 else:
1859 1861 self.ui.debug(b'%s:%s %d%s\n' % (self.topic, item, self.pos, unit))
1860 1862
1861 1863
1862 1864 def gdinitconfig(ui):
1863 1865 """helper function to know if a repo should be created as general delta
1864 1866 """
1865 1867 # experimental config: format.generaldelta
1866 1868 return ui.configbool(b'format', b'generaldelta') or ui.configbool(
1867 1869 b'format', b'usegeneraldelta'
1868 1870 )
1869 1871
1870 1872
1871 1873 def gddeltaconfig(ui):
1872 1874 """helper function to know if incoming delta should be optimised
1873 1875 """
1874 1876 # experimental config: format.generaldelta
1875 1877 return ui.configbool(b'format', b'generaldelta')
1876 1878
1877 1879
1878 1880 class simplekeyvaluefile(object):
1879 1881 """A simple file with key=value lines
1880 1882
1881 1883 Keys must be alphanumerics and start with a letter, values must not
1882 1884 contain '\n' characters"""
1883 1885
1884 1886 firstlinekey = b'__firstline'
1885 1887
1886 1888 def __init__(self, vfs, path, keys=None):
1887 1889 self.vfs = vfs
1888 1890 self.path = path
1889 1891
1890 1892 def read(self, firstlinenonkeyval=False):
1891 1893 """Read the contents of a simple key-value file
1892 1894
1893 1895 'firstlinenonkeyval' indicates whether the first line of file should
1894 1896 be treated as a key-value pair or reuturned fully under the
1895 1897 __firstline key."""
1896 1898 lines = self.vfs.readlines(self.path)
1897 1899 d = {}
1898 1900 if firstlinenonkeyval:
1899 1901 if not lines:
1900 1902 e = _(b"empty simplekeyvalue file")
1901 1903 raise error.CorruptedState(e)
1902 1904 # we don't want to include '\n' in the __firstline
1903 1905 d[self.firstlinekey] = lines[0][:-1]
1904 1906 del lines[0]
1905 1907
1906 1908 try:
1907 1909 # the 'if line.strip()' part prevents us from failing on empty
1908 1910 # lines which only contain '\n' therefore are not skipped
1909 1911 # by 'if line'
1910 1912 updatedict = dict(
1911 1913 line[:-1].split(b'=', 1) for line in lines if line.strip()
1912 1914 )
1913 1915 if self.firstlinekey in updatedict:
1914 1916 e = _(b"%r can't be used as a key")
1915 1917 raise error.CorruptedState(e % self.firstlinekey)
1916 1918 d.update(updatedict)
1917 1919 except ValueError as e:
1918 1920 raise error.CorruptedState(stringutil.forcebytestr(e))
1919 1921 return d
1920 1922
1921 1923 def write(self, data, firstline=None):
1922 1924 """Write key=>value mapping to a file
1923 1925 data is a dict. Keys must be alphanumerical and start with a letter.
1924 1926 Values must not contain newline characters.
1925 1927
1926 1928 If 'firstline' is not None, it is written to file before
1927 1929 everything else, as it is, not in a key=value form"""
1928 1930 lines = []
1929 1931 if firstline is not None:
1930 1932 lines.append(b'%s\n' % firstline)
1931 1933
1932 1934 for k, v in data.items():
1933 1935 if k == self.firstlinekey:
1934 1936 e = b"key name '%s' is reserved" % self.firstlinekey
1935 1937 raise error.ProgrammingError(e)
1936 1938 if not k[0:1].isalpha():
1937 1939 e = b"keys must start with a letter in a key-value file"
1938 1940 raise error.ProgrammingError(e)
1939 1941 if not k.isalnum():
1940 1942 e = b"invalid key name in a simple key-value file"
1941 1943 raise error.ProgrammingError(e)
1942 1944 if b'\n' in v:
1943 1945 e = b"invalid value in a simple key-value file"
1944 1946 raise error.ProgrammingError(e)
1945 1947 lines.append(b"%s=%s\n" % (k, v))
1946 1948 with self.vfs(self.path, mode=b'wb', atomictemp=True) as fp:
1947 1949 fp.write(b''.join(lines))
1948 1950
1949 1951
1950 1952 _reportobsoletedsource = [
1951 1953 b'debugobsolete',
1952 1954 b'pull',
1953 1955 b'push',
1954 1956 b'serve',
1955 1957 b'unbundle',
1956 1958 ]
1957 1959
1958 1960 _reportnewcssource = [
1959 1961 b'pull',
1960 1962 b'unbundle',
1961 1963 ]
1962 1964
1963 1965
1964 1966 def prefetchfiles(repo, revmatches):
1965 1967 """Invokes the registered file prefetch functions, allowing extensions to
1966 1968 ensure the corresponding files are available locally, before the command
1967 1969 uses them.
1968 1970
1969 1971 Args:
1970 1972 revmatches: a list of (revision, match) tuples to indicate the files to
1971 1973 fetch at each revision. If any of the match elements is None, it matches
1972 1974 all files.
1973 1975 """
1974 1976
1975 1977 def _matcher(m):
1976 1978 if m:
1977 1979 assert isinstance(m, matchmod.basematcher)
1978 1980 # The command itself will complain about files that don't exist, so
1979 1981 # don't duplicate the message.
1980 1982 return matchmod.badmatch(m, lambda fn, msg: None)
1981 1983 else:
1982 1984 return matchall(repo)
1983 1985
1984 1986 revbadmatches = [(rev, _matcher(match)) for (rev, match) in revmatches]
1985 1987
1986 1988 fileprefetchhooks(repo, revbadmatches)
1987 1989
1988 1990
1989 1991 # a list of (repo, revs, match) prefetch functions
1990 1992 fileprefetchhooks = util.hooks()
1991 1993
1992 1994 # A marker that tells the evolve extension to suppress its own reporting
1993 1995 _reportstroubledchangesets = True
1994 1996
1995 1997
1996 1998 def registersummarycallback(repo, otr, txnname=b'', as_validator=False):
1997 1999 """register a callback to issue a summary after the transaction is closed
1998 2000
1999 2001 If as_validator is true, then the callbacks are registered as transaction
2000 2002 validators instead
2001 2003 """
2002 2004
2003 2005 def txmatch(sources):
2004 2006 return any(txnname.startswith(source) for source in sources)
2005 2007
2006 2008 categories = []
2007 2009
2008 2010 def reportsummary(func):
2009 2011 """decorator for report callbacks."""
2010 2012 # The repoview life cycle is shorter than the one of the actual
2011 2013 # underlying repository. So the filtered object can die before the
2012 2014 # weakref is used leading to troubles. We keep a reference to the
2013 2015 # unfiltered object and restore the filtering when retrieving the
2014 2016 # repository through the weakref.
2015 2017 filtername = repo.filtername
2016 2018 reporef = weakref.ref(repo.unfiltered())
2017 2019
2018 2020 def wrapped(tr):
2019 2021 repo = reporef()
2020 2022 if filtername:
2021 2023 assert repo is not None # help pytype
2022 2024 repo = repo.filtered(filtername)
2023 2025 func(repo, tr)
2024 2026
2025 2027 newcat = b'%02i-txnreport' % len(categories)
2026 2028 if as_validator:
2027 2029 otr.addvalidator(newcat, wrapped)
2028 2030 else:
2029 2031 otr.addpostclose(newcat, wrapped)
2030 2032 categories.append(newcat)
2031 2033 return wrapped
2032 2034
2033 2035 @reportsummary
2034 2036 def reportchangegroup(repo, tr):
2035 2037 cgchangesets = tr.changes.get(b'changegroup-count-changesets', 0)
2036 2038 cgrevisions = tr.changes.get(b'changegroup-count-revisions', 0)
2037 2039 cgfiles = tr.changes.get(b'changegroup-count-files', 0)
2038 2040 cgheads = tr.changes.get(b'changegroup-count-heads', 0)
2039 2041 if cgchangesets or cgrevisions or cgfiles:
2040 2042 htext = b""
2041 2043 if cgheads:
2042 2044 htext = _(b" (%+d heads)") % cgheads
2043 2045 msg = _(b"added %d changesets with %d changes to %d files%s\n")
2044 2046 if as_validator:
2045 2047 msg = _(b"adding %d changesets with %d changes to %d files%s\n")
2046 2048 assert repo is not None # help pytype
2047 2049 repo.ui.status(msg % (cgchangesets, cgrevisions, cgfiles, htext))
2048 2050
2049 2051 if txmatch(_reportobsoletedsource):
2050 2052
2051 2053 @reportsummary
2052 2054 def reportobsoleted(repo, tr):
2053 2055 obsoleted = obsutil.getobsoleted(repo, tr)
2054 2056 newmarkers = len(tr.changes.get(b'obsmarkers', ()))
2055 2057 if newmarkers:
2056 2058 repo.ui.status(_(b'%i new obsolescence markers\n') % newmarkers)
2057 2059 if obsoleted:
2058 2060 msg = _(b'obsoleted %i changesets\n')
2059 2061 if as_validator:
2060 2062 msg = _(b'obsoleting %i changesets\n')
2061 2063 repo.ui.status(msg % len(obsoleted))
2062 2064
2063 2065 if obsolete.isenabled(
2064 2066 repo, obsolete.createmarkersopt
2065 2067 ) and repo.ui.configbool(
2066 2068 b'experimental', b'evolution.report-instabilities'
2067 2069 ):
2068 2070 instabilitytypes = [
2069 2071 (b'orphan', b'orphan'),
2070 2072 (b'phase-divergent', b'phasedivergent'),
2071 2073 (b'content-divergent', b'contentdivergent'),
2072 2074 ]
2073 2075
2074 2076 def getinstabilitycounts(repo):
2075 2077 filtered = repo.changelog.filteredrevs
2076 2078 counts = {}
2077 2079 for instability, revset in instabilitytypes:
2078 2080 counts[instability] = len(
2079 2081 set(obsolete.getrevs(repo, revset)) - filtered
2080 2082 )
2081 2083 return counts
2082 2084
2083 2085 oldinstabilitycounts = getinstabilitycounts(repo)
2084 2086
2085 2087 @reportsummary
2086 2088 def reportnewinstabilities(repo, tr):
2087 2089 newinstabilitycounts = getinstabilitycounts(repo)
2088 2090 for instability, revset in instabilitytypes:
2089 2091 delta = (
2090 2092 newinstabilitycounts[instability]
2091 2093 - oldinstabilitycounts[instability]
2092 2094 )
2093 2095 msg = getinstabilitymessage(delta, instability)
2094 2096 if msg:
2095 2097 repo.ui.warn(msg)
2096 2098
2097 2099 if txmatch(_reportnewcssource):
2098 2100
2099 2101 @reportsummary
2100 2102 def reportnewcs(repo, tr):
2101 2103 """Report the range of new revisions pulled/unbundled."""
2102 2104 origrepolen = tr.changes.get(b'origrepolen', len(repo))
2103 2105 unfi = repo.unfiltered()
2104 2106 if origrepolen >= len(unfi):
2105 2107 return
2106 2108
2107 2109 # Compute the bounds of new visible revisions' range.
2108 2110 revs = smartset.spanset(repo, start=origrepolen)
2109 2111 if revs:
2110 2112 minrev, maxrev = repo[revs.min()], repo[revs.max()]
2111 2113
2112 2114 if minrev == maxrev:
2113 2115 revrange = minrev
2114 2116 else:
2115 2117 revrange = b'%s:%s' % (minrev, maxrev)
2116 2118 draft = len(repo.revs(b'%ld and draft()', revs))
2117 2119 secret = len(repo.revs(b'%ld and secret()', revs))
2118 2120 if not (draft or secret):
2119 2121 msg = _(b'new changesets %s\n') % revrange
2120 2122 elif draft and secret:
2121 2123 msg = _(b'new changesets %s (%d drafts, %d secrets)\n')
2122 2124 msg %= (revrange, draft, secret)
2123 2125 elif draft:
2124 2126 msg = _(b'new changesets %s (%d drafts)\n')
2125 2127 msg %= (revrange, draft)
2126 2128 elif secret:
2127 2129 msg = _(b'new changesets %s (%d secrets)\n')
2128 2130 msg %= (revrange, secret)
2129 2131 else:
2130 2132 errormsg = b'entered unreachable condition'
2131 2133 raise error.ProgrammingError(errormsg)
2132 2134 repo.ui.status(msg)
2133 2135
2134 2136 # search new changesets directly pulled as obsolete
2135 2137 duplicates = tr.changes.get(b'revduplicates', ())
2136 2138 obsadded = unfi.revs(
2137 2139 b'(%d: + %ld) and obsolete()', origrepolen, duplicates
2138 2140 )
2139 2141 cl = repo.changelog
2140 2142 extinctadded = [r for r in obsadded if r not in cl]
2141 2143 if extinctadded:
2142 2144 # They are not just obsolete, but obsolete and invisible
2143 2145 # we call them "extinct" internally but the terms have not been
2144 2146 # exposed to users.
2145 2147 msg = b'(%d other changesets obsolete on arrival)\n'
2146 2148 repo.ui.status(msg % len(extinctadded))
2147 2149
2148 2150 @reportsummary
2149 2151 def reportphasechanges(repo, tr):
2150 2152 """Report statistics of phase changes for changesets pre-existing
2151 2153 pull/unbundle.
2152 2154 """
2153 2155 origrepolen = tr.changes.get(b'origrepolen', len(repo))
2154 2156 published = []
2155 2157 for revs, (old, new) in tr.changes.get(b'phases', []):
2156 2158 if new != phases.public:
2157 2159 continue
2158 2160 published.extend(rev for rev in revs if rev < origrepolen)
2159 2161 if not published:
2160 2162 return
2161 2163 msg = _(b'%d local changesets published\n')
2162 2164 if as_validator:
2163 2165 msg = _(b'%d local changesets will be published\n')
2164 2166 repo.ui.status(msg % len(published))
2165 2167
2166 2168
2167 2169 def getinstabilitymessage(delta, instability):
2168 2170 """function to return the message to show warning about new instabilities
2169 2171
2170 2172 exists as a separate function so that extension can wrap to show more
2171 2173 information like how to fix instabilities"""
2172 2174 if delta > 0:
2173 2175 return _(b'%i new %s changesets\n') % (delta, instability)
2174 2176
2175 2177
2176 2178 def nodesummaries(repo, nodes, maxnumnodes=4):
2177 2179 if len(nodes) <= maxnumnodes or repo.ui.verbose:
2178 2180 return b' '.join(short(h) for h in nodes)
2179 2181 first = b' '.join(short(h) for h in nodes[:maxnumnodes])
2180 2182 return _(b"%s and %d others") % (first, len(nodes) - maxnumnodes)
2181 2183
2182 2184
2183 2185 def enforcesinglehead(repo, tr, desc, accountclosed=False):
2184 2186 """check that no named branch has multiple heads"""
2185 2187 if desc in (b'strip', b'repair'):
2186 2188 # skip the logic during strip
2187 2189 return
2188 2190 visible = repo.filtered(b'visible')
2189 2191 # possible improvement: we could restrict the check to affected branch
2190 2192 bm = visible.branchmap()
2191 2193 for name in bm:
2192 2194 heads = bm.branchheads(name, closed=accountclosed)
2193 2195 if len(heads) > 1:
2194 2196 msg = _(b'rejecting multiple heads on branch "%s"')
2195 2197 msg %= name
2196 2198 hint = _(b'%d heads: %s')
2197 2199 hint %= (len(heads), nodesummaries(repo, heads))
2198 2200 raise error.Abort(msg, hint=hint)
2199 2201
2200 2202
2201 2203 def wrapconvertsink(sink):
2202 2204 """Allow extensions to wrap the sink returned by convcmd.convertsink()
2203 2205 before it is used, whether or not the convert extension was formally loaded.
2204 2206 """
2205 2207 return sink
2206 2208
2207 2209
2208 2210 def unhidehashlikerevs(repo, specs, hiddentype):
2209 2211 """parse the user specs and unhide changesets whose hash or revision number
2210 2212 is passed.
2211 2213
2212 2214 hiddentype can be: 1) 'warn': warn while unhiding changesets
2213 2215 2) 'nowarn': don't warn while unhiding changesets
2214 2216
2215 2217 returns a repo object with the required changesets unhidden
2216 2218 """
2217 2219 if not repo.filtername or not repo.ui.configbool(
2218 2220 b'experimental', b'directaccess'
2219 2221 ):
2220 2222 return repo
2221 2223
2222 2224 if repo.filtername not in (b'visible', b'visible-hidden'):
2223 2225 return repo
2224 2226
2225 2227 symbols = set()
2226 2228 for spec in specs:
2227 2229 try:
2228 2230 tree = revsetlang.parse(spec)
2229 2231 except error.ParseError: # will be reported by scmutil.revrange()
2230 2232 continue
2231 2233
2232 2234 symbols.update(revsetlang.gethashlikesymbols(tree))
2233 2235
2234 2236 if not symbols:
2235 2237 return repo
2236 2238
2237 2239 revs = _getrevsfromsymbols(repo, symbols)
2238 2240
2239 2241 if not revs:
2240 2242 return repo
2241 2243
2242 2244 if hiddentype == b'warn':
2243 2245 unfi = repo.unfiltered()
2244 2246 revstr = b", ".join([pycompat.bytestr(unfi[l]) for l in revs])
2245 2247 repo.ui.warn(
2246 2248 _(
2247 2249 b"warning: accessing hidden changesets for write "
2248 2250 b"operation: %s\n"
2249 2251 )
2250 2252 % revstr
2251 2253 )
2252 2254
2253 2255 # we have to use new filtername to separate branch/tags cache until we can
2254 2256 # disbale these cache when revisions are dynamically pinned.
2255 2257 return repo.filtered(b'visible-hidden', revs)
2256 2258
2257 2259
2258 2260 def _getrevsfromsymbols(repo, symbols):
2259 2261 """parse the list of symbols and returns a set of revision numbers of hidden
2260 2262 changesets present in symbols"""
2261 2263 revs = set()
2262 2264 unfi = repo.unfiltered()
2263 2265 unficl = unfi.changelog
2264 2266 cl = repo.changelog
2265 2267 tiprev = len(unficl)
2266 2268 allowrevnums = repo.ui.configbool(b'experimental', b'directaccess.revnums')
2267 2269 for s in symbols:
2268 2270 try:
2269 2271 n = int(s)
2270 2272 if n <= tiprev:
2271 2273 if not allowrevnums:
2272 2274 continue
2273 2275 else:
2274 2276 if n not in cl:
2275 2277 revs.add(n)
2276 2278 continue
2277 2279 except ValueError:
2278 2280 pass
2279 2281
2280 2282 try:
2281 2283 s = resolvehexnodeidprefix(unfi, s)
2282 2284 except (error.LookupError, error.WdirUnsupported):
2283 2285 s = None
2284 2286
2285 2287 if s is not None:
2286 2288 rev = unficl.rev(s)
2287 2289 if rev not in cl:
2288 2290 revs.add(rev)
2289 2291
2290 2292 return revs
2291 2293
2292 2294
2293 2295 def bookmarkrevs(repo, mark):
2294 2296 """
2295 2297 Select revisions reachable by a given bookmark
2296 2298 """
2297 2299 return repo.revs(
2298 2300 b"ancestors(bookmark(%s)) - "
2299 2301 b"ancestors(head() and not bookmark(%s)) - "
2300 2302 b"ancestors(bookmark() and not bookmark(%s))",
2301 2303 mark,
2302 2304 mark,
2303 2305 mark,
2304 2306 )
@@ -1,30 +1,30 b''
1 1 $ cat >> $HGRCPATH << EOF
2 2 > [extensions]
3 3 > absorb=
4 4 > EOF
5 5
6 6 Abort absorb if there is an unfinished operation.
7 7
8 8 $ hg init abortunresolved
9 9 $ cd abortunresolved
10 10
11 11 $ echo "foo1" > foo.whole
12 12 $ hg commit -Aqm "foo 1"
13 13
14 14 $ hg update null
15 15 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
16 16 $ echo "foo2" > foo.whole
17 17 $ hg commit -Aqm "foo 2"
18 18
19 19 $ hg --config extensions.rebase= rebase -r 1 -d 0
20 20 rebasing 1:c3b6dc0e177a tip "foo 2"
21 21 merging foo.whole
22 22 warning: conflicts while merging foo.whole! (edit, then use 'hg resolve --mark')
23 23 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
24 24 [240]
25 25
26 26 $ hg --config extensions.rebase= absorb
27 27 abort: rebase in progress
28 28 (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop')
29 [255]
29 [20]
30 30
@@ -1,430 +1,430 b''
1 1 Testing changing branch on commits
2 2 ==================================
3 3
4 4 Setup
5 5
6 6 $ cat >> $HGRCPATH << EOF
7 7 > [alias]
8 8 > glog = log -G -T "{rev}:{node|short} {desc}\n{branch} ({bookmarks})"
9 9 > [experimental]
10 10 > evolution = createmarkers
11 11 > [extensions]
12 12 > rebase=
13 13 > EOF
14 14
15 15 $ hg init repo
16 16 $ cd repo
17 17 $ for ch in a b c d e; do echo foo >> $ch; hg ci -Aqm "Added "$ch; done
18 18 $ hg glog
19 19 @ 4:aa98ab95a928 Added e
20 20 | default ()
21 21 o 3:62615734edd5 Added d
22 22 | default ()
23 23 o 2:28ad74487de9 Added c
24 24 | default ()
25 25 o 1:29becc82797a Added b
26 26 | default ()
27 27 o 0:18d04c59bb5d Added a
28 28 default ()
29 29
30 30 $ hg branches
31 31 default 4:aa98ab95a928
32 32
33 33 Try without passing a new branch name
34 34
35 35 $ hg branch -r .
36 36 abort: no branch name specified for the revisions
37 37 [10]
38 38
39 39 Setting an invalid branch name
40 40
41 41 $ hg branch -r . a:b
42 42 abort: ':' cannot be used in a name
43 43 [255]
44 44 $ hg branch -r . tip
45 45 abort: the name 'tip' is reserved
46 46 [255]
47 47 $ hg branch -r . 1234
48 48 abort: cannot use an integer as a name
49 49 [255]
50 50
51 51 Change on non-linear set of commits
52 52
53 53 $ hg branch -r 2 -r 4 foo
54 54 abort: cannot change branch of non-linear revisions
55 55 [10]
56 56
57 57 Change in middle of the stack (linear commits)
58 58
59 59 $ hg branch -r 1::3 foo
60 60 abort: cannot change branch of changeset with children
61 61 [255]
62 62
63 63 Change with dirty working directory
64 64
65 65 $ echo bar > a
66 66 $ hg branch -r . foo
67 67 abort: uncommitted changes
68 [255]
68 [20]
69 69
70 70 $ hg revert --all
71 71 reverting a
72 72
73 73 Change on empty revision set
74 74
75 75 $ hg branch -r 'draft() - all()' foo
76 76 abort: empty revision set
77 77 [10]
78 78
79 79 Changing branch on linear set of commits from head
80 80
81 81 Without obsmarkers
82 82
83 83 $ hg branch -r 3:4 foo --config experimental.evolution=!
84 84 changed branch on 2 changesets
85 85 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/62615734edd5-e86bd13a-branch-change.hg
86 86 $ hg glog
87 87 @ 4:3938acfb5c0f Added e
88 88 | foo ()
89 89 o 3:9435da006bdc Added d
90 90 | foo ()
91 91 o 2:28ad74487de9 Added c
92 92 | default ()
93 93 o 1:29becc82797a Added b
94 94 | default ()
95 95 o 0:18d04c59bb5d Added a
96 96 default ()
97 97
98 98 $ hg branches
99 99 foo 4:3938acfb5c0f
100 100 default 2:28ad74487de9 (inactive)
101 101
102 102 With obsmarkers
103 103
104 104 $ hg branch -r 3::4 bar
105 105 changed branch on 2 changesets
106 106 $ hg glog
107 107 @ 6:7c1991464886 Added e
108 108 | bar ()
109 109 o 5:1ea05e93925f Added d
110 110 | bar ()
111 111 o 2:28ad74487de9 Added c
112 112 | default ()
113 113 o 1:29becc82797a Added b
114 114 | default ()
115 115 o 0:18d04c59bb5d Added a
116 116 default ()
117 117
118 118 $ hg branches
119 119 bar 6:7c1991464886
120 120 default 2:28ad74487de9 (inactive)
121 121
122 122 Change branch name to an existing branch
123 123
124 124 $ hg branch -r . default
125 125 abort: a branch of the same name already exists
126 126 [10]
127 127
128 128 Changing on a branch head which is not topological head
129 129
130 130 $ hg branch -r 2 stable
131 131 abort: cannot change branch of changeset with children
132 132 [255]
133 133
134 134 Enabling the allowunstable config and trying to change branch on a branch head
135 135 which is not a topological head
136 136
137 137 $ echo "[experimental]" >> .hg/hgrc
138 138 $ echo "evolution.allowunstable=yes" >> .hg/hgrc
139 139 $ hg branch -r 2 foo
140 140 changed branch on 1 changesets
141 141 2 new orphan changesets
142 142
143 143 Changing branch of an obsoleted changeset
144 144
145 145 $ hg branch -r 4 foobar
146 146 abort: hidden revision '4' was rewritten as: 7c1991464886!
147 147 (use --hidden to access hidden revisions)
148 148 [255]
149 149
150 150 $ hg branch -r 4 --hidden foobar
151 151 abort: cannot change branch of a obsolete changeset
152 152 [10]
153 153
154 154 Make sure bookmark movement is correct
155 155
156 156 $ hg bookmark b1
157 157 $ hg glog -r '.^::'
158 158 @ 6:7c1991464886 Added e
159 159 | bar (b1)
160 160 * 5:1ea05e93925f Added d
161 161 | bar ()
162 162 ~
163 163
164 164 $ hg branch -r '(.^)::' wat --debug
165 165 changing branch of '1ea05e93925f806d875a2163f9b76764be644636' from 'bar' to 'wat'
166 166 committing files:
167 167 d
168 168 committing manifest
169 169 committing changelog
170 170 new node id is 343660ccab7400da637bd6a211d07f413536d718
171 171 changing branch of '7c19914648869f5b02fc7fed31ddee9783fdd680' from 'bar' to 'wat'
172 172 committing files:
173 173 e
174 174 committing manifest
175 175 committing changelog
176 176 new node id is de1404b45a69f8cc6437d7679033ee33e9efb4ba
177 177 moving bookmarks ['b1'] from 7c19914648869f5b02fc7fed31ddee9783fdd680 to de1404b45a69f8cc6437d7679033ee33e9efb4ba
178 178 resolving manifests
179 179 branchmerge: False, force: False, partial: False
180 180 ancestor: 7c1991464886, local: 7c1991464886+, remote: de1404b45a69
181 181 starting 4 threads for background file closing (?)
182 182 changed branch on 2 changesets
183 183 updating the branch cache
184 184 invalid branch cache (served): tip differs
185 185
186 186 $ hg glog -r '(.^)::'
187 187 @ 9:de1404b45a69 Added e
188 188 | wat (b1)
189 189 * 8:343660ccab74 Added d
190 190 | wat ()
191 191 ~
192 192
193 193 Make sure phase handling is correct
194 194
195 195 $ echo foo >> bar
196 196 $ hg ci -Aqm "added bar" --secret
197 197 1 new orphan changesets
198 198 $ hg glog -r .
199 199 @ 10:8ad1294c1660 added bar
200 200 | wat (b1)
201 201 ~
202 202 $ hg branch -r . secret
203 203 changed branch on 1 changesets
204 204 $ hg phase -r .
205 205 11: secret
206 206
207 207 $ hg branches
208 208 secret 11:38a9b2d53f98
209 209 foo 7:8a4729a5e2b8
210 210 wat 9:de1404b45a69 (inactive)
211 211 default 2:28ad74487de9 (inactive)
212 212 $ hg branch
213 213 secret
214 214
215 215 Changing branch of another head, different from one on which we are
216 216
217 217 $ hg glog
218 218 @ 11:38a9b2d53f98 added bar
219 219 | secret (b1)
220 220 * 9:de1404b45a69 Added e
221 221 | wat ()
222 222 * 8:343660ccab74 Added d
223 223 | wat ()
224 224 | o 7:8a4729a5e2b8 Added c
225 225 | | foo ()
226 226 x | 2:28ad74487de9 Added c
227 227 |/ default ()
228 228 o 1:29becc82797a Added b
229 229 | default ()
230 230 o 0:18d04c59bb5d Added a
231 231 default ()
232 232
233 233 $ hg branch
234 234 secret
235 235
236 236 $ hg branch -r 7 foobar
237 237 changed branch on 1 changesets
238 238
239 239 The current branch must be preserved
240 240 $ hg branch
241 241 secret
242 242
243 243 Changing branch on multiple heads at once
244 244
245 245 $ hg rebase -s 8 -d 12 --keepbranches -q
246 246
247 247 $ hg rebase -s 14 -d 1 --keepbranches -q
248 248
249 249 $ hg branch -r 0: stable
250 250 changed branch on 6 changesets
251 251 $ hg glog
252 252 @ 23:6a5ddbcfb870 added bar
253 253 | stable (b1)
254 254 o 22:baedc6e98a67 Added e
255 255 | stable ()
256 256 | o 21:99ac7bf8aad1 Added d
257 257 | | stable ()
258 258 | o 20:0ecb4d39c4bd Added c
259 259 |/ stable ()
260 260 o 19:fd45b986b109 Added b
261 261 | stable ()
262 262 o 18:204d2769eca2 Added a
263 263 stable ()
264 264
265 265 $ hg branches
266 266 stable 23:6a5ddbcfb870
267 267
268 268 $ hg branch
269 269 stable
270 270
271 271 Changing to same branch is no-op
272 272
273 273 $ hg branch -r 19::21 stable
274 274 changed branch on 0 changesets
275 275
276 276 Changing branch name to existing branch name if the branch of parent of root of
277 277 revs is same as the new branch name
278 278
279 279 $ hg branch -r 20::21 bugfix
280 280 changed branch on 2 changesets
281 281 $ hg glog
282 282 o 25:714defe1cf34 Added d
283 283 | bugfix ()
284 284 o 24:98394def28fc Added c
285 285 | bugfix ()
286 286 | @ 23:6a5ddbcfb870 added bar
287 287 | | stable (b1)
288 288 | o 22:baedc6e98a67 Added e
289 289 |/ stable ()
290 290 o 19:fd45b986b109 Added b
291 291 | stable ()
292 292 o 18:204d2769eca2 Added a
293 293 stable ()
294 294
295 295 $ hg branch -r 24:25 stable
296 296 changed branch on 2 changesets
297 297 $ hg glog
298 298 o 27:4ec342341562 Added d
299 299 | stable ()
300 300 o 26:83f48859c2de Added c
301 301 | stable ()
302 302 | @ 23:6a5ddbcfb870 added bar
303 303 | | stable (b1)
304 304 | o 22:baedc6e98a67 Added e
305 305 |/ stable ()
306 306 o 19:fd45b986b109 Added b
307 307 | stable ()
308 308 o 18:204d2769eca2 Added a
309 309 stable ()
310 310
311 311 Changing branch of a merge commit
312 312
313 313 $ hg branch -q ghi
314 314 $ echo f > f
315 315 $ hg ci -qAm 'Added f'
316 316 $ hg up -q 27
317 317 $ hg branch -q jkl
318 318 $ echo g > g
319 319 $ hg ci -qAm 'Added g'
320 320 $ hg glog -r 'heads(:)'
321 321 @ 29:6bc1c6c2c9da Added g
322 322 | jkl ()
323 323 ~
324 324 o 28:2f1019bd29d2 Added f
325 325 | ghi (b1)
326 326 ~
327 327
328 328 $ hg branch -q default
329 329 $ hg merge -r 28
330 330 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
331 331 (branch merge, don't forget to commit)
332 332 $ hg branch -r . abcd
333 333 abort: outstanding uncommitted merge
334 [255]
334 [20]
335 335
336 336 $ hg ci -m "Merge commit"
337 337 $ hg glog -r 'parents(.)::'
338 338 @ 30:4d56e6b1eb6b Merge commit
339 339 |\ default ()
340 340 | o 29:6bc1c6c2c9da Added g
341 341 | | jkl ()
342 342 | ~
343 343 o 28:2f1019bd29d2 Added f
344 344 | ghi (b1)
345 345 ~
346 346
347 347 $ hg branch -r . ghi
348 348 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
349 349 changed branch on 1 changesets
350 350 $ hg branch -r . jkl
351 351 changed branch on 1 changesets
352 352 $ hg branch -r . default
353 353 changed branch on 1 changesets
354 354 $ hg branch -r . stable
355 355 abort: a branch of the same name already exists
356 356 [10]
357 357
358 358 $ hg branch -r . stable --force
359 359 changed branch on 1 changesets
360 360 $ hg branches
361 361 stable 34:d1c2addda4a2
362 362 jkl 29:6bc1c6c2c9da (inactive)
363 363 ghi 28:2f1019bd29d2 (inactive)
364 364
365 365 Changing branch on public changeset
366 366
367 367 $ hg phase -r . -p
368 368 $ hg branch -r . def
369 369 abort: cannot change branch of public changesets
370 370 (see 'hg help phases' for details)
371 371 [255]
372 372
373 373 Merge commit with conflicts, with evolution and without
374 374
375 375 $ mklozenge() {
376 376 > echo foo > a
377 377 > hg ci -qAm foo
378 378 > echo bar > a
379 379 > hg ci -qm bar
380 380 > hg up -q '.^'
381 381 > echo baz > a
382 382 > hg ci -qm baz
383 383 > hg merge -q -t :local
384 384 > echo neither > a
385 385 > hg ci -qm neither
386 386 > }
387 387
388 388 $ cd ..
389 389 $ hg init merge-with-evolution
390 390 $ cd merge-with-evolution
391 391 $ mklozenge
392 392
393 393 $ hg branch -r '(.^)::' abc
394 394 changed branch on 2 changesets
395 395 $ hg glog
396 396 @ 5:c07fa8b34d54 neither
397 397 |\ abc ()
398 398 | o 4:f2aa51777cc9 baz
399 399 | | abc ()
400 400 o | 1:2e33c4f0856b bar
401 401 |/ default ()
402 402 o 0:91cfb6004abf foo
403 403 default ()
404 404 $ hg cat a
405 405 neither
406 406
407 407 $ cd ..
408 408 $ hg init merge-without-evolution
409 409 $ cd merge-without-evolution
410 410 $ mklozenge
411 411 $ cat > .hg/hgrc << EOF
412 412 > [experimental]
413 413 > evolution = no
414 414 > evolution.allowunstable = no
415 415 > EOF
416 416
417 417 $ hg branch -r '(.^)::' abc
418 418 changed branch on 2 changesets
419 419 saved backup bundle to $TESTTMP/merge-without-evolution/.hg/strip-backup/9a3a2af368f4-8db1a361-branch-change.hg
420 420 $ hg glog
421 421 @ 3:c07fa8b34d54 neither
422 422 |\ abc ()
423 423 | o 2:f2aa51777cc9 baz
424 424 | | abc ()
425 425 o | 1:2e33c4f0856b bar
426 426 |/ default ()
427 427 o 0:91cfb6004abf foo
428 428 default ()
429 429 $ hg cat a
430 430 neither
@@ -1,157 +1,157 b''
1 1 #testcases abortcommand abortflag
2 2 #if abortflag
3 3 $ cat >> $HGRCPATH <<EOF
4 4 > [alias]
5 5 > abort = merge --abort
6 6 > EOF
7 7 #endif
8 8
9 9 $ addcommit () {
10 10 > echo $1 > $1
11 11 > hg add $1
12 12 > hg commit -d "${2} 0" -m $1
13 13 > }
14 14
15 15 $ commit () {
16 16 > hg commit -d "${2} 0" -m $1
17 17 > }
18 18
19 19 $ hg init a
20 20 $ cd a
21 21 $ addcommit "A" 0
22 22 $ addcommit "B" 1
23 23 $ echo "C" >> A
24 24 $ commit "C" 2
25 25
26 26 $ hg update -C 0
27 27 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
28 28 $ echo "D" >> A
29 29 $ commit "D" 3
30 30 created new head
31 31
32 32 State before the merge
33 33
34 34 $ hg status
35 35 $ hg id
36 36 e45016d2b3d3 tip
37 37 $ hg summary
38 38 parent: 3:e45016d2b3d3 tip
39 39 D
40 40 branch: default
41 41 commit: (clean)
42 42 update: 2 new changesets, 2 branch heads (merge)
43 43 phases: 4 draft
44 44
45 45 Testing the abort functionality first in case of conflicts
46 46
47 47 $ hg abort
48 48 abort: no merge in progress (abortflag !)
49 49 abort: no operation in progress (abortcommand !)
50 [255]
50 [20]
51 51
52 52 $ hg merge
53 53 merging A
54 54 warning: conflicts while merging A! (edit, then use 'hg resolve --mark')
55 55 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
56 56 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
57 57 [1]
58 58
59 59 $ hg merge --abort e4501
60 60 abort: cannot specify a node with --abort
61 61 [10]
62 62 $ hg merge --abort --rev e4501
63 63 abort: cannot specify both --abort and --rev
64 64 [10]
65 65
66 66 #if abortcommand
67 67 when in dry-run mode
68 68 $ hg abort --dry-run
69 69 merge in progress, will be aborted
70 70 #endif
71 71
72 72 $ hg abort
73 73 aborting the merge, updating back to e45016d2b3d3
74 74 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
75 75
76 76 Checking that we got back in the same state
77 77
78 78 $ hg status
79 79 ? A.orig
80 80 $ hg id
81 81 e45016d2b3d3 tip
82 82 $ hg summary
83 83 parent: 3:e45016d2b3d3 tip
84 84 D
85 85 branch: default
86 86 commit: 1 unknown (clean)
87 87 update: 2 new changesets, 2 branch heads (merge)
88 88 phases: 4 draft
89 89
90 90 Merging a conflict araises
91 91
92 92 $ hg merge
93 93 merging A
94 94 warning: conflicts while merging A! (edit, then use 'hg resolve --mark')
95 95 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
96 96 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
97 97 [1]
98 98
99 99 Correct the conflict without marking the file as resolved
100 100
101 101 $ echo "ABCD" > A
102 102 $ hg commit -m "Merged"
103 103 abort: unresolved merge conflicts (see 'hg help resolve')
104 104 [255]
105 105
106 106 Mark the conflict as resolved and commit
107 107
108 108 $ hg resolve -m A
109 109 (no more unresolved files)
110 110 $ hg commit -m "Merged"
111 111
112 112 Test that if a file is removed but not marked resolved, the commit still fails
113 113 (issue4972)
114 114
115 115 $ hg up ".^"
116 116 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
117 117 $ hg merge 2
118 118 merging A
119 119 warning: conflicts while merging A! (edit, then use 'hg resolve --mark')
120 120 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
121 121 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
122 122 [1]
123 123 $ hg rm --force A
124 124 $ hg commit -m merged
125 125 abort: unresolved merge conflicts (see 'hg help resolve')
126 126 [255]
127 127
128 128 $ hg resolve -ma
129 129 (no more unresolved files)
130 130 $ hg commit -m merged
131 131 created new head
132 132
133 133 Testing the abort functionality in case of no conflicts
134 134
135 135 $ hg update -C 0
136 136 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
137 137 $ addcommit "E" 4
138 138 created new head
139 139 $ hg id
140 140 68352a18a7c4 tip
141 141
142 142 $ hg merge -r 4
143 143 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
144 144 (branch merge, don't forget to commit)
145 145
146 146 $ hg merge --preview --abort
147 147 abort: cannot specify both --abort and --preview
148 148 [10]
149 149
150 150 $ hg abort
151 151 aborting the merge, updating back to 68352a18a7c4
152 152 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
153 153
154 154 $ hg id
155 155 68352a18a7c4 tip
156 156
157 157 $ cd ..
@@ -1,442 +1,442 b''
1 1 #require serve
2 2
3 3 $ echo "[extensions]" >> $HGRCPATH
4 4 $ echo "fetch=" >> $HGRCPATH
5 5
6 6 test fetch with default branches only
7 7
8 8 $ hg init a
9 9 $ echo a > a/a
10 10 $ hg --cwd a commit -Ama
11 11 adding a
12 12 $ hg clone a b
13 13 updating to branch default
14 14 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
15 15 $ hg clone a c
16 16 updating to branch default
17 17 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
18 18 $ echo b > a/b
19 19 $ hg --cwd a commit -Amb
20 20 adding b
21 21 $ hg --cwd a parents -q
22 22 1:d2ae7f538514
23 23
24 24 should pull one change
25 25
26 26 $ hg --cwd b fetch ../a
27 27 pulling from ../a
28 28 searching for changes
29 29 adding changesets
30 30 adding manifests
31 31 adding file changes
32 32 added 1 changesets with 1 changes to 1 files
33 33 new changesets d2ae7f538514
34 34 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
35 35 $ hg --cwd b parents -q
36 36 1:d2ae7f538514
37 37 $ echo c > c/c
38 38 $ hg --cwd c commit -Amc
39 39 adding c
40 40 $ hg clone c d
41 41 updating to branch default
42 42 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
43 43 $ hg clone c e
44 44 updating to branch default
45 45 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 46
47 47 We cannot use the default commit message if fetching from a local
48 48 repo, because the path of the repo will be included in the commit
49 49 message, making every commit appear different.
50 50 should merge c into a
51 51
52 52 $ hg --cwd c fetch -d '0 0' -m 'automated merge' ../a
53 53 pulling from ../a
54 54 searching for changes
55 55 adding changesets
56 56 adding manifests
57 57 adding file changes
58 58 added 1 changesets with 1 changes to 1 files (+1 heads)
59 59 new changesets d2ae7f538514
60 60 updating to 2:d2ae7f538514
61 61 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
62 62 merging with 1:d36c0562f908
63 63 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 64 new changeset 3:a323a0c43ec4 merges remote changes with local
65 65 $ ls -A c
66 66 .hg
67 67 a
68 68 b
69 69 c
70 70 $ hg serve --cwd a -a localhost -p $HGPORT -d --pid-file=hg.pid
71 71 $ cat a/hg.pid >> "$DAEMON_PIDS"
72 72
73 73 fetch over http, no auth
74 74 (this also tests that editor is invoked if '--edit' is specified)
75 75
76 76 $ HGEDITOR=cat hg --cwd d fetch --edit http://localhost:$HGPORT/
77 77 pulling from http://localhost:$HGPORT/
78 78 searching for changes
79 79 adding changesets
80 80 adding manifests
81 81 adding file changes
82 82 added 1 changesets with 1 changes to 1 files (+1 heads)
83 83 new changesets d2ae7f538514
84 84 updating to 2:d2ae7f538514
85 85 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
86 86 merging with 1:d36c0562f908
87 87 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 88 Automated merge with http://localhost:$HGPORT/
89 89
90 90
91 91 HG: Enter commit message. Lines beginning with 'HG:' are removed.
92 92 HG: Leave message empty to abort commit.
93 93 HG: --
94 94 HG: user: test
95 95 HG: branch merge
96 96 HG: branch 'default'
97 97 HG: changed c
98 98 new changeset 3:* merges remote changes with local (glob)
99 99 $ hg --cwd d tip --template '{desc}\n'
100 100 Automated merge with http://localhost:$HGPORT/
101 101 $ hg --cwd d status --rev 'tip^1' --rev tip
102 102 A c
103 103 $ hg --cwd d status --rev 'tip^2' --rev tip
104 104 A b
105 105
106 106 fetch over http with auth (should be hidden in desc)
107 107 (this also tests that editor is not invoked if '--edit' is not
108 108 specified, even though commit message is not specified explicitly)
109 109
110 110 $ HGEDITOR=cat hg --cwd e fetch http://user:password@localhost:$HGPORT/
111 111 pulling from http://user:***@localhost:$HGPORT/
112 112 searching for changes
113 113 adding changesets
114 114 adding manifests
115 115 adding file changes
116 116 added 1 changesets with 1 changes to 1 files (+1 heads)
117 117 new changesets d2ae7f538514
118 118 updating to 2:d2ae7f538514
119 119 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
120 120 merging with 1:d36c0562f908
121 121 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
122 122 new changeset 3:* merges remote changes with local (glob)
123 123 $ hg --cwd e tip --template '{desc}\n'
124 124 Automated merge with http://localhost:$HGPORT/
125 125 $ hg clone a f
126 126 updating to branch default
127 127 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
128 128 $ hg clone a g
129 129 updating to branch default
130 130 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
131 131 $ echo f > f/f
132 132 $ hg --cwd f ci -Amf
133 133 adding f
134 134 $ echo g > g/g
135 135 $ hg --cwd g ci -Amg
136 136 adding g
137 137 $ hg clone -q f h
138 138 $ hg clone -q g i
139 139
140 140 should merge f into g
141 141
142 142 $ hg --cwd g fetch -d '0 0' --switch -m 'automated merge' ../f
143 143 pulling from ../f
144 144 searching for changes
145 145 adding changesets
146 146 adding manifests
147 147 adding file changes
148 148 added 1 changesets with 1 changes to 1 files (+1 heads)
149 149 new changesets 6343ca3eff20
150 150 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
151 151 merging with 3:6343ca3eff20
152 152 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
153 153 new changeset 4:f7faa0b7d3c6 merges remote changes with local
154 154 $ rm i/g
155 155
156 156 should abort, because i is modified
157 157
158 158 $ hg --cwd i fetch ../h
159 159 abort: uncommitted changes
160 [255]
160 [20]
161 161
162 162 test fetch with named branches
163 163
164 164 $ hg init nbase
165 165 $ echo base > nbase/a
166 166 $ hg -R nbase ci -Am base
167 167 adding a
168 168 $ hg -R nbase branch a
169 169 marked working directory as branch a
170 170 (branches are permanent and global, did you want a bookmark?)
171 171 $ echo a > nbase/a
172 172 $ hg -R nbase ci -m a
173 173 $ hg -R nbase up -C 0
174 174 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
175 175 $ hg -R nbase branch b
176 176 marked working directory as branch b
177 177 $ echo b > nbase/b
178 178 $ hg -R nbase ci -Am b
179 179 adding b
180 180
181 181 pull in change on foreign branch
182 182
183 183 $ hg clone nbase n1
184 184 updating to branch default
185 185 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
186 186 $ hg clone nbase n2
187 187 updating to branch default
188 188 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
189 189 $ hg -R n1 up -C a
190 190 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
191 191 $ echo aa > n1/a
192 192 $ hg -R n1 ci -m a1
193 193 $ hg -R n2 up -C b
194 194 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
195 195 $ hg -R n2 fetch -m 'merge' n1
196 196 pulling from n1
197 197 searching for changes
198 198 adding changesets
199 199 adding manifests
200 200 adding file changes
201 201 added 1 changesets with 1 changes to 1 files
202 202 new changesets 8fdc9284bbc5
203 203
204 204 parent should be 2 (no automatic update)
205 205
206 206 $ hg -R n2 parents --template '{rev}\n'
207 207 2
208 208 $ rm -fr n1 n2
209 209
210 210 pull in changes on both foreign and local branches
211 211
212 212 $ hg clone nbase n1
213 213 updating to branch default
214 214 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
215 215 $ hg clone nbase n2
216 216 updating to branch default
217 217 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
218 218 $ hg -R n1 up -C a
219 219 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
220 220 $ echo aa > n1/a
221 221 $ hg -R n1 ci -m a1
222 222 $ hg -R n1 up -C b
223 223 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
224 224 $ echo bb > n1/b
225 225 $ hg -R n1 ci -m b1
226 226 $ hg -R n2 up -C b
227 227 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
228 228 $ hg -R n2 fetch -m 'merge' n1
229 229 pulling from n1
230 230 searching for changes
231 231 adding changesets
232 232 adding manifests
233 233 adding file changes
234 234 added 2 changesets with 2 changes to 2 files
235 235 new changesets 8fdc9284bbc5:3c4a837a864f
236 236 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
237 237
238 238 parent should be 4 (fast forward)
239 239
240 240 $ hg -R n2 parents --template '{rev}\n'
241 241 4
242 242 $ rm -fr n1 n2
243 243
244 244 pull changes on foreign (2 new heads) and local (1 new head) branches
245 245 with a local change
246 246
247 247 $ hg clone nbase n1
248 248 updating to branch default
249 249 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
250 250 $ hg clone nbase n2
251 251 updating to branch default
252 252 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
253 253 $ hg -R n1 up -C a
254 254 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
255 255 $ echo a1 > n1/a
256 256 $ hg -R n1 ci -m a1
257 257 $ hg -R n1 up -C b
258 258 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
259 259 $ echo bb > n1/b
260 260 $ hg -R n1 ci -m b1
261 261 $ hg -R n1 up -C 1
262 262 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
263 263 $ echo a2 > n1/a
264 264 $ hg -R n1 ci -m a2
265 265 created new head
266 266 $ hg -R n2 up -C b
267 267 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
268 268 $ echo change >> n2/c
269 269 $ hg -R n2 ci -A -m local
270 270 adding c
271 271 $ hg -R n2 fetch -d '0 0' -m 'merge' n1
272 272 pulling from n1
273 273 searching for changes
274 274 adding changesets
275 275 adding manifests
276 276 adding file changes
277 277 added 3 changesets with 3 changes to 2 files (+2 heads)
278 278 new changesets d05ce59ff88d:a7954de24e4c
279 279 updating to 5:3c4a837a864f
280 280 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
281 281 merging with 3:1267f84a9ea5
282 282 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
283 283 new changeset 7:2cf2a1261f21 merges remote changes with local
284 284
285 285 parent should be 7 (new merge changeset)
286 286
287 287 $ hg -R n2 parents --template '{rev}\n'
288 288 7
289 289 $ rm -fr n1 n2
290 290
291 291 pull in changes on foreign (merge of local branch) and local (2 new
292 292 heads) with a local change
293 293
294 294 $ hg clone nbase n1
295 295 updating to branch default
296 296 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
297 297 $ hg clone nbase n2
298 298 updating to branch default
299 299 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
300 300 $ hg -R n1 up -C a
301 301 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
302 302 $ hg -R n1 merge b
303 303 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
304 304 (branch merge, don't forget to commit)
305 305 $ hg -R n1 ci -m merge
306 306 $ hg -R n1 up -C 2
307 307 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
308 308 $ echo c > n1/a
309 309 $ hg -R n1 ci -m c
310 310 $ hg -R n1 up -C 2
311 311 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
312 312 $ echo cc > n1/a
313 313 $ hg -R n1 ci -m cc
314 314 created new head
315 315 $ hg -R n2 up -C b
316 316 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
317 317 $ echo change >> n2/b
318 318 $ hg -R n2 ci -A -m local
319 319 $ hg -R n2 fetch -m 'merge' n1
320 320 pulling from n1
321 321 searching for changes
322 322 adding changesets
323 323 adding manifests
324 324 adding file changes
325 325 added 3 changesets with 2 changes to 1 files (+2 heads)
326 326 new changesets b84e8d0f020f:3d3bf54f99c0
327 327 not merging with 1 other new branch heads (use "hg heads ." and "hg merge" to merge them)
328 328 [1]
329 329
330 330 parent should be 3 (fetch did not merge anything)
331 331
332 332 $ hg -R n2 parents --template '{rev}\n'
333 333 3
334 334 $ rm -fr n1 n2
335 335
336 336 pull in change on different branch than dirstate
337 337
338 338 $ hg init n1
339 339 $ echo a > n1/a
340 340 $ hg -R n1 ci -Am initial
341 341 adding a
342 342 $ hg clone n1 n2
343 343 updating to branch default
344 344 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
345 345 $ echo b > n1/a
346 346 $ hg -R n1 ci -m next
347 347 $ hg -R n2 branch topic
348 348 marked working directory as branch topic
349 349 (branches are permanent and global, did you want a bookmark?)
350 350 $ hg -R n2 fetch -m merge n1
351 351 abort: working directory not at branch tip
352 352 (use 'hg update' to check out branch tip)
353 353 [255]
354 354
355 355 parent should be 0 (fetch did not update or merge anything)
356 356
357 357 $ hg -R n2 parents --template '{rev}\n'
358 358 0
359 359 $ rm -fr n1 n2
360 360
361 361 test fetch with inactive branches
362 362
363 363 $ hg init ib1
364 364 $ echo a > ib1/a
365 365 $ hg --cwd ib1 ci -Am base
366 366 adding a
367 367 $ hg --cwd ib1 branch second
368 368 marked working directory as branch second
369 369 (branches are permanent and global, did you want a bookmark?)
370 370 $ echo b > ib1/b
371 371 $ hg --cwd ib1 ci -Am onsecond
372 372 adding b
373 373 $ hg --cwd ib1 branch -f default
374 374 marked working directory as branch default
375 375 $ echo c > ib1/c
376 376 $ hg --cwd ib1 ci -Am newdefault
377 377 adding c
378 378 created new head
379 379 $ hg clone ib1 ib2
380 380 updating to branch default
381 381 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
382 382
383 383 fetch should succeed
384 384
385 385 $ hg --cwd ib2 fetch ../ib1
386 386 pulling from ../ib1
387 387 searching for changes
388 388 no changes found
389 389 $ rm -fr ib1 ib2
390 390
391 391 test issue1726
392 392
393 393 $ hg init i1726r1
394 394 $ echo a > i1726r1/a
395 395 $ hg --cwd i1726r1 ci -Am base
396 396 adding a
397 397 $ hg clone i1726r1 i1726r2
398 398 updating to branch default
399 399 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
400 400 $ echo b > i1726r1/a
401 401 $ hg --cwd i1726r1 ci -m second
402 402 $ echo c > i1726r2/a
403 403 $ hg --cwd i1726r2 ci -m third
404 404 $ HGMERGE=true hg --cwd i1726r2 fetch ../i1726r1
405 405 pulling from ../i1726r1
406 406 searching for changes
407 407 adding changesets
408 408 adding manifests
409 409 adding file changes
410 410 added 1 changesets with 1 changes to 1 files (+1 heads)
411 411 new changesets 7837755a2789
412 412 updating to 2:7837755a2789
413 413 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
414 414 merging with 1:d1f0c6c48ebd
415 415 merging a
416 416 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
417 417 new changeset 3:* merges remote changes with local (glob)
418 418 $ hg --cwd i1726r2 heads default --template '{rev}\n'
419 419 3
420 420
421 421 test issue2047
422 422
423 423 $ hg -q init i2047a
424 424 $ cd i2047a
425 425 $ echo a > a
426 426 $ hg -q ci -Am a
427 427 $ hg -q branch stable
428 428 $ echo b > b
429 429 $ hg -q ci -Am b
430 430 $ cd ..
431 431 $ hg -q clone -r 0 i2047a i2047b
432 432 $ cd i2047b
433 433 $ hg fetch ../i2047a
434 434 pulling from ../i2047a
435 435 searching for changes
436 436 adding changesets
437 437 adding manifests
438 438 adding file changes
439 439 added 1 changesets with 1 changes to 1 files
440 440 new changesets c8735224de5c
441 441
442 442 $ cd ..
@@ -1,1693 +1,1693 b''
1 1 A script that implements uppercasing of specific lines in a file. This
2 2 approximates the behavior of code formatters well enough for our tests.
3 3
4 4 $ UPPERCASEPY="$TESTTMP/uppercase.py"
5 5 $ cat > $UPPERCASEPY <<EOF
6 6 > import sys
7 7 > from mercurial.utils.procutil import setbinary
8 8 > setbinary(sys.stdin)
9 9 > setbinary(sys.stdout)
10 10 > lines = set()
11 11 > for arg in sys.argv[1:]:
12 12 > if arg == 'all':
13 13 > sys.stdout.write(sys.stdin.read().upper())
14 14 > sys.exit(0)
15 15 > else:
16 16 > first, last = arg.split('-')
17 17 > lines.update(range(int(first), int(last) + 1))
18 18 > for i, line in enumerate(sys.stdin.readlines()):
19 19 > if i + 1 in lines:
20 20 > sys.stdout.write(line.upper())
21 21 > else:
22 22 > sys.stdout.write(line)
23 23 > EOF
24 24 $ TESTLINES="foo\nbar\nbaz\nqux\n"
25 25 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY
26 26 foo
27 27 bar
28 28 baz
29 29 qux
30 30 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY all
31 31 FOO
32 32 BAR
33 33 BAZ
34 34 QUX
35 35 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 1-1
36 36 FOO
37 37 bar
38 38 baz
39 39 qux
40 40 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 1-2
41 41 FOO
42 42 BAR
43 43 baz
44 44 qux
45 45 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 2-3
46 46 foo
47 47 BAR
48 48 BAZ
49 49 qux
50 50 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 2-2 4-4
51 51 foo
52 52 BAR
53 53 baz
54 54 QUX
55 55
56 56 Set up the config with two simple fixers: one that fixes specific line ranges,
57 57 and one that always fixes the whole file. They both "fix" files by converting
58 58 letters to uppercase. They use different file extensions, so each test case can
59 59 choose which behavior to use by naming files.
60 60
61 61 $ cat >> $HGRCPATH <<EOF
62 62 > [extensions]
63 63 > fix =
64 64 > [experimental]
65 65 > evolution.createmarkers=True
66 66 > evolution.allowunstable=True
67 67 > [fix]
68 68 > uppercase-whole-file:command="$PYTHON" $UPPERCASEPY all
69 69 > uppercase-whole-file:pattern=set:**.whole
70 70 > uppercase-changed-lines:command="$PYTHON" $UPPERCASEPY
71 71 > uppercase-changed-lines:linerange={first}-{last}
72 72 > uppercase-changed-lines:pattern=set:**.changed
73 73 > EOF
74 74
75 75 Help text for fix.
76 76
77 77 $ hg help fix
78 78 hg fix [OPTION]... [FILE]...
79 79
80 80 rewrite file content in changesets or working directory
81 81
82 82 Runs any configured tools to fix the content of files. Only affects files
83 83 with changes, unless file arguments are provided. Only affects changed
84 84 lines of files, unless the --whole flag is used. Some tools may always
85 85 affect the whole file regardless of --whole.
86 86
87 87 If --working-dir is used, files with uncommitted changes in the working
88 88 copy will be fixed. Note that no backup are made.
89 89
90 90 If revisions are specified with --source, those revisions and their
91 91 descendants will be checked, and they may be replaced with new revisions
92 92 that have fixed file content. By automatically including the descendants,
93 93 no merging, rebasing, or evolution will be required. If an ancestor of the
94 94 working copy is included, then the working copy itself will also be fixed,
95 95 and the working copy will be updated to the fixed parent.
96 96
97 97 When determining what lines of each file to fix at each revision, the
98 98 whole set of revisions being fixed is considered, so that fixes to earlier
99 99 revisions are not forgotten in later ones. The --base flag can be used to
100 100 override this default behavior, though it is not usually desirable to do
101 101 so.
102 102
103 103 (use 'hg help -e fix' to show help for the fix extension)
104 104
105 105 options ([+] can be repeated):
106 106
107 107 --all fix all non-public non-obsolete revisions
108 108 --base REV [+] revisions to diff against (overrides automatic selection,
109 109 and applies to every revision being fixed)
110 110 -s --source REV [+] fix the specified revisions and their descendants
111 111 -w --working-dir fix the working directory
112 112 --whole always fix every line of a file
113 113
114 114 (some details hidden, use --verbose to show complete help)
115 115
116 116 $ hg help -e fix
117 117 fix extension - rewrite file content in changesets or working copy
118 118 (EXPERIMENTAL)
119 119
120 120 Provides a command that runs configured tools on the contents of modified
121 121 files, writing back any fixes to the working copy or replacing changesets.
122 122
123 123 Here is an example configuration that causes 'hg fix' to apply automatic
124 124 formatting fixes to modified lines in C++ code:
125 125
126 126 [fix]
127 127 clang-format:command=clang-format --assume-filename={rootpath}
128 128 clang-format:linerange=--lines={first}:{last}
129 129 clang-format:pattern=set:**.cpp or **.hpp
130 130
131 131 The :command suboption forms the first part of the shell command that will be
132 132 used to fix a file. The content of the file is passed on standard input, and
133 133 the fixed file content is expected on standard output. Any output on standard
134 134 error will be displayed as a warning. If the exit status is not zero, the file
135 135 will not be affected. A placeholder warning is displayed if there is a non-
136 136 zero exit status but no standard error output. Some values may be substituted
137 137 into the command:
138 138
139 139 {rootpath} The path of the file being fixed, relative to the repo root
140 140 {basename} The name of the file being fixed, without the directory path
141 141
142 142 If the :linerange suboption is set, the tool will only be run if there are
143 143 changed lines in a file. The value of this suboption is appended to the shell
144 144 command once for every range of changed lines in the file. Some values may be
145 145 substituted into the command:
146 146
147 147 {first} The 1-based line number of the first line in the modified range
148 148 {last} The 1-based line number of the last line in the modified range
149 149
150 150 Deleted sections of a file will be ignored by :linerange, because there is no
151 151 corresponding line range in the version being fixed.
152 152
153 153 By default, tools that set :linerange will only be executed if there is at
154 154 least one changed line range. This is meant to prevent accidents like running
155 155 a code formatter in such a way that it unexpectedly reformats the whole file.
156 156 If such a tool needs to operate on unchanged files, it should set the
157 157 :skipclean suboption to false.
158 158
159 159 The :pattern suboption determines which files will be passed through each
160 160 configured tool. See 'hg help patterns' for possible values. However, all
161 161 patterns are relative to the repo root, even if that text says they are
162 162 relative to the current working directory. If there are file arguments to 'hg
163 163 fix', the intersection of these patterns is used.
164 164
165 165 There is also a configurable limit for the maximum size of file that will be
166 166 processed by 'hg fix':
167 167
168 168 [fix]
169 169 maxfilesize = 2MB
170 170
171 171 Normally, execution of configured tools will continue after a failure
172 172 (indicated by a non-zero exit status). It can also be configured to abort
173 173 after the first such failure, so that no files will be affected if any tool
174 174 fails. This abort will also cause 'hg fix' to exit with a non-zero status:
175 175
176 176 [fix]
177 177 failure = abort
178 178
179 179 When multiple tools are configured to affect a file, they execute in an order
180 180 defined by the :priority suboption. The priority suboption has a default value
181 181 of zero for each tool. Tools are executed in order of descending priority. The
182 182 execution order of tools with equal priority is unspecified. For example, you
183 183 could use the 'sort' and 'head' utilities to keep only the 10 smallest numbers
184 184 in a text file by ensuring that 'sort' runs before 'head':
185 185
186 186 [fix]
187 187 sort:command = sort -n
188 188 head:command = head -n 10
189 189 sort:pattern = numbers.txt
190 190 head:pattern = numbers.txt
191 191 sort:priority = 2
192 192 head:priority = 1
193 193
194 194 To account for changes made by each tool, the line numbers used for
195 195 incremental formatting are recomputed before executing the next tool. So, each
196 196 tool may see different values for the arguments added by the :linerange
197 197 suboption.
198 198
199 199 Each fixer tool is allowed to return some metadata in addition to the fixed
200 200 file content. The metadata must be placed before the file content on stdout,
201 201 separated from the file content by a zero byte. The metadata is parsed as a
202 202 JSON value (so, it should be UTF-8 encoded and contain no zero bytes). A fixer
203 203 tool is expected to produce this metadata encoding if and only if the
204 204 :metadata suboption is true:
205 205
206 206 [fix]
207 207 tool:command = tool --prepend-json-metadata
208 208 tool:metadata = true
209 209
210 210 The metadata values are passed to hooks, which can be used to print summaries
211 211 or perform other post-fixing work. The supported hooks are:
212 212
213 213 "postfixfile"
214 214 Run once for each file in each revision where any fixer tools made changes
215 215 to the file content. Provides "$HG_REV" and "$HG_PATH" to identify the file,
216 216 and "$HG_METADATA" with a map of fixer names to metadata values from fixer
217 217 tools that affected the file. Fixer tools that didn't affect the file have a
218 218 value of None. Only fixer tools that executed are present in the metadata.
219 219
220 220 "postfix"
221 221 Run once after all files and revisions have been handled. Provides
222 222 "$HG_REPLACEMENTS" with information about what revisions were created and
223 223 made obsolete. Provides a boolean "$HG_WDIRWRITTEN" to indicate whether any
224 224 files in the working copy were updated. Provides a list "$HG_METADATA"
225 225 mapping fixer tool names to lists of metadata values returned from
226 226 executions that modified a file. This aggregates the same metadata
227 227 previously passed to the "postfixfile" hook.
228 228
229 229 Fixer tools are run in the repository's root directory. This allows them to
230 230 read configuration files from the working copy, or even write to the working
231 231 copy. The working copy is not updated to match the revision being fixed. In
232 232 fact, several revisions may be fixed in parallel. Writes to the working copy
233 233 are not amended into the revision being fixed; fixer tools should always write
234 234 fixed file content back to stdout as documented above.
235 235
236 236 list of commands:
237 237
238 238 fix rewrite file content in changesets or working directory
239 239
240 240 (use 'hg help -v -e fix' to show built-in aliases and global options)
241 241
242 242 There is no default behavior in the absence of --rev and --working-dir.
243 243
244 244 $ hg init badusage
245 245 $ cd badusage
246 246
247 247 $ hg fix
248 248 abort: no changesets specified
249 249 (use --source or --working-dir)
250 250 [255]
251 251 $ hg fix --whole
252 252 abort: no changesets specified
253 253 (use --source or --working-dir)
254 254 [255]
255 255 $ hg fix --base 0
256 256 abort: no changesets specified
257 257 (use --source or --working-dir)
258 258 [255]
259 259
260 260 Fixing a public revision isn't allowed. It should abort early enough that
261 261 nothing happens, even to the working directory.
262 262
263 263 $ printf "hello\n" > hello.whole
264 264 $ hg commit -Aqm "hello"
265 265 $ hg phase -r 0 --public
266 266 $ hg fix -r 0
267 267 abort: cannot fix public changesets
268 268 (see 'hg help phases' for details)
269 269 [255]
270 270 $ hg fix -r 0 --working-dir
271 271 abort: cannot fix public changesets
272 272 (see 'hg help phases' for details)
273 273 [255]
274 274 $ hg cat -r tip hello.whole
275 275 hello
276 276 $ cat hello.whole
277 277 hello
278 278
279 279 $ cd ..
280 280
281 281 Fixing a clean working directory should do nothing. Even the --whole flag
282 282 shouldn't cause any clean files to be fixed. Specifying a clean file explicitly
283 283 should only fix it if the fixer always fixes the whole file. The combination of
284 284 an explicit filename and --whole should format the entire file regardless.
285 285
286 286 $ hg init fixcleanwdir
287 287 $ cd fixcleanwdir
288 288
289 289 $ printf "hello\n" > hello.changed
290 290 $ printf "world\n" > hello.whole
291 291 $ hg commit -Aqm "foo"
292 292 $ hg fix --working-dir
293 293 $ hg diff
294 294 $ hg fix --working-dir --whole
295 295 $ hg diff
296 296 $ hg fix --working-dir *
297 297 $ cat *
298 298 hello
299 299 WORLD
300 300 $ hg revert --all --no-backup
301 301 reverting hello.whole
302 302 $ hg fix --working-dir * --whole
303 303 $ cat *
304 304 HELLO
305 305 WORLD
306 306
307 307 The same ideas apply to fixing a revision, so we create a revision that doesn't
308 308 modify either of the files in question and try fixing it. This also tests that
309 309 we ignore a file that doesn't match any configured fixer.
310 310
311 311 $ hg revert --all --no-backup
312 312 reverting hello.changed
313 313 reverting hello.whole
314 314 $ printf "unimportant\n" > some.file
315 315 $ hg commit -Aqm "some other file"
316 316
317 317 $ hg fix -r .
318 318 $ hg cat -r tip *
319 319 hello
320 320 world
321 321 unimportant
322 322 $ hg fix -r . --whole
323 323 $ hg cat -r tip *
324 324 hello
325 325 world
326 326 unimportant
327 327 $ hg fix -r . *
328 328 $ hg cat -r tip *
329 329 hello
330 330 WORLD
331 331 unimportant
332 332 $ hg fix -r . * --whole --config experimental.evolution.allowdivergence=true
333 333 2 new content-divergent changesets
334 334 $ hg cat -r tip *
335 335 HELLO
336 336 WORLD
337 337 unimportant
338 338
339 339 $ cd ..
340 340
341 341 Fixing the working directory should still work if there are no revisions.
342 342
343 343 $ hg init norevisions
344 344 $ cd norevisions
345 345
346 346 $ printf "something\n" > something.whole
347 347 $ hg add
348 348 adding something.whole
349 349 $ hg fix --working-dir
350 350 $ cat something.whole
351 351 SOMETHING
352 352
353 353 $ cd ..
354 354
355 355 Test the effect of fixing the working directory for each possible status, with
356 356 and without providing explicit file arguments.
357 357
358 358 $ hg init implicitlyfixstatus
359 359 $ cd implicitlyfixstatus
360 360
361 361 $ printf "modified\n" > modified.whole
362 362 $ printf "removed\n" > removed.whole
363 363 $ printf "deleted\n" > deleted.whole
364 364 $ printf "clean\n" > clean.whole
365 365 $ printf "ignored.whole" > .hgignore
366 366 $ hg commit -Aqm "stuff"
367 367
368 368 $ printf "modified!!!\n" > modified.whole
369 369 $ printf "unknown\n" > unknown.whole
370 370 $ printf "ignored\n" > ignored.whole
371 371 $ printf "added\n" > added.whole
372 372 $ hg add added.whole
373 373 $ hg remove removed.whole
374 374 $ rm deleted.whole
375 375
376 376 $ hg status --all
377 377 M modified.whole
378 378 A added.whole
379 379 R removed.whole
380 380 ! deleted.whole
381 381 ? unknown.whole
382 382 I ignored.whole
383 383 C .hgignore
384 384 C clean.whole
385 385
386 386 $ hg fix --working-dir
387 387
388 388 $ hg status --all
389 389 M modified.whole
390 390 A added.whole
391 391 R removed.whole
392 392 ! deleted.whole
393 393 ? unknown.whole
394 394 I ignored.whole
395 395 C .hgignore
396 396 C clean.whole
397 397
398 398 $ cat *.whole
399 399 ADDED
400 400 clean
401 401 ignored
402 402 MODIFIED!!!
403 403 unknown
404 404
405 405 $ printf "modified!!!\n" > modified.whole
406 406 $ printf "added\n" > added.whole
407 407
408 408 Listing the files explicitly causes untracked files to also be fixed, but
409 409 ignored files are still unaffected.
410 410
411 411 $ hg fix --working-dir *.whole
412 412
413 413 $ hg status --all
414 414 M clean.whole
415 415 M modified.whole
416 416 A added.whole
417 417 R removed.whole
418 418 ! deleted.whole
419 419 ? unknown.whole
420 420 I ignored.whole
421 421 C .hgignore
422 422
423 423 $ cat *.whole
424 424 ADDED
425 425 CLEAN
426 426 ignored
427 427 MODIFIED!!!
428 428 UNKNOWN
429 429
430 430 $ cd ..
431 431
432 432 Test that incremental fixing works on files with additions, deletions, and
433 433 changes in multiple line ranges. Note that deletions do not generally cause
434 434 neighboring lines to be fixed, so we don't return a line range for purely
435 435 deleted sections. In the future we should support a :deletion config that
436 436 allows fixers to know where deletions are located.
437 437
438 438 $ hg init incrementalfixedlines
439 439 $ cd incrementalfixedlines
440 440
441 441 $ printf "a\nb\nc\nd\ne\nf\ng\n" > foo.txt
442 442 $ hg commit -Aqm "foo"
443 443 $ printf "zz\na\nc\ndd\nee\nff\nf\ngg\n" > foo.txt
444 444
445 445 $ hg --config "fix.fail:command=echo" \
446 446 > --config "fix.fail:linerange={first}:{last}" \
447 447 > --config "fix.fail:pattern=foo.txt" \
448 448 > fix --working-dir
449 449 $ cat foo.txt
450 450 1:1 4:6 8:8
451 451
452 452 $ cd ..
453 453
454 454 Test that --whole fixes all lines regardless of the diffs present.
455 455
456 456 $ hg init wholeignoresdiffs
457 457 $ cd wholeignoresdiffs
458 458
459 459 $ printf "a\nb\nc\nd\ne\nf\ng\n" > foo.changed
460 460 $ hg commit -Aqm "foo"
461 461 $ printf "zz\na\nc\ndd\nee\nff\nf\ngg\n" > foo.changed
462 462
463 463 $ hg fix --working-dir
464 464 $ cat foo.changed
465 465 ZZ
466 466 a
467 467 c
468 468 DD
469 469 EE
470 470 FF
471 471 f
472 472 GG
473 473
474 474 $ hg fix --working-dir --whole
475 475 $ cat foo.changed
476 476 ZZ
477 477 A
478 478 C
479 479 DD
480 480 EE
481 481 FF
482 482 F
483 483 GG
484 484
485 485 $ cd ..
486 486
487 487 We should do nothing with symlinks, and their targets should be unaffected. Any
488 488 other behavior would be more complicated to implement and harder to document.
489 489
490 490 #if symlink
491 491 $ hg init dontmesswithsymlinks
492 492 $ cd dontmesswithsymlinks
493 493
494 494 $ printf "hello\n" > hello.whole
495 495 $ ln -s hello.whole hellolink
496 496 $ hg add
497 497 adding hello.whole
498 498 adding hellolink
499 499 $ hg fix --working-dir hellolink
500 500 $ hg status
501 501 A hello.whole
502 502 A hellolink
503 503
504 504 $ cd ..
505 505 #endif
506 506
507 507 We should allow fixers to run on binary files, even though this doesn't sound
508 508 like a common use case. There's not much benefit to disallowing it, and users
509 509 can add "and not binary()" to their filesets if needed. The Mercurial
510 510 philosophy is generally to not handle binary files specially anyway.
511 511
512 512 $ hg init cantouchbinaryfiles
513 513 $ cd cantouchbinaryfiles
514 514
515 515 $ printf "hello\0\n" > hello.whole
516 516 $ hg add
517 517 adding hello.whole
518 518 $ hg fix --working-dir 'set:binary()'
519 519 $ cat hello.whole
520 520 HELLO\x00 (esc)
521 521
522 522 $ cd ..
523 523
524 524 We have a config for the maximum size of file we will attempt to fix. This can
525 525 be helpful to avoid running unsuspecting fixer tools on huge inputs, which
526 526 could happen by accident without a well considered configuration. A more
527 527 precise configuration could use the size() fileset function if one global limit
528 528 is undesired.
529 529
530 530 $ hg init maxfilesize
531 531 $ cd maxfilesize
532 532
533 533 $ printf "this file is huge\n" > hello.whole
534 534 $ hg add
535 535 adding hello.whole
536 536 $ hg --config fix.maxfilesize=10 fix --working-dir
537 537 ignoring file larger than 10 bytes: hello.whole
538 538 $ cat hello.whole
539 539 this file is huge
540 540
541 541 $ cd ..
542 542
543 543 If we specify a file to fix, other files should be left alone, even if they
544 544 have changes.
545 545
546 546 $ hg init fixonlywhatitellyouto
547 547 $ cd fixonlywhatitellyouto
548 548
549 549 $ printf "fix me!\n" > fixme.whole
550 550 $ printf "not me.\n" > notme.whole
551 551 $ hg add
552 552 adding fixme.whole
553 553 adding notme.whole
554 554 $ hg fix --working-dir fixme.whole
555 555 $ cat *.whole
556 556 FIX ME!
557 557 not me.
558 558
559 559 $ cd ..
560 560
561 561 If we try to fix a missing file, we still fix other files.
562 562
563 563 $ hg init fixmissingfile
564 564 $ cd fixmissingfile
565 565
566 566 $ printf "fix me!\n" > foo.whole
567 567 $ hg add
568 568 adding foo.whole
569 569 $ hg fix --working-dir foo.whole bar.whole
570 570 bar.whole: $ENOENT$
571 571 $ cat *.whole
572 572 FIX ME!
573 573
574 574 $ cd ..
575 575
576 576 Specifying a directory name should fix all its files and subdirectories.
577 577
578 578 $ hg init fixdirectory
579 579 $ cd fixdirectory
580 580
581 581 $ mkdir -p dir1/dir2
582 582 $ printf "foo\n" > foo.whole
583 583 $ printf "bar\n" > dir1/bar.whole
584 584 $ printf "baz\n" > dir1/dir2/baz.whole
585 585 $ hg add
586 586 adding dir1/bar.whole
587 587 adding dir1/dir2/baz.whole
588 588 adding foo.whole
589 589 $ hg fix --working-dir dir1
590 590 $ cat foo.whole dir1/bar.whole dir1/dir2/baz.whole
591 591 foo
592 592 BAR
593 593 BAZ
594 594
595 595 $ cd ..
596 596
597 597 Fixing a file in the working directory that needs no fixes should not actually
598 598 write back to the file, so for example the mtime shouldn't change.
599 599
600 600 $ hg init donttouchunfixedfiles
601 601 $ cd donttouchunfixedfiles
602 602
603 603 $ printf "NO FIX NEEDED\n" > foo.whole
604 604 $ hg add
605 605 adding foo.whole
606 606 $ cp -p foo.whole foo.whole.orig
607 607 $ cp -p foo.whole.orig foo.whole
608 608 $ sleep 2 # mtime has a resolution of one or two seconds.
609 609 $ hg fix --working-dir
610 610 $ f foo.whole.orig --newer foo.whole
611 611 foo.whole.orig: newer than foo.whole
612 612
613 613 $ cd ..
614 614
615 615 When a fixer prints to stderr, we don't assume that it has failed. We show the
616 616 error messages to the user, and we still let the fixer affect the file it was
617 617 fixing if its exit code is zero. Some code formatters might emit error messages
618 618 on stderr and nothing on stdout, which would cause us the clear the file,
619 619 except that they also exit with a non-zero code. We show the user which fixer
620 620 emitted the stderr, and which revision, but we assume that the fixer will print
621 621 the filename if it is relevant (since the issue may be non-specific). There is
622 622 also a config to abort (without affecting any files whatsoever) if we see any
623 623 tool with a non-zero exit status.
624 624
625 625 $ hg init showstderr
626 626 $ cd showstderr
627 627
628 628 $ printf "hello\n" > hello.txt
629 629 $ hg add
630 630 adding hello.txt
631 631 $ cat > $TESTTMP/work.sh <<'EOF'
632 632 > printf 'HELLO\n'
633 633 > printf "$@: some\nerror that didn't stop the tool" >&2
634 634 > exit 0 # success despite the stderr output
635 635 > EOF
636 636 $ hg --config "fix.work:command=sh $TESTTMP/work.sh {rootpath}" \
637 637 > --config "fix.work:pattern=hello.txt" \
638 638 > fix --working-dir
639 639 [wdir] work: hello.txt: some
640 640 [wdir] work: error that didn't stop the tool
641 641 $ cat hello.txt
642 642 HELLO
643 643
644 644 $ printf "goodbye\n" > hello.txt
645 645 $ printf "foo\n" > foo.whole
646 646 $ hg add
647 647 adding foo.whole
648 648 $ cat > $TESTTMP/fail.sh <<'EOF'
649 649 > printf 'GOODBYE\n'
650 650 > printf "$@: some\nerror that did stop the tool\n" >&2
651 651 > exit 42 # success despite the stdout output
652 652 > EOF
653 653 $ hg --config "fix.fail:command=sh $TESTTMP/fail.sh {rootpath}" \
654 654 > --config "fix.fail:pattern=hello.txt" \
655 655 > --config "fix.failure=abort" \
656 656 > fix --working-dir
657 657 [wdir] fail: hello.txt: some
658 658 [wdir] fail: error that did stop the tool
659 659 abort: no fixes will be applied
660 660 (use --config fix.failure=continue to apply any successful fixes anyway)
661 661 [255]
662 662 $ cat hello.txt
663 663 goodbye
664 664 $ cat foo.whole
665 665 foo
666 666
667 667 $ hg --config "fix.fail:command=sh $TESTTMP/fail.sh {rootpath}" \
668 668 > --config "fix.fail:pattern=hello.txt" \
669 669 > fix --working-dir
670 670 [wdir] fail: hello.txt: some
671 671 [wdir] fail: error that did stop the tool
672 672 $ cat hello.txt
673 673 goodbye
674 674 $ cat foo.whole
675 675 FOO
676 676
677 677 $ hg --config "fix.fail:command=exit 42" \
678 678 > --config "fix.fail:pattern=hello.txt" \
679 679 > fix --working-dir
680 680 [wdir] fail: exited with status 42
681 681
682 682 $ cd ..
683 683
684 684 Fixing the working directory and its parent revision at the same time should
685 685 check out the replacement revision for the parent. This prevents any new
686 686 uncommitted changes from appearing. We test this for a clean working directory
687 687 and a dirty one. In both cases, all lines/files changed since the grandparent
688 688 will be fixed. The grandparent is the "baserev" for both the parent and the
689 689 working copy.
690 690
691 691 $ hg init fixdotandcleanwdir
692 692 $ cd fixdotandcleanwdir
693 693
694 694 $ printf "hello\n" > hello.whole
695 695 $ printf "world\n" > world.whole
696 696 $ hg commit -Aqm "the parent commit"
697 697
698 698 $ hg parents --template '{rev} {desc}\n'
699 699 0 the parent commit
700 700 $ hg fix --working-dir -r .
701 701 $ hg parents --template '{rev} {desc}\n'
702 702 1 the parent commit
703 703 $ hg cat -r . *.whole
704 704 HELLO
705 705 WORLD
706 706 $ cat *.whole
707 707 HELLO
708 708 WORLD
709 709 $ hg status
710 710
711 711 $ cd ..
712 712
713 713 Same test with a dirty working copy.
714 714
715 715 $ hg init fixdotanddirtywdir
716 716 $ cd fixdotanddirtywdir
717 717
718 718 $ printf "hello\n" > hello.whole
719 719 $ printf "world\n" > world.whole
720 720 $ hg commit -Aqm "the parent commit"
721 721
722 722 $ printf "hello,\n" > hello.whole
723 723 $ printf "world!\n" > world.whole
724 724
725 725 $ hg parents --template '{rev} {desc}\n'
726 726 0 the parent commit
727 727 $ hg fix --working-dir -r .
728 728 $ hg parents --template '{rev} {desc}\n'
729 729 1 the parent commit
730 730 $ hg cat -r . *.whole
731 731 HELLO
732 732 WORLD
733 733 $ cat *.whole
734 734 HELLO,
735 735 WORLD!
736 736 $ hg status
737 737 M hello.whole
738 738 M world.whole
739 739
740 740 $ cd ..
741 741
742 742 When we have a chain of commits that change mutually exclusive lines of code,
743 743 we should be able to do incremental fixing that causes each commit in the chain
744 744 to include fixes made to the previous commits. This prevents children from
745 745 backing out the fixes made in their parents. A dirty working directory is
746 746 conceptually similar to another commit in the chain.
747 747
748 748 $ hg init incrementallyfixchain
749 749 $ cd incrementallyfixchain
750 750
751 751 $ cat > file.changed <<EOF
752 752 > first
753 753 > second
754 754 > third
755 755 > fourth
756 756 > fifth
757 757 > EOF
758 758 $ hg commit -Aqm "the common ancestor (the baserev)"
759 759 $ cat > file.changed <<EOF
760 760 > first (changed)
761 761 > second
762 762 > third
763 763 > fourth
764 764 > fifth
765 765 > EOF
766 766 $ hg commit -Aqm "the first commit to fix"
767 767 $ cat > file.changed <<EOF
768 768 > first (changed)
769 769 > second
770 770 > third (changed)
771 771 > fourth
772 772 > fifth
773 773 > EOF
774 774 $ hg commit -Aqm "the second commit to fix"
775 775 $ cat > file.changed <<EOF
776 776 > first (changed)
777 777 > second
778 778 > third (changed)
779 779 > fourth
780 780 > fifth (changed)
781 781 > EOF
782 782
783 783 $ hg fix -r . -r '.^' --working-dir
784 784
785 785 $ hg parents --template '{rev}\n'
786 786 4
787 787 $ hg cat -r '.^^' file.changed
788 788 first
789 789 second
790 790 third
791 791 fourth
792 792 fifth
793 793 $ hg cat -r '.^' file.changed
794 794 FIRST (CHANGED)
795 795 second
796 796 third
797 797 fourth
798 798 fifth
799 799 $ hg cat -r . file.changed
800 800 FIRST (CHANGED)
801 801 second
802 802 THIRD (CHANGED)
803 803 fourth
804 804 fifth
805 805 $ cat file.changed
806 806 FIRST (CHANGED)
807 807 second
808 808 THIRD (CHANGED)
809 809 fourth
810 810 FIFTH (CHANGED)
811 811
812 812 $ cd ..
813 813
814 814 If we incrementally fix a merge commit, we should fix any lines that changed
815 815 versus either parent. You could imagine only fixing the intersection or some
816 816 other subset, but this is necessary if either parent is being fixed. It
817 817 prevents us from forgetting fixes made in either parent.
818 818
819 819 $ hg init incrementallyfixmergecommit
820 820 $ cd incrementallyfixmergecommit
821 821
822 822 $ printf "a\nb\nc\n" > file.changed
823 823 $ hg commit -Aqm "ancestor"
824 824
825 825 $ printf "aa\nb\nc\n" > file.changed
826 826 $ hg commit -m "change a"
827 827
828 828 $ hg checkout '.^'
829 829 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
830 830 $ printf "a\nb\ncc\n" > file.changed
831 831 $ hg commit -m "change c"
832 832 created new head
833 833
834 834 $ hg merge
835 835 merging file.changed
836 836 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
837 837 (branch merge, don't forget to commit)
838 838 $ hg commit -m "merge"
839 839 $ hg cat -r . file.changed
840 840 aa
841 841 b
842 842 cc
843 843
844 844 $ hg fix -r . --working-dir
845 845 $ hg cat -r . file.changed
846 846 AA
847 847 b
848 848 CC
849 849
850 850 $ cd ..
851 851
852 852 Abort fixing revisions if there is an unfinished operation. We don't want to
853 853 make things worse by editing files or stripping/obsoleting things. Also abort
854 854 fixing the working directory if there are unresolved merge conflicts.
855 855
856 856 $ hg init abortunresolved
857 857 $ cd abortunresolved
858 858
859 859 $ echo "foo1" > foo.whole
860 860 $ hg commit -Aqm "foo 1"
861 861
862 862 $ hg update null
863 863 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
864 864 $ echo "foo2" > foo.whole
865 865 $ hg commit -Aqm "foo 2"
866 866
867 867 $ hg --config extensions.rebase= rebase -r 1 -d 0
868 868 rebasing 1:c3b6dc0e177a tip "foo 2"
869 869 merging foo.whole
870 870 warning: conflicts while merging foo.whole! (edit, then use 'hg resolve --mark')
871 871 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
872 872 [240]
873 873
874 874 $ hg --config extensions.rebase= fix --working-dir
875 875 abort: unresolved conflicts
876 876 (use 'hg resolve')
877 877 [255]
878 878
879 879 $ hg --config extensions.rebase= fix -r .
880 880 abort: rebase in progress
881 881 (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop')
882 [255]
882 [20]
883 883
884 884 $ cd ..
885 885
886 886 When fixing a file that was renamed, we should diff against the source of the
887 887 rename for incremental fixing and we should correctly reproduce the rename in
888 888 the replacement revision.
889 889
890 890 $ hg init fixrenamecommit
891 891 $ cd fixrenamecommit
892 892
893 893 $ printf "a\nb\nc\n" > source.changed
894 894 $ hg commit -Aqm "source revision"
895 895 $ hg move source.changed dest.changed
896 896 $ printf "a\nb\ncc\n" > dest.changed
897 897 $ hg commit -m "dest revision"
898 898
899 899 $ hg fix -r .
900 900 $ hg log -r tip --copies --template "{file_copies}\n"
901 901 dest.changed (source.changed)
902 902 $ hg cat -r tip dest.changed
903 903 a
904 904 b
905 905 CC
906 906
907 907 $ cd ..
908 908
909 909 When fixing revisions that remove files we must ensure that the replacement
910 910 actually removes the file, whereas it could accidentally leave it unchanged or
911 911 write an empty string to it.
912 912
913 913 $ hg init fixremovedfile
914 914 $ cd fixremovedfile
915 915
916 916 $ printf "foo\n" > foo.whole
917 917 $ printf "bar\n" > bar.whole
918 918 $ hg commit -Aqm "add files"
919 919 $ hg remove bar.whole
920 920 $ hg commit -m "remove file"
921 921 $ hg status --change .
922 922 R bar.whole
923 923 $ hg fix -r . foo.whole
924 924 $ hg status --change tip
925 925 M foo.whole
926 926 R bar.whole
927 927
928 928 $ cd ..
929 929
930 930 If fixing a revision finds no fixes to make, no replacement revision should be
931 931 created.
932 932
933 933 $ hg init nofixesneeded
934 934 $ cd nofixesneeded
935 935
936 936 $ printf "FOO\n" > foo.whole
937 937 $ hg commit -Aqm "add file"
938 938 $ hg log --template '{rev}\n'
939 939 0
940 940 $ hg fix -r .
941 941 $ hg log --template '{rev}\n'
942 942 0
943 943
944 944 $ cd ..
945 945
946 946 If fixing a commit reverts all the changes in the commit, we replace it with a
947 947 commit that changes no files.
948 948
949 949 $ hg init nochangesleft
950 950 $ cd nochangesleft
951 951
952 952 $ printf "FOO\n" > foo.whole
953 953 $ hg commit -Aqm "add file"
954 954 $ printf "foo\n" > foo.whole
955 955 $ hg commit -m "edit file"
956 956 $ hg status --change .
957 957 M foo.whole
958 958 $ hg fix -r .
959 959 $ hg status --change tip
960 960
961 961 $ cd ..
962 962
963 963 If we fix a parent and child revision together, the child revision must be
964 964 replaced if the parent is replaced, even if the diffs of the child needed no
965 965 fixes. However, we're free to not replace revisions that need no fixes and have
966 966 no ancestors that are replaced.
967 967
968 968 $ hg init mustreplacechild
969 969 $ cd mustreplacechild
970 970
971 971 $ printf "FOO\n" > foo.whole
972 972 $ hg commit -Aqm "add foo"
973 973 $ printf "foo\n" > foo.whole
974 974 $ hg commit -m "edit foo"
975 975 $ printf "BAR\n" > bar.whole
976 976 $ hg commit -Aqm "add bar"
977 977
978 978 $ hg log --graph --template '{rev} {files}'
979 979 @ 2 bar.whole
980 980 |
981 981 o 1 foo.whole
982 982 |
983 983 o 0 foo.whole
984 984
985 985 $ hg fix -r 0:2
986 986 $ hg log --graph --template '{rev} {files}'
987 987 o 4 bar.whole
988 988 |
989 989 o 3
990 990 |
991 991 | @ 2 bar.whole
992 992 | |
993 993 | x 1 foo.whole
994 994 |/
995 995 o 0 foo.whole
996 996
997 997
998 998 $ cd ..
999 999
1000 1000 It's also possible that the child needs absolutely no changes, but we still
1001 1001 need to replace it to update its parent. If we skipped replacing the child
1002 1002 because it had no file content changes, it would become an orphan for no good
1003 1003 reason.
1004 1004
1005 1005 $ hg init mustreplacechildevenifnop
1006 1006 $ cd mustreplacechildevenifnop
1007 1007
1008 1008 $ printf "Foo\n" > foo.whole
1009 1009 $ hg commit -Aqm "add a bad foo"
1010 1010 $ printf "FOO\n" > foo.whole
1011 1011 $ hg commit -m "add a good foo"
1012 1012 $ hg fix -r . -r '.^'
1013 1013 $ hg log --graph --template '{rev} {desc}'
1014 1014 o 3 add a good foo
1015 1015 |
1016 1016 o 2 add a bad foo
1017 1017
1018 1018 @ 1 add a good foo
1019 1019 |
1020 1020 x 0 add a bad foo
1021 1021
1022 1022
1023 1023 $ cd ..
1024 1024
1025 1025 Similar to the case above, the child revision may become empty as a result of
1026 1026 fixing its parent. We should still create an empty replacement child.
1027 1027 TODO: determine how this should interact with ui.allowemptycommit given that
1028 1028 the empty replacement could have children.
1029 1029
1030 1030 $ hg init mustreplacechildevenifempty
1031 1031 $ cd mustreplacechildevenifempty
1032 1032
1033 1033 $ printf "foo\n" > foo.whole
1034 1034 $ hg commit -Aqm "add foo"
1035 1035 $ printf "Foo\n" > foo.whole
1036 1036 $ hg commit -m "edit foo"
1037 1037 $ hg fix -r . -r '.^'
1038 1038 $ hg log --graph --template '{rev} {desc}\n' --stat
1039 1039 o 3 edit foo
1040 1040 |
1041 1041 o 2 add foo
1042 1042 foo.whole | 1 +
1043 1043 1 files changed, 1 insertions(+), 0 deletions(-)
1044 1044
1045 1045 @ 1 edit foo
1046 1046 | foo.whole | 2 +-
1047 1047 | 1 files changed, 1 insertions(+), 1 deletions(-)
1048 1048 |
1049 1049 x 0 add foo
1050 1050 foo.whole | 1 +
1051 1051 1 files changed, 1 insertions(+), 0 deletions(-)
1052 1052
1053 1053
1054 1054 $ cd ..
1055 1055
1056 1056 Fixing a secret commit should replace it with another secret commit.
1057 1057
1058 1058 $ hg init fixsecretcommit
1059 1059 $ cd fixsecretcommit
1060 1060
1061 1061 $ printf "foo\n" > foo.whole
1062 1062 $ hg commit -Aqm "add foo" --secret
1063 1063 $ hg fix -r .
1064 1064 $ hg log --template '{rev} {phase}\n'
1065 1065 1 secret
1066 1066 0 secret
1067 1067
1068 1068 $ cd ..
1069 1069
1070 1070 We should also preserve phase when fixing a draft commit while the user has
1071 1071 their default set to secret.
1072 1072
1073 1073 $ hg init respectphasesnewcommit
1074 1074 $ cd respectphasesnewcommit
1075 1075
1076 1076 $ printf "foo\n" > foo.whole
1077 1077 $ hg commit -Aqm "add foo"
1078 1078 $ hg --config phases.newcommit=secret fix -r .
1079 1079 $ hg log --template '{rev} {phase}\n'
1080 1080 1 draft
1081 1081 0 draft
1082 1082
1083 1083 $ cd ..
1084 1084
1085 1085 Debug output should show what fixer commands are being subprocessed, which is
1086 1086 useful for anyone trying to set up a new config.
1087 1087
1088 1088 $ hg init debugoutput
1089 1089 $ cd debugoutput
1090 1090
1091 1091 $ printf "foo\nbar\nbaz\n" > foo.changed
1092 1092 $ hg commit -Aqm "foo"
1093 1093 $ printf "Foo\nbar\nBaz\n" > foo.changed
1094 1094 $ hg --debug fix --working-dir
1095 1095 subprocess: * $TESTTMP/uppercase.py 1-1 3-3 (glob)
1096 1096
1097 1097 $ cd ..
1098 1098
1099 1099 Fixing an obsolete revision can cause divergence, so we abort unless the user
1100 1100 configures to allow it. This is not yet smart enough to know whether there is a
1101 1101 successor, but even then it is not likely intentional or idiomatic to fix an
1102 1102 obsolete revision.
1103 1103
1104 1104 $ hg init abortobsoleterev
1105 1105 $ cd abortobsoleterev
1106 1106
1107 1107 $ printf "foo\n" > foo.changed
1108 1108 $ hg commit -Aqm "foo"
1109 1109 $ hg debugobsolete `hg parents --template '{node}'`
1110 1110 1 new obsolescence markers
1111 1111 obsoleted 1 changesets
1112 1112 $ hg --hidden fix -r 0
1113 1113 abort: fixing obsolete revision could cause divergence
1114 1114 [255]
1115 1115
1116 1116 $ hg --hidden fix -r 0 --config experimental.evolution.allowdivergence=true
1117 1117 $ hg cat -r tip foo.changed
1118 1118 FOO
1119 1119
1120 1120 $ cd ..
1121 1121
1122 1122 Test all of the available substitution values for fixer commands.
1123 1123
1124 1124 $ hg init substitution
1125 1125 $ cd substitution
1126 1126
1127 1127 $ mkdir foo
1128 1128 $ printf "hello\ngoodbye\n" > foo/bar
1129 1129 $ hg add
1130 1130 adding foo/bar
1131 1131 $ hg --config "fix.fail:command=printf '%s\n' '{rootpath}' '{basename}'" \
1132 1132 > --config "fix.fail:linerange='{first}' '{last}'" \
1133 1133 > --config "fix.fail:pattern=foo/bar" \
1134 1134 > fix --working-dir
1135 1135 $ cat foo/bar
1136 1136 foo/bar
1137 1137 bar
1138 1138 1
1139 1139 2
1140 1140
1141 1141 $ cd ..
1142 1142
1143 1143 The --base flag should allow picking the revisions to diff against for changed
1144 1144 files and incremental line formatting.
1145 1145
1146 1146 $ hg init baseflag
1147 1147 $ cd baseflag
1148 1148
1149 1149 $ printf "one\ntwo\n" > foo.changed
1150 1150 $ printf "bar\n" > bar.changed
1151 1151 $ hg commit -Aqm "first"
1152 1152 $ printf "one\nTwo\n" > foo.changed
1153 1153 $ hg commit -m "second"
1154 1154 $ hg fix -w --base .
1155 1155 $ hg status
1156 1156 $ hg fix -w --base null
1157 1157 $ cat foo.changed
1158 1158 ONE
1159 1159 TWO
1160 1160 $ cat bar.changed
1161 1161 BAR
1162 1162
1163 1163 $ cd ..
1164 1164
1165 1165 If the user asks to fix the parent of another commit, they are asking to create
1166 1166 an orphan. We must respect experimental.evolution.allowunstable.
1167 1167
1168 1168 $ hg init allowunstable
1169 1169 $ cd allowunstable
1170 1170
1171 1171 $ printf "one\n" > foo.whole
1172 1172 $ hg commit -Aqm "first"
1173 1173 $ printf "two\n" > foo.whole
1174 1174 $ hg commit -m "second"
1175 1175 $ hg --config experimental.evolution.allowunstable=False fix -r '.^'
1176 1176 abort: cannot fix changeset with children
1177 1177 [255]
1178 1178 $ hg fix -r '.^'
1179 1179 1 new orphan changesets
1180 1180 $ hg cat -r 2 foo.whole
1181 1181 ONE
1182 1182
1183 1183 $ cd ..
1184 1184
1185 1185 The --base flag affects the set of files being fixed. So while the --whole flag
1186 1186 makes the base irrelevant for changed line ranges, it still changes the
1187 1187 meaning and effect of the command. In this example, no files or lines are fixed
1188 1188 until we specify the base, but then we do fix unchanged lines.
1189 1189
1190 1190 $ hg init basewhole
1191 1191 $ cd basewhole
1192 1192 $ printf "foo1\n" > foo.changed
1193 1193 $ hg commit -Aqm "first"
1194 1194 $ printf "foo2\n" >> foo.changed
1195 1195 $ printf "bar\n" > bar.changed
1196 1196 $ hg commit -Aqm "second"
1197 1197
1198 1198 $ hg fix --working-dir --whole
1199 1199 $ cat *.changed
1200 1200 bar
1201 1201 foo1
1202 1202 foo2
1203 1203
1204 1204 $ hg fix --working-dir --base 0 --whole
1205 1205 $ cat *.changed
1206 1206 BAR
1207 1207 FOO1
1208 1208 FOO2
1209 1209
1210 1210 $ cd ..
1211 1211
1212 1212 The execution order of tools can be controlled. This example doesn't work if
1213 1213 you sort after truncating, but the config defines the correct order while the
1214 1214 definitions are out of order (which might imply the incorrect order given the
1215 1215 implementation of fix). The goal is to use multiple tools to select the lowest
1216 1216 5 numbers in the file.
1217 1217
1218 1218 $ hg init priorityexample
1219 1219 $ cd priorityexample
1220 1220
1221 1221 $ cat >> .hg/hgrc <<EOF
1222 1222 > [fix]
1223 1223 > head:command = head -n 5
1224 1224 > head:pattern = numbers.txt
1225 1225 > head:priority = 1
1226 1226 > sort:command = sort -n
1227 1227 > sort:pattern = numbers.txt
1228 1228 > sort:priority = 2
1229 1229 > EOF
1230 1230
1231 1231 $ printf "8\n2\n3\n6\n7\n4\n9\n5\n1\n0\n" > numbers.txt
1232 1232 $ hg add -q
1233 1233 $ hg fix -w
1234 1234 $ cat numbers.txt
1235 1235 0
1236 1236 1
1237 1237 2
1238 1238 3
1239 1239 4
1240 1240
1241 1241 And of course we should be able to break this by reversing the execution order.
1242 1242 Test negative priorities while we're at it.
1243 1243
1244 1244 $ cat >> .hg/hgrc <<EOF
1245 1245 > [fix]
1246 1246 > head:priority = -1
1247 1247 > sort:priority = -2
1248 1248 > EOF
1249 1249 $ printf "8\n2\n3\n6\n7\n4\n9\n5\n1\n0\n" > numbers.txt
1250 1250 $ hg fix -w
1251 1251 $ cat numbers.txt
1252 1252 2
1253 1253 3
1254 1254 6
1255 1255 7
1256 1256 8
1257 1257
1258 1258 $ cd ..
1259 1259
1260 1260 It's possible for repeated applications of a fixer tool to create cycles in the
1261 1261 generated content of a file. For example, two users with different versions of
1262 1262 a code formatter might fight over the formatting when they run hg fix. In the
1263 1263 absence of other changes, this means we could produce commits with the same
1264 1264 hash in subsequent runs of hg fix. This is a problem unless we support
1265 1265 obsolescence cycles well. We avoid this by adding an extra field to the
1266 1266 successor which forces it to have a new hash. That's why this test creates
1267 1267 three revisions instead of two.
1268 1268
1269 1269 $ hg init cyclictool
1270 1270 $ cd cyclictool
1271 1271
1272 1272 $ cat >> .hg/hgrc <<EOF
1273 1273 > [fix]
1274 1274 > swapletters:command = tr ab ba
1275 1275 > swapletters:pattern = foo
1276 1276 > EOF
1277 1277
1278 1278 $ echo ab > foo
1279 1279 $ hg commit -Aqm foo
1280 1280
1281 1281 $ hg fix -r 0
1282 1282 $ hg fix -r 1
1283 1283
1284 1284 $ hg cat -r 0 foo --hidden
1285 1285 ab
1286 1286 $ hg cat -r 1 foo --hidden
1287 1287 ba
1288 1288 $ hg cat -r 2 foo
1289 1289 ab
1290 1290
1291 1291 $ cd ..
1292 1292
1293 1293 We run fixer tools in the repo root so they can look for config files or other
1294 1294 important things in the working directory. This does NOT mean we are
1295 1295 reconstructing a working copy of every revision being fixed; we're just giving
1296 1296 the tool knowledge of the repo's location in case it can do something
1297 1297 reasonable with that.
1298 1298
1299 1299 $ hg init subprocesscwd
1300 1300 $ cd subprocesscwd
1301 1301
1302 1302 $ cat >> .hg/hgrc <<EOF
1303 1303 > [fix]
1304 1304 > printcwd:command = "$PYTHON" -c "import os; print(os.getcwd())"
1305 1305 > printcwd:pattern = relpath:foo/bar
1306 1306 > filesetpwd:command = "$PYTHON" -c "import os; print('fs: ' + os.getcwd())"
1307 1307 > filesetpwd:pattern = set:**quux
1308 1308 > EOF
1309 1309
1310 1310 $ mkdir foo
1311 1311 $ printf "bar\n" > foo/bar
1312 1312 $ printf "quux\n" > quux
1313 1313 $ hg commit -Aqm blah
1314 1314
1315 1315 $ hg fix -w -r . foo/bar
1316 1316 $ hg cat -r tip foo/bar
1317 1317 $TESTTMP/subprocesscwd
1318 1318 $ cat foo/bar
1319 1319 $TESTTMP/subprocesscwd
1320 1320
1321 1321 $ cd foo
1322 1322
1323 1323 $ hg fix -w -r . bar
1324 1324 $ hg cat -r tip bar ../quux
1325 1325 $TESTTMP/subprocesscwd
1326 1326 quux
1327 1327 $ cat bar ../quux
1328 1328 $TESTTMP/subprocesscwd
1329 1329 quux
1330 1330 $ echo modified > bar
1331 1331 $ hg fix -w bar
1332 1332 $ cat bar
1333 1333 $TESTTMP/subprocesscwd
1334 1334
1335 1335 Apparently fixing p1() and its descendants doesn't include wdir() unless
1336 1336 explicitly stated.
1337 1337
1338 1338 $ hg fix -r '.::'
1339 1339 $ hg cat -r . ../quux
1340 1340 quux
1341 1341 $ hg cat -r tip ../quux
1342 1342 fs: $TESTTMP/subprocesscwd
1343 1343 $ cat ../quux
1344 1344 quux
1345 1345
1346 1346 Clean files are not fixed unless explicitly named
1347 1347 $ echo 'dirty' > ../quux
1348 1348
1349 1349 $ hg fix --working-dir
1350 1350 $ cat ../quux
1351 1351 fs: $TESTTMP/subprocesscwd
1352 1352
1353 1353 $ cd ../..
1354 1354
1355 1355 Tools configured without a pattern are ignored. It would be too dangerous to
1356 1356 run them on all files, because this might happen while testing a configuration
1357 1357 that also deletes all of the file content. There is no reasonable subset of the
1358 1358 files to use as a default. Users should be explicit about what files are
1359 1359 affected by a tool. This test also confirms that we don't crash when the
1360 1360 pattern config is missing, and that we only warn about it once.
1361 1361
1362 1362 $ hg init nopatternconfigured
1363 1363 $ cd nopatternconfigured
1364 1364
1365 1365 $ printf "foo" > foo
1366 1366 $ printf "bar" > bar
1367 1367 $ hg add -q
1368 1368 $ hg fix --debug --working-dir --config "fix.nopattern:command=echo fixed"
1369 1369 fixer tool has no pattern configuration: nopattern
1370 1370 $ cat foo bar
1371 1371 foobar (no-eol)
1372 1372 $ hg fix --debug --working-dir --config "fix.nocommand:pattern=foo.bar"
1373 1373 fixer tool has no command configuration: nocommand
1374 1374
1375 1375 $ cd ..
1376 1376
1377 1377 Tools can be disabled. Disabled tools do nothing but print a debug message.
1378 1378
1379 1379 $ hg init disabled
1380 1380 $ cd disabled
1381 1381
1382 1382 $ printf "foo\n" > foo
1383 1383 $ hg add -q
1384 1384 $ hg fix --debug --working-dir --config "fix.disabled:command=echo fixed" \
1385 1385 > --config "fix.disabled:pattern=foo" \
1386 1386 > --config "fix.disabled:enabled=false"
1387 1387 ignoring disabled fixer tool: disabled
1388 1388 $ cat foo
1389 1389 foo
1390 1390
1391 1391 $ cd ..
1392 1392
1393 1393 Test that we can configure a fixer to affect all files regardless of the cwd.
1394 1394 The way we invoke matching must not prohibit this.
1395 1395
1396 1396 $ hg init affectallfiles
1397 1397 $ cd affectallfiles
1398 1398
1399 1399 $ mkdir foo bar
1400 1400 $ printf "foo" > foo/file
1401 1401 $ printf "bar" > bar/file
1402 1402 $ printf "baz" > baz_file
1403 1403 $ hg add -q
1404 1404
1405 1405 $ cd bar
1406 1406 $ hg fix --working-dir --config "fix.cooltool:command=echo fixed" \
1407 1407 > --config "fix.cooltool:pattern=glob:**"
1408 1408 $ cd ..
1409 1409
1410 1410 $ cat foo/file
1411 1411 fixed
1412 1412 $ cat bar/file
1413 1413 fixed
1414 1414 $ cat baz_file
1415 1415 fixed
1416 1416
1417 1417 $ cd ..
1418 1418
1419 1419 Tools should be able to run on unchanged files, even if they set :linerange.
1420 1420 This includes a corner case where deleted chunks of a file are not considered
1421 1421 changes.
1422 1422
1423 1423 $ hg init skipclean
1424 1424 $ cd skipclean
1425 1425
1426 1426 $ printf "a\nb\nc\n" > foo
1427 1427 $ printf "a\nb\nc\n" > bar
1428 1428 $ printf "a\nb\nc\n" > baz
1429 1429 $ hg commit -Aqm "base"
1430 1430
1431 1431 $ printf "a\nc\n" > foo
1432 1432 $ printf "a\nx\nc\n" > baz
1433 1433
1434 1434 $ cat >> print.py <<EOF
1435 1435 > import sys
1436 1436 > for a in sys.argv[1:]:
1437 1437 > print(a)
1438 1438 > EOF
1439 1439
1440 1440 $ hg fix --working-dir foo bar baz \
1441 1441 > --config "fix.changedlines:command=\"$PYTHON\" print.py \"Line ranges:\"" \
1442 1442 > --config 'fix.changedlines:linerange="{first} through {last}"' \
1443 1443 > --config 'fix.changedlines:pattern=glob:**' \
1444 1444 > --config 'fix.changedlines:skipclean=false'
1445 1445
1446 1446 $ cat foo
1447 1447 Line ranges:
1448 1448 $ cat bar
1449 1449 Line ranges:
1450 1450 $ cat baz
1451 1451 Line ranges:
1452 1452 2 through 2
1453 1453
1454 1454 $ cd ..
1455 1455
1456 1456 Test various cases around merges. We were previously dropping files if they were
1457 1457 created on only the p2 side of the merge, so let's test permutations of:
1458 1458 * added, was fixed
1459 1459 * added, considered for fixing but was already good
1460 1460 * added, not considered for fixing
1461 1461 * modified, was fixed
1462 1462 * modified, considered for fixing but was already good
1463 1463 * modified, not considered for fixing
1464 1464
1465 1465 Before the bug was fixed where we would drop files, this test demonstrated the
1466 1466 following issues:
1467 1467 * new_in_r1.ignored, new_in_r1_already_good.changed, and
1468 1468 > mod_in_r1_already_good.changed were NOT in the manifest for the merge commit
1469 1469 * mod_in_r1.ignored had its contents from r0, NOT r1.
1470 1470
1471 1471 We're also setting a named branch for every commit to demonstrate that the
1472 1472 branch is kept intact and there aren't issues updating to another branch in the
1473 1473 middle of fix.
1474 1474
1475 1475 $ hg init merge_keeps_files
1476 1476 $ cd merge_keeps_files
1477 1477 $ for f in r0 mod_in_r1 mod_in_r2 mod_in_merge mod_in_child; do
1478 1478 > for c in changed whole ignored; do
1479 1479 > printf "hello\n" > $f.$c
1480 1480 > done
1481 1481 > printf "HELLO\n" > "mod_in_${f}_already_good.changed"
1482 1482 > done
1483 1483 $ hg branch -q r0
1484 1484 $ hg ci -Aqm 'r0'
1485 1485 $ hg phase -p
1486 1486 $ make_test_files() {
1487 1487 > printf "world\n" >> "mod_in_$1.changed"
1488 1488 > printf "world\n" >> "mod_in_$1.whole"
1489 1489 > printf "world\n" >> "mod_in_$1.ignored"
1490 1490 > printf "WORLD\n" >> "mod_in_$1_already_good.changed"
1491 1491 > printf "new in $1\n" > "new_in_$1.changed"
1492 1492 > printf "new in $1\n" > "new_in_$1.whole"
1493 1493 > printf "new in $1\n" > "new_in_$1.ignored"
1494 1494 > printf "ALREADY GOOD, NEW IN THIS REV\n" > "new_in_$1_already_good.changed"
1495 1495 > }
1496 1496 $ make_test_commit() {
1497 1497 > make_test_files "$1"
1498 1498 > hg branch -q "$1"
1499 1499 > hg ci -Aqm "$2"
1500 1500 > }
1501 1501 $ make_test_commit r1 "merge me, pt1"
1502 1502 $ hg co -q ".^"
1503 1503 $ make_test_commit r2 "merge me, pt2"
1504 1504 $ hg merge -qr 1
1505 1505 $ make_test_commit merge "evil merge"
1506 1506 $ make_test_commit child "child of merge"
1507 1507 $ make_test_files wdir
1508 1508 $ hg fix -r 'not public()' -w
1509 1509 $ hg log -G -T'{rev}:{shortest(node,8)}: branch:{branch} desc:{desc}'
1510 1510 @ 8:c22ce900: branch:child desc:child of merge
1511 1511 |
1512 1512 o 7:5a30615a: branch:merge desc:evil merge
1513 1513 |\
1514 1514 | o 6:4e5acdc4: branch:r2 desc:merge me, pt2
1515 1515 | |
1516 1516 o | 5:eea01878: branch:r1 desc:merge me, pt1
1517 1517 |/
1518 1518 o 0:0c548d87: branch:r0 desc:r0
1519 1519
1520 1520 $ hg files -r tip
1521 1521 mod_in_child.changed
1522 1522 mod_in_child.ignored
1523 1523 mod_in_child.whole
1524 1524 mod_in_child_already_good.changed
1525 1525 mod_in_merge.changed
1526 1526 mod_in_merge.ignored
1527 1527 mod_in_merge.whole
1528 1528 mod_in_merge_already_good.changed
1529 1529 mod_in_mod_in_child_already_good.changed
1530 1530 mod_in_mod_in_merge_already_good.changed
1531 1531 mod_in_mod_in_r1_already_good.changed
1532 1532 mod_in_mod_in_r2_already_good.changed
1533 1533 mod_in_r0_already_good.changed
1534 1534 mod_in_r1.changed
1535 1535 mod_in_r1.ignored
1536 1536 mod_in_r1.whole
1537 1537 mod_in_r1_already_good.changed
1538 1538 mod_in_r2.changed
1539 1539 mod_in_r2.ignored
1540 1540 mod_in_r2.whole
1541 1541 mod_in_r2_already_good.changed
1542 1542 new_in_child.changed
1543 1543 new_in_child.ignored
1544 1544 new_in_child.whole
1545 1545 new_in_child_already_good.changed
1546 1546 new_in_merge.changed
1547 1547 new_in_merge.ignored
1548 1548 new_in_merge.whole
1549 1549 new_in_merge_already_good.changed
1550 1550 new_in_r1.changed
1551 1551 new_in_r1.ignored
1552 1552 new_in_r1.whole
1553 1553 new_in_r1_already_good.changed
1554 1554 new_in_r2.changed
1555 1555 new_in_r2.ignored
1556 1556 new_in_r2.whole
1557 1557 new_in_r2_already_good.changed
1558 1558 r0.changed
1559 1559 r0.ignored
1560 1560 r0.whole
1561 1561 $ for f in "$(hg files -r tip)"; do hg cat -r tip $f -T'{path}:\n{data}\n'; done
1562 1562 mod_in_child.changed:
1563 1563 hello
1564 1564 WORLD
1565 1565
1566 1566 mod_in_child.ignored:
1567 1567 hello
1568 1568 world
1569 1569
1570 1570 mod_in_child.whole:
1571 1571 HELLO
1572 1572 WORLD
1573 1573
1574 1574 mod_in_child_already_good.changed:
1575 1575 WORLD
1576 1576
1577 1577 mod_in_merge.changed:
1578 1578 hello
1579 1579 WORLD
1580 1580
1581 1581 mod_in_merge.ignored:
1582 1582 hello
1583 1583 world
1584 1584
1585 1585 mod_in_merge.whole:
1586 1586 HELLO
1587 1587 WORLD
1588 1588
1589 1589 mod_in_merge_already_good.changed:
1590 1590 WORLD
1591 1591
1592 1592 mod_in_mod_in_child_already_good.changed:
1593 1593 HELLO
1594 1594
1595 1595 mod_in_mod_in_merge_already_good.changed:
1596 1596 HELLO
1597 1597
1598 1598 mod_in_mod_in_r1_already_good.changed:
1599 1599 HELLO
1600 1600
1601 1601 mod_in_mod_in_r2_already_good.changed:
1602 1602 HELLO
1603 1603
1604 1604 mod_in_r0_already_good.changed:
1605 1605 HELLO
1606 1606
1607 1607 mod_in_r1.changed:
1608 1608 hello
1609 1609 WORLD
1610 1610
1611 1611 mod_in_r1.ignored:
1612 1612 hello
1613 1613 world
1614 1614
1615 1615 mod_in_r1.whole:
1616 1616 HELLO
1617 1617 WORLD
1618 1618
1619 1619 mod_in_r1_already_good.changed:
1620 1620 WORLD
1621 1621
1622 1622 mod_in_r2.changed:
1623 1623 hello
1624 1624 WORLD
1625 1625
1626 1626 mod_in_r2.ignored:
1627 1627 hello
1628 1628 world
1629 1629
1630 1630 mod_in_r2.whole:
1631 1631 HELLO
1632 1632 WORLD
1633 1633
1634 1634 mod_in_r2_already_good.changed:
1635 1635 WORLD
1636 1636
1637 1637 new_in_child.changed:
1638 1638 NEW IN CHILD
1639 1639
1640 1640 new_in_child.ignored:
1641 1641 new in child
1642 1642
1643 1643 new_in_child.whole:
1644 1644 NEW IN CHILD
1645 1645
1646 1646 new_in_child_already_good.changed:
1647 1647 ALREADY GOOD, NEW IN THIS REV
1648 1648
1649 1649 new_in_merge.changed:
1650 1650 NEW IN MERGE
1651 1651
1652 1652 new_in_merge.ignored:
1653 1653 new in merge
1654 1654
1655 1655 new_in_merge.whole:
1656 1656 NEW IN MERGE
1657 1657
1658 1658 new_in_merge_already_good.changed:
1659 1659 ALREADY GOOD, NEW IN THIS REV
1660 1660
1661 1661 new_in_r1.changed:
1662 1662 NEW IN R1
1663 1663
1664 1664 new_in_r1.ignored:
1665 1665 new in r1
1666 1666
1667 1667 new_in_r1.whole:
1668 1668 NEW IN R1
1669 1669
1670 1670 new_in_r1_already_good.changed:
1671 1671 ALREADY GOOD, NEW IN THIS REV
1672 1672
1673 1673 new_in_r2.changed:
1674 1674 NEW IN R2
1675 1675
1676 1676 new_in_r2.ignored:
1677 1677 new in r2
1678 1678
1679 1679 new_in_r2.whole:
1680 1680 NEW IN R2
1681 1681
1682 1682 new_in_r2_already_good.changed:
1683 1683 ALREADY GOOD, NEW IN THIS REV
1684 1684
1685 1685 r0.changed:
1686 1686 hello
1687 1687
1688 1688 r0.ignored:
1689 1689 hello
1690 1690
1691 1691 r0.whole:
1692 1692 hello
1693 1693
@@ -1,771 +1,771 b''
1 1 #testcases abortcommand abortflag
2 2
3 3 #if abortflag
4 4 $ cat >> $HGRCPATH <<EOF
5 5 > [alias]
6 6 > abort = graft --abort
7 7 > EOF
8 8 #endif
9 9
10 10
11 11 Testing the reading of old format graftstate file with newer mercurial
12 12
13 13 $ hg init oldgraft
14 14 $ cd oldgraft
15 15 $ for ch in a b c; do echo foo > $ch; hg add $ch; hg ci -Aqm "added "$ch; done;
16 16 $ hg log -GT "{rev}:{node|short} {desc}\n"
17 17 @ 2:8be98ac1a569 added c
18 18 |
19 19 o 1:80e6d2c47cfe added b
20 20 |
21 21 o 0:f7ad41964313 added a
22 22
23 23 $ hg up 0
24 24 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
25 25 $ echo bar > b
26 26 $ hg add b
27 27 $ hg ci -m "bar to b"
28 28 created new head
29 29 $ hg graft -r 1 -r 2
30 30 grafting 1:80e6d2c47cfe "added b"
31 31 merging b
32 32 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
33 33 abort: unresolved conflicts, can't continue
34 34 (use 'hg resolve' and 'hg graft --continue')
35 35 [1]
36 36
37 37 Writing the nodes in old format to graftstate
38 38
39 39 $ hg log -r 1 -r 2 -T '{node}\n' > .hg/graftstate
40 40 $ echo foo > b
41 41 $ hg resolve -m
42 42 (no more unresolved files)
43 43 continue: hg graft --continue
44 44 $ hg graft --continue
45 45 grafting 1:80e6d2c47cfe "added b"
46 46 grafting 2:8be98ac1a569 "added c"
47 47
48 48 Testing that --user is preserved during conflicts and value is reused while
49 49 running `hg graft --continue`
50 50
51 51 $ hg log -G
52 52 @ changeset: 5:711e9fa999f1
53 53 | tag: tip
54 54 | user: test
55 55 | date: Thu Jan 01 00:00:00 1970 +0000
56 56 | summary: added c
57 57 |
58 58 o changeset: 4:e5ad7353b408
59 59 | user: test
60 60 | date: Thu Jan 01 00:00:00 1970 +0000
61 61 | summary: added b
62 62 |
63 63 o changeset: 3:9e887f7a939c
64 64 | parent: 0:f7ad41964313
65 65 | user: test
66 66 | date: Thu Jan 01 00:00:00 1970 +0000
67 67 | summary: bar to b
68 68 |
69 69 | o changeset: 2:8be98ac1a569
70 70 | | user: test
71 71 | | date: Thu Jan 01 00:00:00 1970 +0000
72 72 | | summary: added c
73 73 | |
74 74 | o changeset: 1:80e6d2c47cfe
75 75 |/ user: test
76 76 | date: Thu Jan 01 00:00:00 1970 +0000
77 77 | summary: added b
78 78 |
79 79 o changeset: 0:f7ad41964313
80 80 user: test
81 81 date: Thu Jan 01 00:00:00 1970 +0000
82 82 summary: added a
83 83
84 84
85 85 $ hg up '.^^'
86 86 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
87 87
88 88 $ hg graft -r 1 -r 2 --user batman
89 89 grafting 1:80e6d2c47cfe "added b"
90 90 merging b
91 91 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
92 92 abort: unresolved conflicts, can't continue
93 93 (use 'hg resolve' and 'hg graft --continue')
94 94 [1]
95 95
96 96 $ echo wat > b
97 97 $ hg resolve -m
98 98 (no more unresolved files)
99 99 continue: hg graft --continue
100 100
101 101 $ hg graft --continue
102 102 grafting 1:80e6d2c47cfe "added b"
103 103 grafting 2:8be98ac1a569 "added c"
104 104
105 105 $ hg log -Gr 3::
106 106 @ changeset: 7:11a36ffaacf2
107 107 | tag: tip
108 108 | user: batman
109 109 | date: Thu Jan 01 00:00:00 1970 +0000
110 110 | summary: added c
111 111 |
112 112 o changeset: 6:76803afc6511
113 113 | parent: 3:9e887f7a939c
114 114 | user: batman
115 115 | date: Thu Jan 01 00:00:00 1970 +0000
116 116 | summary: added b
117 117 |
118 118 | o changeset: 5:711e9fa999f1
119 119 | | user: test
120 120 | | date: Thu Jan 01 00:00:00 1970 +0000
121 121 | | summary: added c
122 122 | |
123 123 | o changeset: 4:e5ad7353b408
124 124 |/ user: test
125 125 | date: Thu Jan 01 00:00:00 1970 +0000
126 126 | summary: added b
127 127 |
128 128 o changeset: 3:9e887f7a939c
129 129 | parent: 0:f7ad41964313
130 130 ~ user: test
131 131 date: Thu Jan 01 00:00:00 1970 +0000
132 132 summary: bar to b
133 133
134 134 Test that --date is preserved and reused in `hg graft --continue`
135 135
136 136 $ hg up '.^^'
137 137 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
138 138 $ hg graft -r 1 -r 2 --date '1234560000 120'
139 139 grafting 1:80e6d2c47cfe "added b"
140 140 merging b
141 141 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
142 142 abort: unresolved conflicts, can't continue
143 143 (use 'hg resolve' and 'hg graft --continue')
144 144 [1]
145 145
146 146 $ echo foobar > b
147 147 $ hg resolve -m
148 148 (no more unresolved files)
149 149 continue: hg graft --continue
150 150 $ hg graft --continue
151 151 grafting 1:80e6d2c47cfe "added b"
152 152 grafting 2:8be98ac1a569 "added c"
153 153
154 154 $ hg log -Gr '.^^::.'
155 155 @ changeset: 9:1896b76e007a
156 156 | tag: tip
157 157 | user: test
158 158 | date: Fri Feb 13 21:18:00 2009 -0002
159 159 | summary: added c
160 160 |
161 161 o changeset: 8:ce2b4f1632af
162 162 | parent: 3:9e887f7a939c
163 163 | user: test
164 164 | date: Fri Feb 13 21:18:00 2009 -0002
165 165 | summary: added b
166 166 |
167 167 o changeset: 3:9e887f7a939c
168 168 | parent: 0:f7ad41964313
169 169 ~ user: test
170 170 date: Thu Jan 01 00:00:00 1970 +0000
171 171 summary: bar to b
172 172
173 173 Test that --log is preserved and reused in `hg graft --continue`
174 174
175 175 $ hg up '.^^'
176 176 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
177 177 $ hg graft -r 1 -r 2 --log
178 178 grafting 1:80e6d2c47cfe "added b"
179 179 merging b
180 180 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
181 181 abort: unresolved conflicts, can't continue
182 182 (use 'hg resolve' and 'hg graft --continue')
183 183 [1]
184 184
185 185 $ echo foobar > b
186 186 $ hg resolve -m
187 187 (no more unresolved files)
188 188 continue: hg graft --continue
189 189
190 190 $ hg graft --continue
191 191 grafting 1:80e6d2c47cfe "added b"
192 192 grafting 2:8be98ac1a569 "added c"
193 193
194 194 $ hg log -GT "{rev}:{node|short} {desc}" -r '.^^::.'
195 195 @ 11:30c1050a58b2 added c
196 196 | (grafted from 8be98ac1a56990c2d9ca6861041b8390af7bd6f3)
197 197 o 10:ec7eda2313e2 added b
198 198 | (grafted from 80e6d2c47cfe5b3185519568327a17a061c7efb6)
199 199 o 3:9e887f7a939c bar to b
200 200 |
201 201 ~
202 202
203 203 $ cd ..
204 204
205 205 Testing the --stop flag of `hg graft` which stops the interrupted graft
206 206
207 207 $ hg init stopgraft
208 208 $ cd stopgraft
209 209 $ for ch in a b c d; do echo $ch > $ch; hg add $ch; hg ci -Aqm "added "$ch; done;
210 210
211 211 $ hg log -G
212 212 @ changeset: 3:9150fe93bec6
213 213 | tag: tip
214 214 | user: test
215 215 | date: Thu Jan 01 00:00:00 1970 +0000
216 216 | summary: added d
217 217 |
218 218 o changeset: 2:155349b645be
219 219 | user: test
220 220 | date: Thu Jan 01 00:00:00 1970 +0000
221 221 | summary: added c
222 222 |
223 223 o changeset: 1:5f6d8a4bf34a
224 224 | user: test
225 225 | date: Thu Jan 01 00:00:00 1970 +0000
226 226 | summary: added b
227 227 |
228 228 o changeset: 0:9092f1db7931
229 229 user: test
230 230 date: Thu Jan 01 00:00:00 1970 +0000
231 231 summary: added a
232 232
233 233 $ hg up '.^^'
234 234 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
235 235
236 236 $ echo foo > d
237 237 $ hg ci -Aqm "added foo to d"
238 238
239 239 $ hg graft --stop
240 240 abort: no interrupted graft found
241 [255]
241 [20]
242 242
243 243 $ hg graft -r 3
244 244 grafting 3:9150fe93bec6 "added d"
245 245 merging d
246 246 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
247 247 abort: unresolved conflicts, can't continue
248 248 (use 'hg resolve' and 'hg graft --continue')
249 249 [1]
250 250
251 251 $ hg graft --stop --continue
252 252 abort: cannot specify both --stop and --continue
253 253 [10]
254 254
255 255 $ hg graft --stop -U
256 256 abort: cannot specify both --stop and --user
257 257 [10]
258 258 $ hg graft --stop --rev 4
259 259 abort: cannot specify both --stop and --rev
260 260 [10]
261 261 $ hg graft --stop --log
262 262 abort: cannot specify both --stop and --log
263 263 [10]
264 264
265 265 $ hg graft --stop
266 266 stopped the interrupted graft
267 267 working directory is now at a0deacecd59d
268 268
269 269 $ hg diff
270 270
271 271 $ hg log -Gr '.'
272 272 @ changeset: 4:a0deacecd59d
273 273 | tag: tip
274 274 ~ parent: 1:5f6d8a4bf34a
275 275 user: test
276 276 date: Thu Jan 01 00:00:00 1970 +0000
277 277 summary: added foo to d
278 278
279 279 $ hg graft -r 2 -r 3
280 280 grafting 2:155349b645be "added c"
281 281 grafting 3:9150fe93bec6 "added d"
282 282 merging d
283 283 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
284 284 abort: unresolved conflicts, can't continue
285 285 (use 'hg resolve' and 'hg graft --continue')
286 286 [1]
287 287
288 288 $ hg graft --stop
289 289 stopped the interrupted graft
290 290 working directory is now at 75b447541a9e
291 291
292 292 $ hg diff
293 293
294 294 $ hg log -G -T "{rev}:{node|short} {desc}"
295 295 @ 5:75b447541a9e added c
296 296 |
297 297 o 4:a0deacecd59d added foo to d
298 298 |
299 299 | o 3:9150fe93bec6 added d
300 300 | |
301 301 | o 2:155349b645be added c
302 302 |/
303 303 o 1:5f6d8a4bf34a added b
304 304 |
305 305 o 0:9092f1db7931 added a
306 306
307 307 $ cd ..
308 308
309 309 Testing the --abort flag for `hg graft` which aborts and rollback to state
310 310 before the graft
311 311
312 312 $ hg init abortgraft
313 313 $ cd abortgraft
314 314 $ for ch in a b c d; do echo $ch > $ch; hg add $ch; hg ci -Aqm "added "$ch; done;
315 315
316 316 $ hg up '.^^'
317 317 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
318 318
319 319 $ echo x > x
320 320 $ hg ci -Aqm "added x"
321 321 $ hg up '.^'
322 322 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
323 323 $ echo foo > c
324 324 $ hg ci -Aqm "added foo to c"
325 325
326 326 $ hg log -GT "{rev}:{node|short} {desc}"
327 327 @ 5:36b793615f78 added foo to c
328 328 |
329 329 | o 4:863a25e1a9ea added x
330 330 |/
331 331 | o 3:9150fe93bec6 added d
332 332 | |
333 333 | o 2:155349b645be added c
334 334 |/
335 335 o 1:5f6d8a4bf34a added b
336 336 |
337 337 o 0:9092f1db7931 added a
338 338
339 339 $ hg up 9150fe93bec6
340 340 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
341 341
342 342 $ hg abort
343 343 abort: no interrupted graft to abort (abortflag !)
344 344 abort: no operation in progress (abortcommand !)
345 [255]
345 [20]
346 346
347 347 when stripping is required
348 348 $ hg graft -r 4 -r 5
349 349 grafting 4:863a25e1a9ea "added x"
350 350 grafting 5:36b793615f78 "added foo to c" (tip)
351 351 merging c
352 352 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
353 353 abort: unresolved conflicts, can't continue
354 354 (use 'hg resolve' and 'hg graft --continue')
355 355 [1]
356 356
357 357 $ hg graft --continue --abort
358 358 abort: cannot specify both --abort and --continue
359 359 [10]
360 360
361 361 $ hg graft --abort --stop
362 362 abort: cannot specify both --abort and --stop
363 363 [10]
364 364
365 365 $ hg graft --abort --currentuser
366 366 abort: cannot specify both --abort and --user
367 367 [10]
368 368
369 369 $ hg graft --abort --edit
370 370 abort: cannot specify both --abort and --edit
371 371 [10]
372 372
373 373 #if abortcommand
374 374 when in dry-run mode
375 375 $ hg abort --dry-run
376 376 graft in progress, will be aborted
377 377 #endif
378 378
379 379 $ hg abort
380 380 graft aborted
381 381 working directory is now at 9150fe93bec6
382 382 $ hg log -GT "{rev}:{node|short} {desc}"
383 383 o 5:36b793615f78 added foo to c
384 384 |
385 385 | o 4:863a25e1a9ea added x
386 386 |/
387 387 | @ 3:9150fe93bec6 added d
388 388 | |
389 389 | o 2:155349b645be added c
390 390 |/
391 391 o 1:5f6d8a4bf34a added b
392 392 |
393 393 o 0:9092f1db7931 added a
394 394
395 395 when stripping is not required
396 396 $ hg graft -r 5
397 397 grafting 5:36b793615f78 "added foo to c" (tip)
398 398 merging c
399 399 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
400 400 abort: unresolved conflicts, can't continue
401 401 (use 'hg resolve' and 'hg graft --continue')
402 402 [1]
403 403
404 404 $ hg abort
405 405 graft aborted
406 406 working directory is now at 9150fe93bec6
407 407 $ hg log -GT "{rev}:{node|short} {desc}"
408 408 o 5:36b793615f78 added foo to c
409 409 |
410 410 | o 4:863a25e1a9ea added x
411 411 |/
412 412 | @ 3:9150fe93bec6 added d
413 413 | |
414 414 | o 2:155349b645be added c
415 415 |/
416 416 o 1:5f6d8a4bf34a added b
417 417 |
418 418 o 0:9092f1db7931 added a
419 419
420 420 when some of the changesets became public
421 421
422 422 $ hg graft -r 4 -r 5
423 423 grafting 4:863a25e1a9ea "added x"
424 424 grafting 5:36b793615f78 "added foo to c" (tip)
425 425 merging c
426 426 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
427 427 abort: unresolved conflicts, can't continue
428 428 (use 'hg resolve' and 'hg graft --continue')
429 429 [1]
430 430
431 431 $ hg log -GT "{rev}:{node|short} {desc}"
432 432 @ 6:6ec71c037d94 added x
433 433 |
434 434 | % 5:36b793615f78 added foo to c
435 435 | |
436 436 | | o 4:863a25e1a9ea added x
437 437 | |/
438 438 o | 3:9150fe93bec6 added d
439 439 | |
440 440 o | 2:155349b645be added c
441 441 |/
442 442 o 1:5f6d8a4bf34a added b
443 443 |
444 444 o 0:9092f1db7931 added a
445 445
446 446 $ hg phase -r 6 --public
447 447
448 448 $ hg abort
449 449 cannot clean up public changesets 6ec71c037d94
450 450 graft aborted
451 451 working directory is now at 6ec71c037d94
452 452
453 453 when we created new changesets on top of existing one
454 454
455 455 $ hg up '.^^'
456 456 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
457 457 $ echo y > y
458 458 $ hg ci -Aqm "added y"
459 459 $ echo z > z
460 460 $ hg ci -Aqm "added z"
461 461
462 462 $ hg up 3
463 463 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
464 464 $ hg log -GT "{rev}:{node|short} {desc}"
465 465 o 8:637f9e9bbfd4 added z
466 466 |
467 467 o 7:123221671fd4 added y
468 468 |
469 469 | o 6:6ec71c037d94 added x
470 470 | |
471 471 | | o 5:36b793615f78 added foo to c
472 472 | | |
473 473 | | | o 4:863a25e1a9ea added x
474 474 | | |/
475 475 | @ | 3:9150fe93bec6 added d
476 476 |/ /
477 477 o / 2:155349b645be added c
478 478 |/
479 479 o 1:5f6d8a4bf34a added b
480 480 |
481 481 o 0:9092f1db7931 added a
482 482
483 483 $ hg graft -r 8 -r 7 -r 5
484 484 grafting 8:637f9e9bbfd4 "added z" (tip)
485 485 grafting 7:123221671fd4 "added y"
486 486 grafting 5:36b793615f78 "added foo to c"
487 487 merging c
488 488 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
489 489 abort: unresolved conflicts, can't continue
490 490 (use 'hg resolve' and 'hg graft --continue')
491 491 [1]
492 492
493 493 $ cd ..
494 494 $ hg init pullrepo
495 495 $ cd pullrepo
496 496 $ cat >> .hg/hgrc <<EOF
497 497 > [phases]
498 498 > publish=False
499 499 > EOF
500 500 $ hg pull ../abortgraft --config phases.publish=False
501 501 pulling from ../abortgraft
502 502 requesting all changes
503 503 adding changesets
504 504 adding manifests
505 505 adding file changes
506 506 added 11 changesets with 9 changes to 8 files (+4 heads)
507 507 new changesets 9092f1db7931:6b98ff0062dd (6 drafts)
508 508 (run 'hg heads' to see heads, 'hg merge' to merge)
509 509 $ hg up 9
510 510 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
511 511 $ echo w > w
512 512 $ hg ci -Aqm "added w" --config phases.publish=False
513 513
514 514 $ cd ../abortgraft
515 515 $ hg pull ../pullrepo
516 516 pulling from ../pullrepo
517 517 searching for changes
518 518 adding changesets
519 519 adding manifests
520 520 adding file changes
521 521 added 1 changesets with 1 changes to 1 files (+1 heads)
522 522 new changesets 311dfc6cf3bf (1 drafts)
523 523 (run 'hg heads .' to see heads, 'hg merge' to merge)
524 524
525 525 $ hg abort
526 526 new changesets detected on destination branch, can't strip
527 527 graft aborted
528 528 working directory is now at 6b98ff0062dd
529 529
530 530 $ cd ..
531 531
532 532 ============================
533 533 Testing --no-commit option:|
534 534 ============================
535 535
536 536 $ hg init nocommit
537 537 $ cd nocommit
538 538 $ echo a > a
539 539 $ hg ci -qAma
540 540 $ echo b > b
541 541 $ hg ci -qAmb
542 542 $ hg up -q 0
543 543 $ echo c > c
544 544 $ hg ci -qAmc
545 545 $ hg log -GT "{rev}:{node|short} {desc}\n"
546 546 @ 2:d36c0562f908 c
547 547 |
548 548 | o 1:d2ae7f538514 b
549 549 |/
550 550 o 0:cb9a9f314b8b a
551 551
552 552
553 553 Check reporting when --no-commit used with non-applicable options:
554 554
555 555 $ hg graft 1 --no-commit -e
556 556 abort: cannot specify both --no-commit and --edit
557 557 [10]
558 558
559 559 $ hg graft 1 --no-commit --log
560 560 abort: cannot specify both --no-commit and --log
561 561 [10]
562 562
563 563 $ hg graft 1 --no-commit -D
564 564 abort: cannot specify both --no-commit and --currentdate
565 565 [10]
566 566
567 567 Test --no-commit is working:
568 568 $ hg graft 1 --no-commit
569 569 grafting 1:d2ae7f538514 "b"
570 570
571 571 $ hg log -GT "{rev}:{node|short} {desc}\n"
572 572 @ 2:d36c0562f908 c
573 573 |
574 574 | o 1:d2ae7f538514 b
575 575 |/
576 576 o 0:cb9a9f314b8b a
577 577
578 578
579 579 $ hg diff
580 580 diff -r d36c0562f908 b
581 581 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
582 582 +++ b/b Thu Jan 01 00:00:00 1970 +0000
583 583 @@ -0,0 +1,1 @@
584 584 +b
585 585
586 586 Prepare wrdir to check --no-commit is resepected after --continue:
587 587
588 588 $ hg up -qC
589 589 $ echo A>a
590 590 $ hg ci -qm "A in file a"
591 591 $ hg up -q 1
592 592 $ echo B>a
593 593 $ hg ci -qm "B in file a"
594 594 $ hg log -GT "{rev}:{node|short} {desc}\n"
595 595 @ 4:2aa9ad1006ff B in file a
596 596 |
597 597 | o 3:09e253b87e17 A in file a
598 598 | |
599 599 | o 2:d36c0562f908 c
600 600 | |
601 601 o | 1:d2ae7f538514 b
602 602 |/
603 603 o 0:cb9a9f314b8b a
604 604
605 605
606 606 $ hg graft 3 --no-commit
607 607 grafting 3:09e253b87e17 "A in file a"
608 608 merging a
609 609 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
610 610 abort: unresolved conflicts, can't continue
611 611 (use 'hg resolve' and 'hg graft --continue')
612 612 [1]
613 613
614 614 Resolve conflict:
615 615 $ echo A>a
616 616 $ hg resolve --mark
617 617 (no more unresolved files)
618 618 continue: hg graft --continue
619 619
620 620 $ hg graft --continue
621 621 grafting 3:09e253b87e17 "A in file a"
622 622 $ hg log -GT "{rev}:{node|short} {desc}\n"
623 623 @ 4:2aa9ad1006ff B in file a
624 624 |
625 625 | o 3:09e253b87e17 A in file a
626 626 | |
627 627 | o 2:d36c0562f908 c
628 628 | |
629 629 o | 1:d2ae7f538514 b
630 630 |/
631 631 o 0:cb9a9f314b8b a
632 632
633 633 $ hg diff
634 634 diff -r 2aa9ad1006ff a
635 635 --- a/a Thu Jan 01 00:00:00 1970 +0000
636 636 +++ b/a Thu Jan 01 00:00:00 1970 +0000
637 637 @@ -1,1 +1,1 @@
638 638 -B
639 639 +A
640 640
641 641 $ hg up -qC
642 642
643 643 Check --no-commit is resepected when passed with --continue:
644 644
645 645 $ hg graft 3
646 646 grafting 3:09e253b87e17 "A in file a"
647 647 merging a
648 648 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
649 649 abort: unresolved conflicts, can't continue
650 650 (use 'hg resolve' and 'hg graft --continue')
651 651 [1]
652 652
653 653 Resolve conflict:
654 654 $ echo A>a
655 655 $ hg resolve --mark
656 656 (no more unresolved files)
657 657 continue: hg graft --continue
658 658
659 659 $ hg graft --continue --no-commit
660 660 grafting 3:09e253b87e17 "A in file a"
661 661 $ hg diff
662 662 diff -r 2aa9ad1006ff a
663 663 --- a/a Thu Jan 01 00:00:00 1970 +0000
664 664 +++ b/a Thu Jan 01 00:00:00 1970 +0000
665 665 @@ -1,1 +1,1 @@
666 666 -B
667 667 +A
668 668
669 669 $ hg log -GT "{rev}:{node|short} {desc}\n"
670 670 @ 4:2aa9ad1006ff B in file a
671 671 |
672 672 | o 3:09e253b87e17 A in file a
673 673 | |
674 674 | o 2:d36c0562f908 c
675 675 | |
676 676 o | 1:d2ae7f538514 b
677 677 |/
678 678 o 0:cb9a9f314b8b a
679 679
680 680 $ hg up -qC
681 681
682 682 Test --no-commit when graft multiple revisions:
683 683 When there is conflict:
684 684 $ hg graft -r "2::3" --no-commit
685 685 grafting 2:d36c0562f908 "c"
686 686 grafting 3:09e253b87e17 "A in file a"
687 687 merging a
688 688 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
689 689 abort: unresolved conflicts, can't continue
690 690 (use 'hg resolve' and 'hg graft --continue')
691 691 [1]
692 692
693 693 $ echo A>a
694 694 $ hg resolve --mark
695 695 (no more unresolved files)
696 696 continue: hg graft --continue
697 697 $ hg graft --continue
698 698 grafting 3:09e253b87e17 "A in file a"
699 699 $ hg diff
700 700 diff -r 2aa9ad1006ff a
701 701 --- a/a Thu Jan 01 00:00:00 1970 +0000
702 702 +++ b/a Thu Jan 01 00:00:00 1970 +0000
703 703 @@ -1,1 +1,1 @@
704 704 -B
705 705 +A
706 706 diff -r 2aa9ad1006ff c
707 707 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
708 708 +++ b/c Thu Jan 01 00:00:00 1970 +0000
709 709 @@ -0,0 +1,1 @@
710 710 +c
711 711
712 712 $ hg log -GT "{rev}:{node|short} {desc}\n"
713 713 @ 4:2aa9ad1006ff B in file a
714 714 |
715 715 | o 3:09e253b87e17 A in file a
716 716 | |
717 717 | o 2:d36c0562f908 c
718 718 | |
719 719 o | 1:d2ae7f538514 b
720 720 |/
721 721 o 0:cb9a9f314b8b a
722 722
723 723 $ hg up -qC
724 724
725 725 When there is no conflict:
726 726 $ echo d>d
727 727 $ hg add d -q
728 728 $ hg ci -qmd
729 729 $ hg up 3 -q
730 730 $ hg log -GT "{rev}:{node|short} {desc}\n"
731 731 o 5:baefa8927fc0 d
732 732 |
733 733 o 4:2aa9ad1006ff B in file a
734 734 |
735 735 | @ 3:09e253b87e17 A in file a
736 736 | |
737 737 | o 2:d36c0562f908 c
738 738 | |
739 739 o | 1:d2ae7f538514 b
740 740 |/
741 741 o 0:cb9a9f314b8b a
742 742
743 743
744 744 $ hg graft -r 1 -r 5 --no-commit
745 745 grafting 1:d2ae7f538514 "b"
746 746 grafting 5:baefa8927fc0 "d" (tip)
747 747 $ hg diff
748 748 diff -r 09e253b87e17 b
749 749 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
750 750 +++ b/b Thu Jan 01 00:00:00 1970 +0000
751 751 @@ -0,0 +1,1 @@
752 752 +b
753 753 diff -r 09e253b87e17 d
754 754 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
755 755 +++ b/d Thu Jan 01 00:00:00 1970 +0000
756 756 @@ -0,0 +1,1 @@
757 757 +d
758 758 $ hg log -GT "{rev}:{node|short} {desc}\n"
759 759 o 5:baefa8927fc0 d
760 760 |
761 761 o 4:2aa9ad1006ff B in file a
762 762 |
763 763 | @ 3:09e253b87e17 A in file a
764 764 | |
765 765 | o 2:d36c0562f908 c
766 766 | |
767 767 o | 1:d2ae7f538514 b
768 768 |/
769 769 o 0:cb9a9f314b8b a
770 770
771 771 $ cd ..
@@ -1,934 +1,934 b''
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extdiff]
3 3 > # for portability:
4 4 > pdiff = sh "$RUNTESTDIR/pdiff"
5 5 > EOF
6 6
7 7 Create a repo with some stuff in it:
8 8
9 9 $ hg init a
10 10 $ cd a
11 11 $ echo a > a
12 12 $ echo a > d
13 13 $ echo a > e
14 14 $ hg ci -qAm0
15 15 $ echo b > a
16 16 $ hg ci -m1 -u bar
17 17 $ hg mv a b
18 18 $ hg ci -m2
19 19 $ hg cp b c
20 20 $ hg ci -m3 -u baz
21 21 $ echo b > d
22 22 $ echo f > e
23 23 $ hg ci -m4
24 24 $ hg up -q 3
25 25 $ echo b > e
26 26 $ hg branch -q stable
27 27 $ hg ci -m5
28 28 $ hg merge -q default --tool internal:local # for conflicts in e, choose 5 and ignore 4
29 29 $ hg branch -q default
30 30 $ hg ci -m6
31 31 $ hg phase --public 3
32 32 $ hg phase --force --secret 6
33 33
34 34 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
35 35 @ test@6.secret: 6
36 36 |\
37 37 | o test@5.draft: 5
38 38 | |
39 39 o | test@4.draft: 4
40 40 |/
41 41 o baz@3.public: 3
42 42 |
43 43 o test@2.public: 2
44 44 |
45 45 o bar@1.public: 1
46 46 |
47 47 o test@0.public: 0
48 48
49 49 Test --base for grafting the merge of 4 from the perspective of 5, thus only getting the change to d
50 50
51 51 $ hg up -cqr 3
52 52 $ hg graft -r 6 --base 5
53 53 grafting 6:25a2b029d3ae "6" (tip)
54 54 merging e
55 55 $ hg st --change .
56 56 M d
57 57
58 58 $ hg -q strip . --config extensions.strip=
59 59
60 60 Test --base for collapsing changesets 2 and 3, thus getting both b and c
61 61
62 62 $ hg up -cqr 0
63 63 $ hg graft -r 3 --base 1
64 64 grafting 3:4c60f11aa304 "3"
65 65 merging a and b to b
66 66 merging a and c to c
67 67 $ hg st --change .
68 68 A b
69 69 A c
70 70 R a
71 71
72 72 $ hg -q strip . --config extensions.strip=
73 73
74 74 Specifying child as --base revision fails safely (perhaps slightly confusing, but consistent)
75 75
76 76 $ hg graft -r 2 --base 3
77 77 grafting 2:5c095ad7e90f "2"
78 78 note: possible conflict - c was deleted and renamed to:
79 79 a
80 80 note: graft of 2:5c095ad7e90f created no changes to commit
81 81
82 82 Can't continue without starting:
83 83
84 84 $ hg -q up -cr tip
85 85 $ hg rm -q e
86 86 $ hg graft --continue
87 87 abort: no graft in progress
88 [255]
88 [20]
89 89 $ hg revert -r . -q e
90 90
91 91 Need to specify a rev:
92 92
93 93 $ hg graft
94 94 abort: no revisions specified
95 95 [10]
96 96
97 97 Can't graft ancestor:
98 98
99 99 $ hg graft 1 2
100 100 skipping ancestor revision 1:5d205f8b35b6
101 101 skipping ancestor revision 2:5c095ad7e90f
102 102 [255]
103 103
104 104 Specify revisions with -r:
105 105
106 106 $ hg graft -r 1 -r 2
107 107 skipping ancestor revision 1:5d205f8b35b6
108 108 skipping ancestor revision 2:5c095ad7e90f
109 109 [255]
110 110
111 111 $ hg graft -r 1 2
112 112 warning: inconsistent use of --rev might give unexpected revision ordering!
113 113 skipping ancestor revision 2:5c095ad7e90f
114 114 skipping ancestor revision 1:5d205f8b35b6
115 115 [255]
116 116
117 117 Conflicting date/user options:
118 118
119 119 $ hg up -q 0
120 120 $ hg graft -U --user foo 2
121 121 abort: cannot specify both --user and --currentuser
122 122 [10]
123 123 $ hg graft -D --date '0 0' 2
124 124 abort: cannot specify both --date and --currentdate
125 125 [10]
126 126
127 127 Can't graft with dirty wd:
128 128
129 129 $ hg up -q 0
130 130 $ echo foo > a
131 131 $ hg graft 1
132 132 abort: uncommitted changes
133 [255]
133 [20]
134 134 $ hg revert a
135 135
136 136 Graft a rename:
137 137 (this also tests that editor is invoked if '--edit' is specified)
138 138
139 139 $ hg status --rev "2^1" --rev 2
140 140 A b
141 141 R a
142 142 $ HGEDITOR=cat hg graft 2 -u foo --edit
143 143 grafting 2:5c095ad7e90f "2"
144 144 merging a and b to b
145 145 2
146 146
147 147
148 148 HG: Enter commit message. Lines beginning with 'HG:' are removed.
149 149 HG: Leave message empty to abort commit.
150 150 HG: --
151 151 HG: user: foo
152 152 HG: branch 'default'
153 153 HG: added b
154 154 HG: removed a
155 155 $ hg export tip --git
156 156 # HG changeset patch
157 157 # User foo
158 158 # Date 0 0
159 159 # Thu Jan 01 00:00:00 1970 +0000
160 160 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
161 161 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
162 162 2
163 163
164 164 diff --git a/a b/b
165 165 rename from a
166 166 rename to b
167 167
168 168 Look for extra:source
169 169
170 170 $ hg log --debug -r tip
171 171 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
172 172 tag: tip
173 173 phase: draft
174 174 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
175 175 parent: -1:0000000000000000000000000000000000000000
176 176 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
177 177 user: foo
178 178 date: Thu Jan 01 00:00:00 1970 +0000
179 179 files+: b
180 180 files-: a
181 181 extra: branch=default
182 182 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
183 183 description:
184 184 2
185 185
186 186
187 187
188 188 Graft out of order, skipping a merge and a duplicate
189 189 (this also tests that editor is not invoked if '--edit' is not specified)
190 190
191 191 $ hg graft 1 5 4 3 'merge()' 2 -n
192 192 skipping ungraftable merge revision 6
193 193 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
194 194 grafting 1:5d205f8b35b6 "1"
195 195 grafting 5:97f8bfe72746 "5"
196 196 grafting 4:9c233e8e184d "4"
197 197 grafting 3:4c60f11aa304 "3"
198 198
199 199 $ HGEDITOR=cat hg graft 1 5 'merge()' 2 --debug
200 200 skipping ungraftable merge revision 6
201 201 scanning for duplicate grafts
202 202 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
203 203 grafting 1:5d205f8b35b6 "1"
204 204 unmatched files in local:
205 205 b
206 206 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
207 207 on local side:
208 208 src: 'a' -> dst: 'b' *
209 209 checking for directory renames
210 210 resolving manifests
211 211 branchmerge: True, force: True, partial: False
212 212 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
213 213 preserving b for resolve of b
214 214 starting 4 threads for background file closing (?)
215 215 b: local copied/moved from a -> m (premerge)
216 216 picked tool ':merge' for b (binary False symlink False changedelete False)
217 217 merging b and a to b
218 218 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
219 219 premerge successful
220 220 committing files:
221 221 b
222 222 committing manifest
223 223 committing changelog
224 224 updating the branch cache
225 225 grafting 5:97f8bfe72746 "5"
226 226 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
227 227 on local side:
228 228 src: 'c' -> dst: 'b'
229 229 checking for directory renames
230 230 resolving manifests
231 231 branchmerge: True, force: True, partial: False
232 232 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
233 233 e: remote is newer -> g
234 234 getting e
235 235 committing files:
236 236 e
237 237 committing manifest
238 238 committing changelog
239 239 updating the branch cache
240 240 $ HGEDITOR=cat hg graft 4 3 --log --debug
241 241 scanning for duplicate grafts
242 242 grafting 4:9c233e8e184d "4"
243 243 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
244 244 on local side:
245 245 src: 'c' -> dst: 'b'
246 246 checking for directory renames
247 247 resolving manifests
248 248 branchmerge: True, force: True, partial: False
249 249 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
250 250 d: remote is newer -> g
251 251 getting d
252 252 preserving e for resolve of e
253 253 e: versions differ -> m (premerge)
254 254 picked tool ':merge' for e (binary False symlink False changedelete False)
255 255 merging e
256 256 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
257 257 e: versions differ -> m (merge)
258 258 picked tool ':merge' for e (binary False symlink False changedelete False)
259 259 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
260 260 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
261 261 abort: unresolved conflicts, can't continue
262 262 (use 'hg resolve' and 'hg graft --continue')
263 263 [1]
264 264
265 265 Summary should mention graft:
266 266
267 267 $ hg summary |grep graft
268 268 commit: 2 modified, 2 unknown, 1 unresolved (graft in progress)
269 269
270 270 Using status to get more context
271 271
272 272 $ hg status --verbose
273 273 M d
274 274 M e
275 275 ? a.orig
276 276 ? e.orig
277 277 # The repository is in an unfinished *graft* state.
278 278
279 279 # Unresolved merge conflicts:
280 280 #
281 281 # e
282 282 #
283 283 # To mark files as resolved: hg resolve --mark FILE
284 284
285 285 # To continue: hg graft --continue
286 286 # To abort: hg graft --abort
287 287 # To stop: hg graft --stop
288 288
289 289
290 290 Commit while interrupted should fail:
291 291
292 292 $ hg ci -m 'commit interrupted graft'
293 293 abort: graft in progress
294 294 (use 'hg graft --continue' or 'hg graft --stop' to stop)
295 [255]
295 [20]
296 296
297 297 Abort the graft and try committing:
298 298
299 299 $ hg up -C .
300 300 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
301 301 $ echo c >> e
302 302 $ hg ci -mtest
303 303
304 304 $ hg strip . --config extensions.strip=
305 305 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
306 306 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
307 307
308 308 Graft again:
309 309
310 310 $ hg graft 1 5 4 3 'merge()' 2
311 311 skipping ungraftable merge revision 6
312 312 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
313 313 skipping revision 1:5d205f8b35b6 (already grafted to 8:6b9e5368ca4e)
314 314 skipping revision 5:97f8bfe72746 (already grafted to 9:1905859650ec)
315 315 grafting 4:9c233e8e184d "4"
316 316 merging e
317 317 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
318 318 abort: unresolved conflicts, can't continue
319 319 (use 'hg resolve' and 'hg graft --continue')
320 320 [1]
321 321
322 322 Continue without resolve should fail:
323 323
324 324 $ hg graft -c
325 325 grafting 4:9c233e8e184d "4"
326 326 abort: unresolved merge conflicts (see 'hg help resolve')
327 327 [255]
328 328
329 329 Fix up:
330 330
331 331 $ echo b > e
332 332 $ hg resolve -m e
333 333 (no more unresolved files)
334 334 continue: hg graft --continue
335 335
336 336 Continue with a revision should fail:
337 337
338 338 $ hg graft -c 6
339 339 abort: can't specify --continue and revisions
340 340 [10]
341 341
342 342 $ hg graft -c -r 6
343 343 abort: can't specify --continue and revisions
344 344 [10]
345 345
346 346 Continue for real, clobber usernames
347 347
348 348 $ hg graft -c -U
349 349 grafting 4:9c233e8e184d "4"
350 350 grafting 3:4c60f11aa304 "3"
351 351
352 352 Compare with original:
353 353
354 354 $ hg diff -r 6
355 355 $ hg status --rev 0:. -C
356 356 M d
357 357 M e
358 358 A b
359 359 a
360 360 A c
361 361 a
362 362 R a
363 363
364 364 View graph:
365 365
366 366 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
367 367 @ test@11.draft: 3
368 368 |
369 369 o test@10.draft: 4
370 370 |
371 371 o test@9.draft: 5
372 372 |
373 373 o bar@8.draft: 1
374 374 |
375 375 o foo@7.draft: 2
376 376 |
377 377 | o test@6.secret: 6
378 378 | |\
379 379 | | o test@5.draft: 5
380 380 | | |
381 381 | o | test@4.draft: 4
382 382 | |/
383 383 | o baz@3.public: 3
384 384 | |
385 385 | o test@2.public: 2
386 386 | |
387 387 | o bar@1.public: 1
388 388 |/
389 389 o test@0.public: 0
390 390
391 391 Graft again onto another branch should preserve the original source
392 392 $ hg up -q 0
393 393 $ echo 'g'>g
394 394 $ hg add g
395 395 $ hg ci -m 7
396 396 created new head
397 397 $ hg graft 7
398 398 grafting 7:ef0ef43d49e7 "2"
399 399
400 400 $ hg log -r 7 --template '{rev}:{node}\n'
401 401 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
402 402 $ hg log -r 2 --template '{rev}:{node}\n'
403 403 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
404 404
405 405 $ hg log --debug -r tip
406 406 changeset: 13:7a4785234d87ec1aa420ed6b11afe40fa73e12a9
407 407 tag: tip
408 408 phase: draft
409 409 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
410 410 parent: -1:0000000000000000000000000000000000000000
411 411 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
412 412 user: foo
413 413 date: Thu Jan 01 00:00:00 1970 +0000
414 414 files+: b
415 415 files-: a
416 416 extra: branch=default
417 417 extra: intermediate-source=ef0ef43d49e79e81ddafdc7997401ba0041efc82
418 418 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
419 419 description:
420 420 2
421 421
422 422
423 423 Disallow grafting an already grafted cset onto its original branch
424 424 $ hg up -q 6
425 425 $ hg graft 7
426 426 skipping already grafted revision 7:ef0ef43d49e7 (was grafted from 2:5c095ad7e90f)
427 427 [255]
428 428
429 429 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13
430 430 --- */hg-5c095ad7e90f.patch * (glob)
431 431 +++ */hg-7a4785234d87.patch * (glob)
432 432 @@ -1,18 +1,18 @@
433 433 # HG changeset patch
434 434 -# User test
435 435 +# User foo
436 436 # Date 0 0
437 437 # Thu Jan 01 00:00:00 1970 +0000
438 438 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
439 439 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
440 440 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
441 441 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
442 442 2
443 443
444 444 -diff -r 5d205f8b35b6 -r 5c095ad7e90f a
445 445 +diff -r b592ea63bb0c -r 7a4785234d87 a
446 446 --- a/a Thu Jan 01 00:00:00 1970 +0000
447 447 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
448 448 @@ -1,1 +0,0 @@
449 449 --b
450 450 -diff -r 5d205f8b35b6 -r 5c095ad7e90f b
451 451 +-a
452 452 +diff -r b592ea63bb0c -r 7a4785234d87 b
453 453 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
454 454 +++ b/b Thu Jan 01 00:00:00 1970 +0000
455 455 @@ -0,0 +1,1 @@
456 456 -+b
457 457 ++a
458 458 [1]
459 459
460 460 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13 -X .
461 461 --- */hg-5c095ad7e90f.patch * (glob)
462 462 +++ */hg-7a4785234d87.patch * (glob)
463 463 @@ -1,8 +1,8 @@
464 464 # HG changeset patch
465 465 -# User test
466 466 +# User foo
467 467 # Date 0 0
468 468 # Thu Jan 01 00:00:00 1970 +0000
469 469 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
470 470 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
471 471 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
472 472 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
473 473 2
474 474
475 475 [1]
476 476
477 477 Disallow grafting already grafted csets with the same origin onto each other
478 478 $ hg up -q 13
479 479 $ hg graft 2
480 480 skipping revision 2:5c095ad7e90f (already grafted to 13:7a4785234d87)
481 481 [255]
482 482 $ hg graft 7
483 483 skipping already grafted revision 7:ef0ef43d49e7 (13:7a4785234d87 also has origin 2:5c095ad7e90f)
484 484 [255]
485 485
486 486 $ hg up -q 7
487 487 $ hg graft 2
488 488 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
489 489 [255]
490 490 $ hg graft tip
491 491 skipping already grafted revision 13:7a4785234d87 (7:ef0ef43d49e7 also has origin 2:5c095ad7e90f)
492 492 [255]
493 493
494 494 Graft with --log
495 495
496 496 $ hg up -Cq 1
497 497 $ hg graft 3 --log -u foo
498 498 grafting 3:4c60f11aa304 "3"
499 499 $ hg log --template '{rev}:{node|short} {parents} {desc}\n' -r tip
500 500 14:0c921c65ef1e 1:5d205f8b35b6 3
501 501 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
502 502
503 503 Resolve conflicted graft
504 504 $ hg up -q 0
505 505 $ echo b > a
506 506 $ hg ci -m 8
507 507 created new head
508 508 $ echo c > a
509 509 $ hg ci -m 9
510 510 $ hg graft 1 --tool internal:fail
511 511 grafting 1:5d205f8b35b6 "1"
512 512 abort: unresolved conflicts, can't continue
513 513 (use 'hg resolve' and 'hg graft --continue')
514 514 [1]
515 515 $ hg resolve --all
516 516 merging a
517 517 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
518 518 [1]
519 519 $ cat a
520 520 <<<<<<< local: aaa4406d4f0a - test: 9
521 521 c
522 522 =======
523 523 b
524 524 >>>>>>> graft: 5d205f8b35b6 - bar: 1
525 525 $ echo b > a
526 526 $ hg resolve -m a
527 527 (no more unresolved files)
528 528 continue: hg graft --continue
529 529 $ hg graft -c
530 530 grafting 1:5d205f8b35b6 "1"
531 531 $ hg export tip --git
532 532 # HG changeset patch
533 533 # User bar
534 534 # Date 0 0
535 535 # Thu Jan 01 00:00:00 1970 +0000
536 536 # Node ID f67661df0c4804d301f064f332b57e7d5ddaf2be
537 537 # Parent aaa4406d4f0ae9befd6e58c82ec63706460cbca6
538 538 1
539 539
540 540 diff --git a/a b/a
541 541 --- a/a
542 542 +++ b/a
543 543 @@ -1,1 +1,1 @@
544 544 -c
545 545 +b
546 546
547 547 Resolve conflicted graft with rename
548 548 $ echo c > a
549 549 $ hg ci -m 10
550 550 $ hg graft 2 --tool internal:fail
551 551 grafting 2:5c095ad7e90f "2"
552 552 abort: unresolved conflicts, can't continue
553 553 (use 'hg resolve' and 'hg graft --continue')
554 554 [1]
555 555 $ hg resolve --all
556 556 merging a and b to b
557 557 (no more unresolved files)
558 558 continue: hg graft --continue
559 559 $ hg graft -c
560 560 grafting 2:5c095ad7e90f "2"
561 561 $ hg export tip --git
562 562 # HG changeset patch
563 563 # User test
564 564 # Date 0 0
565 565 # Thu Jan 01 00:00:00 1970 +0000
566 566 # Node ID 9627f653b421c61fc1ea4c4e366745070fa3d2bc
567 567 # Parent ee295f490a40b97f3d18dd4c4f1c8936c233b612
568 568 2
569 569
570 570 diff --git a/a b/b
571 571 rename from a
572 572 rename to b
573 573
574 574 Test simple origin(), with and without args
575 575 $ hg log -r 'origin()'
576 576 changeset: 1:5d205f8b35b6
577 577 user: bar
578 578 date: Thu Jan 01 00:00:00 1970 +0000
579 579 summary: 1
580 580
581 581 changeset: 2:5c095ad7e90f
582 582 user: test
583 583 date: Thu Jan 01 00:00:00 1970 +0000
584 584 summary: 2
585 585
586 586 changeset: 3:4c60f11aa304
587 587 user: baz
588 588 date: Thu Jan 01 00:00:00 1970 +0000
589 589 summary: 3
590 590
591 591 changeset: 4:9c233e8e184d
592 592 user: test
593 593 date: Thu Jan 01 00:00:00 1970 +0000
594 594 summary: 4
595 595
596 596 changeset: 5:97f8bfe72746
597 597 branch: stable
598 598 parent: 3:4c60f11aa304
599 599 user: test
600 600 date: Thu Jan 01 00:00:00 1970 +0000
601 601 summary: 5
602 602
603 603 $ hg log -r 'origin(7)'
604 604 changeset: 2:5c095ad7e90f
605 605 user: test
606 606 date: Thu Jan 01 00:00:00 1970 +0000
607 607 summary: 2
608 608
609 609 Now transplant a graft to test following through copies
610 610 $ hg up -q 0
611 611 $ hg branch -q dev
612 612 $ hg ci -qm "dev branch"
613 613 $ hg --config extensions.transplant= transplant -q 7
614 614 $ hg log -r 'origin(.)'
615 615 changeset: 2:5c095ad7e90f
616 616 user: test
617 617 date: Thu Jan 01 00:00:00 1970 +0000
618 618 summary: 2
619 619
620 620 Test that the graft and transplant markers in extra are converted, allowing
621 621 origin() to still work. Note that these recheck the immediately preceeding two
622 622 tests.
623 623 $ hg --quiet --config extensions.convert= --config convert.hg.saverev=True convert . ../converted
624 624
625 625 The graft case
626 626 $ hg -R ../converted log -r 7 --template "{rev}: {node}\n{join(extras, '\n')}\n"
627 627 7: 7ae846e9111fc8f57745634250c7b9ac0a60689b
628 628 branch=default
629 629 convert_revision=ef0ef43d49e79e81ddafdc7997401ba0041efc82
630 630 source=e0213322b2c1a5d5d236c74e79666441bee67a7d
631 631 $ hg -R ../converted log -r 'origin(7)'
632 632 changeset: 2:e0213322b2c1
633 633 user: test
634 634 date: Thu Jan 01 00:00:00 1970 +0000
635 635 summary: 2
636 636
637 637 Test that template correctly expands more than one 'extra' (issue4362), and that
638 638 'intermediate-source' is converted.
639 639 $ hg -R ../converted log -r 13 --template "{extras % ' Extra: {extra}\n'}"
640 640 Extra: branch=default
641 641 Extra: convert_revision=7a4785234d87ec1aa420ed6b11afe40fa73e12a9
642 642 Extra: intermediate-source=7ae846e9111fc8f57745634250c7b9ac0a60689b
643 643 Extra: source=e0213322b2c1a5d5d236c74e79666441bee67a7d
644 644
645 645 The transplant case
646 646 $ hg -R ../converted log -r tip --template "{rev}: {node}\n{join(extras, '\n')}\n"
647 647 21: fbb6c5cc81002f2b4b49c9d731404688bcae5ade
648 648 branch=dev
649 649 convert_revision=7e61b508e709a11d28194a5359bc3532d910af21
650 650 transplant_source=z\xe8F\xe9\x11\x1f\xc8\xf5wEcBP\xc7\xb9\xac\n`h\x9b
651 651 $ hg -R ../converted log -r 'origin(tip)'
652 652 changeset: 2:e0213322b2c1
653 653 user: test
654 654 date: Thu Jan 01 00:00:00 1970 +0000
655 655 summary: 2
656 656
657 657
658 658 Test simple destination
659 659 $ hg log -r 'destination()'
660 660 changeset: 7:ef0ef43d49e7
661 661 parent: 0:68795b066622
662 662 user: foo
663 663 date: Thu Jan 01 00:00:00 1970 +0000
664 664 summary: 2
665 665
666 666 changeset: 8:6b9e5368ca4e
667 667 user: bar
668 668 date: Thu Jan 01 00:00:00 1970 +0000
669 669 summary: 1
670 670
671 671 changeset: 9:1905859650ec
672 672 user: test
673 673 date: Thu Jan 01 00:00:00 1970 +0000
674 674 summary: 5
675 675
676 676 changeset: 10:52dc0b4c6907
677 677 user: test
678 678 date: Thu Jan 01 00:00:00 1970 +0000
679 679 summary: 4
680 680
681 681 changeset: 11:882b35362a6b
682 682 user: test
683 683 date: Thu Jan 01 00:00:00 1970 +0000
684 684 summary: 3
685 685
686 686 changeset: 13:7a4785234d87
687 687 user: foo
688 688 date: Thu Jan 01 00:00:00 1970 +0000
689 689 summary: 2
690 690
691 691 changeset: 14:0c921c65ef1e
692 692 parent: 1:5d205f8b35b6
693 693 user: foo
694 694 date: Thu Jan 01 00:00:00 1970 +0000
695 695 summary: 3
696 696
697 697 changeset: 17:f67661df0c48
698 698 user: bar
699 699 date: Thu Jan 01 00:00:00 1970 +0000
700 700 summary: 1
701 701
702 702 changeset: 19:9627f653b421
703 703 user: test
704 704 date: Thu Jan 01 00:00:00 1970 +0000
705 705 summary: 2
706 706
707 707 changeset: 21:7e61b508e709
708 708 branch: dev
709 709 tag: tip
710 710 user: foo
711 711 date: Thu Jan 01 00:00:00 1970 +0000
712 712 summary: 2
713 713
714 714 $ hg log -r 'destination(2)'
715 715 changeset: 7:ef0ef43d49e7
716 716 parent: 0:68795b066622
717 717 user: foo
718 718 date: Thu Jan 01 00:00:00 1970 +0000
719 719 summary: 2
720 720
721 721 changeset: 13:7a4785234d87
722 722 user: foo
723 723 date: Thu Jan 01 00:00:00 1970 +0000
724 724 summary: 2
725 725
726 726 changeset: 19:9627f653b421
727 727 user: test
728 728 date: Thu Jan 01 00:00:00 1970 +0000
729 729 summary: 2
730 730
731 731 changeset: 21:7e61b508e709
732 732 branch: dev
733 733 tag: tip
734 734 user: foo
735 735 date: Thu Jan 01 00:00:00 1970 +0000
736 736 summary: 2
737 737
738 738 Transplants of grafts can find a destination...
739 739 $ hg log -r 'destination(7)'
740 740 changeset: 21:7e61b508e709
741 741 branch: dev
742 742 tag: tip
743 743 user: foo
744 744 date: Thu Jan 01 00:00:00 1970 +0000
745 745 summary: 2
746 746
747 747 ... grafts of grafts unfortunately can't
748 748 $ hg graft -q 13 --debug
749 749 scanning for duplicate grafts
750 750 grafting 13:7a4785234d87 "2"
751 751 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
752 752 on local side:
753 753 src: 'a' -> dst: 'b' *
754 754 on remote side:
755 755 src: 'a' -> dst: 'b' *
756 756 checking for directory renames
757 757 resolving manifests
758 758 branchmerge: True, force: True, partial: False
759 759 ancestor: b592ea63bb0c, local: 7e61b508e709+, remote: 7a4785234d87
760 760 starting 4 threads for background file closing (?)
761 761 nothing to commit, clearing merge state
762 762 note: graft of 13:7a4785234d87 created no changes to commit
763 763 $ hg log -r 'destination(13)'
764 764 All copies of a cset
765 765 $ hg log -r 'origin(13) or destination(origin(13))'
766 766 changeset: 2:5c095ad7e90f
767 767 user: test
768 768 date: Thu Jan 01 00:00:00 1970 +0000
769 769 summary: 2
770 770
771 771 changeset: 7:ef0ef43d49e7
772 772 parent: 0:68795b066622
773 773 user: foo
774 774 date: Thu Jan 01 00:00:00 1970 +0000
775 775 summary: 2
776 776
777 777 changeset: 13:7a4785234d87
778 778 user: foo
779 779 date: Thu Jan 01 00:00:00 1970 +0000
780 780 summary: 2
781 781
782 782 changeset: 19:9627f653b421
783 783 user: test
784 784 date: Thu Jan 01 00:00:00 1970 +0000
785 785 summary: 2
786 786
787 787 changeset: 21:7e61b508e709
788 788 branch: dev
789 789 tag: tip
790 790 user: foo
791 791 date: Thu Jan 01 00:00:00 1970 +0000
792 792 summary: 2
793 793
794 794
795 795 graft skips ancestors
796 796
797 797 $ hg graft 21 3
798 798 skipping ancestor revision 21:7e61b508e709
799 799 grafting 3:4c60f11aa304 "3"
800 800 merging b and c to c
801 801
802 802 graft with --force (still doesn't graft merges)
803 803
804 804 $ hg graft 19 0 6
805 805 skipping ungraftable merge revision 6
806 806 skipping ancestor revision 0:68795b066622
807 807 grafting 19:9627f653b421 "2"
808 808 merging b
809 809 note: graft of 19:9627f653b421 created no changes to commit
810 810 $ hg graft 19 0 6 --force
811 811 skipping ungraftable merge revision 6
812 812 grafting 19:9627f653b421 "2"
813 813 merging b
814 814 note: graft of 19:9627f653b421 created no changes to commit
815 815 grafting 0:68795b066622 "0"
816 816
817 817 graft --force after backout. Do the backout with graft too, to make
818 818 sure we support issue6248.
819 819
820 820 $ echo abc > a
821 821 $ hg ci -m 24
822 822 $ hg graft --base . -r ".^" --no-commit
823 823 grafting 23:b1cac6de36a9 "0"
824 824 $ hg commit -m 'Backed out changeset 2e7ea477be26'
825 825 $ hg graft 24
826 826 skipping ancestor revision 24:2e7ea477be26
827 827 [255]
828 828 $ hg graft 24 --force
829 829 grafting 24:2e7ea477be26 "24"
830 830 merging a
831 831 $ cat a
832 832 abc
833 833
834 834 graft --continue after --force
835 835
836 836 $ echo def > a
837 837 $ hg ci -m 27
838 838 $ hg graft 24 --force --tool internal:fail
839 839 grafting 24:2e7ea477be26 "24"
840 840 abort: unresolved conflicts, can't continue
841 841 (use 'hg resolve' and 'hg graft --continue')
842 842 [1]
843 843 $ hg resolve --all
844 844 merging a
845 845 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
846 846 [1]
847 847 $ echo abc > a
848 848 $ hg resolve -m a
849 849 (no more unresolved files)
850 850 continue: hg graft --continue
851 851 $ hg graft -c
852 852 grafting 24:2e7ea477be26 "24"
853 853 $ cat a
854 854 abc
855 855
856 856 graft --continue after --base with conflits
857 857
858 858 $ echo base > d
859 859 $ hg ci -m _
860 860 $ hg graft -r 6
861 861 skipping ungraftable merge revision 6
862 862 [255]
863 863 $ hg graft -r 6 --base 5
864 864 grafting 6:25a2b029d3ae "6"
865 865 merging d
866 866 merging e
867 867 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
868 868 abort: unresolved conflicts, can't continue
869 869 (use 'hg resolve' and 'hg graft --continue')
870 870 [1]
871 871 $ echo a > d && hg resolve -qm
872 872 continue: hg graft --continue
873 873 $ hg graft --continue
874 874 grafting 6:25a2b029d3ae "6"
875 875
876 876 Continue testing same origin policy, using revision numbers from test above
877 877 but do some destructive editing of the repo:
878 878
879 879 $ hg up -qC 7
880 880 $ hg tag -l -r 13 tmp
881 881 $ hg --config extensions.strip= strip 2
882 882 saved backup bundle to $TESTTMP/a/.hg/strip-backup/5c095ad7e90f-d323a1e4-backup.hg
883 883 $ hg graft tmp
884 884 skipping already grafted revision 8:7a4785234d87 (2:ef0ef43d49e7 also has unknown origin 5c095ad7e90f)
885 885 [255]
886 886
887 887 Empty graft
888 888
889 889 $ hg up -qr 22
890 890 $ hg tag -f something
891 891 $ hg graft -qr 23
892 892 $ hg graft -f 23
893 893 grafting 23:72d9c7c75bcc "24"
894 894 note: graft of 23:72d9c7c75bcc created no changes to commit
895 895
896 896 $ cd ..
897 897
898 898 Graft to duplicate a commit
899 899
900 900 $ hg init graftsibling
901 901 $ cd graftsibling
902 902 $ touch a
903 903 $ hg commit -qAm a
904 904 $ touch b
905 905 $ hg commit -qAm b
906 906 $ hg log -G -T '{rev}\n'
907 907 @ 1
908 908 |
909 909 o 0
910 910
911 911 $ hg up -q 0
912 912 $ hg graft -r 1
913 913 grafting 1:0e067c57feba "b" (tip)
914 914 $ hg log -G -T '{rev}\n'
915 915 @ 2
916 916 |
917 917 | o 1
918 918 |/
919 919 o 0
920 920
921 921 Graft to duplicate a commit twice
922 922
923 923 $ hg up -q 0
924 924 $ hg graft -r 2
925 925 grafting 2:044ec77f6389 "b" (tip)
926 926 $ hg log -G -T '{rev}\n'
927 927 @ 3
928 928 |
929 929 | o 2
930 930 |/
931 931 | o 1
932 932 |/
933 933 o 0
934 934
@@ -1,605 +1,605 b''
1 1 #testcases abortcommand abortflag
2 2
3 3 #if abortflag
4 4 $ cat >> $HGRCPATH <<EOF
5 5 > [alias]
6 6 > abort = histedit --abort
7 7 > EOF
8 8 #endif
9 9
10 10 Test argument handling and various data parsing
11 11 ==================================================
12 12
13 13
14 14 Enable extensions used by this test.
15 15 $ cat >>$HGRCPATH <<EOF
16 16 > [extensions]
17 17 > histedit=
18 18 > EOF
19 19
20 20 Repo setup.
21 21 $ hg init foo
22 22 $ cd foo
23 23 $ echo alpha >> alpha
24 24 $ hg addr
25 25 adding alpha
26 26 $ hg ci -m one
27 27 $ echo alpha >> alpha
28 28 $ hg ci -m two
29 29 $ echo alpha >> alpha
30 30 $ hg ci -m three
31 31 $ echo alpha >> alpha
32 32 $ hg ci -m four
33 33 $ echo alpha >> alpha
34 34 $ hg ci -m five
35 35
36 36 $ hg log --style compact --graph
37 37 @ 4[tip] 08d98a8350f3 1970-01-01 00:00 +0000 test
38 38 | five
39 39 |
40 40 o 3 c8e68270e35a 1970-01-01 00:00 +0000 test
41 41 | four
42 42 |
43 43 o 2 eb57da33312f 1970-01-01 00:00 +0000 test
44 44 | three
45 45 |
46 46 o 1 579e40513370 1970-01-01 00:00 +0000 test
47 47 | two
48 48 |
49 49 o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test
50 50 one
51 51
52 52
53 53 histedit --continue/--abort with no existing state
54 54 --------------------------------------------------
55 55
56 56 $ hg histedit --continue
57 57 abort: no histedit in progress
58 [255]
58 [20]
59 59 $ hg abort
60 60 abort: no histedit in progress (abortflag !)
61 61 abort: no operation in progress (abortcommand !)
62 [255]
62 [20]
63 63
64 64 Run a dummy edit to make sure we get tip^^ correctly via revsingle.
65 65 --------------------------------------------------------------------
66 66
67 67 $ HGEDITOR=cat hg histedit "tip^^"
68 68 pick eb57da33312f 2 three
69 69 pick c8e68270e35a 3 four
70 70 pick 08d98a8350f3 4 five
71 71
72 72 # Edit history between eb57da33312f and 08d98a8350f3
73 73 #
74 74 # Commits are listed from least to most recent
75 75 #
76 76 # You can reorder changesets by reordering the lines
77 77 #
78 78 # Commands:
79 79 #
80 80 # e, edit = use commit, but stop for amending
81 81 # m, mess = edit commit message without changing commit content
82 82 # p, pick = use commit
83 83 # b, base = checkout changeset and apply further changesets from there
84 84 # d, drop = remove commit from history
85 85 # f, fold = use commit, but combine it with the one above
86 86 # r, roll = like fold, but discard this commit's description and date
87 87 #
88 88
89 89 Run on a revision not ancestors of the current working directory.
90 90 --------------------------------------------------------------------
91 91
92 92 $ hg up 2
93 93 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
94 94 $ hg histedit -r 4
95 95 abort: 08d98a8350f3 is not an ancestor of working directory
96 96 [255]
97 97 $ hg up --quiet
98 98
99 99
100 100 Test that we pick the minimum of a revrange
101 101 ---------------------------------------
102 102
103 103 $ HGEDITOR=cat hg histedit '2::' --commands - << EOF
104 104 > pick eb57da33312f 2 three
105 105 > pick c8e68270e35a 3 four
106 106 > pick 08d98a8350f3 4 five
107 107 > EOF
108 108 $ hg up --quiet
109 109
110 110 $ HGEDITOR=cat hg histedit 'tip:2' --commands - << EOF
111 111 > pick eb57da33312f 2 three
112 112 > pick c8e68270e35a 3 four
113 113 > pick 08d98a8350f3 4 five
114 114 > EOF
115 115 $ hg up --quiet
116 116
117 117 Test config specified default
118 118 -----------------------------
119 119
120 120 $ HGEDITOR=cat hg histedit --config "histedit.defaultrev=only(.) - ::eb57da33312f" --commands - << EOF
121 121 > pick c8e68270e35a 3 four
122 122 > pick 08d98a8350f3 4 five
123 123 > EOF
124 124
125 125 Test invalid config default
126 126 ---------------------------
127 127
128 128 $ hg histedit --config "histedit.defaultrev="
129 129 abort: config option histedit.defaultrev can't be empty
130 130 [255]
131 131
132 132 Run on a revision not descendants of the initial parent
133 133 --------------------------------------------------------------------
134 134
135 135 Test the message shown for inconsistent histedit state, which may be
136 136 created (and forgotten) by Mercurial earlier than 2.7. This emulates
137 137 Mercurial earlier than 2.7 by renaming ".hg/histedit-state"
138 138 temporarily.
139 139
140 140 $ hg log -G -T '{rev} {shortest(node)} {desc}\n' -r 2::
141 141 @ 4 08d9 five
142 142 |
143 143 o 3 c8e6 four
144 144 |
145 145 o 2 eb57 three
146 146 |
147 147 ~
148 148 $ HGEDITOR=cat hg histedit -r 4 --commands - << EOF
149 149 > edit 08d98a8350f3 4 five
150 150 > EOF
151 151 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
152 152 Editing (08d98a8350f3), you may commit or record as needed now.
153 153 (hg histedit --continue to resume)
154 154 [240]
155 155
156 156 $ hg graft --continue
157 157 abort: no graft in progress
158 158 (continue: hg histedit --continue)
159 [255]
159 [20]
160 160
161 161 $ mv .hg/histedit-state .hg/histedit-state.back
162 162 $ hg update --quiet --clean 2
163 163 $ echo alpha >> alpha
164 164 $ mv .hg/histedit-state.back .hg/histedit-state
165 165
166 166 $ hg histedit --continue
167 167 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-histedit.hg
168 168 $ hg log -G -T '{rev} {shortest(node)} {desc}\n' -r 2::
169 169 @ 4 f5ed five
170 170 |
171 171 | o 3 c8e6 four
172 172 |/
173 173 o 2 eb57 three
174 174 |
175 175 ~
176 176
177 177 $ hg unbundle -q $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-histedit.hg
178 178 $ hg strip -q -r f5ed --config extensions.strip=
179 179 $ hg up -q 08d98a8350f3
180 180
181 181 Test that missing revisions are detected
182 182 ---------------------------------------
183 183
184 184 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
185 185 > pick eb57da33312f 2 three
186 186 > pick 08d98a8350f3 4 five
187 187 > EOF
188 188 hg: parse error: missing rules for changeset c8e68270e35a
189 189 (use "drop c8e68270e35a" to discard, see also: 'hg help -e histedit.config')
190 190 [255]
191 191
192 192 Test that extra revisions are detected
193 193 ---------------------------------------
194 194
195 195 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
196 196 > pick 6058cbb6cfd7 0 one
197 197 > pick c8e68270e35a 3 four
198 198 > pick 08d98a8350f3 4 five
199 199 > EOF
200 200 hg: parse error: pick "6058cbb6cfd7" changeset was not a candidate
201 201 (only use listed changesets)
202 202 [255]
203 203
204 204 Test malformed line
205 205 ---------------------------------------
206 206
207 207 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
208 208 > pickeb57da33312f2three
209 209 > pick c8e68270e35a 3 four
210 210 > pick 08d98a8350f3 4 five
211 211 > EOF
212 212 hg: parse error: malformed line "pickeb57da33312f2three"
213 213 [255]
214 214
215 215 Test unknown changeset
216 216 ---------------------------------------
217 217
218 218 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
219 219 > pick 0123456789ab 2 three
220 220 > pick c8e68270e35a 3 four
221 221 > pick 08d98a8350f3 4 five
222 222 > EOF
223 223 hg: parse error: unknown changeset 0123456789ab listed
224 224 [255]
225 225
226 226 Test unknown command
227 227 ---------------------------------------
228 228
229 229 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
230 230 > coin eb57da33312f 2 three
231 231 > pick c8e68270e35a 3 four
232 232 > pick 08d98a8350f3 4 five
233 233 > EOF
234 234 hg: parse error: unknown action "coin"
235 235 [255]
236 236
237 237 Test duplicated changeset
238 238 ---------------------------------------
239 239
240 240 So one is missing and one appear twice.
241 241
242 242 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
243 243 > pick eb57da33312f 2 three
244 244 > pick eb57da33312f 2 three
245 245 > pick 08d98a8350f3 4 five
246 246 > EOF
247 247 hg: parse error: duplicated command for changeset eb57da33312f
248 248 [255]
249 249
250 250 Test bogus rev
251 251 ---------------------------------------
252 252
253 253 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
254 254 > pick eb57da33312f 2 three
255 255 > pick 0u98
256 256 > pick 08d98a8350f3 4 five
257 257 > EOF
258 258 hg: parse error: invalid changeset 0u98
259 259 [255]
260 260
261 261 Test short version of command
262 262 ---------------------------------------
263 263
264 264 Note: we use varying amounts of white space between command name and changeset
265 265 short hash. This tests issue3893.
266 266
267 267 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
268 268 > pick eb57da33312f 2 three
269 269 > p c8e68270e35a 3 four
270 270 > f 08d98a8350f3 4 five
271 271 > EOF
272 272 four
273 273 ***
274 274 five
275 275
276 276
277 277
278 278 HG: Enter commit message. Lines beginning with 'HG:' are removed.
279 279 HG: Leave message empty to abort commit.
280 280 HG: --
281 281 HG: user: test
282 282 HG: branch 'default'
283 283 HG: changed alpha
284 284 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/c8e68270e35a-63d8b8d8-histedit.hg
285 285
286 286 $ hg update -q 2
287 287 $ echo x > x
288 288 $ hg add x
289 289 $ hg commit -m'x' x
290 290 created new head
291 291 $ hg histedit -r 'heads(all())'
292 292 abort: The specified revisions must have exactly one common root
293 293 [255]
294 294
295 295 Test that trimming description using multi-byte characters
296 296 --------------------------------------------------------------------
297 297
298 298 $ "$PYTHON" <<EOF
299 299 > fp = open('logfile', 'wb')
300 300 > fp.write(b'12345678901234567890123456789012345678901234567890' +
301 301 > b'12345') # there are 5 more columns for 80 columns
302 302 >
303 303 > # 2 x 4 = 8 columns, but 3 x 4 = 12 bytes
304 304 > fp.write(u'\u3042\u3044\u3046\u3048'.encode('utf-8'))
305 305 >
306 306 > fp.close()
307 307 > EOF
308 308 $ echo xx >> x
309 309 $ hg --encoding utf-8 commit --logfile logfile
310 310
311 311 $ HGEDITOR=cat hg --encoding utf-8 histedit tip
312 312 pick 3d3ea1f3a10b 5 1234567890123456789012345678901234567890123456789012345\xe3\x81\x82... (esc)
313 313
314 314 # Edit history between 3d3ea1f3a10b and 3d3ea1f3a10b
315 315 #
316 316 # Commits are listed from least to most recent
317 317 #
318 318 # You can reorder changesets by reordering the lines
319 319 #
320 320 # Commands:
321 321 #
322 322 # e, edit = use commit, but stop for amending
323 323 # m, mess = edit commit message without changing commit content
324 324 # p, pick = use commit
325 325 # b, base = checkout changeset and apply further changesets from there
326 326 # d, drop = remove commit from history
327 327 # f, fold = use commit, but combine it with the one above
328 328 # r, roll = like fold, but discard this commit's description and date
329 329 #
330 330
331 331 Test --continue with --keep
332 332
333 333 $ hg strip -q -r . --config extensions.strip=
334 334 $ hg histedit '.^' -q --keep --commands - << EOF
335 335 > edit eb57da33312f 2 three
336 336 > pick f3cfcca30c44 4 x
337 337 > EOF
338 338 Editing (eb57da33312f), you may commit or record as needed now.
339 339 (hg histedit --continue to resume)
340 340 [240]
341 341 $ echo edit >> alpha
342 342 $ hg histedit -q --continue
343 343 $ hg log -G -T '{rev}:{node|short} {desc}'
344 344 @ 6:8fda0c726bf2 x
345 345 |
346 346 o 5:63379946892c three
347 347 |
348 348 | o 4:f3cfcca30c44 x
349 349 | |
350 350 | | o 3:2a30f3cfee78 four
351 351 | |/ ***
352 352 | | five
353 353 | o 2:eb57da33312f three
354 354 |/
355 355 o 1:579e40513370 two
356 356 |
357 357 o 0:6058cbb6cfd7 one
358 358
359 359
360 360 Test that abort fails gracefully on exception
361 361 ----------------------------------------------
362 362 $ hg histedit . -q --commands - << EOF
363 363 > edit 8fda0c726bf2 6 x
364 364 > EOF
365 365 Editing (8fda0c726bf2), you may commit or record as needed now.
366 366 (hg histedit --continue to resume)
367 367 [240]
368 368 Corrupt histedit state file
369 369 $ sed 's/8fda0c726bf2/123456789012/' .hg/histedit-state > ../corrupt-histedit
370 370 $ mv ../corrupt-histedit .hg/histedit-state
371 371 $ hg abort
372 372 warning: encountered an exception during histedit --abort; the repository may not have been completely cleaned up
373 373 abort: $TESTTMP/foo/.hg/strip-backup/*-histedit.hg: $ENOENT$ (glob) (windows !)
374 374 abort: $ENOENT$: '$TESTTMP/foo/.hg/strip-backup/*-histedit.hg' (glob) (no-windows !)
375 375 [255]
376 376 Histedit state has been exited
377 377 $ hg summary -q
378 378 parent: 5:63379946892c
379 379 commit: 1 added, 1 unknown (new branch head)
380 380 update: 4 new changesets (update)
381 381
382 382 $ cd ..
383 383
384 384 Set up default base revision tests
385 385
386 386 $ hg init defaultbase
387 387 $ cd defaultbase
388 388 $ touch foo
389 389 $ hg -q commit -A -m root
390 390 $ echo 1 > foo
391 391 $ hg commit -m 'public 1'
392 392 $ hg phase --force --public -r .
393 393 $ echo 2 > foo
394 394 $ hg commit -m 'draft after public'
395 395 $ hg -q up -r 1
396 396 $ echo 3 > foo
397 397 $ hg commit -m 'head 1 public'
398 398 created new head
399 399 $ hg phase --force --public -r .
400 400 $ echo 4 > foo
401 401 $ hg commit -m 'head 1 draft 1'
402 402 $ echo 5 > foo
403 403 $ hg commit -m 'head 1 draft 2'
404 404 $ hg -q up -r 2
405 405 $ echo 6 > foo
406 406 $ hg commit -m 'head 2 commit 1'
407 407 $ echo 7 > foo
408 408 $ hg commit -m 'head 2 commit 2'
409 409 $ hg -q up -r 2
410 410 $ echo 8 > foo
411 411 $ hg commit -m 'head 3'
412 412 created new head
413 413 $ hg -q up -r 2
414 414 $ echo 9 > foo
415 415 $ hg commit -m 'head 4'
416 416 created new head
417 417 $ hg merge --tool :local -r 8
418 418 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
419 419 (branch merge, don't forget to commit)
420 420 $ hg commit -m 'merge head 3 into head 4'
421 421 $ echo 11 > foo
422 422 $ hg commit -m 'commit 1 after merge'
423 423 $ echo 12 > foo
424 424 $ hg commit -m 'commit 2 after merge'
425 425
426 426 $ hg log -G -T '{rev}:{node|short} {phase} {desc}\n'
427 427 @ 12:8cde254db839 draft commit 2 after merge
428 428 |
429 429 o 11:6f2f0241f119 draft commit 1 after merge
430 430 |
431 431 o 10:90506cc76b00 draft merge head 3 into head 4
432 432 |\
433 433 | o 9:f8607a373a97 draft head 4
434 434 | |
435 435 o | 8:0da92be05148 draft head 3
436 436 |/
437 437 | o 7:4c35cdf97d5e draft head 2 commit 2
438 438 | |
439 439 | o 6:931820154288 draft head 2 commit 1
440 440 |/
441 441 | o 5:8cdc02b9bc63 draft head 1 draft 2
442 442 | |
443 443 | o 4:463b8c0d2973 draft head 1 draft 1
444 444 | |
445 445 | o 3:23a0c4eefcbf public head 1 public
446 446 | |
447 447 o | 2:4117331c3abb draft draft after public
448 448 |/
449 449 o 1:4426d359ea59 public public 1
450 450 |
451 451 o 0:54136a8ddf32 public root
452 452
453 453
454 454 Default base revision should stop at public changesets
455 455
456 456 $ hg -q up 8cdc02b9bc63
457 457 $ hg histedit --commands - <<EOF
458 458 > pick 463b8c0d2973
459 459 > pick 8cdc02b9bc63
460 460 > EOF
461 461
462 462 Default base revision should stop at branchpoint
463 463
464 464 $ hg -q up 4c35cdf97d5e
465 465 $ hg histedit --commands - <<EOF
466 466 > pick 931820154288
467 467 > pick 4c35cdf97d5e
468 468 > EOF
469 469
470 470 Default base revision should stop at merge commit
471 471
472 472 $ hg -q up 8cde254db839
473 473 $ hg histedit --commands - <<EOF
474 474 > pick 6f2f0241f119
475 475 > pick 8cde254db839
476 476 > EOF
477 477
478 478 commit --amend should abort if histedit is in progress
479 479 (issue4800) and markers are not being created.
480 480 Eventually, histedit could perhaps look at `source` extra,
481 481 in which case this test should be revisited.
482 482
483 483 $ hg -q up 8cde254db839
484 484 $ hg histedit 6f2f0241f119 --commands - <<EOF
485 485 > pick 8cde254db839
486 486 > edit 6f2f0241f119
487 487 > EOF
488 488 merging foo
489 489 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
490 490 Fix up the change (pick 8cde254db839)
491 491 (hg histedit --continue to resume)
492 492 [240]
493 493 $ hg resolve -m --all
494 494 (no more unresolved files)
495 495 continue: hg histedit --continue
496 496 $ hg histedit --cont
497 497 merging foo
498 498 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
499 499 Editing (6f2f0241f119), you may commit or record as needed now.
500 500 (hg histedit --continue to resume)
501 501 [240]
502 502 $ hg resolve -m --all
503 503 (no more unresolved files)
504 504 continue: hg histedit --continue
505 505 $ hg commit --amend -m 'reject this fold'
506 506 abort: histedit in progress
507 507 (use 'hg histedit --continue' or 'hg histedit --abort')
508 [255]
508 [20]
509 509
510 510 With markers enabled, histedit does not get confused, and
511 511 amend should not be blocked by the ongoing histedit.
512 512
513 513 $ cat >>$HGRCPATH <<EOF
514 514 > [experimental]
515 515 > evolution.createmarkers=True
516 516 > evolution.allowunstable=True
517 517 > EOF
518 518 $ hg commit --amend -m 'allow this fold'
519 519 $ hg histedit --continue
520 520
521 521 $ cd ..
522 522
523 523 Test autoverb feature
524 524
525 525 $ hg init autoverb
526 526 $ cd autoverb
527 527 $ echo alpha >> alpha
528 528 $ hg ci -qAm one
529 529 $ echo alpha >> alpha
530 530 $ hg ci -qm two
531 531 $ echo beta >> beta
532 532 $ hg ci -qAm "roll! one"
533 533
534 534 $ hg log --style compact --graph
535 535 @ 2[tip] 4f34d0f8b5fa 1970-01-01 00:00 +0000 test
536 536 | roll! one
537 537 |
538 538 o 1 579e40513370 1970-01-01 00:00 +0000 test
539 539 | two
540 540 |
541 541 o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test
542 542 one
543 543
544 544
545 545 Check that 'roll' is selected by default
546 546
547 547 $ HGEDITOR=cat hg histedit 0 --config experimental.histedit.autoverb=True
548 548 pick 6058cbb6cfd7 0 one
549 549 roll 4f34d0f8b5fa 2 roll! one
550 550 pick 579e40513370 1 two
551 551
552 552 # Edit history between 6058cbb6cfd7 and 4f34d0f8b5fa
553 553 #
554 554 # Commits are listed from least to most recent
555 555 #
556 556 # You can reorder changesets by reordering the lines
557 557 #
558 558 # Commands:
559 559 #
560 560 # e, edit = use commit, but stop for amending
561 561 # m, mess = edit commit message without changing commit content
562 562 # p, pick = use commit
563 563 # b, base = checkout changeset and apply further changesets from there
564 564 # d, drop = remove commit from history
565 565 # f, fold = use commit, but combine it with the one above
566 566 # r, roll = like fold, but discard this commit's description and date
567 567 #
568 568
569 569 $ cd ..
570 570
571 571 Check that histedit's commands accept revsets
572 572 $ hg init bar
573 573 $ cd bar
574 574 $ echo w >> a
575 575 $ hg ci -qAm "adds a"
576 576 $ echo x >> b
577 577 $ hg ci -qAm "adds b"
578 578 $ echo y >> c
579 579 $ hg ci -qAm "adds c"
580 580 $ echo z >> d
581 581 $ hg ci -qAm "adds d"
582 582 $ hg log -G -T '{rev} {desc}\n'
583 583 @ 3 adds d
584 584 |
585 585 o 2 adds c
586 586 |
587 587 o 1 adds b
588 588 |
589 589 o 0 adds a
590 590
591 591 $ HGEDITOR=cat hg histedit "2" --commands - << EOF
592 592 > base -4 adds c
593 593 > pick 2 adds c
594 594 > pick tip adds d
595 595 > EOF
596 596 $ hg log -G -T '{rev} {desc}\n'
597 597 @ 5 adds d
598 598 |
599 599 o 4 adds c
600 600 |
601 601 | o 1 adds b
602 602 |/
603 603 o 0 adds a
604 604
605 605
@@ -1,556 +1,556 b''
1 1 $ . "$TESTDIR/histedit-helpers.sh"
2 2
3 3 $ cat >> $HGRCPATH <<EOF
4 4 > [extensions]
5 5 > histedit=
6 6 > strip=
7 7 > mockmakedate = $TESTDIR/mockmakedate.py
8 8 > EOF
9 9
10 10 $ initrepo ()
11 11 > {
12 12 > hg init r
13 13 > cd r
14 14 > for x in a b c d e f g; do
15 15 > echo $x > $x
16 16 > hg add $x
17 17 > hg ci -m $x
18 18 > done
19 19 > }
20 20
21 21 $ initrepo
22 22
23 23 log before edit
24 24 $ hg log --graph
25 25 @ changeset: 6:3c6a8ed2ebe8
26 26 | tag: tip
27 27 | user: test
28 28 | date: Thu Jan 01 00:00:00 1970 +0000
29 29 | summary: g
30 30 |
31 31 o changeset: 5:652413bf663e
32 32 | user: test
33 33 | date: Thu Jan 01 00:00:00 1970 +0000
34 34 | summary: f
35 35 |
36 36 o changeset: 4:e860deea161a
37 37 | user: test
38 38 | date: Thu Jan 01 00:00:00 1970 +0000
39 39 | summary: e
40 40 |
41 41 o changeset: 3:055a42cdd887
42 42 | user: test
43 43 | date: Thu Jan 01 00:00:00 1970 +0000
44 44 | summary: d
45 45 |
46 46 o changeset: 2:177f92b77385
47 47 | user: test
48 48 | date: Thu Jan 01 00:00:00 1970 +0000
49 49 | summary: c
50 50 |
51 51 o changeset: 1:d2ae7f538514
52 52 | user: test
53 53 | date: Thu Jan 01 00:00:00 1970 +0000
54 54 | summary: b
55 55 |
56 56 o changeset: 0:cb9a9f314b8b
57 57 user: test
58 58 date: Thu Jan 01 00:00:00 1970 +0000
59 59 summary: a
60 60
61 61 dirty a file
62 62 $ echo a > g
63 63 $ hg histedit 177f92b77385 --commands - 2>&1 << EOF
64 64 > EOF
65 65 abort: uncommitted changes
66 [255]
66 [20]
67 67 $ echo g > g
68 68
69 69 edit the history
70 70 $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle
71 71 > pick 177f92b77385 c
72 72 > pick 055a42cdd887 d
73 73 > edit e860deea161a e
74 74 > pick 652413bf663e f
75 75 > pick 3c6a8ed2ebe8 g
76 76 > EOF
77 77 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
78 78 Editing (e860deea161a), you may commit or record as needed now.
79 79 (hg histedit --continue to resume)
80 80
81 81 try to update and get an error
82 82 $ hg update tip
83 83 abort: histedit in progress
84 84 (use 'hg histedit --continue' or 'hg histedit --abort')
85 [255]
85 [20]
86 86
87 87 edit the plan via the editor
88 88 $ cat >> $TESTTMP/editplan.sh <<EOF
89 89 > cat > \$1 <<EOF2
90 90 > drop e860deea161a e
91 91 > drop 652413bf663e f
92 92 > drop 3c6a8ed2ebe8 g
93 93 > EOF2
94 94 > EOF
95 95 $ HGEDITOR="sh $TESTTMP/editplan.sh" hg histedit --edit-plan
96 96 $ cat .hg/histedit-state
97 97 v1
98 98 055a42cdd88768532f9cf79daa407fc8d138de9b
99 99 3c6a8ed2ebe862cc949d2caa30775dd6f16fb799
100 100 False
101 101 3
102 102 drop
103 103 e860deea161a2f77de56603b340ebbb4536308ae
104 104 drop
105 105 652413bf663ef2a641cab26574e46d5f5a64a55a
106 106 drop
107 107 3c6a8ed2ebe862cc949d2caa30775dd6f16fb799
108 108 0
109 109 strip-backup/177f92b77385-0ebe6a8f-histedit.hg
110 110
111 111 edit the plan via --commands
112 112 $ hg histedit --edit-plan --commands - 2>&1 << EOF
113 113 > edit e860deea161a e
114 114 > pick 652413bf663e f
115 115 > drop 3c6a8ed2ebe8 g
116 116 > EOF
117 117 $ cat .hg/histedit-state
118 118 v1
119 119 055a42cdd88768532f9cf79daa407fc8d138de9b
120 120 3c6a8ed2ebe862cc949d2caa30775dd6f16fb799
121 121 False
122 122 3
123 123 edit
124 124 e860deea161a2f77de56603b340ebbb4536308ae
125 125 pick
126 126 652413bf663ef2a641cab26574e46d5f5a64a55a
127 127 drop
128 128 3c6a8ed2ebe862cc949d2caa30775dd6f16fb799
129 129 0
130 130 strip-backup/177f92b77385-0ebe6a8f-histedit.hg
131 131
132 132 Go at a random point and try to continue
133 133
134 134 $ hg id -n
135 135 3+
136 136 $ hg up 0
137 137 abort: histedit in progress
138 138 (use 'hg histedit --continue' or 'hg histedit --abort')
139 [255]
139 [20]
140 140
141 141 Try to delete necessary commit
142 142 $ hg strip -r 652413b
143 143 abort: histedit in progress, can't strip 652413bf663e
144 144 [255]
145 145
146 146 commit, then edit the revision
147 147 $ hg ci -m 'wat'
148 148 created new head
149 149 $ echo a > e
150 150
151 151 qnew should fail while we're in the middle of the edit step
152 152
153 153 $ hg --config extensions.mq= qnew please-fail
154 154 abort: histedit in progress
155 155 (use 'hg histedit --continue' or 'hg histedit --abort')
156 [255]
156 [20]
157 157 $ HGEDITOR='echo foobaz > ' hg histedit --continue 2>&1 | fixbundle
158 158
159 159 $ hg log --graph
160 160 @ changeset: 6:b5f70786f9b0
161 161 | tag: tip
162 162 | user: test
163 163 | date: Thu Jan 01 00:00:00 1970 +0000
164 164 | summary: f
165 165 |
166 166 o changeset: 5:a5e1ba2f7afb
167 167 | user: test
168 168 | date: Thu Jan 01 00:00:00 1970 +0000
169 169 | summary: foobaz
170 170 |
171 171 o changeset: 4:1a60820cd1f6
172 172 | user: test
173 173 | date: Thu Jan 01 00:00:00 1970 +0000
174 174 | summary: wat
175 175 |
176 176 o changeset: 3:055a42cdd887
177 177 | user: test
178 178 | date: Thu Jan 01 00:00:00 1970 +0000
179 179 | summary: d
180 180 |
181 181 o changeset: 2:177f92b77385
182 182 | user: test
183 183 | date: Thu Jan 01 00:00:00 1970 +0000
184 184 | summary: c
185 185 |
186 186 o changeset: 1:d2ae7f538514
187 187 | user: test
188 188 | date: Thu Jan 01 00:00:00 1970 +0000
189 189 | summary: b
190 190 |
191 191 o changeset: 0:cb9a9f314b8b
192 192 user: test
193 193 date: Thu Jan 01 00:00:00 1970 +0000
194 194 summary: a
195 195
196 196
197 197 $ hg cat e
198 198 a
199 199
200 200 Stripping necessary commits should not break --abort
201 201
202 202 $ hg histedit 1a60820cd1f6 --commands - 2>&1 << EOF| fixbundle
203 203 > edit 1a60820cd1f6 wat
204 204 > pick a5e1ba2f7afb foobaz
205 205 > pick b5f70786f9b0 g
206 206 > EOF
207 207 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
208 208 Editing (1a60820cd1f6), you may commit or record as needed now.
209 209 (hg histedit --continue to resume)
210 210
211 211 $ mv .hg/histedit-state .hg/histedit-state.bak
212 212 $ hg strip -q -r b5f70786f9b0
213 213 $ mv .hg/histedit-state.bak .hg/histedit-state
214 214 $ hg histedit --abort
215 215 adding changesets
216 216 adding manifests
217 217 adding file changes
218 218 added 1 changesets with 1 changes to 3 files
219 219 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
220 220 $ hg log -r .
221 221 changeset: 6:b5f70786f9b0
222 222 tag: tip
223 223 user: test
224 224 date: Thu Jan 01 00:00:00 1970 +0000
225 225 summary: f
226 226
227 227
228 228 check histedit_source
229 229
230 230 $ hg log --debug --rev 5
231 231 changeset: 5:a5e1ba2f7afb899ef1581cea528fd885d2fca70d
232 232 phase: draft
233 233 parent: 4:1a60820cd1f6004a362aa622ebc47d59bc48eb34
234 234 parent: -1:0000000000000000000000000000000000000000
235 235 manifest: 5:5ad3be8791f39117565557781f5464363b918a45
236 236 user: test
237 237 date: Thu Jan 01 00:00:00 1970 +0000
238 238 files: e
239 239 extra: branch=default
240 240 extra: histedit_source=e860deea161a2f77de56603b340ebbb4536308ae
241 241 description:
242 242 foobaz
243 243
244 244
245 245
246 246 $ hg histedit tip --commands - 2>&1 <<EOF| fixbundle
247 247 > edit b5f70786f9b0 f
248 248 > EOF
249 249 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
250 250 Editing (b5f70786f9b0), you may commit or record as needed now.
251 251 (hg histedit --continue to resume)
252 252 $ hg status
253 253 A f
254 254
255 255 $ hg summary
256 256 parent: 5:a5e1ba2f7afb
257 257 foobaz
258 258 branch: default
259 259 commit: 1 added (new branch head)
260 260 update: 1 new changesets (update)
261 261 phases: 7 draft
262 262 hist: 1 remaining (histedit --continue)
263 263
264 264 (test also that editor is invoked if histedit is continued for
265 265 "edit" action)
266 266
267 267 $ HGEDITOR='cat' hg histedit --continue
268 268 f
269 269
270 270
271 271 HG: Enter commit message. Lines beginning with 'HG:' are removed.
272 272 HG: Leave message empty to abort commit.
273 273 HG: --
274 274 HG: user: test
275 275 HG: branch 'default'
276 276 HG: added f
277 277 saved backup bundle to $TESTTMP/r/.hg/strip-backup/b5f70786f9b0-c28d9c86-histedit.hg
278 278
279 279 $ hg status
280 280
281 281 log after edit
282 282 $ hg log --limit 1
283 283 changeset: 6:a107ee126658
284 284 tag: tip
285 285 user: test
286 286 date: Thu Jan 01 00:00:00 1970 +0000
287 287 summary: f
288 288
289 289
290 290 say we'll change the message, but don't.
291 291 $ cat > ../edit.sh <<EOF
292 292 > cat "\$1" | sed s/pick/mess/ > tmp
293 293 > mv tmp "\$1"
294 294 > EOF
295 295 $ HGEDITOR="sh ../edit.sh" hg histedit tip 2>&1 | fixbundle
296 296 $ hg status
297 297 $ hg log --limit 1
298 298 changeset: 6:1fd3b2fe7754
299 299 tag: tip
300 300 user: test
301 301 date: Thu Jan 01 00:00:00 1970 +0000
302 302 summary: f
303 303
304 304
305 305 modify the message
306 306
307 307 check saving last-message.txt, at first
308 308
309 309 $ cat > $TESTTMP/commitfailure.py <<EOF
310 310 > from mercurial import error
311 311 > def reposetup(ui, repo):
312 312 > class commitfailure(repo.__class__):
313 313 > def commit(self, *args, **kwargs):
314 314 > raise error.Abort(b'emulating unexpected abort')
315 315 > repo.__class__ = commitfailure
316 316 > EOF
317 317 $ cat >> .hg/hgrc <<EOF
318 318 > [extensions]
319 319 > # this failure occurs before editor invocation
320 320 > commitfailure = $TESTTMP/commitfailure.py
321 321 > EOF
322 322
323 323 $ cat > $TESTTMP/editor.sh <<EOF
324 324 > echo "==== before editing"
325 325 > cat \$1
326 326 > echo "===="
327 327 > echo "check saving last-message.txt" >> \$1
328 328 > EOF
329 329
330 330 (test that editor is not invoked before transaction starting)
331 331
332 332 $ rm -f .hg/last-message.txt
333 333 $ HGEDITOR="sh $TESTTMP/editor.sh" hg histedit tip --commands - 2>&1 << EOF | fixbundle
334 334 > mess 1fd3b2fe7754 f
335 335 > EOF
336 336 abort: emulating unexpected abort
337 337 $ test -f .hg/last-message.txt
338 338 [1]
339 339
340 340 $ cat >> .hg/hgrc <<EOF
341 341 > [extensions]
342 342 > commitfailure = !
343 343 > EOF
344 344 $ hg histedit --abort -q
345 345
346 346 (test that editor is invoked and commit message is saved into
347 347 "last-message.txt")
348 348
349 349 $ cat >> .hg/hgrc <<EOF
350 350 > [hooks]
351 351 > # this failure occurs after editor invocation
352 352 > pretxncommit.unexpectedabort = false
353 353 > EOF
354 354
355 355 $ hg status --rev '1fd3b2fe7754^1' --rev 1fd3b2fe7754
356 356 A f
357 357
358 358 $ rm -f .hg/last-message.txt
359 359 $ HGEDITOR="sh $TESTTMP/editor.sh" hg histedit tip --commands - 2>&1 << EOF
360 360 > mess 1fd3b2fe7754 f
361 361 > EOF
362 362 ==== before editing
363 363 f
364 364
365 365
366 366 HG: Enter commit message. Lines beginning with 'HG:' are removed.
367 367 HG: Leave message empty to abort commit.
368 368 HG: --
369 369 HG: user: test
370 370 HG: branch 'default'
371 371 HG: added f
372 372 ====
373 373 transaction abort!
374 374 rollback completed
375 375 note: commit message saved in .hg/last-message.txt
376 376 note: use 'hg commit --logfile .hg/last-message.txt --edit' to reuse it
377 377 abort: pretxncommit.unexpectedabort hook exited with status 1
378 378 [255]
379 379 $ cat .hg/last-message.txt
380 380 f
381 381
382 382
383 383 check saving last-message.txt
384 384
385 385 (test also that editor is invoked if histedit is continued for "message"
386 386 action)
387 387
388 388 $ HGEDITOR=cat hg histedit --continue
389 389 f
390 390
391 391
392 392 HG: Enter commit message. Lines beginning with 'HG:' are removed.
393 393 HG: Leave message empty to abort commit.
394 394 HG: --
395 395 HG: user: test
396 396 HG: branch 'default'
397 397 HG: added f
398 398 transaction abort!
399 399 rollback completed
400 400 note: commit message saved in .hg/last-message.txt
401 401 note: use 'hg commit --logfile .hg/last-message.txt --edit' to reuse it
402 402 abort: pretxncommit.unexpectedabort hook exited with status 1
403 403 [255]
404 404
405 405 $ cat >> .hg/hgrc <<EOF
406 406 > [hooks]
407 407 > pretxncommit.unexpectedabort =
408 408 > EOF
409 409 $ hg histedit --abort -q
410 410
411 411 then, check "modify the message" itself
412 412
413 413 $ hg histedit tip --commands - 2>&1 << EOF | fixbundle
414 414 > mess 1fd3b2fe7754 f
415 415 > EOF
416 416 $ hg status
417 417 $ hg log --limit 1
418 418 changeset: 6:62feedb1200e
419 419 tag: tip
420 420 user: test
421 421 date: Thu Jan 01 00:00:00 1970 +0000
422 422 summary: f
423 423
424 424
425 425 rollback should not work after a histedit
426 426 $ hg rollback
427 427 no rollback information available
428 428 [1]
429 429
430 430 $ cd ..
431 431 $ hg clone -qr0 r r0
432 432 $ cd r0
433 433 $ hg phase -fdr0
434 434 $ hg histedit --commands - 0 2>&1 << EOF
435 435 > edit cb9a9f314b8b a > $EDITED
436 436 > EOF
437 437 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
438 438 Editing (cb9a9f314b8b), you may commit or record as needed now.
439 439 (hg histedit --continue to resume)
440 440 [240]
441 441 $ HGEDITOR=true hg histedit --continue
442 442 saved backup bundle to $TESTTMP/r0/.hg/strip-backup/cb9a9f314b8b-cc5ccb0b-histedit.hg
443 443
444 444 $ hg log -G
445 445 @ changeset: 0:0efcea34f18a
446 446 tag: tip
447 447 user: test
448 448 date: Thu Jan 01 00:00:00 1970 +0000
449 449 summary: a
450 450
451 451 $ echo foo >> b
452 452 $ hg addr
453 453 adding b
454 454 $ hg ci -m 'add b'
455 455 $ echo foo >> a
456 456 $ hg ci -m 'extend a'
457 457 $ hg phase --public 1
458 458 Attempting to fold a change into a public change should not work:
459 459 $ cat > ../edit.sh <<EOF
460 460 > cat "\$1" | sed s/pick/fold/ > tmp
461 461 > mv tmp "\$1"
462 462 > EOF
463 463 $ HGEDITOR="sh ../edit.sh" hg histedit 2
464 464 warning: histedit rules saved to: .hg/histedit-last-edit.txt
465 465 hg: parse error: first changeset cannot use verb "fold"
466 466 [255]
467 467 $ cat .hg/histedit-last-edit.txt
468 468 fold 0012be4a27ea 2 extend a
469 469
470 470 # Edit history between 0012be4a27ea and 0012be4a27ea
471 471 #
472 472 # Commits are listed from least to most recent
473 473 #
474 474 # You can reorder changesets by reordering the lines
475 475 #
476 476 # Commands:
477 477 #
478 478 # e, edit = use commit, but stop for amending
479 479 # m, mess = edit commit message without changing commit content
480 480 # p, fold = use commit
481 481 # b, base = checkout changeset and apply further changesets from there
482 482 # d, drop = remove commit from history
483 483 # f, fold = use commit, but combine it with the one above
484 484 # r, roll = like fold, but discard this commit's description and date
485 485 #
486 486
487 487 $ cd ..
488 488
489 489 ============================================
490 490 Test update-timestamp config option in mess|
491 491 ============================================
492 492
493 493 $ addwithdate ()
494 494 > {
495 495 > echo $1 > $1
496 496 > hg add $1
497 497 > hg ci -m $1 -d "$2 0"
498 498 > }
499 499
500 500 $ initrepo ()
501 501 > {
502 502 > hg init r2
503 503 > cd r2
504 504 > addwithdate a 1
505 505 > addwithdate b 2
506 506 > addwithdate c 3
507 507 > addwithdate d 4
508 508 > addwithdate e 5
509 509 > addwithdate f 6
510 510 > }
511 511
512 512 $ initrepo
513 513
514 514 log before edit
515 515
516 516 $ hg log --limit 1
517 517 changeset: 5:178e35e0ce73
518 518 tag: tip
519 519 user: test
520 520 date: Thu Jan 01 00:00:06 1970 +0000
521 521 summary: f
522 522
523 523 $ hg histedit tip --commands - 2>&1 --config rewrite.update-timestamp=True << EOF | fixbundle
524 524 > mess 178e35e0ce73 f
525 525 > EOF
526 526
527 527 log after edit
528 528
529 529 $ hg log --limit 1
530 530 changeset: 5:98bf456d476b
531 531 tag: tip
532 532 user: test
533 533 date: Thu Jan 01 00:00:00 1970 +0000
534 534 summary: f
535 535
536 536
537 537 $ cd ..
538 538
539 539 warn the user on editing tagged commits
540 540
541 541 $ hg init issue4017
542 542 $ cd issue4017
543 543 $ echo > a
544 544 $ hg ci -Am 'add a'
545 545 adding a
546 546 $ hg tag a
547 547 $ hg tags
548 548 tip 1:bd7ee4f3939b
549 549 a 0:a8a82d372bb3
550 550 $ hg histedit
551 551 warning: tags associated with the given changeset will be lost after histedit.
552 552 do you want to continue (yN)? n
553 553 abort: histedit cancelled
554 554
555 555 [255]
556 556 $ cd ..
@@ -1,83 +1,83 b''
1 1 Test histedit extension: Merge tools
2 2 ====================================
3 3
4 4 Initialization
5 5 ---------------
6 6
7 7 $ . "$TESTDIR/histedit-helpers.sh"
8 8
9 9 $ cat >> $HGRCPATH <<EOF
10 10 > [alias]
11 11 > logt = log --template '{rev}:{node|short} {desc|firstline}\n'
12 12 > [extensions]
13 13 > histedit=
14 14 > mockmakedate = $TESTDIR/mockmakedate.py
15 15 > EOF
16 16
17 17 Merge conflict
18 18 --------------
19 19
20 20 $ hg init r
21 21 $ cd r
22 22 $ cat >> .hg/hgrc <<EOF
23 23 > [command-templates]
24 24 > pre-merge-tool-output='pre-merge message for {node}\n'
25 25 > EOF
26 26
27 27 $ echo foo > file
28 28 $ hg add file
29 29 $ hg ci -m "First" -d "1 0"
30 30 $ echo bar > file
31 31 $ hg ci -m "Second" -d "2 0"
32 32
33 33 $ hg logt --graph
34 34 @ 1:2aa920f62fb9 Second
35 35 |
36 36 o 0:7181f42b8fca First
37 37
38 38
39 39 Invert the order of the commits, but fail the merge.
40 40 $ hg histedit --config ui.merge=false --commands - 2>&1 <<EOF | fixbundle
41 41 > pick 2aa920f62fb9 Second
42 42 > pick 7181f42b8fca First
43 43 > EOF
44 44 merging file
45 45 pre-merge message for b90fa2e91a6d11013945a5f684be45b84a8ca6ec
46 46 merging file failed!
47 47 Fix up the change (pick 7181f42b8fca)
48 48 (hg histedit --continue to resume)
49 49
50 50 $ hg histedit --abort | fixbundle
51 51 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
52 52
53 53 Invert the order of the commits, and pretend the merge succeeded.
54 54 $ hg histedit --config ui.merge=true --commands - 2>&1 <<EOF | fixbundle
55 55 > pick 2aa920f62fb9 Second
56 56 > pick 7181f42b8fca First
57 57 > EOF
58 58 merging file
59 59 pre-merge message for b90fa2e91a6d11013945a5f684be45b84a8ca6ec
60 60 7181f42b8fca: skipping changeset (no changes)
61 61 $ hg histedit --abort
62 62 abort: no histedit in progress
63 [255]
63 [20]
64 64 $ cd ..
65 65
66 66 Test legacy config name
67 67
68 68 $ hg init r2
69 69 $ cd r2
70 70 $ echo foo > file
71 71 $ hg add file
72 72 $ hg ci -m "First"
73 73 $ echo bar > file
74 74 $ hg ci -m "Second"
75 75 $ echo conflict > file
76 76 $ hg co -m 0 --config ui.merge=false \
77 77 > --config ui.pre-merge-tool-output-template='legacy config: {node}\n'
78 78 merging file
79 79 legacy config: 889c9c4d58bd4ce74815efd04a01e0f2bf6765a7
80 80 merging file failed!
81 81 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
82 82 use 'hg resolve' to retry unresolved file merges
83 83 [1]
@@ -1,218 +1,218 b''
1 1 test for old histedit issue #6:
2 2 editing a changeset without any actual change would corrupt the repository
3 3
4 4 $ . "$TESTDIR/histedit-helpers.sh"
5 5
6 6 $ cat >> $HGRCPATH <<EOF
7 7 > [extensions]
8 8 > histedit=
9 9 > EOF
10 10
11 11 $ initrepo ()
12 12 > {
13 13 > dir="$1"
14 14 > comment="$2"
15 15 > if [ -n "${comment}" ]; then
16 16 > echo % ${comment}
17 17 > echo % ${comment} | sed 's:.:-:g'
18 18 > fi
19 19 > hg init ${dir}
20 20 > cd ${dir}
21 21 > for x in a b c d e f ; do
22 22 > echo $x > $x
23 23 > hg add $x
24 24 > hg ci -m $x
25 25 > done
26 26 > cd ..
27 27 > }
28 28
29 29 $ geneditor ()
30 30 > {
31 31 > # generate an editor script for selecting changesets to be edited
32 32 > choice=$1 # changesets that should be edited (using sed line ranges)
33 33 > cat <<EOF | sed 's:^....::'
34 34 > # editing the rules, replacing 'pick' with 'edit' for the chosen lines
35 35 > sed '${choice}s:^pick:edit:' "\$1" > "\${1}.tmp"
36 36 > mv "\${1}.tmp" "\$1"
37 37 > # displaying the resulting rules, minus comments and empty lines
38 38 > sed '/^#/d;/^$/d;s:^:| :' "\$1" >&2
39 39 > EOF
40 40 > }
41 41
42 42 $ startediting ()
43 43 > {
44 44 > # begin an editing session
45 45 > choice="$1" # changesets that should be edited
46 46 > number="$2" # number of changesets considered (from tip)
47 47 > comment="$3"
48 48 > geneditor "${choice}" > edit.sh
49 49 > echo % start editing the history ${comment}
50 50 > HGEDITOR="sh ./edit.sh" hg histedit -- -${number} 2>&1 | fixbundle
51 51 > }
52 52
53 53 $ continueediting ()
54 54 > {
55 55 > # continue an edit already in progress
56 56 > editor="$1" # message editor when finalizing editing
57 57 > comment="$2"
58 58 > echo % finalize changeset editing ${comment}
59 59 > HGEDITOR=${editor} hg histedit --continue 2>&1 | fixbundle
60 60 > }
61 61
62 62 $ graphlog ()
63 63 > {
64 64 > comment="${1:-log}"
65 65 > echo % "${comment}"
66 66 > hg log -G --template '{rev} {node} \"{desc|firstline}\"\n'
67 67 > }
68 68
69 69
70 70 $ initrepo r1 "test editing with no change"
71 71 % test editing with no change
72 72 -----------------------------
73 73 $ cd r1
74 74 $ graphlog "log before editing"
75 75 % log before editing
76 76 @ 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
77 77 |
78 78 o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
79 79 |
80 80 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
81 81 |
82 82 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
83 83 |
84 84 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
85 85 |
86 86 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
87 87
88 88 $ startediting 2 3 "(not changing anything)" # edit the 2nd of 3 changesets
89 89 % start editing the history (not changing anything)
90 90 | pick 055a42cdd887 3 d
91 91 | edit e860deea161a 4 e
92 92 | pick 652413bf663e 5 f
93 93 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
94 94 Editing (e860deea161a), you may commit or record as needed now.
95 95 (hg histedit --continue to resume)
96 96 $ continueediting true "(leaving commit message unaltered)"
97 97 % finalize changeset editing (leaving commit message unaltered)
98 98
99 99
100 100 check state of working copy
101 101 $ hg id
102 102 794fe033d0a0 tip
103 103
104 104 $ graphlog "log after history editing"
105 105 % log after history editing
106 106 @ 5 794fe033d0a030f8df77c5de945fca35c9181c30 "f"
107 107 |
108 108 o 4 04d2fab980779f332dec458cc944f28de8b43435 "e"
109 109 |
110 110 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
111 111 |
112 112 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
113 113 |
114 114 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
115 115 |
116 116 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
117 117
118 118
119 119 $ cd ..
120 120
121 121 $ initrepo r2 "test editing with no change, then abort"
122 122 % test editing with no change, then abort
123 123 -----------------------------------------
124 124 $ cd r2
125 125 $ graphlog "log before editing"
126 126 % log before editing
127 127 @ 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
128 128 |
129 129 o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
130 130 |
131 131 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
132 132 |
133 133 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
134 134 |
135 135 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
136 136 |
137 137 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
138 138
139 139 $ startediting 1,2 3 "(not changing anything)" # edit the 1st two of 3 changesets
140 140 % start editing the history (not changing anything)
141 141 | edit 055a42cdd887 3 d
142 142 | edit e860deea161a 4 e
143 143 | pick 652413bf663e 5 f
144 144 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
145 145 Editing (055a42cdd887), you may commit or record as needed now.
146 146 (hg histedit --continue to resume)
147 147 $ continueediting true "(leaving commit message unaltered)"
148 148 % finalize changeset editing (leaving commit message unaltered)
149 149 Editing (e860deea161a), you may commit or record as needed now.
150 150 (hg histedit --continue to resume)
151 151 $ graphlog "log after first edit"
152 152 % log after first edit
153 153 @ 6 e5ae3ca2f1ffdbd89ec41ebc273a231f7c3022f2 "d"
154 154 |
155 155 | o 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
156 156 | |
157 157 | o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
158 158 | |
159 159 | o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
160 160 |/
161 161 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
162 162 |
163 163 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
164 164 |
165 165 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
166 166
167 167
168 168 abort editing session, after first forcibly updating away
169 169 $ hg up 0
170 170 abort: histedit in progress
171 171 (use 'hg histedit --continue' or 'hg histedit --abort')
172 [255]
172 [20]
173 173 $ mv .hg/histedit-state .hg/histedit-state-ignore
174 174 $ hg up 0
175 175 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
176 176 $ mv .hg/histedit-state-ignore .hg/histedit-state
177 177 $ hg sum
178 178 parent: 0:cb9a9f314b8b
179 179 a
180 180 branch: default
181 181 commit: 1 added, 1 unknown (new branch head)
182 182 update: 6 new changesets (update)
183 183 phases: 7 draft
184 184 hist: 2 remaining (histedit --continue)
185 185
186 186 $ hg histedit --abort 2>&1 | fixbundle
187 187
188 188 modified files should survive the abort when we've moved away already
189 189 $ hg st
190 190 A e
191 191 ? edit.sh
192 192
193 193 $ graphlog "log after abort"
194 194 % log after abort
195 195 o 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
196 196 |
197 197 o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
198 198 |
199 199 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
200 200 |
201 201 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
202 202 |
203 203 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
204 204 |
205 205 @ 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
206 206
207 207 aborting and not changing files can skip mentioning updating (no) files
208 208 $ hg up
209 209 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
210 210 $ hg commit --close-branch -m 'closebranch'
211 211 $ startediting 1 1 "(not changing anything)" # edit the 3rd of 3 changesets
212 212 % start editing the history (not changing anything)
213 213 | edit 292aec348d9e 6 closebranch
214 214 Editing (292aec348d9e), you may commit or record as needed now.
215 215 (hg histedit --continue to resume)
216 216 $ hg histedit --abort
217 217
218 218 $ cd ..
@@ -1,1315 +1,1315 b''
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 $ hg init requirements
5 5 $ cd requirements
6 6
7 7 # largefiles not loaded by default.
8 8
9 9 $ hg config extensions
10 10 [1]
11 11
12 12 # Adding largefiles to requires file will auto-load largefiles extension.
13 13
14 14 $ echo largefiles >> .hg/requires
15 15 $ hg config extensions
16 16 extensions.largefiles=
17 17
18 18 # But only if there is no config entry for the extension already.
19 19
20 20 $ cat > .hg/hgrc << EOF
21 21 > [extensions]
22 22 > largefiles=!
23 23 > EOF
24 24
25 25 $ hg config extensions
26 26 abort: repository requires features unknown to this Mercurial: largefiles!
27 27 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
28 28 [255]
29 29
30 30 $ cat > .hg/hgrc << EOF
31 31 > [extensions]
32 32 > largefiles=
33 33 > EOF
34 34
35 35 $ hg config extensions
36 36 extensions.largefiles=
37 37
38 38 $ cat > .hg/hgrc << EOF
39 39 > [extensions]
40 40 > largefiles = missing.py
41 41 > EOF
42 42
43 43 $ hg config extensions
44 44 \*\*\* failed to import extension largefiles from missing.py: [Errno *] $ENOENT$: 'missing.py' (glob)
45 45 abort: repository requires features unknown to this Mercurial: largefiles!
46 46 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
47 47 [255]
48 48
49 49 $ cd ..
50 50
51 51 Each sections should be independent of each others.
52 52
53 53 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
54 54 $ mkdir "${USERCACHE}"
55 55 $ cat >> $HGRCPATH <<EOF
56 56 > [extensions]
57 57 > largefiles=
58 58 > purge=
59 59 > rebase=
60 60 > transplant=
61 61 > [phases]
62 62 > publish=False
63 63 > [largefiles]
64 64 > minsize=2
65 65 > patterns=glob:**.dat
66 66 > usercache=${USERCACHE}
67 67 > [hooks]
68 68 > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status"
69 69 > EOF
70 70
71 71
72 72
73 73 Test copies and moves from a directory other than root (issue3516)
74 74 =========================================================================
75 75
76 76 $ hg init lf_cpmv
77 77 $ cd lf_cpmv
78 78 $ mkdir dira
79 79 $ mkdir dira/dirb
80 80 $ touch dira/dirb/largefile
81 81 $ hg add --large dira/dirb/largefile
82 82 $ hg commit -m "added"
83 83 Invoking status precommit hook
84 84 A dira/dirb/largefile
85 85 $ cd dira
86 86 $ hg cp dirb/largefile foo/largefile
87 87
88 88 TODO: Ideally, this should mention the largefile, not the standin
89 89 $ hg log -T '{rev}\n' --stat 'set:clean()'
90 90 0
91 91 .hglf/dira/dirb/largefile | 1 +
92 92 1 files changed, 1 insertions(+), 0 deletions(-)
93 93
94 94 $ hg ci -m "deep copy"
95 95 Invoking status precommit hook
96 96 A dira/foo/largefile
97 97 $ find . | sort
98 98 .
99 99 ./dirb
100 100 ./dirb/largefile
101 101 ./foo
102 102 ./foo/largefile
103 103 $ hg mv foo/largefile baz/largefile
104 104 $ hg ci -m "moved"
105 105 Invoking status precommit hook
106 106 A dira/baz/largefile
107 107 R dira/foo/largefile
108 108 $ find . | sort
109 109 .
110 110 ./baz
111 111 ./baz/largefile
112 112 ./dirb
113 113 ./dirb/largefile
114 114 $ cd ..
115 115 $ hg mv dira dirc
116 116 moving .hglf/dira/baz/largefile to .hglf/dirc/baz/largefile
117 117 moving .hglf/dira/dirb/largefile to .hglf/dirc/dirb/largefile
118 118 $ find * | sort
119 119 dirc
120 120 dirc/baz
121 121 dirc/baz/largefile
122 122 dirc/dirb
123 123 dirc/dirb/largefile
124 124
125 125 $ hg clone -q . ../fetch
126 126 $ hg --config extensions.fetch= fetch ../fetch
127 127 abort: uncommitted changes
128 [255]
128 [20]
129 129 $ hg up -qC
130 130 $ cd ..
131 131
132 132 Clone a local repository owned by another user
133 133 ===================================================
134 134
135 135 #if unix-permissions
136 136
137 137 We have to simulate that here by setting $HOME and removing write permissions
138 138 $ ORIGHOME="$HOME"
139 139 $ mkdir alice
140 140 $ HOME="`pwd`/alice"
141 141 $ cd alice
142 142 $ hg init pubrepo
143 143 $ cd pubrepo
144 144 $ dd if=/dev/zero bs=1k count=11k > a-large-file 2> /dev/null
145 145 $ hg add --large a-large-file
146 146 $ hg commit -m "Add a large file"
147 147 Invoking status precommit hook
148 148 A a-large-file
149 149 $ cd ..
150 150 $ chmod -R a-w pubrepo
151 151 $ cd ..
152 152 $ mkdir bob
153 153 $ HOME="`pwd`/bob"
154 154 $ cd bob
155 155 $ hg clone --pull ../alice/pubrepo pubrepo
156 156 requesting all changes
157 157 adding changesets
158 158 adding manifests
159 159 adding file changes
160 160 added 1 changesets with 1 changes to 1 files
161 161 new changesets 09a186cfa6da (1 drafts)
162 162 updating to branch default
163 163 getting changed largefiles
164 164 1 largefiles updated, 0 removed
165 165 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
166 166 $ cd ..
167 167 $ chmod -R u+w alice/pubrepo
168 168 $ HOME="$ORIGHOME"
169 169
170 170 #endif
171 171
172 172
173 173 Symlink to a large largefile should behave the same as a symlink to a normal file
174 174 =====================================================================================
175 175
176 176 #if symlink
177 177
178 178 $ hg init largesymlink
179 179 $ cd largesymlink
180 180 $ dd if=/dev/zero bs=1k count=10k of=largefile 2>/dev/null
181 181 $ hg add --large largefile
182 182 $ hg commit -m "commit a large file"
183 183 Invoking status precommit hook
184 184 A largefile
185 185 $ ln -s largefile largelink
186 186 $ hg add largelink
187 187 $ hg commit -m "commit a large symlink"
188 188 Invoking status precommit hook
189 189 A largelink
190 190 $ rm -f largelink
191 191 $ hg up >/dev/null
192 192 $ test -f largelink
193 193 [1]
194 194 $ test -L largelink
195 195 [1]
196 196 $ rm -f largelink # make next part of the test independent of the previous
197 197 $ hg up -C >/dev/null
198 198 $ test -f largelink
199 199 $ test -L largelink
200 200 $ cd ..
201 201
202 202 #endif
203 203
204 204
205 205 test for pattern matching on 'hg status':
206 206 ==============================================
207 207
208 208
209 209 to boost performance, largefiles checks whether specified patterns are
210 210 related to largefiles in working directory (NOT to STANDIN) or not.
211 211
212 212 $ hg init statusmatch
213 213 $ cd statusmatch
214 214
215 215 $ mkdir -p a/b/c/d
216 216 $ echo normal > a/b/c/d/e.normal.txt
217 217 $ hg add a/b/c/d/e.normal.txt
218 218 $ echo large > a/b/c/d/e.large.txt
219 219 $ hg add --large a/b/c/d/e.large.txt
220 220 $ mkdir -p a/b/c/x
221 221 $ echo normal > a/b/c/x/y.normal.txt
222 222 $ hg add a/b/c/x/y.normal.txt
223 223 $ hg commit -m 'add files'
224 224 Invoking status precommit hook
225 225 A a/b/c/d/e.large.txt
226 226 A a/b/c/d/e.normal.txt
227 227 A a/b/c/x/y.normal.txt
228 228
229 229 (1) no pattern: no performance boost
230 230 $ hg status -A
231 231 C a/b/c/d/e.large.txt
232 232 C a/b/c/d/e.normal.txt
233 233 C a/b/c/x/y.normal.txt
234 234
235 235 (2) pattern not related to largefiles: performance boost
236 236 $ hg status -A a/b/c/x
237 237 C a/b/c/x/y.normal.txt
238 238
239 239 (3) pattern related to largefiles: no performance boost
240 240 $ hg status -A a/b/c/d
241 241 C a/b/c/d/e.large.txt
242 242 C a/b/c/d/e.normal.txt
243 243
244 244 (4) pattern related to STANDIN (not to largefiles): performance boost
245 245 $ hg status -A .hglf/a
246 246 C .hglf/a/b/c/d/e.large.txt
247 247
248 248 (5) mixed case: no performance boost
249 249 $ hg status -A a/b/c/x a/b/c/d
250 250 C a/b/c/d/e.large.txt
251 251 C a/b/c/d/e.normal.txt
252 252 C a/b/c/x/y.normal.txt
253 253
254 254 verify that largefiles doesn't break filesets
255 255
256 256 $ hg log --rev . --exclude "set:binary()"
257 257 changeset: 0:41bd42f10efa
258 258 tag: tip
259 259 user: test
260 260 date: Thu Jan 01 00:00:00 1970 +0000
261 261 summary: add files
262 262
263 263 sharing a largefile repo automatically enables largefiles on the share
264 264
265 265 $ hg share --config extensions.share= . ../shared_lfrepo
266 266 updating working directory
267 267 getting changed largefiles
268 268 1 largefiles updated, 0 removed
269 269 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
270 270 $ grep largefiles ../shared_lfrepo/.hg/requires
271 271 largefiles
272 272
273 273 verify that large files in subrepos handled properly
274 274 $ hg init subrepo
275 275 $ echo "subrepo = subrepo" > .hgsub
276 276 $ hg add .hgsub
277 277 $ hg ci -m "add subrepo"
278 278 Invoking status precommit hook
279 279 A .hgsub
280 280 ? .hgsubstate
281 281 $ echo "rev 1" > subrepo/large.txt
282 282 $ hg add --large subrepo/large.txt
283 283 $ hg sum
284 284 parent: 1:8ee150ea2e9c tip
285 285 add subrepo
286 286 branch: default
287 287 commit: 1 subrepos
288 288 update: (current)
289 289 phases: 2 draft
290 290 $ hg st
291 291 $ hg st -S
292 292 A subrepo/large.txt
293 293 $ hg ci -S -m "commit top repo"
294 294 committing subrepository subrepo
295 295 Invoking status precommit hook
296 296 A large.txt
297 297 Invoking status precommit hook
298 298 M .hgsubstate
299 299 # No differences
300 300 $ hg st -S
301 301 $ hg sum
302 302 parent: 2:ce4cd0c527a6 tip
303 303 commit top repo
304 304 branch: default
305 305 commit: (clean)
306 306 update: (current)
307 307 phases: 3 draft
308 308 $ echo "rev 2" > subrepo/large.txt
309 309 $ hg st -S
310 310 M subrepo/large.txt
311 311 $ hg sum
312 312 parent: 2:ce4cd0c527a6 tip
313 313 commit top repo
314 314 branch: default
315 315 commit: 1 subrepos
316 316 update: (current)
317 317 phases: 3 draft
318 318 $ hg ci -m "this commit should fail without -S"
319 319 abort: uncommitted changes in subrepository "subrepo"
320 320 (use --subrepos for recursive commit)
321 321 [255]
322 322
323 323 Add a normal file to the subrepo, then test archiving
324 324
325 325 $ echo 'normal file' > subrepo/normal.txt
326 326 $ touch large.dat
327 327 $ mv subrepo/large.txt subrepo/renamed-large.txt
328 328 $ hg addremove -S --dry-run
329 329 adding large.dat as a largefile
330 330 removing subrepo/large.txt
331 331 adding subrepo/normal.txt
332 332 adding subrepo/renamed-large.txt
333 333 $ hg status -S
334 334 ! subrepo/large.txt
335 335 ? large.dat
336 336 ? subrepo/normal.txt
337 337 ? subrepo/renamed-large.txt
338 338
339 339 $ hg addremove --dry-run subrepo
340 340 removing subrepo/large.txt
341 341 adding subrepo/normal.txt
342 342 adding subrepo/renamed-large.txt
343 343 $ hg status -S
344 344 ! subrepo/large.txt
345 345 ? large.dat
346 346 ? subrepo/normal.txt
347 347 ? subrepo/renamed-large.txt
348 348 $ cd ..
349 349
350 350 $ hg -R statusmatch addremove --dry-run statusmatch/subrepo
351 351 removing statusmatch/subrepo/large.txt
352 352 adding statusmatch/subrepo/normal.txt
353 353 adding statusmatch/subrepo/renamed-large.txt
354 354 $ hg -R statusmatch status -S
355 355 ! subrepo/large.txt
356 356 ? large.dat
357 357 ? subrepo/normal.txt
358 358 ? subrepo/renamed-large.txt
359 359
360 360 $ hg -R statusmatch addremove --dry-run -S
361 361 adding large.dat as a largefile
362 362 removing subrepo/large.txt
363 363 adding subrepo/normal.txt
364 364 adding subrepo/renamed-large.txt
365 365 $ cd statusmatch
366 366
367 367 $ mv subrepo/renamed-large.txt subrepo/large.txt
368 368 $ hg addremove subrepo
369 369 adding subrepo/normal.txt
370 370 $ hg forget subrepo/normal.txt
371 371
372 372 $ hg addremove -S
373 373 adding large.dat as a largefile
374 374 adding subrepo/normal.txt
375 375 $ rm large.dat
376 376
377 377 $ hg addremove subrepo
378 378 $ hg addremove -S
379 379 removing large.dat
380 380
381 381 Lock in subrepo, otherwise the change isn't archived
382 382
383 383 $ hg ci -S -m "add normal file to top level"
384 384 committing subrepository subrepo
385 385 Invoking status precommit hook
386 386 M large.txt
387 387 A normal.txt
388 388 Invoking status precommit hook
389 389 M .hgsubstate
390 390 $ hg archive -S ../lf_subrepo_archive
391 391 $ find ../lf_subrepo_archive | sort
392 392 ../lf_subrepo_archive
393 393 ../lf_subrepo_archive/.hg_archival.txt
394 394 ../lf_subrepo_archive/.hgsub
395 395 ../lf_subrepo_archive/.hgsubstate
396 396 ../lf_subrepo_archive/a
397 397 ../lf_subrepo_archive/a/b
398 398 ../lf_subrepo_archive/a/b/c
399 399 ../lf_subrepo_archive/a/b/c/d
400 400 ../lf_subrepo_archive/a/b/c/d/e.large.txt
401 401 ../lf_subrepo_archive/a/b/c/d/e.normal.txt
402 402 ../lf_subrepo_archive/a/b/c/x
403 403 ../lf_subrepo_archive/a/b/c/x/y.normal.txt
404 404 ../lf_subrepo_archive/subrepo
405 405 ../lf_subrepo_archive/subrepo/large.txt
406 406 ../lf_subrepo_archive/subrepo/normal.txt
407 407 $ cat ../lf_subrepo_archive/.hg_archival.txt
408 408 repo: 41bd42f10efa43698cc02052ea0977771cba506d
409 409 node: d56a95e6522858bc08a724c4fe2bdee066d1c30b
410 410 branch: default
411 411 latesttag: null
412 412 latesttagdistance: 4
413 413 changessincelatesttag: 4
414 414
415 415 Test update with subrepos.
416 416
417 417 $ hg update 0
418 418 getting changed largefiles
419 419 0 largefiles updated, 1 removed
420 420 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
421 421 $ hg status -S
422 422 $ hg update tip
423 423 getting changed largefiles
424 424 1 largefiles updated, 0 removed
425 425 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
426 426 $ hg status -S
427 427 # modify a large file
428 428 $ echo "modified" > subrepo/large.txt
429 429 $ hg st -S
430 430 M subrepo/large.txt
431 431 # update -C should revert the change.
432 432 $ hg update -C
433 433 getting changed largefiles
434 434 1 largefiles updated, 0 removed
435 435 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
436 436 $ hg status -S
437 437
438 438 Forget doesn't change the content of the file
439 439 $ echo 'pre-forget content' > subrepo/large.txt
440 440 $ hg forget -v subrepo/large.txt
441 441 removing subrepo/large.txt
442 442 $ cat subrepo/large.txt
443 443 pre-forget content
444 444
445 445 Test reverting a forgotten file
446 446 $ hg revert -R subrepo subrepo/large.txt
447 447 $ hg status -SA subrepo/large.txt
448 448 C subrepo/large.txt
449 449
450 450 $ hg rm -v subrepo/large.txt
451 451 removing subrepo/large.txt
452 452 $ hg revert -R subrepo subrepo/large.txt
453 453 $ rm subrepo/large.txt
454 454 $ hg addremove -S
455 455 removing subrepo/large.txt
456 456 $ hg st -S
457 457 R subrepo/large.txt
458 458
459 459 Test archiving a revision that references a subrepo that is not yet
460 460 cloned (see test-subrepo-recursion.t):
461 461
462 462 $ hg clone -U . ../empty
463 463 $ cd ../empty
464 464 $ hg archive --subrepos -r tip ../archive.tar.gz
465 465 cloning subrepo subrepo from $TESTTMP/statusmatch/subrepo
466 466 $ cd ..
467 467
468 468
469 469
470 470
471 471
472 472
473 473 Test addremove, forget and others
474 474 ==============================================
475 475
476 476 Test that addremove picks up largefiles prior to the initial commit (issue3541)
477 477
478 478 $ hg init addrm2
479 479 $ cd addrm2
480 480 $ touch large.dat
481 481 $ touch large2.dat
482 482 $ touch normal
483 483 $ hg add --large large.dat
484 484 $ hg addremove -v
485 485 adding large2.dat as a largefile
486 486 adding normal
487 487
488 488 Test that forgetting all largefiles reverts to islfilesrepo() == False
489 489 (addremove will add *.dat as normal files now)
490 490 $ hg forget large.dat
491 491 $ hg forget large2.dat
492 492 $ hg addremove -v
493 493 adding large.dat
494 494 adding large2.dat
495 495
496 496 Test commit's addremove option prior to the first commit
497 497 $ hg forget large.dat
498 498 $ hg forget large2.dat
499 499 $ hg add --large large.dat
500 500 $ hg ci -Am "commit"
501 501 adding large2.dat as a largefile
502 502 Invoking status precommit hook
503 503 A large.dat
504 504 A large2.dat
505 505 A normal
506 506 $ find .hglf | sort
507 507 .hglf
508 508 .hglf/large.dat
509 509 .hglf/large2.dat
510 510
511 511 Test actions on largefiles using relative paths from subdir
512 512
513 513 $ mkdir sub
514 514 $ cd sub
515 515 $ echo anotherlarge > anotherlarge
516 516 $ hg add --large anotherlarge
517 517 $ hg st
518 518 A sub/anotherlarge
519 519 $ hg st anotherlarge
520 520 A anotherlarge
521 521 $ hg commit -m anotherlarge anotherlarge
522 522 Invoking status precommit hook
523 523 A sub/anotherlarge
524 524 $ hg log anotherlarge
525 525 changeset: 1:9627a577c5e9
526 526 tag: tip
527 527 user: test
528 528 date: Thu Jan 01 00:00:00 1970 +0000
529 529 summary: anotherlarge
530 530
531 531 $ hg --debug log -T '{rev}: {desc}\n' ../sub/anotherlarge
532 532 updated patterns: ../.hglf/sub/../sub/anotherlarge, ../sub/anotherlarge
533 533 1: anotherlarge
534 534
535 535 $ hg log -G anotherlarge
536 536 @ changeset: 1:9627a577c5e9
537 537 | tag: tip
538 538 ~ user: test
539 539 date: Thu Jan 01 00:00:00 1970 +0000
540 540 summary: anotherlarge
541 541
542 542
543 543 $ hg log glob:another*
544 544 changeset: 1:9627a577c5e9
545 545 tag: tip
546 546 user: test
547 547 date: Thu Jan 01 00:00:00 1970 +0000
548 548 summary: anotherlarge
549 549
550 550 $ hg --debug log -T '{rev}: {desc}\n' -G glob:another*
551 551 updated patterns: glob:../.hglf/sub/another*, glob:another*
552 552 @ 1: anotherlarge
553 553 |
554 554 ~
555 555
556 556 #if no-msys
557 557 $ hg --debug log -T '{rev}: {desc}\n' 'glob:../.hglf/sub/another*' # no-msys
558 558 updated patterns: glob:../.hglf/sub/another*
559 559 1: anotherlarge
560 560
561 561 $ hg --debug log -G -T '{rev}: {desc}\n' 'glob:../.hglf/sub/another*' # no-msys
562 562 updated patterns: glob:../.hglf/sub/another*
563 563 @ 1: anotherlarge
564 564 |
565 565 ~
566 566 #endif
567 567
568 568 $ echo more >> anotherlarge
569 569 $ hg st .
570 570 M anotherlarge
571 571 $ hg cat anotherlarge
572 572 anotherlarge
573 573 $ hg revert anotherlarge
574 574 $ hg st
575 575 ? sub/anotherlarge.orig
576 576
577 577 Test orig files go where we want them
578 578 $ echo moremore >> anotherlarge
579 579 $ hg revert anotherlarge -v --config 'ui.origbackuppath=.hg/origbackups'
580 580 creating directory: $TESTTMP/addrm2/.hg/origbackups/.hglf/sub
581 581 saving current version of ../.hglf/sub/anotherlarge as ../.hg/origbackups/.hglf/sub/anotherlarge
582 582 reverting ../.hglf/sub/anotherlarge
583 583 creating directory: $TESTTMP/addrm2/.hg/origbackups/sub
584 584 found 90c622cf65cebe75c5842f9136c459333faf392e in store
585 585 found 90c622cf65cebe75c5842f9136c459333faf392e in store
586 586 $ ls ../.hg/origbackups/sub
587 587 anotherlarge
588 588 $ cd ..
589 589
590 590 Test glob logging from the root dir
591 591 $ hg log glob:**another*
592 592 changeset: 1:9627a577c5e9
593 593 tag: tip
594 594 user: test
595 595 date: Thu Jan 01 00:00:00 1970 +0000
596 596 summary: anotherlarge
597 597
598 598 $ hg log -G glob:**another*
599 599 @ changeset: 1:9627a577c5e9
600 600 | tag: tip
601 601 ~ user: test
602 602 date: Thu Jan 01 00:00:00 1970 +0000
603 603 summary: anotherlarge
604 604
605 605
606 606 $ cd ..
607 607
608 608 Log from outer space
609 609 $ hg --debug log -R addrm2 -T '{rev}: {desc}\n' 'addrm2/sub/anotherlarge'
610 610 updated patterns: addrm2/.hglf/sub/anotherlarge, addrm2/sub/anotherlarge
611 611 1: anotherlarge
612 612 $ hg --debug log -R addrm2 -T '{rev}: {desc}\n' 'addrm2/.hglf/sub/anotherlarge'
613 613 updated patterns: addrm2/.hglf/sub/anotherlarge
614 614 1: anotherlarge
615 615
616 616
617 617 Check error message while exchange
618 618 =========================================================
619 619
620 620 issue3651: summary/outgoing with largefiles shows "no remote repo"
621 621 unexpectedly
622 622
623 623 $ mkdir issue3651
624 624 $ cd issue3651
625 625
626 626 $ hg init src
627 627 $ echo a > src/a
628 628 $ hg -R src add --large src/a
629 629 $ hg -R src commit -m '#0'
630 630 Invoking status precommit hook
631 631 A a
632 632
633 633 check messages when no remote repository is specified:
634 634 "no remote repo" route for "hg outgoing --large" is not tested here,
635 635 because it can't be reproduced easily.
636 636
637 637 $ hg init clone1
638 638 $ hg -R clone1 -q pull src
639 639 $ hg -R clone1 -q update
640 640 $ hg -R clone1 paths | grep default
641 641 [1]
642 642
643 643 $ hg -R clone1 summary --large
644 644 parent: 0:fc0bd45326d3 tip
645 645 #0
646 646 branch: default
647 647 commit: (clean)
648 648 update: (current)
649 649 phases: 1 draft
650 650 largefiles: (no remote repo)
651 651
652 652 check messages when there is no files to upload:
653 653
654 654 $ hg -q clone src clone2
655 655 $ hg -R clone2 paths | grep default
656 656 default = $TESTTMP/issue3651/src
657 657
658 658 $ hg -R clone2 summary --large
659 659 parent: 0:fc0bd45326d3 tip
660 660 #0
661 661 branch: default
662 662 commit: (clean)
663 663 update: (current)
664 664 phases: 1 draft
665 665 largefiles: (no files to upload)
666 666 $ hg -R clone2 outgoing --large
667 667 comparing with $TESTTMP/issue3651/src
668 668 searching for changes
669 669 no changes found
670 670 largefiles: no files to upload
671 671 [1]
672 672
673 673 $ hg -R clone2 outgoing --large --graph --template "{rev}"
674 674 comparing with $TESTTMP/issue3651/src
675 675 searching for changes
676 676 no changes found
677 677 largefiles: no files to upload
678 678
679 679 check messages when there are files to upload:
680 680
681 681 $ echo b > clone2/b
682 682 $ hg -R clone2 add --large clone2/b
683 683 $ hg -R clone2 commit -m '#1'
684 684 Invoking status precommit hook
685 685 A b
686 686 $ hg -R clone2 summary --large
687 687 parent: 1:1acbe71ce432 tip
688 688 #1
689 689 branch: default
690 690 commit: (clean)
691 691 update: (current)
692 692 phases: 2 draft
693 693 largefiles: 1 entities for 1 files to upload
694 694 $ hg -R clone2 outgoing --large
695 695 comparing with $TESTTMP/issue3651/src
696 696 searching for changes
697 697 changeset: 1:1acbe71ce432
698 698 tag: tip
699 699 user: test
700 700 date: Thu Jan 01 00:00:00 1970 +0000
701 701 summary: #1
702 702
703 703 largefiles to upload (1 entities):
704 704 b
705 705
706 706 $ hg -R clone2 outgoing --large --graph --template "{rev}"
707 707 comparing with $TESTTMP/issue3651/src
708 708 searching for changes
709 709 @ 1
710 710
711 711 largefiles to upload (1 entities):
712 712 b
713 713
714 714
715 715 $ cp clone2/b clone2/b1
716 716 $ cp clone2/b clone2/b2
717 717 $ hg -R clone2 add --large clone2/b1 clone2/b2
718 718 $ hg -R clone2 commit -m '#2: add largefiles referring same entity'
719 719 Invoking status precommit hook
720 720 A b1
721 721 A b2
722 722 $ hg -R clone2 summary --large
723 723 parent: 2:6095d0695d70 tip
724 724 #2: add largefiles referring same entity
725 725 branch: default
726 726 commit: (clean)
727 727 update: (current)
728 728 phases: 3 draft
729 729 largefiles: 1 entities for 3 files to upload
730 730 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
731 731 comparing with $TESTTMP/issue3651/src
732 732 searching for changes
733 733 1:1acbe71ce432
734 734 2:6095d0695d70
735 735 largefiles to upload (1 entities):
736 736 b
737 737 b1
738 738 b2
739 739
740 740 $ hg -R clone2 cat -r 1 clone2/.hglf/b
741 741 89e6c98d92887913cadf06b2adb97f26cde4849b
742 742 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
743 743 comparing with $TESTTMP/issue3651/src
744 744 query 1; heads
745 745 searching for changes
746 746 all remote heads known locally
747 747 1:1acbe71ce432
748 748 2:6095d0695d70
749 749 finding outgoing largefiles: 0/2 revisions (0.00%)
750 750 finding outgoing largefiles: 1/2 revisions (50.00%)
751 751 largefiles to upload (1 entities):
752 752 b
753 753 89e6c98d92887913cadf06b2adb97f26cde4849b
754 754 b1
755 755 89e6c98d92887913cadf06b2adb97f26cde4849b
756 756 b2
757 757 89e6c98d92887913cadf06b2adb97f26cde4849b
758 758
759 759
760 760 $ echo bbb > clone2/b
761 761 $ hg -R clone2 commit -m '#3: add new largefile entity as existing file'
762 762 Invoking status precommit hook
763 763 M b
764 764 $ echo bbbb > clone2/b
765 765 $ hg -R clone2 commit -m '#4: add new largefile entity as existing file'
766 766 Invoking status precommit hook
767 767 M b
768 768 $ cp clone2/b1 clone2/b
769 769 $ hg -R clone2 commit -m '#5: refer existing largefile entity again'
770 770 Invoking status precommit hook
771 771 M b
772 772 $ hg -R clone2 summary --large
773 773 parent: 5:036794ea641c tip
774 774 #5: refer existing largefile entity again
775 775 branch: default
776 776 commit: (clean)
777 777 update: (current)
778 778 phases: 6 draft
779 779 largefiles: 3 entities for 3 files to upload
780 780 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
781 781 comparing with $TESTTMP/issue3651/src
782 782 searching for changes
783 783 1:1acbe71ce432
784 784 2:6095d0695d70
785 785 3:7983dce246cc
786 786 4:233f12ada4ae
787 787 5:036794ea641c
788 788 largefiles to upload (3 entities):
789 789 b
790 790 b1
791 791 b2
792 792
793 793 $ hg -R clone2 cat -r 3 clone2/.hglf/b
794 794 c801c9cfe94400963fcb683246217d5db77f9a9a
795 795 $ hg -R clone2 cat -r 4 clone2/.hglf/b
796 796 13f9ed0898e315bf59dc2973fec52037b6f441a2
797 797 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
798 798 comparing with $TESTTMP/issue3651/src
799 799 query 1; heads
800 800 searching for changes
801 801 all remote heads known locally
802 802 1:1acbe71ce432
803 803 2:6095d0695d70
804 804 3:7983dce246cc
805 805 4:233f12ada4ae
806 806 5:036794ea641c
807 807 finding outgoing largefiles: 0/5 revisions (0.00%)
808 808 finding outgoing largefiles: 1/5 revisions (20.00%)
809 809 finding outgoing largefiles: 2/5 revisions (40.00%)
810 810 finding outgoing largefiles: 3/5 revisions (60.00%)
811 811 finding outgoing largefiles: 4/5 revisions (80.00%)
812 812 largefiles to upload (3 entities):
813 813 b
814 814 13f9ed0898e315bf59dc2973fec52037b6f441a2
815 815 89e6c98d92887913cadf06b2adb97f26cde4849b
816 816 c801c9cfe94400963fcb683246217d5db77f9a9a
817 817 b1
818 818 89e6c98d92887913cadf06b2adb97f26cde4849b
819 819 b2
820 820 89e6c98d92887913cadf06b2adb97f26cde4849b
821 821
822 822
823 823 Pushing revision #1 causes uploading entity 89e6c98d9288, which is
824 824 shared also by largefiles b1, b2 in revision #2 and b in revision #5.
825 825
826 826 Then, entity 89e6c98d9288 is not treated as "outgoing entity" at "hg
827 827 summary" and "hg outgoing", even though files in outgoing revision #2
828 828 and #5 refer it.
829 829
830 830 $ hg -R clone2 push -r 1 -q
831 831 $ hg -R clone2 summary --large
832 832 parent: 5:036794ea641c tip
833 833 #5: refer existing largefile entity again
834 834 branch: default
835 835 commit: (clean)
836 836 update: (current)
837 837 phases: 6 draft
838 838 largefiles: 2 entities for 1 files to upload
839 839 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n"
840 840 comparing with $TESTTMP/issue3651/src
841 841 searching for changes
842 842 2:6095d0695d70
843 843 3:7983dce246cc
844 844 4:233f12ada4ae
845 845 5:036794ea641c
846 846 largefiles to upload (2 entities):
847 847 b
848 848
849 849 $ hg -R clone2 outgoing --large -T "{rev}:{node|short}\n" --debug --config progress.debug=true
850 850 comparing with $TESTTMP/issue3651/src
851 851 query 1; heads
852 852 searching for changes
853 853 all remote heads known locally
854 854 2:6095d0695d70
855 855 3:7983dce246cc
856 856 4:233f12ada4ae
857 857 5:036794ea641c
858 858 finding outgoing largefiles: 0/4 revisions (0.00%)
859 859 finding outgoing largefiles: 1/4 revisions (25.00%)
860 860 finding outgoing largefiles: 2/4 revisions (50.00%)
861 861 finding outgoing largefiles: 3/4 revisions (75.00%)
862 862 largefiles to upload (2 entities):
863 863 b
864 864 13f9ed0898e315bf59dc2973fec52037b6f441a2
865 865 c801c9cfe94400963fcb683246217d5db77f9a9a
866 866
867 867
868 868 $ cd ..
869 869
870 870 merge action 'd' for 'local renamed directory to d2/g' which has no filename
871 871 ==================================================================================
872 872
873 873 $ hg init merge-action
874 874 $ cd merge-action
875 875 $ touch l
876 876 $ hg add --large l
877 877 $ mkdir d1
878 878 $ touch d1/f
879 879 $ hg ci -Aqm0
880 880 Invoking status precommit hook
881 881 A d1/f
882 882 A l
883 883 $ echo > d1/f
884 884 $ touch d1/g
885 885 $ hg ci -Aqm1
886 886 Invoking status precommit hook
887 887 M d1/f
888 888 A d1/g
889 889 $ hg up -qr0
890 890 $ hg mv d1 d2
891 891 moving d1/f to d2/f
892 892 $ hg ci -qm2
893 893 Invoking status precommit hook
894 894 A d2/f
895 895 R d1/f
896 896 $ hg merge
897 897 merging d2/f and d1/f to d2/f
898 898 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
899 899 (branch merge, don't forget to commit)
900 900 $ cd ..
901 901
902 902
903 903 Merge conflicts:
904 904 =====================
905 905
906 906 $ hg init merge
907 907 $ cd merge
908 908 $ echo 0 > f-different
909 909 $ echo 0 > f-same
910 910 $ echo 0 > f-unchanged-1
911 911 $ echo 0 > f-unchanged-2
912 912 $ hg add --large *
913 913 $ hg ci -m0
914 914 Invoking status precommit hook
915 915 A f-different
916 916 A f-same
917 917 A f-unchanged-1
918 918 A f-unchanged-2
919 919 $ echo tmp1 > f-unchanged-1
920 920 $ echo tmp1 > f-unchanged-2
921 921 $ echo tmp1 > f-same
922 922 $ hg ci -m1
923 923 Invoking status precommit hook
924 924 M f-same
925 925 M f-unchanged-1
926 926 M f-unchanged-2
927 927 $ echo 2 > f-different
928 928 $ echo 0 > f-unchanged-1
929 929 $ echo 1 > f-unchanged-2
930 930 $ echo 1 > f-same
931 931 $ hg ci -m2
932 932 Invoking status precommit hook
933 933 M f-different
934 934 M f-same
935 935 M f-unchanged-1
936 936 M f-unchanged-2
937 937 $ hg up -qr0
938 938 $ echo tmp2 > f-unchanged-1
939 939 $ echo tmp2 > f-unchanged-2
940 940 $ echo tmp2 > f-same
941 941 $ hg ci -m3
942 942 Invoking status precommit hook
943 943 M f-same
944 944 M f-unchanged-1
945 945 M f-unchanged-2
946 946 created new head
947 947 $ echo 1 > f-different
948 948 $ echo 1 > f-unchanged-1
949 949 $ echo 0 > f-unchanged-2
950 950 $ echo 1 > f-same
951 951 $ hg ci -m4
952 952 Invoking status precommit hook
953 953 M f-different
954 954 M f-same
955 955 M f-unchanged-1
956 956 M f-unchanged-2
957 957 $ hg merge
958 958 largefile f-different has a merge conflict
959 959 ancestor was 09d2af8dd22201dd8d48e5dcfcaed281ff9422c7
960 960 you can keep (l)ocal e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e or take (o)ther 7448d8798a4380162d4b56f9b452e2f6f9e24e7a.
961 961 what do you want to do? l
962 962 getting changed largefiles
963 963 1 largefiles updated, 0 removed
964 964 0 files updated, 4 files merged, 0 files removed, 0 files unresolved
965 965 (branch merge, don't forget to commit)
966 966 $ cat f-different
967 967 1
968 968 $ cat f-same
969 969 1
970 970 $ cat f-unchanged-1
971 971 1
972 972 $ cat f-unchanged-2
973 973 1
974 974 $ cd ..
975 975
976 976 Test largefile insulation (do not enabled a side effect
977 977 ========================================================
978 978
979 979 Check whether "largefiles" feature is supported only in repositories
980 980 enabling largefiles extension.
981 981
982 982 $ mkdir individualenabling
983 983 $ cd individualenabling
984 984
985 985 $ hg init enabledlocally
986 986 $ echo large > enabledlocally/large
987 987 $ hg -R enabledlocally add --large enabledlocally/large
988 988 $ hg -R enabledlocally commit -m '#0'
989 989 Invoking status precommit hook
990 990 A large
991 991
992 992 $ hg init notenabledlocally
993 993 $ echo large > notenabledlocally/large
994 994 $ hg -R notenabledlocally add --large notenabledlocally/large
995 995 $ hg -R notenabledlocally commit -m '#0'
996 996 Invoking status precommit hook
997 997 A large
998 998
999 999 $ cat >> $HGRCPATH <<EOF
1000 1000 > [extensions]
1001 1001 > # disable globally
1002 1002 > largefiles=!
1003 1003 > EOF
1004 1004 $ cat >> enabledlocally/.hg/hgrc <<EOF
1005 1005 > [extensions]
1006 1006 > # enable locally
1007 1007 > largefiles=
1008 1008 > EOF
1009 1009 $ hg -R enabledlocally root
1010 1010 $TESTTMP/individualenabling/enabledlocally
1011 1011 $ hg -R notenabledlocally root
1012 1012 abort: repository requires features unknown to this Mercurial: largefiles!
1013 1013 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
1014 1014 [255]
1015 1015
1016 1016 $ hg init push-dst
1017 1017 $ hg -R enabledlocally push push-dst
1018 1018 pushing to push-dst
1019 1019 abort: required features are not supported in the destination: largefiles
1020 1020 [255]
1021 1021
1022 1022 $ hg init pull-src
1023 1023 $ hg -R pull-src pull enabledlocally
1024 1024 pulling from enabledlocally
1025 1025 abort: required features are not supported in the destination: largefiles
1026 1026 [255]
1027 1027
1028 1028 $ hg clone enabledlocally clone-dst
1029 1029 abort: repository requires features unknown to this Mercurial: largefiles!
1030 1030 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
1031 1031 [255]
1032 1032 $ test -d clone-dst
1033 1033 [1]
1034 1034 $ hg clone --pull enabledlocally clone-pull-dst
1035 1035 abort: required features are not supported in the destination: largefiles
1036 1036 [255]
1037 1037 $ test -d clone-pull-dst
1038 1038 [1]
1039 1039
1040 1040 #if serve
1041 1041
1042 1042 Test largefiles specific peer setup, when largefiles is enabled
1043 1043 locally (issue4109)
1044 1044
1045 1045 $ hg showconfig extensions | grep largefiles
1046 1046 extensions.largefiles=!
1047 1047 $ mkdir -p $TESTTMP/individualenabling/usercache
1048 1048
1049 1049 $ hg serve -R enabledlocally -d -p $HGPORT --pid-file hg.pid
1050 1050 $ cat hg.pid >> $DAEMON_PIDS
1051 1051
1052 1052 $ hg init pull-dst
1053 1053 $ cat > pull-dst/.hg/hgrc <<EOF
1054 1054 > [extensions]
1055 1055 > # enable locally
1056 1056 > largefiles=
1057 1057 > [largefiles]
1058 1058 > # ignore system cache to force largefiles specific wire proto access
1059 1059 > usercache=$TESTTMP/individualenabling/usercache
1060 1060 > EOF
1061 1061 $ hg -R pull-dst -q pull -u http://localhost:$HGPORT
1062 1062
1063 1063 $ killdaemons.py
1064 1064 #endif
1065 1065
1066 1066 Test overridden functions work correctly even for repos disabling
1067 1067 largefiles (issue4547)
1068 1068
1069 1069 $ hg showconfig extensions | grep largefiles
1070 1070 extensions.largefiles=!
1071 1071
1072 1072 (test updating implied by clone)
1073 1073
1074 1074 $ hg init enabled-but-no-largefiles
1075 1075 $ echo normal1 > enabled-but-no-largefiles/normal1
1076 1076 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal1
1077 1077 $ hg -R enabled-but-no-largefiles commit -m '#0@enabled-but-no-largefiles'
1078 1078 Invoking status precommit hook
1079 1079 A normal1
1080 1080 $ cat >> enabled-but-no-largefiles/.hg/hgrc <<EOF
1081 1081 > [extensions]
1082 1082 > # enable locally
1083 1083 > largefiles=
1084 1084 > EOF
1085 1085 $ hg clone -q enabled-but-no-largefiles no-largefiles
1086 1086
1087 1087 $ echo normal2 > enabled-but-no-largefiles/normal2
1088 1088 $ hg -R enabled-but-no-largefiles add enabled-but-no-largefiles/normal2
1089 1089 $ hg -R enabled-but-no-largefiles commit -m '#1@enabled-but-no-largefiles'
1090 1090 Invoking status precommit hook
1091 1091 A normal2
1092 1092
1093 1093 $ echo normal3 > no-largefiles/normal3
1094 1094 $ hg -R no-largefiles add no-largefiles/normal3
1095 1095 $ hg -R no-largefiles commit -m '#1@no-largefiles'
1096 1096 Invoking status precommit hook
1097 1097 A normal3
1098 1098
1099 1099 $ hg -R no-largefiles -q pull --rebase
1100 1100 Invoking status precommit hook
1101 1101 A normal3
1102 1102
1103 1103 (test reverting)
1104 1104
1105 1105 $ hg init subrepo-root
1106 1106 $ cat >> subrepo-root/.hg/hgrc <<EOF
1107 1107 > [extensions]
1108 1108 > # enable locally
1109 1109 > largefiles=
1110 1110 > EOF
1111 1111 $ echo large > subrepo-root/large
1112 1112 $ mkdir -p subrepo-root/dir/subdir
1113 1113 $ echo large2 > subrepo-root/dir/subdir/large.bin
1114 1114 $ hg -R subrepo-root add --large subrepo-root/large subrepo-root/dir/subdir/large.bin
1115 1115 $ hg clone -q no-largefiles subrepo-root/no-largefiles
1116 1116 $ cat > subrepo-root/.hgsub <<EOF
1117 1117 > no-largefiles = no-largefiles
1118 1118 > EOF
1119 1119 $ hg -R subrepo-root add subrepo-root/.hgsub
1120 1120 $ hg -R subrepo-root commit -m '#0'
1121 1121 Invoking status precommit hook
1122 1122 A .hgsub
1123 1123 A dir/subdir/large.bin
1124 1124 A large
1125 1125 ? .hgsubstate
1126 1126 $ echo dirty >> subrepo-root/large
1127 1127 $ echo dirty >> subrepo-root/no-largefiles/normal1
1128 1128 $ hg -R subrepo-root status -S
1129 1129 M large
1130 1130 M no-largefiles/normal1
1131 1131 $ hg -R subrepo-root extdiff -p echo -S --config extensions.extdiff=
1132 1132 "*\\no-largefiles\\normal1" "*\\no-largefiles\\normal1" (glob) (windows !)
1133 1133 */no-largefiles/normal1 */no-largefiles/normal1 (glob) (no-windows !)
1134 1134 [1]
1135 1135 $ hg -R subrepo-root revert --all
1136 1136 reverting subrepo-root/.hglf/large
1137 1137 reverting subrepo no-largefiles
1138 1138 reverting subrepo-root/no-largefiles/normal1
1139 1139
1140 1140 Move (and then undo) a directory move with only largefiles.
1141 1141
1142 1142 $ cd subrepo-root
1143 1143 $ "$PYTHON" $TESTDIR/list-tree.py .hglf dir* large*
1144 1144 .hglf/
1145 1145 .hglf/dir/
1146 1146 .hglf/dir/subdir/
1147 1147 .hglf/dir/subdir/large.bin
1148 1148 .hglf/large
1149 1149 dir/
1150 1150 dir/subdir/
1151 1151 dir/subdir/large.bin
1152 1152 large
1153 1153 large.orig
1154 1154
1155 1155 $ hg mv dir/subdir dir/subdir2
1156 1156 moving .hglf/dir/subdir/large.bin to .hglf/dir/subdir2/large.bin
1157 1157
1158 1158 $ "$PYTHON" $TESTDIR/list-tree.py .hglf dir* large*
1159 1159 .hglf/
1160 1160 .hglf/dir/
1161 1161 .hglf/dir/subdir2/
1162 1162 .hglf/dir/subdir2/large.bin
1163 1163 .hglf/large
1164 1164 dir/
1165 1165 dir/subdir2/
1166 1166 dir/subdir2/large.bin
1167 1167 large
1168 1168 large.orig
1169 1169 $ hg status -C
1170 1170 A dir/subdir2/large.bin
1171 1171 dir/subdir/large.bin
1172 1172 R dir/subdir/large.bin
1173 1173 ? large.orig
1174 1174
1175 1175 $ echo 'modified' > dir/subdir2/large.bin
1176 1176 $ hg status -C
1177 1177 A dir/subdir2/large.bin
1178 1178 dir/subdir/large.bin
1179 1179 R dir/subdir/large.bin
1180 1180 ? large.orig
1181 1181
1182 1182 $ hg revert --all
1183 1183 forgetting .hglf/dir/subdir2/large.bin
1184 1184 undeleting .hglf/dir/subdir/large.bin
1185 1185 reverting subrepo no-largefiles
1186 1186
1187 1187 $ hg status -C
1188 1188 ? dir/subdir2/large.bin
1189 1189 ? large.orig
1190 1190
1191 1191 The content of the forgotten file shouldn't be clobbered
1192 1192
1193 1193 $ cat dir/subdir2/large.bin
1194 1194 modified
1195 1195
1196 1196 The standin for subdir2 should be deleted, not just dropped
1197 1197
1198 1198 $ "$PYTHON" $TESTDIR/list-tree.py .hglf dir* large*
1199 1199 .hglf/
1200 1200 .hglf/dir/
1201 1201 .hglf/dir/subdir/
1202 1202 .hglf/dir/subdir/large.bin
1203 1203 .hglf/large
1204 1204 dir/
1205 1205 dir/subdir/
1206 1206 dir/subdir/large.bin
1207 1207 dir/subdir2/
1208 1208 dir/subdir2/large.bin
1209 1209 large
1210 1210 large.orig
1211 1211
1212 1212 $ rm -r dir/subdir2
1213 1213
1214 1214 'subdir' should not be in the destination. It would be if the subdir2 directory
1215 1215 existed under .hglf/.
1216 1216 $ hg mv dir/subdir dir/subdir2
1217 1217 moving .hglf/dir/subdir/large.bin to .hglf/dir/subdir2/large.bin
1218 1218
1219 1219 $ hg status -C
1220 1220 A dir/subdir2/large.bin
1221 1221 dir/subdir/large.bin
1222 1222 R dir/subdir/large.bin
1223 1223 ? large.orig
1224 1224
1225 1225 $ "$PYTHON" $TESTDIR/list-tree.py .hglf dir* large*
1226 1226 .hglf/
1227 1227 .hglf/dir/
1228 1228 .hglf/dir/subdir2/
1229 1229 .hglf/dir/subdir2/large.bin
1230 1230 .hglf/large
1231 1231 dir/
1232 1232 dir/subdir2/
1233 1233 dir/subdir2/large.bin
1234 1234 large
1235 1235 large.orig
1236 1236
1237 1237 Start from scratch, and rename something other than the final path component.
1238 1238
1239 1239 $ hg up -qC .
1240 1240 $ hg --config extensions.purge= purge
1241 1241
1242 1242 $ hg mv dir/subdir dir2/subdir
1243 1243 moving .hglf/dir/subdir/large.bin to .hglf/dir2/subdir/large.bin
1244 1244
1245 1245 $ hg status -C
1246 1246 A dir2/subdir/large.bin
1247 1247 dir/subdir/large.bin
1248 1248 R dir/subdir/large.bin
1249 1249
1250 1250 $ "$PYTHON" $TESTDIR/list-tree.py .hglf dir* large*
1251 1251 .hglf/
1252 1252 .hglf/dir2/
1253 1253 .hglf/dir2/subdir/
1254 1254 .hglf/dir2/subdir/large.bin
1255 1255 .hglf/large
1256 1256 dir2/
1257 1257 dir2/subdir/
1258 1258 dir2/subdir/large.bin
1259 1259 large
1260 1260
1261 1261 $ hg revert --all
1262 1262 forgetting .hglf/dir2/subdir/large.bin
1263 1263 undeleting .hglf/dir/subdir/large.bin
1264 1264 reverting subrepo no-largefiles
1265 1265
1266 1266 $ hg status -C
1267 1267 ? dir2/subdir/large.bin
1268 1268
1269 1269 $ "$PYTHON" $TESTDIR/list-tree.py .hglf dir* large*
1270 1270 .hglf/
1271 1271 .hglf/dir/
1272 1272 .hglf/dir/subdir/
1273 1273 .hglf/dir/subdir/large.bin
1274 1274 .hglf/large
1275 1275 dir/
1276 1276 dir/subdir/
1277 1277 dir/subdir/large.bin
1278 1278 dir2/
1279 1279 dir2/subdir/
1280 1280 dir2/subdir/large.bin
1281 1281 large
1282 1282
1283 1283 $ cd ../..
1284 1284
1285 1285 Test "pull --rebase" when rebase is enabled before largefiles (issue3861)
1286 1286 =========================================================================
1287 1287
1288 1288 $ hg showconfig extensions | grep largefiles
1289 1289 extensions.largefiles=!
1290 1290
1291 1291 $ mkdir issue3861
1292 1292 $ cd issue3861
1293 1293 $ hg init src
1294 1294 $ hg clone -q src dst
1295 1295 $ echo a > src/a
1296 1296 $ hg -R src commit -Aqm "#0"
1297 1297 Invoking status precommit hook
1298 1298 A a
1299 1299
1300 1300 $ cat >> dst/.hg/hgrc <<EOF
1301 1301 > [extensions]
1302 1302 > largefiles=
1303 1303 > EOF
1304 1304 $ hg -R dst pull --rebase
1305 1305 pulling from $TESTTMP/issue3861/src
1306 1306 requesting all changes
1307 1307 adding changesets
1308 1308 adding manifests
1309 1309 adding file changes
1310 1310 added 1 changesets with 1 changes to 1 files
1311 1311 new changesets bf5e395ced2c (1 drafts)
1312 1312 nothing to rebase - updating instead
1313 1313 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1314 1314
1315 1315 $ cd ..
@@ -1,141 +1,141 b''
1 1 $ hg init
2 2
3 3 $ echo a > a
4 4 $ hg ci -qAm 'add a'
5 5
6 6 $ hg init subrepo
7 7 $ echo 'subrepo = http://example.net/libfoo' > .hgsub
8 8 $ hg ci -qAm 'added subrepo'
9 9
10 10 $ hg up -qC 0
11 11 $ echo ax > a
12 12 $ hg ci -m 'changed a'
13 13 created new head
14 14
15 15 $ hg up -qC 1
16 16 $ cd subrepo
17 17 $ echo b > b
18 18 $ hg add b
19 19 $ cd ..
20 20
21 21 Should fail, since there are added files to subrepo:
22 22
23 23 $ hg merge
24 24 abort: uncommitted changes in subrepository "subrepo"
25 25 [255]
26 26
27 27 Deleted files trigger a '+' marker in top level repos. Deleted files are also
28 28 noticed by `update --check` in the top level repo.
29 29
30 30 $ hg ci -Sqm 'add b'
31 31 $ echo change > subrepo/b
32 32
33 33 $ hg ci -Sm 'change b'
34 34 committing subrepository subrepo
35 35
36 36 $ rm a
37 37 $ hg id
38 38 9bfe45a197d7+ tip
39 39 $ hg sum
40 40 parent: 4:9bfe45a197d7 tip
41 41 change b
42 42 branch: default
43 43 commit: 1 deleted (clean)
44 44 update: 1 new changesets, 2 branch heads (merge)
45 45 phases: 5 draft
46 46
47 47 $ hg up --check -r '.^'
48 48 abort: uncommitted changes
49 [255]
49 [20]
50 50 $ hg st -S
51 51 ! a
52 52 $ hg up -Cq .
53 53
54 54 Test that dirty is consistent through subrepos
55 55
56 56 $ rm subrepo/b
57 57
58 58 A deleted subrepo file is flagged as dirty, like the top level repo
59 59
60 60 $ hg id --config extensions.blackbox= --config blackbox.dirty=True \
61 61 > --config blackbox.track='command commandfinish'
62 62 9bfe45a197d7+ tip
63 63 $ cat .hg/blackbox.log
64 64 * @9bfe45a197d7b0ab09bf287729dd57e9619c9da5+ (*)> serve --cmdserver chgunix * (glob) (chg !)
65 65 * @9bfe45a197d7b0ab09bf287729dd57e9619c9da5+ (*)> id --config *extensions.blackbox=* --config *blackbox.dirty=True* (glob)
66 66 * @9bfe45a197d7b0ab09bf287729dd57e9619c9da5+ (*)> id --config *extensions.blackbox=* --config *blackbox.dirty=True* exited 0 * (glob)
67 67
68 68 TODO: a deleted file should be listed as such, like the top level repo
69 69
70 70 $ hg sum
71 71 parent: 4:9bfe45a197d7 tip
72 72 change b
73 73 branch: default
74 74 commit: (clean)
75 75 update: 1 new changesets, 2 branch heads (merge)
76 76 phases: 5 draft
77 77
78 78 Modified subrepo files are noticed by `update --check` and `summary`
79 79
80 80 $ echo mod > subrepo/b
81 81 $ hg st -S
82 82 M subrepo/b
83 83
84 84 $ hg up -r '.^' --check
85 85 abort: uncommitted changes in subrepository "subrepo"
86 86 [255]
87 87
88 88 $ hg sum
89 89 parent: 4:9bfe45a197d7 tip
90 90 change b
91 91 branch: default
92 92 commit: 1 subrepos
93 93 update: 1 new changesets, 2 branch heads (merge)
94 94 phases: 5 draft
95 95
96 96 TODO: why is -R needed here? If it's because the subrepo is treated as a
97 97 discrete unit, then this should probably warn or something.
98 98 $ hg revert -R subrepo --no-backup subrepo/b -r .
99 99
100 100 $ rm subrepo/b
101 101 $ hg st -S
102 102 ! subrepo/b
103 103
104 104 `hg update --check` notices a subrepo with a missing file, like it notices a
105 105 missing file in the top level repo.
106 106
107 107 $ hg up -r '.^' --check
108 108 abort: uncommitted changes in subrepository "subrepo"
109 109 [255]
110 110
111 111 $ hg up -r '.^' --config ui.interactive=True << EOF
112 112 > d
113 113 > EOF
114 114 file 'b' was deleted in local [working copy] but was modified in other [destination].
115 115 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
116 116 What do you want to do? d
117 117 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 118
119 119 XXX: There's a difference between wdir() and '.', so there should be a status.
120 120 `hg files -S` from the top is also missing 'subrepo/b'.
121 121
122 122 $ hg st -S
123 123 $ hg st -R subrepo
124 124 $ hg files -R subrepo
125 125 [1]
126 126 $ hg files -R subrepo -r '.'
127 127 subrepo/b
128 128
129 129 $ hg bookmark -r tip @other
130 130 $ echo xyz > subrepo/c
131 131 $ hg ci -SAm 'add c'
132 132 adding subrepo/c
133 133 committing subrepository subrepo
134 134 created new head
135 135 $ rm subrepo/c
136 136
137 137 Merge sees deleted subrepo files as an uncommitted change
138 138
139 139 $ hg merge @other
140 140 abort: uncommitted changes in subrepository "subrepo"
141 141 [255]
@@ -1,439 +1,439 b''
1 1 $ cat <<EOF > merge
2 2 > from __future__ import print_function
3 3 > import sys, os
4 4 >
5 5 > try:
6 6 > import msvcrt
7 7 > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
8 8 > msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
9 9 > except ImportError:
10 10 > pass
11 11 >
12 12 > print("merging for", os.path.basename(sys.argv[1]))
13 13 > EOF
14 14 $ HGMERGE="\"$PYTHON\" ../merge"; export HGMERGE
15 15
16 16 $ hg init t
17 17 $ cd t
18 18 $ echo This is file a1 > a
19 19 $ hg add a
20 20 $ hg commit -m "commit #0"
21 21 $ echo This is file b1 > b
22 22 $ hg add b
23 23 $ hg commit -m "commit #1"
24 24
25 25 $ hg update 0
26 26 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
27 27
28 28 Test interrupted updates by having a non-empty dir with the same name as one
29 29 of the files in a commit we're updating to
30 30
31 31 $ mkdir b && touch b/nonempty
32 32 $ hg up
33 33 abort: Unlinking directory not permitted: *$TESTTMP/t/b* (glob) (windows !)
34 34 abort: Directory not empty: '?\$TESTTMP/t/b'? (re) (no-windows !)
35 35 [255]
36 36 $ hg ci
37 37 abort: last update was interrupted
38 38 (use 'hg update' to get a consistent checkout)
39 [255]
39 [20]
40 40 $ hg sum
41 41 parent: 0:538afb845929
42 42 commit #0
43 43 branch: default
44 44 commit: 1 unknown (interrupted update)
45 45 update: 1 new changesets (update)
46 46 phases: 2 draft
47 47 Detect interrupted update by hg status --verbose
48 48 $ hg status -v
49 49 ? b/nonempty
50 50 # The repository is in an unfinished *update* state.
51 51
52 52 # To continue: hg update .
53 53
54 54
55 55 $ rm b/nonempty
56 56
57 57 $ hg up
58 58 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
59 59 $ hg sum
60 60 parent: 1:b8bb4a988f25 tip
61 61 commit #1
62 62 branch: default
63 63 commit: (clean)
64 64 update: (current)
65 65 phases: 2 draft
66 66
67 67 Prepare a basic merge
68 68
69 69 $ hg up 0
70 70 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
71 71 $ echo This is file c1 > c
72 72 $ hg add c
73 73 $ hg commit -m "commit #2"
74 74 created new head
75 75 $ echo This is file b1 > b
76 76 no merges expected
77 77 $ hg merge -P 1
78 78 changeset: 1:b8bb4a988f25
79 79 user: test
80 80 date: Thu Jan 01 00:00:00 1970 +0000
81 81 summary: commit #1
82 82
83 83 $ hg merge 1
84 84 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
85 85 (branch merge, don't forget to commit)
86 86 $ hg diff --nodates
87 87 diff -r 49035e18a8e6 b
88 88 --- /dev/null
89 89 +++ b/b
90 90 @@ -0,0 +1,1 @@
91 91 +This is file b1
92 92 $ hg status
93 93 M b
94 94 $ cd ..; rm -r t
95 95
96 96 $ hg init t
97 97 $ cd t
98 98 $ echo This is file a1 > a
99 99 $ hg add a
100 100 $ hg commit -m "commit #0"
101 101 $ echo This is file b1 > b
102 102 $ hg add b
103 103 $ hg commit -m "commit #1"
104 104
105 105 $ hg update 0
106 106 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
107 107 $ echo This is file c1 > c
108 108 $ hg add c
109 109 $ hg commit -m "commit #2"
110 110 created new head
111 111 $ echo This is file b2 > b
112 112 merge should fail
113 113 $ hg merge 1
114 114 b: untracked file differs
115 115 abort: untracked files in working directory differ from files in requested revision
116 116 [255]
117 117
118 118 #if symlink
119 119 symlinks to directories should be treated as regular files (issue5027)
120 120 $ rm b
121 121 $ ln -s 'This is file b2' b
122 122 $ hg merge 1
123 123 b: untracked file differs
124 124 abort: untracked files in working directory differ from files in requested revision
125 125 [255]
126 126 symlinks shouldn't be followed
127 127 $ rm b
128 128 $ echo This is file b1 > .hg/b
129 129 $ ln -s .hg/b b
130 130 $ hg merge 1
131 131 b: untracked file differs
132 132 abort: untracked files in working directory differ from files in requested revision
133 133 [255]
134 134
135 135 $ rm b
136 136 $ echo This is file b2 > b
137 137 #endif
138 138
139 139 bad config
140 140 $ hg merge 1 --config merge.checkunknown=x
141 141 abort: merge.checkunknown not valid ('x' is none of 'abort', 'ignore', 'warn')
142 142 [255]
143 143 this merge should fail
144 144 $ hg merge 1 --config merge.checkunknown=abort
145 145 b: untracked file differs
146 146 abort: untracked files in working directory differ from files in requested revision
147 147 [255]
148 148
149 149 this merge should warn
150 150 $ hg merge 1 --config merge.checkunknown=warn
151 151 b: replacing untracked file
152 152 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
153 153 (branch merge, don't forget to commit)
154 154 $ cat b.orig
155 155 This is file b2
156 156 $ hg up --clean 2
157 157 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
158 158 $ mv b.orig b
159 159
160 160 this merge should silently ignore
161 161 $ cat b
162 162 This is file b2
163 163 $ hg merge 1 --config merge.checkunknown=ignore
164 164 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
165 165 (branch merge, don't forget to commit)
166 166
167 167 merge.checkignored
168 168 $ hg up --clean 1
169 169 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
170 170 $ cat >> .hgignore << EOF
171 171 > remoteignored
172 172 > EOF
173 173 $ echo This is file localignored3 > localignored
174 174 $ echo This is file remoteignored3 > remoteignored
175 175 $ hg add .hgignore localignored remoteignored
176 176 $ hg commit -m "commit #3"
177 177
178 178 $ hg up 2
179 179 1 files updated, 0 files merged, 4 files removed, 0 files unresolved
180 180 $ cat >> .hgignore << EOF
181 181 > localignored
182 182 > EOF
183 183 $ hg add .hgignore
184 184 $ hg commit -m "commit #4"
185 185
186 186 remote .hgignore shouldn't be used for determining whether a file is ignored
187 187 $ echo This is file remoteignored4 > remoteignored
188 188 $ hg merge 3 --config merge.checkignored=ignore --config merge.checkunknown=abort
189 189 remoteignored: untracked file differs
190 190 abort: untracked files in working directory differ from files in requested revision
191 191 [255]
192 192 $ hg merge 3 --config merge.checkignored=abort --config merge.checkunknown=ignore
193 193 merging .hgignore
194 194 merging for .hgignore
195 195 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
196 196 (branch merge, don't forget to commit)
197 197 $ cat remoteignored
198 198 This is file remoteignored3
199 199 $ cat remoteignored.orig
200 200 This is file remoteignored4
201 201 $ rm remoteignored.orig
202 202
203 203 local .hgignore should be used for that
204 204 $ hg up --clean 4
205 205 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
206 206 $ echo This is file localignored4 > localignored
207 207 also test other conflicting files to see we output the full set of warnings
208 208 $ echo This is file b2 > b
209 209 $ hg merge 3 --config merge.checkignored=abort --config merge.checkunknown=abort
210 210 b: untracked file differs
211 211 localignored: untracked file differs
212 212 abort: untracked files in working directory differ from files in requested revision
213 213 [255]
214 214 $ hg merge 3 --config merge.checkignored=abort --config merge.checkunknown=ignore
215 215 localignored: untracked file differs
216 216 abort: untracked files in working directory differ from files in requested revision
217 217 [255]
218 218 $ hg merge 3 --config merge.checkignored=warn --config merge.checkunknown=abort
219 219 b: untracked file differs
220 220 abort: untracked files in working directory differ from files in requested revision
221 221 [255]
222 222 $ hg merge 3 --config merge.checkignored=warn --config merge.checkunknown=warn
223 223 b: replacing untracked file
224 224 localignored: replacing untracked file
225 225 merging .hgignore
226 226 merging for .hgignore
227 227 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
228 228 (branch merge, don't forget to commit)
229 229 $ cat localignored
230 230 This is file localignored3
231 231 $ cat localignored.orig
232 232 This is file localignored4
233 233 $ rm localignored.orig
234 234
235 235 $ cat b.orig
236 236 This is file b2
237 237 $ hg up --clean 2
238 238 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
239 239 $ mv b.orig b
240 240
241 241 this merge of b should work
242 242 $ cat b
243 243 This is file b2
244 244 $ hg merge -f 1
245 245 merging b
246 246 merging for b
247 247 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
248 248 (branch merge, don't forget to commit)
249 249 $ hg diff --nodates
250 250 diff -r 49035e18a8e6 b
251 251 --- /dev/null
252 252 +++ b/b
253 253 @@ -0,0 +1,1 @@
254 254 +This is file b2
255 255 $ hg status
256 256 M b
257 257 $ cd ..; rm -r t
258 258
259 259 $ hg init t
260 260 $ cd t
261 261 $ echo This is file a1 > a
262 262 $ hg add a
263 263 $ hg commit -m "commit #0"
264 264 $ echo This is file b1 > b
265 265 $ hg add b
266 266 $ hg commit -m "commit #1"
267 267 $ echo This is file b22 > b
268 268 $ hg commit -m "commit #2"
269 269 $ hg update 1
270 270 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
271 271 $ echo This is file c1 > c
272 272 $ hg add c
273 273 $ hg commit -m "commit #3"
274 274 created new head
275 275
276 276 Contents of b should be "this is file b1"
277 277 $ cat b
278 278 This is file b1
279 279
280 280 $ echo This is file b22 > b
281 281 merge fails
282 282 $ hg merge 2
283 283 abort: uncommitted changes
284 284 (use 'hg status' to list changes)
285 285 [255]
286 286 merge expected!
287 287 $ hg merge -f 2
288 288 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
289 289 (branch merge, don't forget to commit)
290 290 $ hg diff --nodates
291 291 diff -r 85de557015a8 b
292 292 --- a/b
293 293 +++ b/b
294 294 @@ -1,1 +1,1 @@
295 295 -This is file b1
296 296 +This is file b22
297 297 $ hg status
298 298 M b
299 299 $ cd ..; rm -r t
300 300
301 301 $ hg init t
302 302 $ cd t
303 303 $ echo This is file a1 > a
304 304 $ hg add a
305 305 $ hg commit -m "commit #0"
306 306 $ echo This is file b1 > b
307 307 $ hg add b
308 308 $ hg commit -m "commit #1"
309 309 $ echo This is file b22 > b
310 310 $ hg commit -m "commit #2"
311 311 $ hg update 1
312 312 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
313 313 $ echo This is file c1 > c
314 314 $ hg add c
315 315 $ hg commit -m "commit #3"
316 316 created new head
317 317 $ echo This is file b33 > b
318 318 merge of b should fail
319 319 $ hg merge 2
320 320 abort: uncommitted changes
321 321 (use 'hg status' to list changes)
322 322 [255]
323 323 merge of b expected
324 324 $ hg merge -f 2
325 325 merging b
326 326 merging for b
327 327 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
328 328 (branch merge, don't forget to commit)
329 329 $ hg diff --nodates
330 330 diff -r 85de557015a8 b
331 331 --- a/b
332 332 +++ b/b
333 333 @@ -1,1 +1,1 @@
334 334 -This is file b1
335 335 +This is file b33
336 336 $ hg status
337 337 M b
338 338
339 339 Test for issue2364
340 340
341 341 $ hg up -qC .
342 342 $ hg rm b
343 343 $ hg ci -md
344 344 $ hg revert -r -2 b
345 345 $ hg up -q -- -2
346 346
347 347 Test that updated files are treated as "modified", when
348 348 'merge.update()' is aborted before 'merge.recordupdates()' (= parents
349 349 aren't changed), even if none of mode, size and timestamp of them
350 350 isn't changed on the filesystem (see also issue4583).
351 351
352 352 $ cat > $TESTTMP/abort.py <<EOF
353 353 > from __future__ import absolute_import
354 354 > # emulate aborting before "recordupdates()". in this case, files
355 355 > # are changed without updating dirstate
356 356 > from mercurial import (
357 357 > error,
358 358 > extensions,
359 359 > merge,
360 360 > )
361 361 > def applyupdates(orig, *args, **kwargs):
362 362 > orig(*args, **kwargs)
363 363 > raise error.Abort(b'intentional aborting')
364 364 > def extsetup(ui):
365 365 > extensions.wrapfunction(merge, "applyupdates", applyupdates)
366 366 > EOF
367 367
368 368 $ cat >> .hg/hgrc <<EOF
369 369 > [fakedirstatewritetime]
370 370 > # emulate invoking dirstate.write() via repo.status()
371 371 > # at 2000-01-01 00:00
372 372 > fakenow = 200001010000
373 373 > EOF
374 374
375 375 (file gotten from other revision)
376 376
377 377 $ hg update -q -C 2
378 378 $ echo 'THIS IS FILE B5' > b
379 379 $ hg commit -m 'commit #5'
380 380
381 381 $ hg update -q -C 3
382 382 $ cat b
383 383 This is file b1
384 384 $ touch -t 200001010000 b
385 385 $ hg debugrebuildstate
386 386
387 387 $ cat >> .hg/hgrc <<EOF
388 388 > [extensions]
389 389 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
390 390 > abort = $TESTTMP/abort.py
391 391 > EOF
392 392 $ hg merge 5
393 393 abort: intentional aborting
394 394 [255]
395 395 $ cat >> .hg/hgrc <<EOF
396 396 > [extensions]
397 397 > fakedirstatewritetime = !
398 398 > abort = !
399 399 > EOF
400 400
401 401 $ cat b
402 402 THIS IS FILE B5
403 403 $ touch -t 200001010000 b
404 404 $ hg status -A b
405 405 M b
406 406
407 407 (file merged from other revision)
408 408
409 409 $ hg update -q -C 3
410 410 $ echo 'this is file b6' > b
411 411 $ hg commit -m 'commit #6'
412 412 created new head
413 413
414 414 $ cat b
415 415 this is file b6
416 416 $ touch -t 200001010000 b
417 417 $ hg debugrebuildstate
418 418
419 419 $ cat >> .hg/hgrc <<EOF
420 420 > [extensions]
421 421 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
422 422 > abort = $TESTTMP/abort.py
423 423 > EOF
424 424 $ hg merge --tool internal:other 5
425 425 abort: intentional aborting
426 426 [255]
427 427 $ cat >> .hg/hgrc <<EOF
428 428 > [extensions]
429 429 > fakedirstatewritetime = !
430 430 > abort = !
431 431 > EOF
432 432
433 433 $ cat b
434 434 THIS IS FILE B5
435 435 $ touch -t 200001010000 b
436 436 $ hg status -A b
437 437 M b
438 438
439 439 $ cd ..
@@ -1,36 +1,36 b''
1 1 $ hg init
2 2 $ echo This is file a1 > a
3 3 $ echo This is file b1 > b
4 4 $ hg add a b
5 5 $ hg commit -m "commit #0"
6 6 $ echo This is file b22 > b
7 7 $ hg commit -m "comment #1"
8 8 $ hg update 0
9 9 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
10 10 $ rm b
11 11 $ hg commit -A -m "comment #2"
12 12 removing b
13 13 created new head
14 14 $ hg update 1
15 15 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
16 16 $ rm b
17 17 $ hg update -c 2
18 18 abort: uncommitted changes
19 [255]
19 [20]
20 20 $ hg revert b
21 21 $ hg update -c 2
22 22 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
23 23 $ mv a c
24 24
25 25 Should abort:
26 26
27 27 $ hg update 1
28 28 abort: uncommitted changes
29 29 (commit or update --clean to discard changes)
30 30 [255]
31 31 $ mv c a
32 32
33 33 Should succeed:
34 34
35 35 $ hg update 1
36 36 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -1,1622 +1,1622 b''
1 1 $ checkundo()
2 2 > {
3 3 > if [ -f .hg/store/undo ]; then
4 4 > echo ".hg/store/undo still exists after $1"
5 5 > fi
6 6 > }
7 7
8 8 $ cat <<EOF >> $HGRCPATH
9 9 > [extensions]
10 10 > mq =
11 11 > [mq]
12 12 > plain = true
13 13 > EOF
14 14
15 15
16 16 help
17 17
18 18 $ hg help mq
19 19 mq extension - manage a stack of patches
20 20
21 21 This extension lets you work with a stack of patches in a Mercurial
22 22 repository. It manages two stacks of patches - all known patches, and applied
23 23 patches (subset of known patches).
24 24
25 25 Known patches are represented as patch files in the .hg/patches directory.
26 26 Applied patches are both patch files and changesets.
27 27
28 28 Common tasks (use 'hg help COMMAND' for more details):
29 29
30 30 create new patch qnew
31 31 import existing patch qimport
32 32
33 33 print patch series qseries
34 34 print applied patches qapplied
35 35
36 36 add known patch to applied stack qpush
37 37 remove patch from applied stack qpop
38 38 refresh contents of top applied patch qrefresh
39 39
40 40 By default, mq will automatically use git patches when required to avoid
41 41 losing file mode changes, copy records, binary files or empty files creations
42 42 or deletions. This behavior can be configured with:
43 43
44 44 [mq]
45 45 git = auto/keep/yes/no
46 46
47 47 If set to 'keep', mq will obey the [diff] section configuration while
48 48 preserving existing git patches upon qrefresh. If set to 'yes' or 'no', mq
49 49 will override the [diff] section and always generate git or regular patches,
50 50 possibly losing data in the second case.
51 51
52 52 It may be desirable for mq changesets to be kept in the secret phase (see 'hg
53 53 help phases'), which can be enabled with the following setting:
54 54
55 55 [mq]
56 56 secret = True
57 57
58 58 You will by default be managing a patch queue named "patches". You can create
59 59 other, independent patch queues with the 'hg qqueue' command.
60 60
61 61 If the working directory contains uncommitted files, qpush, qpop and qgoto
62 62 abort immediately. If -f/--force is used, the changes are discarded. Setting:
63 63
64 64 [mq]
65 65 keepchanges = True
66 66
67 67 make them behave as if --keep-changes were passed, and non-conflicting local
68 68 changes will be tolerated and preserved. If incompatible options such as
69 69 -f/--force or --exact are passed, this setting is ignored.
70 70
71 71 This extension used to provide a strip command. This command now lives in the
72 72 strip extension.
73 73
74 74 list of commands:
75 75
76 76 Repository creation:
77 77
78 78 qclone clone main and patch repository at same time
79 79
80 80 Change creation:
81 81
82 82 qnew create a new patch
83 83 qrefresh update the current patch
84 84
85 85 Change manipulation:
86 86
87 87 qfold fold the named patches into the current patch
88 88
89 89 Change organization:
90 90
91 91 qapplied print the patches already applied
92 92 qdelete remove patches from queue
93 93 qfinish move applied patches into repository history
94 94 qgoto push or pop patches until named patch is at top of stack
95 95 qguard set or print guards for a patch
96 96 qheader print the header of the topmost or specified patch
97 97 qnext print the name of the next pushable patch
98 98 qpop pop the current patch off the stack
99 99 qprev print the name of the preceding applied patch
100 100 qpush push the next patch onto the stack
101 101 qqueue manage multiple patch queues
102 102 qrename rename a patch
103 103 qselect set or print guarded patches to push
104 104 qseries print the entire series file
105 105 qtop print the name of the current patch
106 106 qunapplied print the patches not yet applied
107 107
108 108 File content management:
109 109
110 110 qdiff diff of the current patch and subsequent modifications
111 111
112 112 Change import/export:
113 113
114 114 qimport import a patch or existing changeset
115 115
116 116 (use 'hg help -v mq' to show built-in aliases and global options)
117 117
118 118 $ hg init a
119 119 $ cd a
120 120 $ echo a > a
121 121 $ hg ci -Ama
122 122 adding a
123 123
124 124 $ hg clone . ../k
125 125 updating to branch default
126 126 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
127 127
128 128 $ mkdir b
129 129 $ echo z > b/z
130 130 $ hg ci -Ama
131 131 adding b/z
132 132
133 133
134 134 qinit
135 135
136 136 $ hg qinit
137 137
138 138 $ cd ..
139 139 $ hg init b
140 140
141 141
142 142 -R qinit
143 143
144 144 $ hg -R b qinit
145 145
146 146 $ hg init c
147 147
148 148
149 149 qinit -c
150 150
151 151 $ hg --cwd c qinit -c
152 152 $ hg -R c/.hg/patches st
153 153 A .hgignore
154 154 A series
155 155
156 156
157 157 qinit; qinit -c
158 158
159 159 $ hg init d
160 160 $ cd d
161 161 $ hg qinit
162 162 $ hg qinit -c
163 163
164 164 qinit -c should create both files if they don't exist
165 165
166 166 $ cat .hg/patches/.hgignore
167 167 ^\.hg
168 168 ^\.mq
169 169 syntax: glob
170 170 status
171 171 guards
172 172 $ cat .hg/patches/series
173 173 $ hg qinit -c
174 174 abort: repository $TESTTMP/d/.hg/patches already exists!
175 175 [255]
176 176 $ cd ..
177 177
178 178 $ echo '% qinit; <stuff>; qinit -c'
179 179 % qinit; <stuff>; qinit -c
180 180 $ hg init e
181 181 $ cd e
182 182 $ hg qnew A
183 183 $ checkundo qnew
184 184 $ echo foo > foo
185 185 $ hg phase -r qbase
186 186 0: draft
187 187 $ hg add foo
188 188 $ hg qrefresh
189 189 $ hg phase -r qbase
190 190 0: draft
191 191 $ hg qnew B
192 192 $ echo >> foo
193 193 $ hg qrefresh
194 194 $ echo status >> .hg/patches/.hgignore
195 195 $ echo bleh >> .hg/patches/.hgignore
196 196 $ hg qinit -c
197 197 adding .hg/patches/A
198 198 adding .hg/patches/B
199 199 $ hg -R .hg/patches status
200 200 A .hgignore
201 201 A A
202 202 A B
203 203 A series
204 204
205 205 qinit -c shouldn't touch these files if they already exist
206 206
207 207 $ cat .hg/patches/.hgignore
208 208 status
209 209 bleh
210 210 $ cat .hg/patches/series
211 211 A
212 212 B
213 213
214 214 add an untracked file
215 215
216 216 $ echo >> .hg/patches/flaf
217 217
218 218 status --mq with color (issue2096)
219 219
220 220 $ hg status --mq --config extensions.color= --config color.mode=ansi --color=always
221 221 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1m.hgignore\x1b[0m (esc)
222 222 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mA\x1b[0m (esc)
223 223 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mB\x1b[0m (esc)
224 224 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mseries\x1b[0m (esc)
225 225 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mflaf\x1b[0m (esc)
226 226
227 227 try the --mq option on a command provided by an extension
228 228
229 229 $ hg purge --mq --verbose --config extensions.purge=
230 230 removing file flaf
231 231
232 232 $ cd ..
233 233
234 234 #if no-outer-repo
235 235
236 236 init --mq without repo
237 237
238 238 $ mkdir f
239 239 $ cd f
240 240 $ hg init --mq
241 241 abort: there is no Mercurial repository here (.hg not found)
242 242 [255]
243 243 $ cd ..
244 244
245 245 #endif
246 246
247 247 init --mq with repo path
248 248
249 249 $ hg init g
250 250 $ hg init --mq g
251 251 $ test -d g/.hg/patches/.hg
252 252
253 253 init --mq with nonexistent directory
254 254
255 255 $ hg init --mq nonexistentdir
256 256 abort: repository nonexistentdir not found!
257 257 [255]
258 258
259 259
260 260 init --mq with bundle (non "local")
261 261
262 262 $ hg -R a bundle --all a.bundle >/dev/null
263 263 $ hg init --mq a.bundle
264 264 abort: only a local queue repository may be initialized
265 265 [255]
266 266
267 267 $ cd a
268 268
269 269 $ hg qnew -m 'foo bar' test.patch
270 270
271 271 $ echo '# comment' > .hg/patches/series.tmp
272 272 $ echo >> .hg/patches/series.tmp # empty line
273 273 $ cat .hg/patches/series >> .hg/patches/series.tmp
274 274 $ mv .hg/patches/series.tmp .hg/patches/series
275 275
276 276
277 277 qrefresh
278 278
279 279 $ echo a >> a
280 280 $ hg qrefresh
281 281 $ cat .hg/patches/test.patch
282 282 foo bar
283 283
284 284 diff -r [a-f0-9]* a (re)
285 285 --- a/a\t(?P<date>.*) (re)
286 286 \+\+\+ b/a\t(?P<date2>.*) (re)
287 287 @@ -1,1 +1,2 @@
288 288 a
289 289 +a
290 290
291 291 empty qrefresh
292 292
293 293 $ hg qrefresh -X a
294 294
295 295 revision:
296 296
297 297 $ hg diff -r -2 -r -1
298 298
299 299 patch:
300 300
301 301 $ cat .hg/patches/test.patch
302 302 foo bar
303 303
304 304
305 305 working dir diff:
306 306
307 307 $ hg diff --nodates -q
308 308 diff -r dde259bd5934 a
309 309 --- a/a
310 310 +++ b/a
311 311 @@ -1,1 +1,2 @@
312 312 a
313 313 +a
314 314
315 315 restore things
316 316
317 317 $ hg qrefresh
318 318 $ checkundo qrefresh
319 319
320 320
321 321 qpop
322 322
323 323 $ hg qpop
324 324 popping test.patch
325 325 patch queue now empty
326 326 $ checkundo qpop
327 327
328 328
329 329 qpush with dump of tag cache
330 330 Dump the tag cache to ensure that it has exactly one head after qpush.
331 331
332 332 $ rm -f .hg/cache/tags2-visible
333 333 $ hg tags > /dev/null
334 334
335 335 .hg/cache/tags2-visible (pre qpush):
336 336
337 337 $ cat .hg/cache/tags2-visible
338 338 1 [\da-f]{40} (re)
339 339 $ hg qpush
340 340 applying test.patch
341 341 now at: test.patch
342 342 $ hg phase -r qbase
343 343 2: draft
344 344 $ hg tags > /dev/null
345 345
346 346 .hg/cache/tags2-visible (post qpush):
347 347
348 348 $ cat .hg/cache/tags2-visible
349 349 2 [\da-f]{40} (re)
350 350 $ checkundo qpush
351 351 $ cd ..
352 352
353 353
354 354 pop/push outside repo
355 355 $ hg -R a qpop
356 356 popping test.patch
357 357 patch queue now empty
358 358 $ hg -R a qpush
359 359 applying test.patch
360 360 now at: test.patch
361 361
362 362 $ cd a
363 363 $ hg qnew test2.patch
364 364
365 365 qrefresh in subdir
366 366
367 367 $ cd b
368 368 $ echo a > a
369 369 $ hg add a
370 370 $ hg qrefresh
371 371
372 372 pop/push -a in subdir
373 373
374 374 $ hg qpop -a
375 375 popping test2.patch
376 376 popping test.patch
377 377 patch queue now empty
378 378 $ hg --traceback qpush -a
379 379 applying test.patch
380 380 applying test2.patch
381 381 now at: test2.patch
382 382
383 383
384 384 setting columns & formatted tests truncating (issue1912)
385 385
386 386 $ COLUMNS=4 hg qseries --config ui.formatted=true --color=no
387 387 test.patch
388 388 test2.patch
389 389 $ COLUMNS=20 hg qseries --config ui.formatted=true -vs --color=no
390 390 0 A test.patch: f...
391 391 1 A test2.patch:
392 392 $ hg qpop
393 393 popping test2.patch
394 394 now at: test.patch
395 395 $ hg qseries -vs
396 396 0 A test.patch: foo bar
397 397 1 U test2.patch:
398 398 $ hg sum | grep mq
399 399 mq: 1 applied, 1 unapplied
400 400 $ hg qpush
401 401 applying test2.patch
402 402 now at: test2.patch
403 403 $ hg sum | grep mq
404 404 mq: 2 applied
405 405 $ hg qapplied
406 406 test.patch
407 407 test2.patch
408 408 $ hg qtop
409 409 test2.patch
410 410
411 411
412 412 prev
413 413
414 414 $ hg qapp -1
415 415 test.patch
416 416
417 417 next
418 418
419 419 $ hg qunapp -1
420 420 all patches applied
421 421 [1]
422 422
423 423 $ hg qpop
424 424 popping test2.patch
425 425 now at: test.patch
426 426
427 427 commit should fail
428 428
429 429 $ hg commit
430 430 abort: cannot commit over an applied mq patch
431 431 [255]
432 432
433 433 push should fail if draft
434 434
435 435 $ hg push ../../k
436 436 pushing to ../../k
437 437 abort: source has mq patches applied
438 438 [255]
439 439
440 440
441 441 import should fail
442 442
443 443 $ hg st .
444 444 $ echo foo >> ../a
445 445 $ hg diff > ../../import.diff
446 446 $ hg revert --no-backup ../a
447 447 $ hg import ../../import.diff
448 448 abort: cannot import over an applied patch
449 449 [255]
450 450 $ hg st
451 451
452 452 import --no-commit should succeed
453 453
454 454 $ hg import --no-commit ../../import.diff
455 455 applying ../../import.diff
456 456 $ hg st
457 457 M a
458 458 $ hg revert --no-backup ../a
459 459
460 460
461 461 qunapplied
462 462
463 463 $ hg qunapplied
464 464 test2.patch
465 465
466 466
467 467 qpush/qpop with index
468 468
469 469 $ hg qnew test1b.patch
470 470 $ echo 1b > 1b
471 471 $ hg add 1b
472 472 $ hg qrefresh
473 473 $ hg qpush 2
474 474 applying test2.patch
475 475 now at: test2.patch
476 476 $ hg qpop 0
477 477 popping test2.patch
478 478 popping test1b.patch
479 479 now at: test.patch
480 480 $ hg qpush test.patch+1
481 481 applying test1b.patch
482 482 now at: test1b.patch
483 483 $ hg qpush test.patch+2
484 484 applying test2.patch
485 485 now at: test2.patch
486 486 $ hg qpop test2.patch-1
487 487 popping test2.patch
488 488 now at: test1b.patch
489 489 $ hg qpop test2.patch-2
490 490 popping test1b.patch
491 491 now at: test.patch
492 492 $ hg qpush test1b.patch+1
493 493 applying test1b.patch
494 494 applying test2.patch
495 495 now at: test2.patch
496 496
497 497
498 498 qpush --move
499 499
500 500 $ hg qpop -a
501 501 popping test2.patch
502 502 popping test1b.patch
503 503 popping test.patch
504 504 patch queue now empty
505 505 $ hg qguard test1b.patch -- -negguard
506 506 $ hg qguard test2.patch -- +posguard
507 507 $ hg qpush --move test2.patch # can't move guarded patch
508 508 cannot push 'test2.patch' - guarded by '+posguard'
509 509 [1]
510 510 $ hg qselect posguard
511 511 number of unguarded, unapplied patches has changed from 2 to 3
512 512 $ hg qpush --move test2.patch # move to front
513 513 applying test2.patch
514 514 now at: test2.patch
515 515 $ hg qpush --move test1b.patch # negative guard unselected
516 516 applying test1b.patch
517 517 now at: test1b.patch
518 518 $ hg qpush --move test.patch # noop move
519 519 applying test.patch
520 520 now at: test.patch
521 521 $ hg qseries -v
522 522 0 A test2.patch
523 523 1 A test1b.patch
524 524 2 A test.patch
525 525 $ hg qpop -a
526 526 popping test.patch
527 527 popping test1b.patch
528 528 popping test2.patch
529 529 patch queue now empty
530 530
531 531 cleaning up
532 532
533 533 $ hg qselect --none
534 534 guards deactivated
535 535 number of unguarded, unapplied patches has changed from 3 to 2
536 536 $ hg qguard --none test1b.patch
537 537 $ hg qguard --none test2.patch
538 538 $ hg qpush --move test.patch
539 539 applying test.patch
540 540 now at: test.patch
541 541 $ hg qpush --move test1b.patch
542 542 applying test1b.patch
543 543 now at: test1b.patch
544 544 $ hg qpush --move bogus # nonexistent patch
545 545 abort: patch bogus not in series
546 546 [255]
547 547 $ hg qpush --move # no patch
548 548 abort: please specify the patch to move
549 549 [255]
550 550 $ hg qpush --move test.patch # already applied
551 551 abort: cannot push to a previous patch: test.patch
552 552 [255]
553 553 $ sed '2i\
554 554 > # make qtip index different in series and fullseries
555 555 > ' `hg root`/.hg/patches/series > $TESTTMP/sedtmp
556 556 $ cp $TESTTMP/sedtmp `hg root`/.hg/patches/series
557 557 $ cat `hg root`/.hg/patches/series
558 558 # comment
559 559 # make qtip index different in series and fullseries
560 560
561 561 test.patch
562 562 test1b.patch
563 563 test2.patch
564 564 $ hg qpush --move test2.patch
565 565 applying test2.patch
566 566 now at: test2.patch
567 567
568 568
569 569 series after move
570 570
571 571 $ cat `hg root`/.hg/patches/series
572 572 # comment
573 573 # make qtip index different in series and fullseries
574 574
575 575 test.patch
576 576 test1b.patch
577 577 test2.patch
578 578
579 579
580 580 pop, qapplied, qunapplied
581 581
582 582 $ hg qseries -v
583 583 0 A test.patch
584 584 1 A test1b.patch
585 585 2 A test2.patch
586 586
587 587 qapplied -1 test.patch
588 588
589 589 $ hg qapplied -1 test.patch
590 590 only one patch applied
591 591 [1]
592 592
593 593 qapplied -1 test1b.patch
594 594
595 595 $ hg qapplied -1 test1b.patch
596 596 test.patch
597 597
598 598 qapplied -1 test2.patch
599 599
600 600 $ hg qapplied -1 test2.patch
601 601 test1b.patch
602 602
603 603 qapplied -1
604 604
605 605 $ hg qapplied -1
606 606 test1b.patch
607 607
608 608 qapplied
609 609
610 610 $ hg qapplied
611 611 test.patch
612 612 test1b.patch
613 613 test2.patch
614 614
615 615 qapplied test1b.patch
616 616
617 617 $ hg qapplied test1b.patch
618 618 test.patch
619 619 test1b.patch
620 620
621 621 qunapplied -1
622 622
623 623 $ hg qunapplied -1
624 624 all patches applied
625 625 [1]
626 626
627 627 qunapplied
628 628
629 629 $ hg qunapplied
630 630
631 631 popping
632 632
633 633 $ hg qpop
634 634 popping test2.patch
635 635 now at: test1b.patch
636 636
637 637 qunapplied -1
638 638
639 639 $ hg qunapplied -1
640 640 test2.patch
641 641
642 642 qunapplied
643 643
644 644 $ hg qunapplied
645 645 test2.patch
646 646
647 647 qunapplied test2.patch
648 648
649 649 $ hg qunapplied test2.patch
650 650
651 651 qunapplied -1 test2.patch
652 652
653 653 $ hg qunapplied -1 test2.patch
654 654 all patches applied
655 655 [1]
656 656
657 657 popping -a
658 658
659 659 $ hg qpop -a
660 660 popping test1b.patch
661 661 popping test.patch
662 662 patch queue now empty
663 663
664 664 qapplied
665 665
666 666 $ hg qapplied
667 667
668 668 qapplied -1
669 669
670 670 $ hg qapplied -1
671 671 no patches applied
672 672 [1]
673 673 $ hg qpush
674 674 applying test.patch
675 675 now at: test.patch
676 676
677 677
678 678 push should succeed
679 679
680 680 $ hg qpop -a
681 681 popping test.patch
682 682 patch queue now empty
683 683 $ hg push ../../k
684 684 pushing to ../../k
685 685 searching for changes
686 686 adding changesets
687 687 adding manifests
688 688 adding file changes
689 689 added 1 changesets with 1 changes to 1 files
690 690
691 691
692 692 we want to start with some patches applied
693 693
694 694 $ hg qpush -a
695 695 applying test.patch
696 696 applying test1b.patch
697 697 applying test2.patch
698 698 now at: test2.patch
699 699
700 700 % pops all patches and succeeds
701 701
702 702 $ hg qpop -a
703 703 popping test2.patch
704 704 popping test1b.patch
705 705 popping test.patch
706 706 patch queue now empty
707 707
708 708 % does nothing and succeeds
709 709
710 710 $ hg qpop -a
711 711 no patches applied
712 712
713 713 % fails - nothing else to pop
714 714
715 715 $ hg qpop
716 716 no patches applied
717 717 [1]
718 718
719 719 % pushes a patch and succeeds
720 720
721 721 $ hg qpush
722 722 applying test.patch
723 723 now at: test.patch
724 724
725 725 % pops a patch and succeeds
726 726
727 727 $ hg qpop
728 728 popping test.patch
729 729 patch queue now empty
730 730
731 731 % pushes up to test1b.patch and succeeds
732 732
733 733 $ hg qpush test1b.patch
734 734 applying test.patch
735 735 applying test1b.patch
736 736 now at: test1b.patch
737 737
738 738 % does nothing and succeeds
739 739
740 740 $ hg qpush test1b.patch
741 741 qpush: test1b.patch is already at the top
742 742
743 743 % does nothing and succeeds
744 744
745 745 $ hg qpop test1b.patch
746 746 qpop: test1b.patch is already at the top
747 747
748 748 % fails - can't push to this patch
749 749
750 750 $ hg qpush test.patch
751 751 abort: cannot push to a previous patch: test.patch
752 752 [255]
753 753
754 754 % fails - can't pop to this patch
755 755
756 756 $ hg qpop test2.patch
757 757 abort: patch test2.patch is not applied
758 758 [255]
759 759
760 760 % pops up to test.patch and succeeds
761 761
762 762 $ hg qpop test.patch
763 763 popping test1b.patch
764 764 now at: test.patch
765 765
766 766 % pushes all patches and succeeds
767 767
768 768 $ hg qpush -a
769 769 applying test1b.patch
770 770 applying test2.patch
771 771 now at: test2.patch
772 772
773 773 % does nothing and succeeds
774 774
775 775 $ hg qpush -a
776 776 all patches are currently applied
777 777
778 778 % fails - nothing else to push
779 779
780 780 $ hg qpush
781 781 patch series already fully applied
782 782 [1]
783 783
784 784 % does nothing and succeeds
785 785
786 786 $ hg qpush test2.patch
787 787 qpush: test2.patch is already at the top
788 788
789 789 strip
790 790
791 791 $ cd ../../b
792 792 $ echo x>x
793 793 $ hg ci -Ama
794 794 adding x
795 795 $ hg strip tip
796 796 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
797 797 saved backup bundle to $TESTTMP/b/.hg/strip-backup/*-backup.hg (glob)
798 798 $ hg unbundle .hg/strip-backup/*
799 799 adding changesets
800 800 adding manifests
801 801 adding file changes
802 802 added 1 changesets with 1 changes to 1 files
803 803 new changesets 770eb8fce608 (1 drafts)
804 804 (run 'hg update' to get a working copy)
805 805
806 806
807 807 strip with local changes, should complain
808 808
809 809 $ hg up
810 810 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
811 811 $ echo y>y
812 812 $ hg add y
813 813 $ hg strip tip
814 814 abort: uncommitted changes
815 [255]
815 [20]
816 816
817 817 --force strip with local changes
818 818
819 819 $ hg strip -f tip
820 820 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
821 821 saved backup bundle to $TESTTMP/b/.hg/strip-backup/770eb8fce608-0ddcae0f-backup.hg
822 822 $ cd ..
823 823
824 824
825 825 cd b; hg qrefresh
826 826
827 827 $ hg init refresh
828 828 $ cd refresh
829 829 $ echo a > a
830 830 $ hg ci -Ama
831 831 adding a
832 832 $ hg qnew -mfoo foo
833 833 $ echo a >> a
834 834 $ hg qrefresh
835 835 $ mkdir b
836 836 $ cd b
837 837 $ echo f > f
838 838 $ hg add f
839 839 $ hg qrefresh
840 840 $ cat ../.hg/patches/foo
841 841 foo
842 842
843 843 diff -r cb9a9f314b8b a
844 844 --- a/a\t(?P<date>.*) (re)
845 845 \+\+\+ b/a\t(?P<date>.*) (re)
846 846 @@ -1,1 +1,2 @@
847 847 a
848 848 +a
849 849 diff -r cb9a9f314b8b b/f
850 850 --- /dev/null\t(?P<date>.*) (re)
851 851 \+\+\+ b/b/f\t(?P<date>.*) (re)
852 852 @@ -0,0 +1,1 @@
853 853 +f
854 854
855 855 hg qrefresh .
856 856
857 857 $ hg qrefresh .
858 858 $ cat ../.hg/patches/foo
859 859 foo
860 860
861 861 diff -r cb9a9f314b8b b/f
862 862 --- /dev/null\t(?P<date>.*) (re)
863 863 \+\+\+ b/b/f\t(?P<date>.*) (re)
864 864 @@ -0,0 +1,1 @@
865 865 +f
866 866 $ hg status
867 867 M a
868 868
869 869
870 870 qpush failure
871 871
872 872 $ cd ..
873 873 $ hg qrefresh
874 874 $ hg qnew -mbar bar
875 875 $ echo foo > foo
876 876 $ echo bar > bar
877 877 $ hg add foo bar
878 878 $ hg qrefresh
879 879 $ hg qpop -a
880 880 popping bar
881 881 popping foo
882 882 patch queue now empty
883 883 $ echo bar > foo
884 884 $ hg qpush -a
885 885 applying foo
886 886 applying bar
887 887 file foo already exists
888 888 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
889 889 patch failed, unable to continue (try -v)
890 890 patch failed, rejects left in working directory
891 891 errors during apply, please fix and qrefresh bar
892 892 [2]
893 893 $ hg st
894 894 ? foo
895 895 ? foo.rej
896 896
897 897
898 898 mq tags
899 899
900 900 $ hg log --template '{rev} {tags}\n' -r qparent:qtip
901 901 0 qparent
902 902 1 foo qbase
903 903 2 bar qtip tip
904 904
905 905 mq revset
906 906
907 907 $ hg log -r 'mq()' --template '{rev}\n'
908 908 1
909 909 2
910 910 $ hg help revisions.mq
911 911 "mq()"
912 912 Changesets managed by MQ.
913 913
914 914
915 915 bad node in status
916 916
917 917 $ hg qpop
918 918 popping bar
919 919 now at: foo
920 920 $ hg strip -qn tip
921 921 $ hg tip
922 922 changeset: 0:cb9a9f314b8b
923 923 tag: tip
924 924 user: test
925 925 date: Thu Jan 01 00:00:00 1970 +0000
926 926 summary: a
927 927
928 928 $ hg branches
929 929 default 0:cb9a9f314b8b
930 930 $ hg qpop
931 931 no patches applied
932 932 [1]
933 933
934 934 $ cd ..
935 935
936 936
937 937 git patches
938 938
939 939 $ cat >>$HGRCPATH <<EOF
940 940 > [diff]
941 941 > git = True
942 942 > EOF
943 943 $ hg init git
944 944 $ cd git
945 945 $ hg qinit
946 946
947 947 $ hg qnew -m'new file' new
948 948 $ echo foo > new
949 949 #if execbit
950 950 $ chmod +x new
951 951 #endif
952 952 $ hg add new
953 953 $ hg qrefresh
954 954
955 955 $ cat .hg/patches/new
956 956 new file
957 957
958 958 diff --git a/new b/new
959 959 new file mode 100755 (execbit !)
960 960 new file mode 100644 (no-execbit !)
961 961 --- /dev/null
962 962 +++ b/new
963 963 @@ -0,0 +1,1 @@
964 964 +foo
965 965
966 966 $ hg qnew -m'copy file' copy
967 967 $ hg cp new copy
968 968 $ hg qrefresh
969 969 $ cat .hg/patches/copy
970 970 copy file
971 971
972 972 diff --git a/new b/copy
973 973 copy from new
974 974 copy to copy
975 975
976 976 $ hg qpop
977 977 popping copy
978 978 now at: new
979 979 $ hg qpush
980 980 applying copy
981 981 now at: copy
982 982 $ hg qdiff
983 983 diff --git a/new b/copy
984 984 copy from new
985 985 copy to copy
986 986 $ cat >>$HGRCPATH <<EOF
987 987 > [diff]
988 988 > git = False
989 989 > EOF
990 990 $ hg qdiff --git
991 991 diff --git a/new b/copy
992 992 copy from new
993 993 copy to copy
994 994 $ cd ..
995 995
996 996 empty lines in status
997 997
998 998 $ hg init emptystatus
999 999 $ cd emptystatus
1000 1000 $ hg qinit
1001 1001 $ printf '\n\n' > .hg/patches/status
1002 1002 $ hg qser
1003 1003 $ cd ..
1004 1004
1005 1005 bad line in status (without ":")
1006 1006
1007 1007 $ hg init badstatus
1008 1008 $ cd badstatus
1009 1009 $ hg qinit
1010 1010 $ printf 'babar has no colon in this line\n' > .hg/patches/status
1011 1011 $ hg qser
1012 1012 malformated mq status line: ['babar has no colon in this line']
1013 1013 $ cd ..
1014 1014
1015 1015
1016 1016 test file addition in slow path
1017 1017
1018 1018 $ hg init slow
1019 1019 $ cd slow
1020 1020 $ hg qinit
1021 1021 $ echo foo > foo
1022 1022 $ hg add foo
1023 1023 $ hg ci -m 'add foo'
1024 1024 $ hg qnew bar
1025 1025 $ echo bar > bar
1026 1026 $ hg add bar
1027 1027 $ hg mv foo baz
1028 1028 $ hg qrefresh --git
1029 1029 $ hg up -C 0
1030 1030 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
1031 1031 $ echo >> foo
1032 1032 $ hg ci -m 'change foo'
1033 1033 created new head
1034 1034 $ hg up -C 1
1035 1035 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1036 1036 $ hg qrefresh --git
1037 1037 $ cat .hg/patches/bar
1038 1038 diff --git a/bar b/bar
1039 1039 new file mode 100644
1040 1040 --- /dev/null
1041 1041 +++ b/bar
1042 1042 @@ -0,0 +1,1 @@
1043 1043 +bar
1044 1044 diff --git a/foo b/baz
1045 1045 rename from foo
1046 1046 rename to baz
1047 1047 $ hg log -v --template '{rev} {file_copies}\n' -r .
1048 1048 2 baz (foo)
1049 1049 $ hg qrefresh --git
1050 1050 $ cat .hg/patches/bar
1051 1051 diff --git a/bar b/bar
1052 1052 new file mode 100644
1053 1053 --- /dev/null
1054 1054 +++ b/bar
1055 1055 @@ -0,0 +1,1 @@
1056 1056 +bar
1057 1057 diff --git a/foo b/baz
1058 1058 rename from foo
1059 1059 rename to baz
1060 1060 $ hg log -v --template '{rev} {file_copies}\n' -r .
1061 1061 2 baz (foo)
1062 1062 $ hg qrefresh
1063 1063 $ grep 'diff --git' .hg/patches/bar
1064 1064 diff --git a/bar b/bar
1065 1065 diff --git a/foo b/baz
1066 1066
1067 1067
1068 1068 test file move chains in the slow path
1069 1069
1070 1070 $ hg up -C 1
1071 1071 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
1072 1072 $ echo >> foo
1073 1073 $ hg ci -m 'change foo again'
1074 1074 $ hg up -C 2
1075 1075 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1076 1076 $ hg mv bar quux
1077 1077 $ hg mv baz bleh
1078 1078 $ hg qrefresh --git
1079 1079 $ cat .hg/patches/bar
1080 1080 diff --git a/foo b/bleh
1081 1081 rename from foo
1082 1082 rename to bleh
1083 1083 diff --git a/quux b/quux
1084 1084 new file mode 100644
1085 1085 --- /dev/null
1086 1086 +++ b/quux
1087 1087 @@ -0,0 +1,1 @@
1088 1088 +bar
1089 1089 $ hg log -v --template '{rev} {file_copies}\n' -r .
1090 1090 3 bleh (foo)
1091 1091 $ hg mv quux fred
1092 1092 $ hg mv bleh barney
1093 1093 $ hg qrefresh --git
1094 1094 $ cat .hg/patches/bar
1095 1095 diff --git a/foo b/barney
1096 1096 rename from foo
1097 1097 rename to barney
1098 1098 diff --git a/fred b/fred
1099 1099 new file mode 100644
1100 1100 --- /dev/null
1101 1101 +++ b/fred
1102 1102 @@ -0,0 +1,1 @@
1103 1103 +bar
1104 1104 $ hg log -v --template '{rev} {file_copies}\n' -r .
1105 1105 3 barney (foo)
1106 1106
1107 1107
1108 1108 refresh omitting an added file
1109 1109
1110 1110 $ hg qnew baz
1111 1111 $ echo newfile > newfile
1112 1112 $ hg add newfile
1113 1113 $ hg qrefresh
1114 1114 $ hg st -A newfile
1115 1115 C newfile
1116 1116 $ hg qrefresh -X newfile
1117 1117 $ hg st -A newfile
1118 1118 A newfile
1119 1119 $ hg revert newfile
1120 1120 $ rm newfile
1121 1121 $ hg qpop
1122 1122 popping baz
1123 1123 now at: bar
1124 1124
1125 1125 test qdel/qrm
1126 1126
1127 1127 $ hg qdel baz
1128 1128 $ echo p >> .hg/patches/series
1129 1129 $ hg qrm p
1130 1130 $ hg qser
1131 1131 bar
1132 1132
1133 1133 create a git patch
1134 1134
1135 1135 $ echo a > alexander
1136 1136 $ hg add alexander
1137 1137 $ hg qnew -f --git addalexander
1138 1138 $ grep diff .hg/patches/addalexander
1139 1139 diff --git a/alexander b/alexander
1140 1140
1141 1141
1142 1142 create a git binary patch
1143 1143
1144 1144 $ cat > writebin.py <<EOF
1145 1145 > import sys
1146 1146 > path = sys.argv[1]
1147 1147 > open(path, 'wb').write(b'BIN\x00ARY')
1148 1148 > EOF
1149 1149 $ "$PYTHON" writebin.py bucephalus
1150 1150
1151 1151 $ "$PYTHON" "$TESTDIR/md5sum.py" bucephalus
1152 1152 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
1153 1153 $ hg add bucephalus
1154 1154 $ hg qnew -f --git addbucephalus
1155 1155 $ grep diff .hg/patches/addbucephalus
1156 1156 diff --git a/bucephalus b/bucephalus
1157 1157
1158 1158
1159 1159 check binary patches can be popped and pushed
1160 1160
1161 1161 $ hg qpop
1162 1162 popping addbucephalus
1163 1163 now at: addalexander
1164 1164 $ test -f bucephalus && echo % bucephalus should not be there
1165 1165 [1]
1166 1166 $ hg qpush
1167 1167 applying addbucephalus
1168 1168 now at: addbucephalus
1169 1169 $ test -f bucephalus
1170 1170 $ "$PYTHON" "$TESTDIR/md5sum.py" bucephalus
1171 1171 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
1172 1172
1173 1173
1174 1174
1175 1175 strip again
1176 1176
1177 1177 $ cd ..
1178 1178 $ hg init strip
1179 1179 $ cd strip
1180 1180 $ touch foo
1181 1181 $ hg add foo
1182 1182 $ hg ci -m 'add foo'
1183 1183 $ echo >> foo
1184 1184 $ hg ci -m 'change foo 1'
1185 1185 $ hg up -C 0
1186 1186 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1187 1187 $ echo 1 >> foo
1188 1188 $ hg ci -m 'change foo 2'
1189 1189 created new head
1190 1190 $ HGMERGE=true hg merge
1191 1191 merging foo
1192 1192 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
1193 1193 (branch merge, don't forget to commit)
1194 1194 $ hg ci -m merge
1195 1195 $ hg log
1196 1196 changeset: 3:99615015637b
1197 1197 tag: tip
1198 1198 parent: 2:20cbbe65cff7
1199 1199 parent: 1:d2871fc282d4
1200 1200 user: test
1201 1201 date: Thu Jan 01 00:00:00 1970 +0000
1202 1202 summary: merge
1203 1203
1204 1204 changeset: 2:20cbbe65cff7
1205 1205 parent: 0:53245c60e682
1206 1206 user: test
1207 1207 date: Thu Jan 01 00:00:00 1970 +0000
1208 1208 summary: change foo 2
1209 1209
1210 1210 changeset: 1:d2871fc282d4
1211 1211 user: test
1212 1212 date: Thu Jan 01 00:00:00 1970 +0000
1213 1213 summary: change foo 1
1214 1214
1215 1215 changeset: 0:53245c60e682
1216 1216 user: test
1217 1217 date: Thu Jan 01 00:00:00 1970 +0000
1218 1218 summary: add foo
1219 1219
1220 1220 $ hg strip 1
1221 1221 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1222 1222 saved backup bundle to $TESTTMP/strip/.hg/strip-backup/*-backup.hg (glob)
1223 1223 $ checkundo strip
1224 1224 $ hg log
1225 1225 changeset: 1:20cbbe65cff7
1226 1226 tag: tip
1227 1227 user: test
1228 1228 date: Thu Jan 01 00:00:00 1970 +0000
1229 1229 summary: change foo 2
1230 1230
1231 1231 changeset: 0:53245c60e682
1232 1232 user: test
1233 1233 date: Thu Jan 01 00:00:00 1970 +0000
1234 1234 summary: add foo
1235 1235
1236 1236 $ cd ..
1237 1237
1238 1238
1239 1239 qclone
1240 1240
1241 1241 $ qlog()
1242 1242 > {
1243 1243 > echo 'main repo:'
1244 1244 > hg log --template ' rev {rev}: {desc}\n'
1245 1245 > echo 'patch repo:'
1246 1246 > hg -R .hg/patches log --template ' rev {rev}: {desc}\n'
1247 1247 > }
1248 1248 $ hg init qclonesource
1249 1249 $ cd qclonesource
1250 1250 $ echo foo > foo
1251 1251 $ hg add foo
1252 1252 $ hg ci -m 'add foo'
1253 1253 $ hg qinit
1254 1254 $ hg qnew patch1
1255 1255 $ echo bar >> foo
1256 1256 $ hg qrefresh -m 'change foo'
1257 1257 $ cd ..
1258 1258
1259 1259
1260 1260 repo with unversioned patch dir
1261 1261
1262 1262 $ hg qclone qclonesource failure
1263 1263 abort: versioned patch repository not found (see init --mq)
1264 1264 [255]
1265 1265
1266 1266 $ cd qclonesource
1267 1267 $ hg qinit -c
1268 1268 adding .hg/patches/patch1
1269 1269 $ hg qci -m checkpoint
1270 1270 $ qlog
1271 1271 main repo:
1272 1272 rev 1: change foo
1273 1273 rev 0: add foo
1274 1274 patch repo:
1275 1275 rev 0: checkpoint
1276 1276 $ cd ..
1277 1277
1278 1278
1279 1279 repo with patches applied
1280 1280
1281 1281 $ hg qclone qclonesource qclonedest
1282 1282 updating to branch default
1283 1283 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1284 1284 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1285 1285 $ cd qclonedest
1286 1286 $ qlog
1287 1287 main repo:
1288 1288 rev 0: add foo
1289 1289 patch repo:
1290 1290 rev 0: checkpoint
1291 1291 $ cd ..
1292 1292
1293 1293
1294 1294 repo with patches unapplied
1295 1295
1296 1296 $ cd qclonesource
1297 1297 $ hg qpop -a
1298 1298 popping patch1
1299 1299 patch queue now empty
1300 1300 $ qlog
1301 1301 main repo:
1302 1302 rev 0: add foo
1303 1303 patch repo:
1304 1304 rev 0: checkpoint
1305 1305 $ cd ..
1306 1306 $ hg qclone qclonesource qclonedest2
1307 1307 updating to branch default
1308 1308 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1309 1309 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1310 1310 $ cd qclonedest2
1311 1311 $ qlog
1312 1312 main repo:
1313 1313 rev 0: add foo
1314 1314 patch repo:
1315 1315 rev 0: checkpoint
1316 1316 $ cd ..
1317 1317
1318 1318
1319 1319 Issue1033: test applying on an empty file
1320 1320
1321 1321 $ hg init empty
1322 1322 $ cd empty
1323 1323 $ touch a
1324 1324 $ hg ci -Am addempty
1325 1325 adding a
1326 1326 $ echo a > a
1327 1327 $ hg qnew -f -e changea
1328 1328 $ hg qpop
1329 1329 popping changea
1330 1330 patch queue now empty
1331 1331 $ hg qpush
1332 1332 applying changea
1333 1333 now at: changea
1334 1334 $ cd ..
1335 1335
1336 1336 test qpush with --force, issue1087
1337 1337
1338 1338 $ hg init forcepush
1339 1339 $ cd forcepush
1340 1340 $ echo hello > hello.txt
1341 1341 $ echo bye > bye.txt
1342 1342 $ hg ci -Ama
1343 1343 adding bye.txt
1344 1344 adding hello.txt
1345 1345 $ hg qnew -d '0 0' empty
1346 1346 $ hg qpop
1347 1347 popping empty
1348 1348 patch queue now empty
1349 1349 $ echo world >> hello.txt
1350 1350
1351 1351
1352 1352 qpush should fail, local changes
1353 1353
1354 1354 $ hg qpush
1355 1355 abort: local changes found
1356 1356 [255]
1357 1357
1358 1358
1359 1359 apply force, should not discard changes with empty patch
1360 1360
1361 1361 $ hg qpush -f
1362 1362 applying empty
1363 1363 patch empty is empty
1364 1364 now at: empty
1365 1365 $ hg diff --config diff.nodates=True
1366 1366 diff -r d58265112590 hello.txt
1367 1367 --- a/hello.txt
1368 1368 +++ b/hello.txt
1369 1369 @@ -1,1 +1,2 @@
1370 1370 hello
1371 1371 +world
1372 1372 $ hg qdiff --config diff.nodates=True
1373 1373 diff -r 9ecee4f634e3 hello.txt
1374 1374 --- a/hello.txt
1375 1375 +++ b/hello.txt
1376 1376 @@ -1,1 +1,2 @@
1377 1377 hello
1378 1378 +world
1379 1379 $ hg log -l1 -p
1380 1380 changeset: 1:d58265112590
1381 1381 tag: empty
1382 1382 tag: qbase
1383 1383 tag: qtip
1384 1384 tag: tip
1385 1385 user: test
1386 1386 date: Thu Jan 01 00:00:00 1970 +0000
1387 1387 summary: imported patch empty
1388 1388
1389 1389
1390 1390 $ hg qref -d '0 0'
1391 1391 $ hg qpop
1392 1392 popping empty
1393 1393 patch queue now empty
1394 1394 $ echo universe >> hello.txt
1395 1395 $ echo universe >> bye.txt
1396 1396
1397 1397
1398 1398 qpush should fail, local changes
1399 1399
1400 1400 $ hg qpush
1401 1401 abort: local changes found
1402 1402 [255]
1403 1403
1404 1404
1405 1405 apply force, should discard changes in hello, but not bye
1406 1406
1407 1407 $ hg qpush -f --verbose --config 'ui.origbackuppath=.hg/origbackups'
1408 1408 applying empty
1409 1409 creating directory: $TESTTMP/forcepush/.hg/origbackups
1410 1410 saving current version of hello.txt as .hg/origbackups/hello.txt
1411 1411 patching file hello.txt
1412 1412 committing files:
1413 1413 hello.txt
1414 1414 committing manifest
1415 1415 committing changelog
1416 1416 now at: empty
1417 1417 $ hg st
1418 1418 M bye.txt
1419 1419 $ hg diff --config diff.nodates=True
1420 1420 diff -r ba252371dbc1 bye.txt
1421 1421 --- a/bye.txt
1422 1422 +++ b/bye.txt
1423 1423 @@ -1,1 +1,2 @@
1424 1424 bye
1425 1425 +universe
1426 1426 $ hg qdiff --config diff.nodates=True
1427 1427 diff -r 9ecee4f634e3 bye.txt
1428 1428 --- a/bye.txt
1429 1429 +++ b/bye.txt
1430 1430 @@ -1,1 +1,2 @@
1431 1431 bye
1432 1432 +universe
1433 1433 diff -r 9ecee4f634e3 hello.txt
1434 1434 --- a/hello.txt
1435 1435 +++ b/hello.txt
1436 1436 @@ -1,1 +1,3 @@
1437 1437 hello
1438 1438 +world
1439 1439 +universe
1440 1440
1441 1441 test that the previous call to qpush with -f (--force) and --config actually put
1442 1442 the orig files out of the working copy
1443 1443 $ ls .hg/origbackups
1444 1444 hello.txt
1445 1445
1446 1446 test popping revisions not in working dir ancestry
1447 1447
1448 1448 $ hg qseries -v
1449 1449 0 A empty
1450 1450 $ hg up qparent
1451 1451 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1452 1452 $ hg qpop
1453 1453 popping empty
1454 1454 patch queue now empty
1455 1455
1456 1456 $ cd ..
1457 1457 $ hg init deletion-order
1458 1458 $ cd deletion-order
1459 1459
1460 1460 $ touch a
1461 1461 $ hg ci -Aqm0
1462 1462
1463 1463 $ hg qnew rename-dir
1464 1464 $ hg rm a
1465 1465 $ hg qrefresh
1466 1466
1467 1467 $ mkdir a b
1468 1468 $ touch a/a b/b
1469 1469 $ hg add -q a b
1470 1470 $ hg qrefresh
1471 1471
1472 1472
1473 1473 test popping must remove files added in subdirectories first
1474 1474
1475 1475 $ hg qpop
1476 1476 popping rename-dir
1477 1477 patch queue now empty
1478 1478 $ cd ..
1479 1479
1480 1480
1481 1481 test case preservation through patch pushing especially on case
1482 1482 insensitive filesystem
1483 1483
1484 1484 $ hg init casepreserve
1485 1485 $ cd casepreserve
1486 1486
1487 1487 $ hg qnew add-file1
1488 1488 $ echo a > TeXtFiLe.TxT
1489 1489 $ hg add TeXtFiLe.TxT
1490 1490 $ hg qrefresh
1491 1491
1492 1492 $ hg qnew add-file2
1493 1493 $ echo b > AnOtHeRFiLe.TxT
1494 1494 $ hg add AnOtHeRFiLe.TxT
1495 1495 $ hg qrefresh
1496 1496
1497 1497 $ hg qnew modify-file
1498 1498 $ echo c >> AnOtHeRFiLe.TxT
1499 1499 $ hg qrefresh
1500 1500
1501 1501 $ hg qapplied
1502 1502 add-file1
1503 1503 add-file2
1504 1504 modify-file
1505 1505 $ hg qpop -a
1506 1506 popping modify-file
1507 1507 popping add-file2
1508 1508 popping add-file1
1509 1509 patch queue now empty
1510 1510
1511 1511 this qpush causes problems below, if case preservation on case
1512 1512 insensitive filesystem is not enough:
1513 1513 (1) unexpected "adding ..." messages are shown
1514 1514 (2) patching fails in modification of (1) files
1515 1515
1516 1516 $ hg qpush -a
1517 1517 applying add-file1
1518 1518 applying add-file2
1519 1519 applying modify-file
1520 1520 now at: modify-file
1521 1521
1522 1522 Proper phase default with mq:
1523 1523
1524 1524 1. mq.secret=false
1525 1525
1526 1526 $ rm .hg/store/phaseroots
1527 1527 $ hg phase 'qparent::'
1528 1528 -1: public
1529 1529 0: draft
1530 1530 1: draft
1531 1531 2: draft
1532 1532 $ echo '[mq]' >> $HGRCPATH
1533 1533 $ echo 'secret=true' >> $HGRCPATH
1534 1534 $ rm -f .hg/store/phaseroots
1535 1535 $ hg phase 'qparent::'
1536 1536 -1: public
1537 1537 0: secret
1538 1538 1: secret
1539 1539 2: secret
1540 1540
1541 1541 Test that qfinish change phase when mq.secret=true
1542 1542
1543 1543 $ hg qfinish qbase
1544 1544 patch add-file1 finalized without changeset message
1545 1545 $ hg phase 'all()'
1546 1546 0: draft
1547 1547 1: secret
1548 1548 2: secret
1549 1549
1550 1550 Test that qfinish respect phases.new-commit setting
1551 1551
1552 1552 $ echo '[phases]' >> $HGRCPATH
1553 1553 $ echo 'new-commit=secret' >> $HGRCPATH
1554 1554 $ hg qfinish qbase
1555 1555 patch add-file2 finalized without changeset message
1556 1556 $ hg phase 'all()'
1557 1557 0: draft
1558 1558 1: secret
1559 1559 2: secret
1560 1560
1561 1561 (restore env for next test)
1562 1562
1563 1563 $ sed -e 's/new-commit=secret//' $HGRCPATH > $TESTTMP/sedtmp
1564 1564 $ cp $TESTTMP/sedtmp $HGRCPATH
1565 1565 $ hg qimport -r 1 --name add-file2
1566 1566
1567 1567 Test that qfinish preserve phase when mq.secret=false
1568 1568
1569 1569 $ sed -e 's/secret=true/secret=false/' $HGRCPATH > $TESTTMP/sedtmp
1570 1570 $ cp $TESTTMP/sedtmp $HGRCPATH
1571 1571 $ hg qfinish qbase
1572 1572 patch add-file2 finalized without changeset message
1573 1573 $ hg phase 'all()'
1574 1574 0: draft
1575 1575 1: secret
1576 1576 2: secret
1577 1577
1578 1578 Test that secret mq patch does not break hgweb
1579 1579
1580 1580 $ cat > hgweb.cgi <<HGWEB
1581 1581 > from mercurial import demandimport; demandimport.enable()
1582 1582 > from mercurial.hgweb import hgweb
1583 1583 > from mercurial.hgweb import wsgicgi
1584 1584 > import cgitb
1585 1585 > cgitb.enable()
1586 1586 > app = hgweb(b'.', b'test')
1587 1587 > wsgicgi.launch(app)
1588 1588 > HGWEB
1589 1589 $ . "$TESTDIR/cgienv"
1590 1590 #if msys
1591 1591 $ PATH_INFO=//tags; export PATH_INFO
1592 1592 #else
1593 1593 $ PATH_INFO=/tags; export PATH_INFO
1594 1594 #endif
1595 1595 $ QUERY_STRING='style=raw'
1596 1596 $ "$PYTHON" hgweb.cgi | grep '^tip'
1597 1597 tip [0-9a-f]{40} (re)
1598 1598
1599 1599 $ cd ..
1600 1600
1601 1601 Test interaction with revset (issue4426)
1602 1602
1603 1603 $ hg init issue4426
1604 1604 $ cd issue4426
1605 1605
1606 1606 $ echo a > a
1607 1607 $ hg ci -Am a
1608 1608 adding a
1609 1609 $ echo a >> a
1610 1610 $ hg ci -m a
1611 1611 $ echo a >> a
1612 1612 $ hg ci -m a
1613 1613 $ hg qimport -r 0::
1614 1614
1615 1615 reimport things
1616 1616
1617 1617 $ hg qimport -r 1::
1618 1618 abort: revision 2 is already managed
1619 1619 [255]
1620 1620
1621 1621
1622 1622 $ cd ..
@@ -1,160 +1,160 b''
1 1 Path conflict checking is currently disabled by default because of issue5716.
2 2 Turn it on for this test.
3 3
4 4 $ cat >> $HGRCPATH << EOF
5 5 > [experimental]
6 6 > merge.checkpathconflicts=True
7 7 > EOF
8 8
9 9 $ hg init repo
10 10 $ cd repo
11 11 $ echo base > base
12 12 $ hg add base
13 13 $ hg commit -m "base"
14 14 $ hg bookmark -i base
15 15 $ mkdir a
16 16 $ echo 1 > a/b
17 17 $ hg add a/b
18 18 $ hg commit -m "file"
19 19 $ hg bookmark -i file
20 20 $ echo 2 > a/b
21 21 $ hg commit -m "file2"
22 22 $ hg bookmark -i file2
23 23 $ hg up 0
24 24 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
25 25 $ mkdir a
26 26 #if symlink
27 27 $ ln -s c a/b
28 28 #else
29 29 $ touch a/b
30 30 #endif
31 31 $ hg add a/b
32 32 $ hg commit -m "link"
33 33 created new head
34 34 $ hg bookmark -i link
35 35 $ hg up 0
36 36 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
37 37 $ mkdir -p a/b/c
38 38 $ echo 2 > a/b/c/d
39 39 $ hg add a/b/c/d
40 40 $ hg commit -m "dir"
41 41 created new head
42 42 $ hg bookmark -i dir
43 43
44 44 Update - local file conflicts with remote directory:
45 45
46 46 $ hg up -q 0
47 47 $ mkdir a
48 48 $ echo 9 > a/b
49 49 $ hg up dir
50 50 a/b: untracked file conflicts with directory
51 51 abort: untracked files in working directory differ from files in requested revision
52 52 [255]
53 53 $ hg up dir --config merge.checkunknown=warn
54 54 a/b: replacing untracked file
55 55 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
56 56 (activating bookmark dir)
57 57 $ cat a/b.orig
58 58 9
59 59 $ rm a/b.orig
60 60
61 61 Update - local symlink conflicts with remote directory:
62 62
63 63 $ hg up -q 0
64 64 $ mkdir a
65 65 #if symlink
66 66 $ ln -s x a/b
67 67 #else
68 68 $ touch a/b
69 69 #endif
70 70 $ hg up dir
71 71 a/b: untracked file conflicts with directory
72 72 abort: untracked files in working directory differ from files in requested revision
73 73 [255]
74 74 $ hg up dir --config merge.checkunknown=warn
75 75 a/b: replacing untracked file
76 76 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
77 77 (activating bookmark dir)
78 78 #if symlink
79 79 $ readlink.py a/b.orig
80 80 a/b.orig -> x
81 81 #endif
82 82 $ rm a/b.orig
83 83
84 84 Update - local directory conflicts with remote file
85 85
86 86 $ hg up -q 0
87 87 $ mkdir -p a/b/c
88 88 $ echo 9 > a/b/c/d
89 89 $ hg up file
90 90 a/b: untracked directory conflicts with file
91 91 abort: untracked files in working directory differ from files in requested revision
92 92 [255]
93 93 $ hg up file --config merge.checkunknown=warn
94 94 a/b: replacing untracked files in directory
95 95 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
96 96 (activating bookmark file)
97 97 $ cat a/b
98 98 1
99 99 $ test -d a/b.orig
100 100 $ rm -rf a/b.orig
101 101
102 102 Update - local directory conflicts with remote symlink
103 103
104 104 $ hg up -q 0
105 105 $ mkdir -p a/b/c
106 106 $ echo 9 > a/b/c/d
107 107 $ hg up link
108 108 a/b: untracked directory conflicts with file
109 109 abort: untracked files in working directory differ from files in requested revision
110 110 [255]
111 111 $ hg up link --config merge.checkunknown=warn
112 112 a/b: replacing untracked files in directory
113 113 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
114 114 (activating bookmark link)
115 115 #if symlink
116 116 $ readlink.py a/b
117 117 a/b -> c
118 118 #endif
119 119 $ test -d a/b.orig
120 120 $ rm -rf a/b.orig
121 121
122 122 Update - local renamed file conflicts with remote directory
123 123
124 124 $ hg up -q 0
125 125 $ hg mv base a
126 126 $ hg status -C
127 127 A a
128 128 base
129 129 R base
130 130 $ hg up --check dir
131 131 abort: uncommitted changes
132 [255]
132 [20]
133 133 $ hg up dir
134 134 a: path conflict - a file or link has the same name as a directory
135 135 the local file has been renamed to a~d20a80d4def3
136 136 resolve manually then use 'hg resolve --mark a'
137 137 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
138 138 use 'hg resolve' to retry unresolved file merges
139 139 (activating bookmark dir)
140 140 [1]
141 141 $ hg status -C
142 142 A a~d20a80d4def3
143 143 base
144 144 R base
145 145 $ hg resolve --list
146 146 P a
147 147 $ hg up --clean -q 0
148 148
149 149 Update clean - local directory conflicts with changed remote file
150 150
151 151 $ hg up -q file
152 152 $ rm a/b
153 153 $ mkdir a/b
154 154 $ echo 9 > a/b/c
155 155 $ hg up file2 --check --config merge.checkunknown=warn
156 156 abort: uncommitted changes
157 [255]
157 [20]
158 158 $ hg up file2 --clean
159 159 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
160 160 (activating bookmark file2)
@@ -1,248 +1,248 b''
1 1 $ hg init t
2 2 $ cd t
3 3 $ echo 1 > foo
4 4 $ hg ci -Am m
5 5 adding foo
6 6
7 7 $ cd ..
8 8 $ hg clone t tt
9 9 updating to branch default
10 10 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
11 11 $ cd tt
12 12 $ echo 1.1 > foo
13 13 $ hg ci -Am m
14 14
15 15 $ cd ../t
16 16 $ echo 1.2 > foo
17 17 $ hg ci -Am m
18 18
19 19 Should respect config to disable dirty update
20 20 $ hg co -qC 0
21 21 $ echo 2 > foo
22 22 $ hg --config commands.update.check=abort pull -u ../tt
23 23 pulling from ../tt
24 24 searching for changes
25 25 adding changesets
26 26 adding manifests
27 27 adding file changes
28 28 added 1 changesets with 1 changes to 1 files (+1 heads)
29 29 new changesets 107cefe13e42
30 30 1 local changesets published
31 31 abort: uncommitted changes
32 [255]
32 [20]
33 33 $ hg --config extensions.strip= strip --no-backup tip
34 34 $ hg co -qC tip
35 35
36 36 Should not update to the other topological branch:
37 37
38 38 $ hg pull -u ../tt
39 39 pulling from ../tt
40 40 searching for changes
41 41 adding changesets
42 42 adding manifests
43 43 adding file changes
44 44 added 1 changesets with 1 changes to 1 files (+1 heads)
45 45 new changesets 107cefe13e42
46 46 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
47 47 updated to "800c91d5bfc1: m"
48 48 1 other heads for branch "default"
49 49
50 50 $ cd ../tt
51 51
52 52 Should not update to the other branch:
53 53
54 54 $ hg pull -u ../t
55 55 pulling from ../t
56 56 searching for changes
57 57 adding changesets
58 58 adding manifests
59 59 adding file changes
60 60 added 1 changesets with 1 changes to 1 files (+1 heads)
61 61 new changesets 800c91d5bfc1
62 62 1 local changesets published
63 63 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 64 updated to "107cefe13e42: m"
65 65 1 other heads for branch "default"
66 66
67 67 $ HGMERGE=true hg merge
68 68 merging foo
69 69 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
70 70 (branch merge, don't forget to commit)
71 71 $ hg ci -mm
72 72
73 73 $ cd ../t
74 74
75 75 Should work:
76 76
77 77 $ hg pull -u ../tt
78 78 pulling from ../tt
79 79 searching for changes
80 80 adding changesets
81 81 adding manifests
82 82 adding file changes
83 83 added 1 changesets with 1 changes to 1 files (-1 heads)
84 84 new changesets 483b76ad4309
85 85 1 local changesets published
86 86 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
87 87
88 88 Similarity between "hg update" and "hg pull -u" in handling bookmark
89 89 ====================================================================
90 90
91 91 Test that updating activates the bookmark, which matches with the
92 92 explicit destination of the update.
93 93
94 94 $ echo 4 >> foo
95 95 $ hg commit -m "#4"
96 96 $ hg bookmark active-after-pull
97 97 $ cd ../tt
98 98
99 99 (1) activating by --rev BOOKMARK
100 100
101 101 $ hg bookmark -f active-before-pull
102 102 $ hg bookmarks
103 103 * active-before-pull 3:483b76ad4309
104 104
105 105 $ hg pull -u -r active-after-pull
106 106 pulling from $TESTTMP/t
107 107 searching for changes
108 108 adding changesets
109 109 adding manifests
110 110 adding file changes
111 111 adding remote bookmark active-after-pull
112 112 added 1 changesets with 1 changes to 1 files
113 113 new changesets f815b3da6163
114 114 1 local changesets published
115 115 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
116 116 (activating bookmark active-after-pull)
117 117
118 118 $ hg parents -q
119 119 4:f815b3da6163
120 120 $ hg bookmarks
121 121 * active-after-pull 4:f815b3da6163
122 122 active-before-pull 3:483b76ad4309
123 123
124 124 (discard pulled changes)
125 125
126 126 $ hg update -q 483b76ad4309
127 127 $ hg rollback -q
128 128
129 129 (2) activating by URL#BOOKMARK
130 130
131 131 $ hg bookmark -f active-before-pull
132 132 $ hg bookmarks
133 133 * active-before-pull 3:483b76ad4309
134 134
135 135 $ hg pull -u $TESTTMP/t#active-after-pull
136 136 pulling from $TESTTMP/t
137 137 searching for changes
138 138 adding changesets
139 139 adding manifests
140 140 adding file changes
141 141 adding remote bookmark active-after-pull
142 142 added 1 changesets with 1 changes to 1 files
143 143 new changesets f815b3da6163
144 144 1 local changesets published
145 145 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
146 146 (activating bookmark active-after-pull)
147 147
148 148 $ hg parents -q
149 149 4:f815b3da6163
150 150 $ hg bookmarks
151 151 * active-after-pull 4:f815b3da6163
152 152 active-before-pull 3:483b76ad4309
153 153
154 154 (discard pulled changes)
155 155
156 156 $ hg update -q 483b76ad4309
157 157 $ hg rollback -q
158 158
159 159 Test that updating deactivates current active bookmark, if the
160 160 destination of the update is explicitly specified, and it doesn't
161 161 match with the name of any existing bookmarks.
162 162
163 163 $ cd ../t
164 164 $ hg bookmark -d active-after-pull
165 165 $ hg branch bar -q
166 166 $ hg commit -m "#5 (bar #1)"
167 167 $ cd ../tt
168 168
169 169 (1) deactivating by --rev REV
170 170
171 171 $ hg bookmark -f active-before-pull
172 172 $ hg bookmarks
173 173 * active-before-pull 3:483b76ad4309
174 174
175 175 $ hg pull -u -r b5e4babfaaa7
176 176 pulling from $TESTTMP/t
177 177 searching for changes
178 178 adding changesets
179 179 adding manifests
180 180 adding file changes
181 181 added 2 changesets with 1 changes to 1 files
182 182 new changesets f815b3da6163:b5e4babfaaa7
183 183 1 local changesets published
184 184 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
185 185 (leaving bookmark active-before-pull)
186 186
187 187 $ hg parents -q
188 188 5:b5e4babfaaa7
189 189 $ hg bookmarks
190 190 active-before-pull 3:483b76ad4309
191 191
192 192 (discard pulled changes)
193 193
194 194 $ hg update -q 483b76ad4309
195 195 $ hg rollback -q
196 196
197 197 (2) deactivating by --branch BRANCH
198 198
199 199 $ hg bookmark -f active-before-pull
200 200 $ hg bookmarks
201 201 * active-before-pull 3:483b76ad4309
202 202
203 203 $ hg pull -u -b bar
204 204 pulling from $TESTTMP/t
205 205 searching for changes
206 206 adding changesets
207 207 adding manifests
208 208 adding file changes
209 209 added 2 changesets with 1 changes to 1 files
210 210 new changesets f815b3da6163:b5e4babfaaa7
211 211 1 local changesets published
212 212 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
213 213 (leaving bookmark active-before-pull)
214 214
215 215 $ hg parents -q
216 216 5:b5e4babfaaa7
217 217 $ hg bookmarks
218 218 active-before-pull 3:483b76ad4309
219 219
220 220 (discard pulled changes)
221 221
222 222 $ hg update -q 483b76ad4309
223 223 $ hg rollback -q
224 224
225 225 (3) deactivating by URL#ANOTHER-BRANCH
226 226
227 227 $ hg bookmark -f active-before-pull
228 228 $ hg bookmarks
229 229 * active-before-pull 3:483b76ad4309
230 230
231 231 $ hg pull -u $TESTTMP/t#bar
232 232 pulling from $TESTTMP/t
233 233 searching for changes
234 234 adding changesets
235 235 adding manifests
236 236 adding file changes
237 237 added 2 changesets with 1 changes to 1 files
238 238 new changesets f815b3da6163:b5e4babfaaa7
239 239 1 local changesets published
240 240 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
241 241 (leaving bookmark active-before-pull)
242 242
243 243 $ hg parents -q
244 244 5:b5e4babfaaa7
245 245 $ hg bookmarks
246 246 active-before-pull 3:483b76ad4309
247 247
248 248 $ cd ..
@@ -1,473 +1,473 b''
1 1 Create configuration
2 2
3 3 $ echo "[ui]" >> $HGRCPATH
4 4 $ echo "interactive=true" >> $HGRCPATH
5 5
6 6 help record (no record)
7 7
8 8 $ hg help record
9 9 record extension - commands to interactively select changes for
10 10 commit/qrefresh (DEPRECATED)
11 11
12 12 The feature provided by this extension has been moved into core Mercurial as
13 13 'hg commit --interactive'.
14 14
15 15 (use 'hg help extensions' for information on enabling extensions)
16 16
17 17 help qrecord (no record)
18 18
19 19 $ hg help qrecord
20 20 'qrecord' is provided by the following extension:
21 21
22 22 record commands to interactively select changes for commit/qrefresh
23 23 (DEPRECATED)
24 24
25 25 (use 'hg help extensions' for information on enabling extensions)
26 26
27 27 $ echo "[extensions]" >> $HGRCPATH
28 28 $ echo "record=" >> $HGRCPATH
29 29
30 30 help record (record)
31 31
32 32 $ hg help record
33 33 hg record [OPTION]... [FILE]...
34 34
35 35 interactively select changes to commit
36 36
37 37 If a list of files is omitted, all changes reported by 'hg status' will be
38 38 candidates for recording.
39 39
40 40 See 'hg help dates' for a list of formats valid for -d/--date.
41 41
42 42 If using the text interface (see 'hg help config'), you will be prompted
43 43 for whether to record changes to each modified file, and for files with
44 44 multiple changes, for each change to use. For each query, the following
45 45 responses are possible:
46 46
47 47 y - record this change
48 48 n - skip this change
49 49 e - edit this change manually
50 50
51 51 s - skip remaining changes to this file
52 52 f - record remaining changes to this file
53 53
54 54 d - done, skip remaining changes and files
55 55 a - record all changes to all remaining files
56 56 q - quit, recording no changes
57 57
58 58 ? - display help
59 59
60 60 This command is not available when committing a merge.
61 61
62 62 (use 'hg help -e record' to show help for the record extension)
63 63
64 64 options ([+] can be repeated):
65 65
66 66 -A --addremove mark new/missing files as added/removed before
67 67 committing
68 68 --close-branch mark a branch head as closed
69 69 --amend amend the parent of the working directory
70 70 -s --secret use the secret phase for committing
71 71 -e --edit invoke editor on commit messages
72 72 -I --include PATTERN [+] include names matching the given patterns
73 73 -X --exclude PATTERN [+] exclude names matching the given patterns
74 74 -m --message TEXT use text as commit message
75 75 -l --logfile FILE read commit message from file
76 76 -d --date DATE record the specified date as commit date
77 77 -u --user USER record the specified user as committer
78 78 -S --subrepos recurse into subrepositories
79 79 -w --ignore-all-space ignore white space when comparing lines
80 80 -b --ignore-space-change ignore changes in the amount of white space
81 81 -B --ignore-blank-lines ignore changes whose lines are all blank
82 82 -Z --ignore-space-at-eol ignore changes in whitespace at EOL
83 83
84 84 (some details hidden, use --verbose to show complete help)
85 85
86 86 help (no mq, so no qrecord)
87 87
88 88 $ hg help qrecord
89 89 hg qrecord [OPTION]... PATCH [FILE]...
90 90
91 91 interactively record a new patch
92 92
93 93 See 'hg help qnew' & 'hg help record' for more information and usage.
94 94
95 95 (some details hidden, use --verbose to show complete help)
96 96
97 97 $ hg init a
98 98
99 99 qrecord (mq not present)
100 100
101 101 $ hg -R a qrecord
102 102 hg qrecord: invalid arguments
103 103 hg qrecord [OPTION]... PATCH [FILE]...
104 104
105 105 interactively record a new patch
106 106
107 107 (use 'hg qrecord -h' to show more help)
108 108 [255]
109 109
110 110 qrecord patch (mq not present)
111 111
112 112 $ hg -R a qrecord patch
113 113 abort: 'mq' extension not loaded
114 114 [255]
115 115
116 116 help (bad mq)
117 117
118 118 $ echo "mq=nonexistent" >> $HGRCPATH
119 119 $ hg help qrecord
120 120 *** failed to import extension mq from nonexistent: [Errno *] * (glob)
121 121 hg qrecord [OPTION]... PATCH [FILE]...
122 122
123 123 interactively record a new patch
124 124
125 125 See 'hg help qnew' & 'hg help record' for more information and usage.
126 126
127 127 (some details hidden, use --verbose to show complete help)
128 128
129 129 help (mq present)
130 130
131 131 $ sed 's/mq=nonexistent/mq=/' $HGRCPATH > hgrc.tmp
132 132 $ mv hgrc.tmp $HGRCPATH
133 133
134 134 $ hg help qrecord
135 135 hg qrecord [OPTION]... PATCH [FILE]...
136 136
137 137 interactively record a new patch
138 138
139 139 See 'hg help qnew' & 'hg help record' for more information and usage.
140 140
141 141 options ([+] can be repeated):
142 142
143 143 -e --edit invoke editor on commit messages
144 144 -g --git use git extended diff format
145 145 -U --currentuser add "From: <current user>" to patch
146 146 -u --user USER add "From: <USER>" to patch
147 147 -D --currentdate add "Date: <current date>" to patch
148 148 -d --date DATE add "Date: <DATE>" to patch
149 149 -I --include PATTERN [+] include names matching the given patterns
150 150 -X --exclude PATTERN [+] exclude names matching the given patterns
151 151 -m --message TEXT use text as commit message
152 152 -l --logfile FILE read commit message from file
153 153 -w --ignore-all-space ignore white space when comparing lines
154 154 -b --ignore-space-change ignore changes in the amount of white space
155 155 -B --ignore-blank-lines ignore changes whose lines are all blank
156 156 -Z --ignore-space-at-eol ignore changes in whitespace at EOL
157 157 --mq operate on patch repository
158 158
159 159 (some details hidden, use --verbose to show complete help)
160 160
161 161 $ cd a
162 162
163 163 Base commit
164 164
165 165 $ cat > 1.txt <<EOF
166 166 > 1
167 167 > 2
168 168 > 3
169 169 > 4
170 170 > 5
171 171 > EOF
172 172 $ cat > 2.txt <<EOF
173 173 > a
174 174 > b
175 175 > c
176 176 > d
177 177 > e
178 178 > f
179 179 > EOF
180 180
181 181 $ mkdir dir
182 182 $ cat > dir/a.txt <<EOF
183 183 > hello world
184 184 >
185 185 > someone
186 186 > up
187 187 > there
188 188 > loves
189 189 > me
190 190 > EOF
191 191
192 192 $ hg add 1.txt 2.txt dir/a.txt
193 193 $ hg commit -m 'initial checkin'
194 194
195 195 Changing files
196 196
197 197 $ sed -e 's/2/2 2/;s/4/4 4/' 1.txt > 1.txt.new
198 198 $ sed -e 's/b/b b/' 2.txt > 2.txt.new
199 199 $ sed -e 's/hello world/hello world!/' dir/a.txt > dir/a.txt.new
200 200
201 201 $ mv -f 1.txt.new 1.txt
202 202 $ mv -f 2.txt.new 2.txt
203 203 $ mv -f dir/a.txt.new dir/a.txt
204 204
205 205 Whole diff
206 206
207 207 $ hg diff --nodates
208 208 diff -r 1057167b20ef 1.txt
209 209 --- a/1.txt
210 210 +++ b/1.txt
211 211 @@ -1,5 +1,5 @@
212 212 1
213 213 -2
214 214 +2 2
215 215 3
216 216 -4
217 217 +4 4
218 218 5
219 219 diff -r 1057167b20ef 2.txt
220 220 --- a/2.txt
221 221 +++ b/2.txt
222 222 @@ -1,5 +1,5 @@
223 223 a
224 224 -b
225 225 +b b
226 226 c
227 227 d
228 228 e
229 229 diff -r 1057167b20ef dir/a.txt
230 230 --- a/dir/a.txt
231 231 +++ b/dir/a.txt
232 232 @@ -1,4 +1,4 @@
233 233 -hello world
234 234 +hello world!
235 235
236 236 someone
237 237 up
238 238
239 239 qrecord with bad patch name, should abort before prompting
240 240
241 241 $ hg qrecord .hg
242 242 abort: patch name cannot begin with ".hg"
243 243 [255]
244 244 $ hg qrecord ' foo'
245 245 abort: patch name cannot begin or end with whitespace
246 246 [255]
247 247 $ hg qrecord 'foo '
248 248 abort: patch name cannot begin or end with whitespace
249 249 [255]
250 250
251 251 qrecord a.patch
252 252
253 253 $ hg qrecord -d '0 0' -m aaa a.patch <<EOF
254 254 > y
255 255 > y
256 256 > n
257 257 > y
258 258 > y
259 259 > n
260 260 > EOF
261 261 diff --git a/1.txt b/1.txt
262 262 2 hunks, 2 lines changed
263 263 examine changes to '1.txt'?
264 264 (enter ? for help) [Ynesfdaq?] y
265 265
266 266 @@ -1,3 +1,3 @@
267 267 1
268 268 -2
269 269 +2 2
270 270 3
271 271 record change 1/4 to '1.txt'?
272 272 (enter ? for help) [Ynesfdaq?] y
273 273
274 274 @@ -3,3 +3,3 @@
275 275 3
276 276 -4
277 277 +4 4
278 278 5
279 279 record change 2/4 to '1.txt'?
280 280 (enter ? for help) [Ynesfdaq?] n
281 281
282 282 diff --git a/2.txt b/2.txt
283 283 1 hunks, 1 lines changed
284 284 examine changes to '2.txt'?
285 285 (enter ? for help) [Ynesfdaq?] y
286 286
287 287 @@ -1,5 +1,5 @@
288 288 a
289 289 -b
290 290 +b b
291 291 c
292 292 d
293 293 e
294 294 record change 3/4 to '2.txt'?
295 295 (enter ? for help) [Ynesfdaq?] y
296 296
297 297 diff --git a/dir/a.txt b/dir/a.txt
298 298 1 hunks, 1 lines changed
299 299 examine changes to 'dir/a.txt'?
300 300 (enter ? for help) [Ynesfdaq?] n
301 301
302 302
303 303 After qrecord a.patch 'tip'"
304 304
305 305 $ hg tip -p
306 306 changeset: 1:5d1ca63427ee
307 307 tag: a.patch
308 308 tag: qbase
309 309 tag: qtip
310 310 tag: tip
311 311 user: test
312 312 date: Thu Jan 01 00:00:00 1970 +0000
313 313 summary: aaa
314 314
315 315 diff -r 1057167b20ef -r 5d1ca63427ee 1.txt
316 316 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
317 317 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
318 318 @@ -1,5 +1,5 @@
319 319 1
320 320 -2
321 321 +2 2
322 322 3
323 323 4
324 324 5
325 325 diff -r 1057167b20ef -r 5d1ca63427ee 2.txt
326 326 --- a/2.txt Thu Jan 01 00:00:00 1970 +0000
327 327 +++ b/2.txt Thu Jan 01 00:00:00 1970 +0000
328 328 @@ -1,5 +1,5 @@
329 329 a
330 330 -b
331 331 +b b
332 332 c
333 333 d
334 334 e
335 335
336 336
337 337 After qrecord a.patch 'diff'"
338 338
339 339 $ hg diff --nodates
340 340 diff -r 5d1ca63427ee 1.txt
341 341 --- a/1.txt
342 342 +++ b/1.txt
343 343 @@ -1,5 +1,5 @@
344 344 1
345 345 2 2
346 346 3
347 347 -4
348 348 +4 4
349 349 5
350 350 diff -r 5d1ca63427ee dir/a.txt
351 351 --- a/dir/a.txt
352 352 +++ b/dir/a.txt
353 353 @@ -1,4 +1,4 @@
354 354 -hello world
355 355 +hello world!
356 356
357 357 someone
358 358 up
359 359
360 360 qrecord b.patch
361 361
362 362 $ hg qrecord -d '0 0' -m bbb b.patch <<EOF
363 363 > y
364 364 > y
365 365 > y
366 366 > y
367 367 > EOF
368 368 diff --git a/1.txt b/1.txt
369 369 1 hunks, 1 lines changed
370 370 examine changes to '1.txt'?
371 371 (enter ? for help) [Ynesfdaq?] y
372 372
373 373 @@ -1,5 +1,5 @@
374 374 1
375 375 2 2
376 376 3
377 377 -4
378 378 +4 4
379 379 5
380 380 record change 1/2 to '1.txt'?
381 381 (enter ? for help) [Ynesfdaq?] y
382 382
383 383 diff --git a/dir/a.txt b/dir/a.txt
384 384 1 hunks, 1 lines changed
385 385 examine changes to 'dir/a.txt'?
386 386 (enter ? for help) [Ynesfdaq?] y
387 387
388 388 @@ -1,4 +1,4 @@
389 389 -hello world
390 390 +hello world!
391 391
392 392 someone
393 393 up
394 394 record change 2/2 to 'dir/a.txt'?
395 395 (enter ? for help) [Ynesfdaq?] y
396 396
397 397
398 398 After qrecord b.patch 'tip'
399 399
400 400 $ hg tip -p
401 401 changeset: 2:b056198bf878
402 402 tag: b.patch
403 403 tag: qtip
404 404 tag: tip
405 405 user: test
406 406 date: Thu Jan 01 00:00:00 1970 +0000
407 407 summary: bbb
408 408
409 409 diff -r 5d1ca63427ee -r b056198bf878 1.txt
410 410 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
411 411 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
412 412 @@ -1,5 +1,5 @@
413 413 1
414 414 2 2
415 415 3
416 416 -4
417 417 +4 4
418 418 5
419 419 diff -r 5d1ca63427ee -r b056198bf878 dir/a.txt
420 420 --- a/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
421 421 +++ b/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
422 422 @@ -1,4 +1,4 @@
423 423 -hello world
424 424 +hello world!
425 425
426 426 someone
427 427 up
428 428
429 429
430 430 After qrecord b.patch 'diff'
431 431
432 432 $ hg diff --nodates
433 433
434 434 $ cd ..
435 435
436 436 qrecord should throw an error when histedit in process
437 437
438 438 $ hg init issue5981
439 439 $ cd issue5981
440 440 $ cat >> $HGRCPATH <<EOF
441 441 > [extensions]
442 442 > histedit=
443 443 > mq=
444 444 > EOF
445 445 $ echo > a
446 446 $ hg ci -Am 'foo bar'
447 447 adding a
448 448 $ hg log
449 449 changeset: 0:ea55e2ae468f
450 450 tag: tip
451 451 user: test
452 452 date: Thu Jan 01 00:00:00 1970 +0000
453 453 summary: foo bar
454 454
455 455 $ hg histedit tip --commands - 2>&1 <<EOF
456 456 > edit ea55e2ae468f foo bar
457 457 > EOF
458 458 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
459 459 Editing (ea55e2ae468f), you may commit or record as needed now.
460 460 (hg histedit --continue to resume)
461 461 [240]
462 462 $ echo 'foo bar' > a
463 463 $ hg qrecord -d '0 0' -m aaa a.patch <<EOF
464 464 > y
465 465 > y
466 466 > n
467 467 > y
468 468 > y
469 469 > n
470 470 > EOF
471 471 abort: histedit in progress
472 472 (use 'hg histedit --continue' or 'hg histedit --abort')
473 [255]
473 [20]
@@ -1,511 +1,511 b''
1 1 #testcases abortcommand abortflag
2 2 #testcases continuecommand continueflag
3 3
4 4 $ cat >> $HGRCPATH <<EOF
5 5 > [extensions]
6 6 > rebase=
7 7 >
8 8 > [phases]
9 9 > publish=False
10 10 >
11 11 > [alias]
12 12 > tglog = log -G --template "{rev}:{phase} '{desc}' {branches}\n"
13 13 > EOF
14 14
15 15 #if abortflag
16 16 $ cat >> $HGRCPATH <<EOF
17 17 > [alias]
18 18 > abort = rebase --abort
19 19 > EOF
20 20 #endif
21 21
22 22 #if continueflag
23 23 $ cat >> $HGRCPATH <<EOF
24 24 > [alias]
25 25 > continue = rebase --continue
26 26 > EOF
27 27 #endif
28 28
29 29 $ hg init a
30 30 $ cd a
31 31
32 32 $ touch .hg/rebasestate
33 33 $ hg sum
34 34 parent: -1:000000000000 tip (empty repository)
35 35 branch: default
36 36 commit: (clean)
37 37 update: (current)
38 38 abort: .hg/rebasestate is incomplete
39 39 [255]
40 40 $ rm .hg/rebasestate
41 41
42 42 $ echo c1 > common
43 43 $ hg add common
44 44 $ hg ci -m C1
45 45
46 46 $ echo c2 >> common
47 47 $ hg ci -m C2
48 48
49 49 $ echo c3 >> common
50 50 $ hg ci -m C3
51 51
52 52 $ hg up -q -C 1
53 53
54 54 $ echo l1 >> extra
55 55 $ hg add extra
56 56 $ hg ci -m L1
57 57 created new head
58 58
59 59 $ sed -e 's/c2/l2/' common > common.new
60 60 $ mv common.new common
61 61 $ hg ci -m L2
62 62
63 63 $ hg phase --force --secret 2
64 64
65 65 $ hg tglog
66 66 @ 4:draft 'L2'
67 67 |
68 68 o 3:draft 'L1'
69 69 |
70 70 | o 2:secret 'C3'
71 71 |/
72 72 o 1:draft 'C2'
73 73 |
74 74 o 0:draft 'C1'
75 75
76 76
77 77 Conflicting rebase:
78 78
79 79 $ hg rebase -s 3 -d 2
80 80 rebasing 3:3163e20567cc "L1"
81 81 rebasing 4:46f0b057b5c0 tip "L2"
82 82 merging common
83 83 warning: conflicts while merging common! (edit, then use 'hg resolve --mark')
84 84 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
85 85 [240]
86 86
87 87 Insert unsupported advisory merge record:
88 88
89 89 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -x
90 90 $ hg debugmergestate
91 91 local (dest): 3e046f2ecedb793b97ed32108086edd1a162f8bc
92 92 other (source): 46f0b057b5c061d276b91491c22151f78698abd2
93 93 file: common (state "u")
94 94 local path: common (hash 94c8c21d08740f5da9eaa38d1f175c592692f0d1, flags "")
95 95 ancestor path: common (node de0a666fdd9c1a0b0698b90d85064d8bd34f74b6)
96 96 other path: common (node 2f6411de53677f6f1048fef5bf888d67a342e0a5)
97 97 extra: ancestorlinknode = 3163e20567cc93074fbb7a53c8b93312e59dbf2c
98 98 $ hg resolve -l
99 99 U common
100 100
101 101 Insert unsupported mandatory merge record:
102 102
103 103 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -X
104 104 $ hg debugmergestate
105 105 abort: unsupported merge state records: X
106 106 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
107 107 [255]
108 108 $ hg resolve -l
109 109 abort: unsupported merge state records: X
110 110 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
111 111 [255]
112 112 $ hg resolve -ma
113 113 abort: unsupported merge state records: X
114 114 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
115 115 [255]
116 116
117 117 Abort (should clear out unsupported merge state):
118 118
119 119 #if abortcommand
120 120 when in dry-run mode
121 121 $ hg abort --dry-run
122 122 rebase in progress, will be aborted
123 123 #endif
124 124
125 125 $ hg abort
126 126 saved backup bundle to $TESTTMP/a/.hg/strip-backup/3e046f2ecedb-6beef7d5-backup.hg
127 127 rebase aborted
128 128 $ hg debugmergestate
129 129 no merge state found
130 130
131 131 $ hg tglog
132 132 @ 4:draft 'L2'
133 133 |
134 134 o 3:draft 'L1'
135 135 |
136 136 | o 2:secret 'C3'
137 137 |/
138 138 o 1:draft 'C2'
139 139 |
140 140 o 0:draft 'C1'
141 141
142 142 Test safety for inconsistent rebase state, which may be created (and
143 143 forgotten) by Mercurial earlier than 2.7. This emulates Mercurial
144 144 earlier than 2.7 by renaming ".hg/rebasestate" temporarily.
145 145
146 146 $ hg rebase -s 3 -d 2
147 147 rebasing 3:3163e20567cc "L1"
148 148 rebasing 4:46f0b057b5c0 tip "L2"
149 149 merging common
150 150 warning: conflicts while merging common! (edit, then use 'hg resolve --mark')
151 151 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
152 152 [240]
153 153
154 154 $ mv .hg/rebasestate .hg/rebasestate.back
155 155 $ hg update --quiet --clean 2
156 156 $ hg --config extensions.mq= strip --quiet "destination()"
157 157 $ mv .hg/rebasestate.back .hg/rebasestate
158 158
159 159 $ hg continue
160 160 abort: cannot continue inconsistent rebase
161 161 (use "hg rebase --abort" to clear broken state)
162 162 [255]
163 163 $ hg summary | grep '^rebase: '
164 164 rebase: (use "hg rebase --abort" to clear broken state)
165 165 $ hg abort
166 166 rebase aborted (no revision is removed, only broken state is cleared)
167 167
168 168 $ cd ..
169 169
170 170
171 171 Construct new repo:
172 172
173 173 $ hg init b
174 174 $ cd b
175 175
176 176 $ echo a > a
177 177 $ hg ci -Am A
178 178 adding a
179 179
180 180 $ echo b > b
181 181 $ hg ci -Am B
182 182 adding b
183 183
184 184 $ echo c > c
185 185 $ hg ci -Am C
186 186 adding c
187 187
188 188 $ hg up -q 0
189 189
190 190 $ echo b > b
191 191 $ hg ci -Am 'B bis'
192 192 adding b
193 193 created new head
194 194
195 195 $ echo c1 > c
196 196 $ hg ci -Am C1
197 197 adding c
198 198
199 199 $ hg phase --force --secret 1
200 200 $ hg phase --public 1
201 201
202 202 Rebase and abort without generating new changesets:
203 203
204 204 $ hg tglog
205 205 @ 4:draft 'C1'
206 206 |
207 207 o 3:draft 'B bis'
208 208 |
209 209 | o 2:secret 'C'
210 210 | |
211 211 | o 1:public 'B'
212 212 |/
213 213 o 0:public 'A'
214 214
215 215 $ hg rebase -b 4 -d 2
216 216 rebasing 3:a6484957d6b9 "B bis"
217 217 note: not rebasing 3:a6484957d6b9 "B bis", its destination already has all its changes
218 218 rebasing 4:145842775fec tip "C1"
219 219 merging c
220 220 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
221 221 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
222 222 [240]
223 223
224 224 $ hg tglog
225 225 % 4:draft 'C1'
226 226 |
227 227 o 3:draft 'B bis'
228 228 |
229 229 | @ 2:secret 'C'
230 230 | |
231 231 | o 1:public 'B'
232 232 |/
233 233 o 0:public 'A'
234 234
235 235 $ hg rebase -a
236 236 rebase aborted
237 237
238 238 $ hg tglog
239 239 @ 4:draft 'C1'
240 240 |
241 241 o 3:draft 'B bis'
242 242 |
243 243 | o 2:secret 'C'
244 244 | |
245 245 | o 1:public 'B'
246 246 |/
247 247 o 0:public 'A'
248 248
249 249
250 250 $ cd ..
251 251
252 252 rebase abort should not leave working copy in a merge state if tip-1 is public
253 253 (issue4082)
254 254
255 255 $ hg init abortpublic
256 256 $ cd abortpublic
257 257 $ echo a > a && hg ci -Aqm a
258 258 $ hg book master
259 259 $ hg book foo
260 260 $ echo b > b && hg ci -Aqm b
261 261 $ hg up -q master
262 262 $ echo c > c && hg ci -Aqm c
263 263 $ hg phase -p -r .
264 264 $ hg up -q foo
265 265 $ echo C > c && hg ci -Aqm C
266 266 $ hg log -G --template "{rev} {desc} {bookmarks}"
267 267 @ 3 C foo
268 268 |
269 269 | o 2 c master
270 270 | |
271 271 o | 1 b
272 272 |/
273 273 o 0 a
274 274
275 275
276 276 $ hg rebase -d master -r foo
277 277 rebasing 3:6c0f977a22d8 foo tip "C"
278 278 merging c
279 279 warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
280 280 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
281 281 [240]
282 282 $ hg abort
283 283 rebase aborted
284 284 $ hg log -G --template "{rev} {desc} {bookmarks}"
285 285 @ 3 C foo
286 286 |
287 287 | o 2 c master
288 288 | |
289 289 o | 1 b
290 290 |/
291 291 o 0 a
292 292
293 293 $ cd ..
294 294
295 295 Make sure we don't clobber changes in the working directory when the
296 296 user has somehow managed to update to a different revision (issue4009)
297 297
298 298 $ hg init noupdate
299 299 $ cd noupdate
300 300 $ hg book @
301 301 $ echo original > a
302 302 $ hg add a
303 303 $ hg commit -m a
304 304 $ echo x > b
305 305 $ hg add b
306 306 $ hg commit -m b1
307 307 $ hg up 0
308 308 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
309 309 (leaving bookmark @)
310 310 $ hg book foo
311 311 $ echo y > b
312 312 $ hg add b
313 313 $ hg commit -m b2
314 314 created new head
315 315
316 316 $ hg rebase -d @ -b foo --tool=internal:fail
317 317 rebasing 2:070cf4580bb5 foo tip "b2"
318 318 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
319 319 [240]
320 320
321 321 $ mv .hg/rebasestate ./ # so we're allowed to hg up like in mercurial <2.6.3
322 322 $ hg up -C 0 # user does other stuff in the repo
323 323 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
324 324
325 325 $ mv rebasestate .hg/ # user upgrades to 2.7
326 326
327 327 $ echo new > a
328 328 $ hg up 1 # user gets an error saying to run hg rebase --abort
329 329 abort: rebase in progress
330 330 (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop')
331 [255]
331 [20]
332 332
333 333 $ cat a
334 334 new
335 335 $ hg abort
336 336 rebase aborted
337 337 $ cat a
338 338 new
339 339
340 340 $ cd ..
341 341
342 342 test aborting an interrupted series (issue5084)
343 343 $ hg init interrupted
344 344 $ cd interrupted
345 345 $ touch base
346 346 $ hg add base
347 347 $ hg commit -m base
348 348 $ touch a
349 349 $ hg add a
350 350 $ hg commit -m a
351 351 $ echo 1 > a
352 352 $ hg commit -m 1
353 353 $ touch b
354 354 $ hg add b
355 355 $ hg commit -m b
356 356 $ echo 2 >> a
357 357 $ hg commit -m c
358 358 $ touch d
359 359 $ hg add d
360 360 $ hg commit -m d
361 361 $ hg co -q 1
362 362 $ hg rm a
363 363 $ hg commit -m no-a
364 364 created new head
365 365 $ hg co 0
366 366 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
367 367 $ hg log -G --template "{rev} {desc} {bookmarks}"
368 368 o 6 no-a
369 369 |
370 370 | o 5 d
371 371 | |
372 372 | o 4 c
373 373 | |
374 374 | o 3 b
375 375 | |
376 376 | o 2 1
377 377 |/
378 378 o 1 a
379 379 |
380 380 @ 0 base
381 381
382 382 $ hg --config extensions.n=$TESTDIR/failfilemerge.py rebase -s 3 -d tip
383 383 rebasing 3:3a71550954f1 "b"
384 384 rebasing 4:e80b69427d80 "c"
385 385 abort: ^C
386 386 [255]
387 387
388 388 New operations are blocked with the correct state message
389 389
390 390 $ find .hg -name '*state' -prune | sort
391 391 .hg/dirstate
392 392 .hg/merge/state
393 393 .hg/rebasestate
394 394 .hg/undo.backup.dirstate
395 395 .hg/undo.dirstate
396 396 .hg/updatestate
397 397
398 398 $ hg rebase -s 3 -d tip
399 399 abort: rebase in progress
400 400 (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop')
401 [255]
401 [20]
402 402 $ hg up .
403 403 abort: rebase in progress
404 404 (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop')
405 [255]
405 [20]
406 406 $ hg up -C .
407 407 abort: rebase in progress
408 408 (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop')
409 [255]
409 [20]
410 410
411 411 $ hg graft 3
412 412 abort: rebase in progress
413 413 (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop')
414 [255]
414 [20]
415 415
416 416 $ hg abort
417 417 saved backup bundle to $TESTTMP/interrupted/.hg/strip-backup/3d8812cf300d-93041a90-backup.hg
418 418 rebase aborted
419 419 $ hg log -G --template "{rev} {desc} {bookmarks}"
420 420 o 6 no-a
421 421 |
422 422 | o 5 d
423 423 | |
424 424 | o 4 c
425 425 | |
426 426 | o 3 b
427 427 | |
428 428 | o 2 1
429 429 |/
430 430 o 1 a
431 431 |
432 432 @ 0 base
433 433
434 434 $ hg summary
435 435 parent: 0:df4f53cec30a
436 436 base
437 437 branch: default
438 438 commit: (clean)
439 439 update: 6 new changesets (update)
440 440 phases: 7 draft
441 441
442 442 $ cd ..
443 443 On the other hand, make sure we *do* clobber changes whenever we
444 444 haven't somehow managed to update the repo to a different revision
445 445 during a rebase (issue4661)
446 446
447 447 $ hg ini yesupdate
448 448 $ cd yesupdate
449 449 $ echo "initial data" > foo.txt
450 450 $ hg add
451 451 adding foo.txt
452 452 $ hg ci -m "initial checkin"
453 453 $ echo "change 1" > foo.txt
454 454 $ hg ci -m "change 1"
455 455 $ hg up 0
456 456 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
457 457 $ echo "conflicting change 1" > foo.txt
458 458 $ hg ci -m "conflicting 1"
459 459 created new head
460 460 $ echo "conflicting change 2" > foo.txt
461 461 $ hg ci -m "conflicting 2"
462 462
463 463 $ hg rebase -d 1 --tool 'internal:fail'
464 464 rebasing 2:e4ea5cdc9789 "conflicting 1"
465 465 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
466 466 [240]
467 467 $ hg abort
468 468 rebase aborted
469 469 $ hg summary
470 470 parent: 3:b16646383533 tip
471 471 conflicting 2
472 472 branch: default
473 473 commit: (clean)
474 474 update: 1 new changesets, 2 branch heads (merge)
475 475 phases: 4 draft
476 476 $ cd ..
477 477
478 478 test aborting a rebase succeeds after rebasing with skipped commits onto a
479 479 public changeset (issue4896)
480 480
481 481 $ hg init succeedonpublic
482 482 $ cd succeedonpublic
483 483 $ echo 'content' > root
484 484 $ hg commit -A -m 'root' -q
485 485
486 486 set up public branch
487 487 $ echo 'content' > disappear
488 488 $ hg commit -A -m 'disappear public' -q
489 489 commit will cause merge conflict on rebase
490 490 $ echo '' > root
491 491 $ hg commit -m 'remove content public' -q
492 492 $ hg phase --public
493 493
494 494 setup the draft branch that will be rebased onto public commit
495 495 $ hg up -r 0 -q
496 496 $ echo 'content' > disappear
497 497 commit will disappear
498 498 $ hg commit -A -m 'disappear draft' -q
499 499 $ echo 'addedcontADDEDentadded' > root
500 500 commit will cause merge conflict on rebase
501 501 $ hg commit -m 'add content draft' -q
502 502
503 503 $ hg rebase -d 'public()' --tool :merge -q
504 504 note: not rebasing 3:0682fd3dabf5 "disappear draft", its destination already has all its changes
505 505 warning: conflicts while merging root! (edit, then use 'hg resolve --mark')
506 506 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
507 507 [240]
508 508 $ hg abort
509 509 rebase aborted
510 510 $ cd ..
511 511
@@ -1,500 +1,500 b''
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extensions]
3 3 > rebase=
4 4 > drawdag=$TESTDIR/drawdag.py
5 5 >
6 6 > [phases]
7 7 > publish=False
8 8 >
9 9 > [alias]
10 10 > tglog = log -G --template "{rev}:{phase} '{desc}' {branches} {bookmarks}\n"
11 11 > EOF
12 12
13 13 $ hg init a
14 14 $ cd a
15 15 $ echo c1 >common
16 16 $ hg add common
17 17 $ hg ci -m C1
18 18
19 19 $ echo c2 >>common
20 20 $ hg ci -m C2
21 21
22 22 $ echo c3 >>common
23 23 $ hg ci -m C3
24 24
25 25 $ hg up -q -C 1
26 26
27 27 $ echo l1 >>extra
28 28 $ hg add extra
29 29 $ hg ci -m L1
30 30 created new head
31 31
32 32 $ sed -e 's/c2/l2/' common > common.new
33 33 $ mv common.new common
34 34 $ hg ci -m L2
35 35
36 36 $ echo l3 >> extra2
37 37 $ hg add extra2
38 38 $ hg ci -m L3
39 39 $ hg bookmark mybook
40 40
41 41 $ hg phase --force --secret 4
42 42
43 43 $ hg tglog
44 44 @ 5:secret 'L3' mybook
45 45 |
46 46 o 4:secret 'L2'
47 47 |
48 48 o 3:draft 'L1'
49 49 |
50 50 | o 2:draft 'C3'
51 51 |/
52 52 o 1:draft 'C2'
53 53 |
54 54 o 0:draft 'C1'
55 55
56 56 Try to call --continue:
57 57
58 58 $ hg rebase --continue
59 59 abort: no rebase in progress
60 [255]
60 [20]
61 61
62 62 Conflicting rebase:
63 63
64 64 $ hg rebase -s 3 -d 2
65 65 rebasing 3:3163e20567cc "L1"
66 66 rebasing 4:46f0b057b5c0 "L2"
67 67 merging common
68 68 warning: conflicts while merging common! (edit, then use 'hg resolve --mark')
69 69 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
70 70 [240]
71 71
72 72 $ hg status --config commands.status.verbose=1
73 73 M common
74 74 ? common.orig
75 75 # The repository is in an unfinished *rebase* state.
76 76
77 77 # Unresolved merge conflicts:
78 78 #
79 79 # common
80 80 #
81 81 # To mark files as resolved: hg resolve --mark FILE
82 82
83 83 # To continue: hg rebase --continue
84 84 # To abort: hg rebase --abort
85 85 # To stop: hg rebase --stop
86 86
87 87
88 88 Try to continue without solving the conflict:
89 89
90 90 $ hg rebase --continue
91 91 abort: unresolved merge conflicts (see 'hg help resolve')
92 92 [255]
93 93
94 94 Conclude rebase:
95 95
96 96 $ echo 'resolved merge' >common
97 97 $ hg resolve -m common
98 98 (no more unresolved files)
99 99 continue: hg rebase --continue
100 100 $ hg rebase --continue
101 101 already rebased 3:3163e20567cc "L1" as 3e046f2ecedb
102 102 rebasing 4:46f0b057b5c0 "L2"
103 103 rebasing 5:8029388f38dc mybook "L3"
104 104 saved backup bundle to $TESTTMP/a/.hg/strip-backup/3163e20567cc-5ca4656e-rebase.hg
105 105
106 106 $ hg tglog
107 107 @ 5:secret 'L3' mybook
108 108 |
109 109 o 4:secret 'L2'
110 110 |
111 111 o 3:draft 'L1'
112 112 |
113 113 o 2:draft 'C3'
114 114 |
115 115 o 1:draft 'C2'
116 116 |
117 117 o 0:draft 'C1'
118 118
119 119 Check correctness:
120 120
121 121 $ hg cat -r 0 common
122 122 c1
123 123
124 124 $ hg cat -r 1 common
125 125 c1
126 126 c2
127 127
128 128 $ hg cat -r 2 common
129 129 c1
130 130 c2
131 131 c3
132 132
133 133 $ hg cat -r 3 common
134 134 c1
135 135 c2
136 136 c3
137 137
138 138 $ hg cat -r 4 common
139 139 resolved merge
140 140
141 141 $ hg cat -r 5 common
142 142 resolved merge
143 143
144 144 Bookmark stays active after --continue
145 145 $ hg bookmarks
146 146 * mybook 5:d67b21408fc0
147 147
148 148 $ cd ..
149 149
150 150 Check that the right ancestors is used while rebasing a merge (issue4041)
151 151
152 152 $ hg init issue4041
153 153 $ cd issue4041
154 154 $ hg unbundle "$TESTDIR/bundles/issue4041.hg"
155 155 adding changesets
156 156 adding manifests
157 157 adding file changes
158 158 added 11 changesets with 8 changes to 3 files (+1 heads)
159 159 new changesets 24797d4f68de:2f2496ddf49d (11 drafts)
160 160 (run 'hg heads' to see heads)
161 161 $ hg up default
162 162 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
163 163 $ hg log -G
164 164 o changeset: 10:2f2496ddf49d
165 165 |\ branch: f1
166 166 | | tag: tip
167 167 | | parent: 7:4c9fbe56a16f
168 168 | | parent: 9:e31216eec445
169 169 | | user: szhang
170 170 | | date: Thu Sep 05 12:59:39 2013 -0400
171 171 | | summary: merge
172 172 | |
173 173 | o changeset: 9:e31216eec445
174 174 | | branch: f1
175 175 | | user: szhang
176 176 | | date: Thu Sep 05 12:59:10 2013 -0400
177 177 | | summary: more changes to f1
178 178 | |
179 179 | o changeset: 8:8e4e2c1a07ae
180 180 | |\ branch: f1
181 181 | | | parent: 2:4bc80088dc6b
182 182 | | | parent: 6:400110238667
183 183 | | | user: szhang
184 184 | | | date: Thu Sep 05 12:57:59 2013 -0400
185 185 | | | summary: bad merge
186 186 | | |
187 187 o | | changeset: 7:4c9fbe56a16f
188 188 |/ / branch: f1
189 189 | | parent: 2:4bc80088dc6b
190 190 | | user: szhang
191 191 | | date: Thu Sep 05 12:54:00 2013 -0400
192 192 | | summary: changed f1
193 193 | |
194 194 | o changeset: 6:400110238667
195 195 | | branch: f2
196 196 | | parent: 4:12e8ec6bb010
197 197 | | user: szhang
198 198 | | date: Tue Sep 03 13:58:02 2013 -0400
199 199 | | summary: changed f2 on f2
200 200 | |
201 201 | | @ changeset: 5:d79e2059b5c0
202 202 | | | parent: 3:8a951942e016
203 203 | | | user: szhang
204 204 | | | date: Tue Sep 03 13:57:39 2013 -0400
205 205 | | | summary: changed f2 on default
206 206 | | |
207 207 | o | changeset: 4:12e8ec6bb010
208 208 | |/ branch: f2
209 209 | | user: szhang
210 210 | | date: Tue Sep 03 13:57:18 2013 -0400
211 211 | | summary: created f2 branch
212 212 | |
213 213 | o changeset: 3:8a951942e016
214 214 | | parent: 0:24797d4f68de
215 215 | | user: szhang
216 216 | | date: Tue Sep 03 13:57:11 2013 -0400
217 217 | | summary: added f2.txt
218 218 | |
219 219 o | changeset: 2:4bc80088dc6b
220 220 | | branch: f1
221 221 | | user: szhang
222 222 | | date: Tue Sep 03 13:56:20 2013 -0400
223 223 | | summary: added f1.txt
224 224 | |
225 225 o | changeset: 1:ef53c9e6b608
226 226 |/ branch: f1
227 227 | user: szhang
228 228 | date: Tue Sep 03 13:55:26 2013 -0400
229 229 | summary: created f1 branch
230 230 |
231 231 o changeset: 0:24797d4f68de
232 232 user: szhang
233 233 date: Tue Sep 03 13:55:08 2013 -0400
234 234 summary: added default.txt
235 235
236 236 $ hg rebase -s9 -d2 --debug # use debug to really check merge base used
237 237 rebase onto 4bc80088dc6b starting from e31216eec445
238 238 rebasing on disk
239 239 rebase status stored
240 240 rebasing 9:e31216eec445 "more changes to f1"
241 241 future parents are 2 and -1
242 242 update to 2:4bc80088dc6b
243 243 resolving manifests
244 244 branchmerge: False, force: True, partial: False
245 245 ancestor: d79e2059b5c0+, local: d79e2059b5c0+, remote: 4bc80088dc6b
246 246 f2.txt: other deleted -> r
247 247 removing f2.txt
248 248 f1.txt: remote created -> g
249 249 getting f1.txt
250 250 merge against 9:e31216eec445
251 251 detach base 8:8e4e2c1a07ae
252 252 resolving manifests
253 253 branchmerge: True, force: True, partial: False
254 254 ancestor: 8e4e2c1a07ae, local: 4bc80088dc6b+, remote: e31216eec445
255 255 f1.txt: remote is newer -> g
256 256 getting f1.txt
257 257 committing files:
258 258 f1.txt
259 259 committing manifest
260 260 committing changelog
261 261 updating the branch cache
262 262 rebased as 19c888675e13
263 263 rebase status stored
264 264 rebasing 10:2f2496ddf49d tip "merge"
265 265 future parents are 11 and 7
266 266 already in destination
267 267 merge against 10:2f2496ddf49d
268 268 detach base 9:e31216eec445
269 269 resolving manifests
270 270 branchmerge: True, force: True, partial: False
271 271 ancestor: e31216eec445, local: 19c888675e13+, remote: 2f2496ddf49d
272 272 f1.txt: remote is newer -> g
273 273 getting f1.txt
274 274 committing files:
275 275 f1.txt
276 276 committing manifest
277 277 committing changelog
278 278 updating the branch cache
279 279 rebased as 2a7f09cac94c
280 280 rebase status stored
281 281 rebase merging completed
282 282 update back to initial working directory parent
283 283 resolving manifests
284 284 branchmerge: False, force: False, partial: False
285 285 ancestor: 2a7f09cac94c, local: 2a7f09cac94c+, remote: d79e2059b5c0
286 286 f1.txt: other deleted -> r
287 287 removing f1.txt
288 288 f2.txt: remote created -> g
289 289 getting f2.txt
290 290 2 changesets found
291 291 list of changesets:
292 292 e31216eec445e44352c5f01588856059466a24c9
293 293 2f2496ddf49d69b5ef23ad8cf9fb2e0e4faf0ac2
294 294 bundle2-output-bundle: "HG20", (1 params) 3 parts total
295 295 bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload
296 296 bundle2-output-part: "cache:rev-branch-cache" (advisory) streamed payload
297 297 bundle2-output-part: "phase-heads" 24 bytes payload
298 298 saved backup bundle to $TESTTMP/issue4041/.hg/strip-backup/e31216eec445-15f7a814-rebase.hg
299 299 3 changesets found
300 300 list of changesets:
301 301 4c9fbe56a16f30c0d5dcc40ec1a97bbe3325209c
302 302 19c888675e133ab5dff84516926a65672eaf04d9
303 303 2a7f09cac94c7f4b73ebd5cd1a62d3b2e8e336bf
304 304 bundle2-output-bundle: "HG20", 3 parts total
305 305 bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload
306 306 bundle2-output-part: "cache:rev-branch-cache" (advisory) streamed payload
307 307 bundle2-output-part: "phase-heads" 24 bytes payload
308 308 adding branch
309 309 bundle2-input-bundle: with-transaction
310 310 bundle2-input-part: "changegroup" (params: 1 mandatory 1 advisory) supported
311 311 adding changesets
312 312 add changeset 4c9fbe56a16f
313 313 add changeset 19c888675e13
314 314 add changeset 2a7f09cac94c
315 315 adding manifests
316 316 adding file changes
317 317 adding f1.txt revisions
318 318 bundle2-input-part: total payload size 1686
319 319 bundle2-input-part: "cache:rev-branch-cache" (advisory) supported
320 320 bundle2-input-part: total payload size 74
321 321 truncating cache/rbc-revs-v1 to 56
322 322 bundle2-input-part: "phase-heads" supported
323 323 bundle2-input-part: total payload size 24
324 324 bundle2-input-bundle: 3 parts total
325 325 added 2 changesets with 2 changes to 1 files
326 326 updating the branch cache
327 327 invalid branch cache (served): tip differs
328 328 invalid branch cache (served.hidden): tip differs
329 329 rebase completed
330 330
331 331 Test minimization of merge conflicts
332 332 $ hg up -q null
333 333 $ echo a > a
334 334 $ hg add a
335 335 $ hg commit -q -m 'a'
336 336 $ echo b >> a
337 337 $ hg commit -q -m 'ab'
338 338 $ hg bookmark ab
339 339 $ hg up -q '.^'
340 340 $ echo b >> a
341 341 $ echo c >> a
342 342 $ hg commit -q -m 'abc'
343 343 $ hg rebase -s 7bc217434fc1 -d ab --keep
344 344 rebasing 13:7bc217434fc1 tip "abc"
345 345 merging a
346 346 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
347 347 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
348 348 [240]
349 349 $ hg diff
350 350 diff -r 328e4ab1f7cc a
351 351 --- a/a Thu Jan 01 00:00:00 1970 +0000
352 352 +++ b/a * (glob)
353 353 @@ -1,2 +1,6 @@
354 354 a
355 355 b
356 356 +<<<<<<< dest: 328e4ab1f7cc ab - test: ab
357 357 +=======
358 358 +c
359 359 +>>>>>>> source: 7bc217434fc1 - test: abc
360 360 $ hg rebase --abort
361 361 rebase aborted
362 362 $ hg up -q -C 7bc217434fc1
363 363 $ hg rebase -s . -d ab --keep -t internal:merge3
364 364 rebasing 13:7bc217434fc1 tip "abc"
365 365 merging a
366 366 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
367 367 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
368 368 [240]
369 369 $ hg diff
370 370 diff -r 328e4ab1f7cc a
371 371 --- a/a Thu Jan 01 00:00:00 1970 +0000
372 372 +++ b/a * (glob)
373 373 @@ -1,2 +1,8 @@
374 374 a
375 375 +<<<<<<< dest: 328e4ab1f7cc ab - test: ab
376 376 b
377 377 +||||||| base
378 378 +=======
379 379 +b
380 380 +c
381 381 +>>>>>>> source: 7bc217434fc1 - test: abc
382 382
383 383 Test rebase with obsstore turned on and off (issue5606)
384 384
385 385 $ cd $TESTTMP
386 386 $ hg init b
387 387 $ cd b
388 388 $ hg debugdrawdag <<'EOS'
389 389 > D
390 390 > |
391 391 > C
392 392 > |
393 393 > B E
394 394 > |/
395 395 > A
396 396 > EOS
397 397
398 398 $ hg update E -q
399 399 $ echo 3 > B
400 400 $ hg commit --amend -m E -A B -q
401 401 $ hg rebase -r B+D -d . --config experimental.evolution=true
402 402 rebasing 1:112478962961 B "B"
403 403 merging B
404 404 warning: conflicts while merging B! (edit, then use 'hg resolve --mark')
405 405 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
406 406 [240]
407 407
408 408 $ echo 4 > B
409 409 $ hg resolve -m
410 410 (no more unresolved files)
411 411 continue: hg rebase --continue
412 412 $ hg rebase --continue --config experimental.evolution=none
413 413 rebasing 1:112478962961 B "B"
414 414 rebasing 3:f585351a92f8 D "D"
415 415 warning: orphaned descendants detected, not stripping 112478962961
416 416 saved backup bundle to $TESTTMP/b/.hg/strip-backup/f585351a92f8-e536a9e4-rebase.hg
417 417
418 418 $ rm .hg/localtags
419 419 $ hg tglog
420 420 o 5:draft 'D'
421 421 |
422 422 o 4:draft 'B'
423 423 |
424 424 @ 3:draft 'E'
425 425 |
426 426 | o 2:draft 'C'
427 427 | |
428 428 | o 1:draft 'B'
429 429 |/
430 430 o 0:draft 'A'
431 431
432 432
433 433 Test where the conflict happens when rebasing a merge commit
434 434
435 435 $ cd $TESTTMP
436 436 $ hg init conflict-in-merge
437 437 $ cd conflict-in-merge
438 438 $ hg debugdrawdag <<'EOS'
439 439 > F # F/conflict = foo\n
440 440 > |\
441 441 > D E
442 442 > |/
443 443 > C B # B/conflict = bar\n
444 444 > |/
445 445 > A
446 446 > EOS
447 447
448 448 $ hg co F
449 449 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
450 450 $ hg rebase -d B
451 451 rebasing 2:dc0947a82db8 C "C"
452 452 rebasing 3:e7b3f00ed42e D "D"
453 453 rebasing 4:03ca77807e91 E "E"
454 454 rebasing 5:9a6b91dc2044 F tip "F"
455 455 merging conflict
456 456 warning: conflicts while merging conflict! (edit, then use 'hg resolve --mark')
457 457 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
458 458 [240]
459 459 $ hg tglog
460 460 @ 8:draft 'E'
461 461 |
462 462 | @ 7:draft 'D'
463 463 |/
464 464 o 6:draft 'C'
465 465 |
466 466 | % 5:draft 'F'
467 467 | |\
468 468 | | o 4:draft 'E'
469 469 | | |
470 470 | o | 3:draft 'D'
471 471 | |/
472 472 | o 2:draft 'C'
473 473 | |
474 474 o | 1:draft 'B'
475 475 |/
476 476 o 0:draft 'A'
477 477
478 478 $ echo baz > conflict
479 479 $ hg resolve -m
480 480 (no more unresolved files)
481 481 continue: hg rebase --continue
482 482 $ hg rebase -c
483 483 already rebased 2:dc0947a82db8 C "C" as 0199610c343e
484 484 already rebased 3:e7b3f00ed42e D "D" as f0dd538aaa63
485 485 already rebased 4:03ca77807e91 E "E" as cbf25af8347d
486 486 rebasing 5:9a6b91dc2044 F "F"
487 487 saved backup bundle to $TESTTMP/conflict-in-merge/.hg/strip-backup/dc0947a82db8-ca7e7d5b-rebase.hg
488 488 $ hg tglog
489 489 @ 5:draft 'F'
490 490 |\
491 491 | o 4:draft 'E'
492 492 | |
493 493 o | 3:draft 'D'
494 494 |/
495 495 o 2:draft 'C'
496 496 |
497 497 o 1:draft 'B'
498 498 |
499 499 o 0:draft 'A'
500 500
@@ -1,974 +1,974 b''
1 1 #require symlink execbit
2 2 $ cat << EOF >> $HGRCPATH
3 3 > [phases]
4 4 > publish=False
5 5 > [extensions]
6 6 > amend=
7 7 > rebase=
8 8 > debugdrawdag=$TESTDIR/drawdag.py
9 9 > strip=
10 10 > [rebase]
11 11 > experimental.inmemory=1
12 12 > [diff]
13 13 > git=1
14 14 > [alias]
15 15 > tglog = log -G --template "{rev}: {node|short} '{desc}'\n"
16 16 > EOF
17 17
18 18 Rebase a simple DAG:
19 19 $ hg init repo1
20 20 $ cd repo1
21 21 $ hg debugdrawdag <<'EOS'
22 22 > c b
23 23 > |/
24 24 > d
25 25 > |
26 26 > a
27 27 > EOS
28 28 $ hg up -C a
29 29 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
30 30 $ hg tglog
31 31 o 3: 814f6bd05178 'c'
32 32 |
33 33 | o 2: db0e82a16a62 'b'
34 34 |/
35 35 o 1: 02952614a83d 'd'
36 36 |
37 37 @ 0: b173517d0057 'a'
38 38
39 39 $ hg cat -r 3 c
40 40 c (no-eol)
41 41 $ hg cat -r 2 b
42 42 b (no-eol)
43 43 $ hg rebase --debug -r b -d c | grep rebasing
44 44 rebasing in memory
45 45 rebasing 2:db0e82a16a62 b "b"
46 46 $ hg tglog
47 47 o 3: ca58782ad1e4 'b'
48 48 |
49 49 o 2: 814f6bd05178 'c'
50 50 |
51 51 o 1: 02952614a83d 'd'
52 52 |
53 53 @ 0: b173517d0057 'a'
54 54
55 55 $ hg cat -r 3 b
56 56 b (no-eol)
57 57 $ hg cat -r 2 c
58 58 c (no-eol)
59 59 $ cd ..
60 60
61 61 Case 2:
62 62 $ hg init repo2
63 63 $ cd repo2
64 64 $ hg debugdrawdag <<'EOS'
65 65 > c b
66 66 > |/
67 67 > d
68 68 > |
69 69 > a
70 70 > EOS
71 71
72 72 Add a symlink and executable file:
73 73 $ hg up -C c
74 74 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
75 75 $ ln -s somefile e
76 76 $ echo f > f
77 77 $ chmod +x f
78 78 $ hg add e f
79 79 $ hg amend -q
80 80 $ hg up -Cq a
81 81
82 82 Write files to the working copy, and ensure they're still there after the rebase
83 83 $ echo "abc" > a
84 84 $ ln -s def b
85 85 $ echo "ghi" > c
86 86 $ echo "jkl" > d
87 87 $ echo "mno" > e
88 88 $ hg tglog
89 89 o 3: f56b71190a8f 'c'
90 90 |
91 91 | o 2: db0e82a16a62 'b'
92 92 |/
93 93 o 1: 02952614a83d 'd'
94 94 |
95 95 @ 0: b173517d0057 'a'
96 96
97 97 $ hg cat -r 3 c
98 98 c (no-eol)
99 99 $ hg cat -r 2 b
100 100 b (no-eol)
101 101 $ hg cat -r 3 e
102 102 somefile (no-eol)
103 103 $ hg rebase --debug -s b -d a | grep rebasing
104 104 rebasing in memory
105 105 rebasing 2:db0e82a16a62 b "b"
106 106 $ hg tglog
107 107 o 3: fc055c3b4d33 'b'
108 108 |
109 109 | o 2: f56b71190a8f 'c'
110 110 | |
111 111 | o 1: 02952614a83d 'd'
112 112 |/
113 113 @ 0: b173517d0057 'a'
114 114
115 115 $ hg cat -r 2 c
116 116 c (no-eol)
117 117 $ hg cat -r 3 b
118 118 b (no-eol)
119 119 $ hg rebase --debug -s 1 -d 3 | grep rebasing
120 120 rebasing in memory
121 121 rebasing 1:02952614a83d d "d"
122 122 rebasing 2:f56b71190a8f "c"
123 123 $ hg tglog
124 124 o 3: 753feb6fd12a 'c'
125 125 |
126 126 o 2: 09c044d2cb43 'd'
127 127 |
128 128 o 1: fc055c3b4d33 'b'
129 129 |
130 130 @ 0: b173517d0057 'a'
131 131
132 132 Ensure working copy files are still there:
133 133 $ cat a
134 134 abc
135 135 $ readlink.py b
136 136 b -> def
137 137 $ cat e
138 138 mno
139 139
140 140 Ensure symlink and executable files were rebased properly:
141 141 $ hg up -Cq 3
142 142 $ readlink.py e
143 143 e -> somefile
144 144 $ ls -l f | cut -c -10
145 145 -rwxr-xr-x
146 146
147 147 Rebase the working copy parent
148 148 $ hg up -C 3
149 149 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 150 $ hg rebase -r 3 -d 0 --debug | grep rebasing
151 151 rebasing in memory
152 152 rebasing 3:753feb6fd12a tip "c"
153 153 $ hg tglog
154 154 @ 3: 844a7de3e617 'c'
155 155 |
156 156 | o 2: 09c044d2cb43 'd'
157 157 | |
158 158 | o 1: fc055c3b4d33 'b'
159 159 |/
160 160 o 0: b173517d0057 'a'
161 161
162 162
163 163 Test reporting of path conflicts
164 164
165 165 $ hg rm a
166 166 $ mkdir a
167 167 $ touch a/a
168 168 $ hg ci -Am "a/a"
169 169 adding a/a
170 170 $ hg tglog
171 171 @ 4: daf7dfc139cb 'a/a'
172 172 |
173 173 o 3: 844a7de3e617 'c'
174 174 |
175 175 | o 2: 09c044d2cb43 'd'
176 176 | |
177 177 | o 1: fc055c3b4d33 'b'
178 178 |/
179 179 o 0: b173517d0057 'a'
180 180
181 181 $ hg rebase -r . -d 2
182 182 rebasing 4:daf7dfc139cb tip "a/a"
183 183 saved backup bundle to $TESTTMP/repo2/.hg/strip-backup/daf7dfc139cb-fdbfcf4f-rebase.hg
184 184
185 185 $ hg tglog
186 186 @ 4: c6ad37a4f250 'a/a'
187 187 |
188 188 | o 3: 844a7de3e617 'c'
189 189 | |
190 190 o | 2: 09c044d2cb43 'd'
191 191 | |
192 192 o | 1: fc055c3b4d33 'b'
193 193 |/
194 194 o 0: b173517d0057 'a'
195 195
196 196 $ echo foo > foo
197 197 $ hg ci -Aqm "added foo"
198 198 $ hg up '.^'
199 199 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
200 200 $ echo bar > bar
201 201 $ hg ci -Aqm "added bar"
202 202 $ hg rm a/a
203 203 $ echo a > a
204 204 $ hg ci -Aqm "added a back!"
205 205 $ hg tglog
206 206 @ 7: 855e9797387e 'added a back!'
207 207 |
208 208 o 6: d14530e5e3e6 'added bar'
209 209 |
210 210 | o 5: 9b94b9373deb 'added foo'
211 211 |/
212 212 o 4: c6ad37a4f250 'a/a'
213 213 |
214 214 | o 3: 844a7de3e617 'c'
215 215 | |
216 216 o | 2: 09c044d2cb43 'd'
217 217 | |
218 218 o | 1: fc055c3b4d33 'b'
219 219 |/
220 220 o 0: b173517d0057 'a'
221 221
222 222 $ hg rebase -r . -d 5
223 223 rebasing 7:855e9797387e tip "added a back!"
224 224 saved backup bundle to $TESTTMP/repo2/.hg/strip-backup/855e9797387e-81ee4c5d-rebase.hg
225 225
226 226 $ hg tglog
227 227 @ 7: bb3f02be2688 'added a back!'
228 228 |
229 229 | o 6: d14530e5e3e6 'added bar'
230 230 | |
231 231 o | 5: 9b94b9373deb 'added foo'
232 232 |/
233 233 o 4: c6ad37a4f250 'a/a'
234 234 |
235 235 | o 3: 844a7de3e617 'c'
236 236 | |
237 237 o | 2: 09c044d2cb43 'd'
238 238 | |
239 239 o | 1: fc055c3b4d33 'b'
240 240 |/
241 241 o 0: b173517d0057 'a'
242 242
243 243 $ mkdir -p c/subdir
244 244 $ echo c > c/subdir/file.txt
245 245 $ hg add c/subdir/file.txt
246 246 $ hg ci -m 'c/subdir/file.txt'
247 247 $ hg rebase -r . -d 3 -n
248 248 starting dry-run rebase; repository will not be changed
249 249 rebasing 8:e147e6e3c490 tip "c/subdir/file.txt"
250 250 abort: error: 'c/subdir/file.txt' conflicts with file 'c' in 3.
251 251 [255]
252 252 FIXME: shouldn't need this, but when we hit path conflicts in dryrun mode, we
253 253 don't clean up rebasestate.
254 254 $ hg rebase --abort
255 255 rebase aborted
256 256 $ hg rebase -r 3 -d . -n
257 257 starting dry-run rebase; repository will not be changed
258 258 rebasing 3:844a7de3e617 "c"
259 259 abort: error: file 'c' cannot be written because 'c/' is a directory in e147e6e3c490 (containing 1 entries: c/subdir/file.txt)
260 260 [255]
261 261
262 262 $ cd ..
263 263
264 264 Test path auditing (issue5818)
265 265
266 266 $ mkdir lib_
267 267 $ ln -s lib_ lib
268 268 $ hg init repo
269 269 $ cd repo
270 270 $ mkdir -p ".$TESTTMP/lib"
271 271 $ touch ".$TESTTMP/lib/a"
272 272 $ hg add ".$TESTTMP/lib/a"
273 273 $ hg ci -m 'a'
274 274
275 275 $ touch ".$TESTTMP/lib/b"
276 276 $ hg add ".$TESTTMP/lib/b"
277 277 $ hg ci -m 'b'
278 278
279 279 $ hg up -q '.^'
280 280 $ touch ".$TESTTMP/lib/c"
281 281 $ hg add ".$TESTTMP/lib/c"
282 282 $ hg ci -m 'c'
283 283 created new head
284 284 $ hg rebase -s 1 -d .
285 285 rebasing 1:* "b" (glob)
286 286 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-rebase.hg (glob)
287 287 $ cd ..
288 288
289 289 Test dry-run rebasing
290 290
291 291 $ hg init repo3
292 292 $ cd repo3
293 293 $ echo a>a
294 294 $ hg ci -Aqma
295 295 $ echo b>b
296 296 $ hg ci -Aqmb
297 297 $ echo c>c
298 298 $ hg ci -Aqmc
299 299 $ echo d>d
300 300 $ hg ci -Aqmd
301 301 $ echo e>e
302 302 $ hg ci -Aqme
303 303
304 304 $ hg up 1 -q
305 305 $ echo f>f
306 306 $ hg ci -Amf
307 307 adding f
308 308 created new head
309 309 $ echo g>g
310 310 $ hg ci -Aqmg
311 311 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
312 312 @ 6:baf10c5166d4 test
313 313 | g
314 314 |
315 315 o 5:6343ca3eff20 test
316 316 | f
317 317 |
318 318 | o 4:e860deea161a test
319 319 | | e
320 320 | |
321 321 | o 3:055a42cdd887 test
322 322 | | d
323 323 | |
324 324 | o 2:177f92b77385 test
325 325 |/ c
326 326 |
327 327 o 1:d2ae7f538514 test
328 328 | b
329 329 |
330 330 o 0:cb9a9f314b8b test
331 331 a
332 332
333 333 Make sure it throws error while passing --continue or --abort with --dry-run
334 334 $ hg rebase -s 2 -d 6 -n --continue
335 335 abort: cannot specify both --continue and --dry-run
336 336 [10]
337 337 $ hg rebase -s 2 -d 6 -n --abort
338 338 abort: cannot specify both --abort and --dry-run
339 339 [10]
340 340
341 341 Check dryrun gives correct results when there is no conflict in rebasing
342 342 $ hg rebase -s 2 -d 6 -n
343 343 starting dry-run rebase; repository will not be changed
344 344 rebasing 2:177f92b77385 "c"
345 345 rebasing 3:055a42cdd887 "d"
346 346 rebasing 4:e860deea161a "e"
347 347 dry-run rebase completed successfully; run without -n/--dry-run to perform this rebase
348 348
349 349 $ hg diff
350 350 $ hg status
351 351
352 352 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
353 353 @ 6:baf10c5166d4 test
354 354 | g
355 355 |
356 356 o 5:6343ca3eff20 test
357 357 | f
358 358 |
359 359 | o 4:e860deea161a test
360 360 | | e
361 361 | |
362 362 | o 3:055a42cdd887 test
363 363 | | d
364 364 | |
365 365 | o 2:177f92b77385 test
366 366 |/ c
367 367 |
368 368 o 1:d2ae7f538514 test
369 369 | b
370 370 |
371 371 o 0:cb9a9f314b8b test
372 372 a
373 373
374 374 Check dryrun working with --collapse when there is no conflict
375 375 $ hg rebase -s 2 -d 6 -n --collapse
376 376 starting dry-run rebase; repository will not be changed
377 377 rebasing 2:177f92b77385 "c"
378 378 rebasing 3:055a42cdd887 "d"
379 379 rebasing 4:e860deea161a "e"
380 380 dry-run rebase completed successfully; run without -n/--dry-run to perform this rebase
381 381
382 382 Check dryrun gives correct results when there is conflict in rebasing
383 383 Make a conflict:
384 384 $ hg up 6 -q
385 385 $ echo conflict>e
386 386 $ hg ci -Aqm "conflict with e"
387 387 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
388 388 @ 7:d2c195b28050 test
389 389 | conflict with e
390 390 |
391 391 o 6:baf10c5166d4 test
392 392 | g
393 393 |
394 394 o 5:6343ca3eff20 test
395 395 | f
396 396 |
397 397 | o 4:e860deea161a test
398 398 | | e
399 399 | |
400 400 | o 3:055a42cdd887 test
401 401 | | d
402 402 | |
403 403 | o 2:177f92b77385 test
404 404 |/ c
405 405 |
406 406 o 1:d2ae7f538514 test
407 407 | b
408 408 |
409 409 o 0:cb9a9f314b8b test
410 410 a
411 411
412 412 $ hg rebase -s 2 -d 7 -n
413 413 starting dry-run rebase; repository will not be changed
414 414 rebasing 2:177f92b77385 "c"
415 415 rebasing 3:055a42cdd887 "d"
416 416 rebasing 4:e860deea161a "e"
417 417 merging e
418 418 hit a merge conflict
419 419 [1]
420 420 $ hg diff
421 421 $ hg status
422 422 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
423 423 @ 7:d2c195b28050 test
424 424 | conflict with e
425 425 |
426 426 o 6:baf10c5166d4 test
427 427 | g
428 428 |
429 429 o 5:6343ca3eff20 test
430 430 | f
431 431 |
432 432 | o 4:e860deea161a test
433 433 | | e
434 434 | |
435 435 | o 3:055a42cdd887 test
436 436 | | d
437 437 | |
438 438 | o 2:177f92b77385 test
439 439 |/ c
440 440 |
441 441 o 1:d2ae7f538514 test
442 442 | b
443 443 |
444 444 o 0:cb9a9f314b8b test
445 445 a
446 446
447 447 Check dryrun working with --collapse when there is conflicts
448 448 $ hg rebase -s 2 -d 7 -n --collapse
449 449 starting dry-run rebase; repository will not be changed
450 450 rebasing 2:177f92b77385 "c"
451 451 rebasing 3:055a42cdd887 "d"
452 452 rebasing 4:e860deea161a "e"
453 453 merging e
454 454 hit a merge conflict
455 455 [1]
456 456
457 457 In-memory rebase that fails due to merge conflicts
458 458
459 459 $ hg rebase -s 2 -d 7
460 460 rebasing 2:177f92b77385 "c"
461 461 rebasing 3:055a42cdd887 "d"
462 462 rebasing 4:e860deea161a "e"
463 463 merging e
464 464 hit merge conflicts; rebasing that commit again in the working copy
465 465 merging e
466 466 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
467 467 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
468 468 [240]
469 469 $ hg rebase --abort
470 470 saved backup bundle to $TESTTMP/repo3/.hg/strip-backup/c1e524d4287c-f91f82e1-backup.hg
471 471 rebase aborted
472 472
473 473 Retrying without in-memory merge won't lose working copy changes
474 474 $ cd ..
475 475 $ hg clone repo3 repo3-dirty -q
476 476 $ cd repo3-dirty
477 477 $ echo dirty > a
478 478 $ hg rebase -s 2 -d 7
479 479 rebasing 2:177f92b77385 "c"
480 480 rebasing 3:055a42cdd887 "d"
481 481 rebasing 4:e860deea161a "e"
482 482 merging e
483 483 hit merge conflicts; rebasing that commit again in the working copy
484 484 transaction abort!
485 485 rollback completed
486 486 abort: uncommitted changes
487 [255]
487 [20]
488 488 $ cat a
489 489 dirty
490 490
491 491 Retrying without in-memory merge won't lose merge state
492 492 $ cd ..
493 493 $ hg clone repo3 repo3-merge-state -q
494 494 $ cd repo3-merge-state
495 495 $ hg merge 4
496 496 merging e
497 497 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
498 498 2 files updated, 0 files merged, 0 files removed, 1 files unresolved
499 499 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
500 500 [1]
501 501 $ hg resolve -l
502 502 U e
503 503 $ hg rebase -s 2 -d 7
504 504 abort: outstanding uncommitted merge
505 505 (use 'hg commit' or 'hg merge --abort')
506 [255]
506 [20]
507 507 $ hg resolve -l
508 508 U e
509 509
510 510 ==========================
511 511 Test for --confirm option|
512 512 ==========================
513 513 $ cd ..
514 514 $ hg clone repo3 repo4 -q
515 515 $ cd repo4
516 516 $ hg strip 7 -q
517 517 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
518 518 @ 6:baf10c5166d4 test
519 519 | g
520 520 |
521 521 o 5:6343ca3eff20 test
522 522 | f
523 523 |
524 524 | o 4:e860deea161a test
525 525 | | e
526 526 | |
527 527 | o 3:055a42cdd887 test
528 528 | | d
529 529 | |
530 530 | o 2:177f92b77385 test
531 531 |/ c
532 532 |
533 533 o 1:d2ae7f538514 test
534 534 | b
535 535 |
536 536 o 0:cb9a9f314b8b test
537 537 a
538 538
539 539 Check it gives error when both --dryrun and --confirm is used:
540 540 $ hg rebase -s 2 -d . --confirm --dry-run
541 541 abort: cannot specify both --confirm and --dry-run
542 542 [10]
543 543 $ hg rebase -s 2 -d . --confirm --abort
544 544 abort: cannot specify both --abort and --confirm
545 545 [10]
546 546 $ hg rebase -s 2 -d . --confirm --continue
547 547 abort: cannot specify both --continue and --confirm
548 548 [10]
549 549
550 550 Test --confirm option when there are no conflicts:
551 551 $ hg rebase -s 2 -d . --keep --config ui.interactive=True --confirm << EOF
552 552 > n
553 553 > EOF
554 554 starting in-memory rebase
555 555 rebasing 2:177f92b77385 "c"
556 556 rebasing 3:055a42cdd887 "d"
557 557 rebasing 4:e860deea161a "e"
558 558 rebase completed successfully
559 559 apply changes (yn)? n
560 560 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
561 561 @ 6:baf10c5166d4 test
562 562 | g
563 563 |
564 564 o 5:6343ca3eff20 test
565 565 | f
566 566 |
567 567 | o 4:e860deea161a test
568 568 | | e
569 569 | |
570 570 | o 3:055a42cdd887 test
571 571 | | d
572 572 | |
573 573 | o 2:177f92b77385 test
574 574 |/ c
575 575 |
576 576 o 1:d2ae7f538514 test
577 577 | b
578 578 |
579 579 o 0:cb9a9f314b8b test
580 580 a
581 581
582 582 $ hg rebase -s 2 -d . --keep --config ui.interactive=True --confirm << EOF
583 583 > y
584 584 > EOF
585 585 starting in-memory rebase
586 586 rebasing 2:177f92b77385 "c"
587 587 rebasing 3:055a42cdd887 "d"
588 588 rebasing 4:e860deea161a "e"
589 589 rebase completed successfully
590 590 apply changes (yn)? y
591 591 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
592 592 o 9:9fd28f55f6dc test
593 593 | e
594 594 |
595 595 o 8:12cbf031f469 test
596 596 | d
597 597 |
598 598 o 7:c83b1da5b1ae test
599 599 | c
600 600 |
601 601 @ 6:baf10c5166d4 test
602 602 | g
603 603 |
604 604 o 5:6343ca3eff20 test
605 605 | f
606 606 |
607 607 | o 4:e860deea161a test
608 608 | | e
609 609 | |
610 610 | o 3:055a42cdd887 test
611 611 | | d
612 612 | |
613 613 | o 2:177f92b77385 test
614 614 |/ c
615 615 |
616 616 o 1:d2ae7f538514 test
617 617 | b
618 618 |
619 619 o 0:cb9a9f314b8b test
620 620 a
621 621
622 622 Test --confirm option when there is a conflict
623 623 $ hg up tip -q
624 624 $ echo ee>e
625 625 $ hg ci --amend -m "conflict with e" -q
626 626 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
627 627 @ 9:906d72f66a59 test
628 628 | conflict with e
629 629 |
630 630 o 8:12cbf031f469 test
631 631 | d
632 632 |
633 633 o 7:c83b1da5b1ae test
634 634 | c
635 635 |
636 636 o 6:baf10c5166d4 test
637 637 | g
638 638 |
639 639 o 5:6343ca3eff20 test
640 640 | f
641 641 |
642 642 | o 4:e860deea161a test
643 643 | | e
644 644 | |
645 645 | o 3:055a42cdd887 test
646 646 | | d
647 647 | |
648 648 | o 2:177f92b77385 test
649 649 |/ c
650 650 |
651 651 o 1:d2ae7f538514 test
652 652 | b
653 653 |
654 654 o 0:cb9a9f314b8b test
655 655 a
656 656
657 657 $ hg rebase -s 4 -d . --keep --confirm
658 658 starting in-memory rebase
659 659 rebasing 4:e860deea161a "e"
660 660 merging e
661 661 hit a merge conflict
662 662 [1]
663 663 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
664 664 @ 9:906d72f66a59 test
665 665 | conflict with e
666 666 |
667 667 o 8:12cbf031f469 test
668 668 | d
669 669 |
670 670 o 7:c83b1da5b1ae test
671 671 | c
672 672 |
673 673 o 6:baf10c5166d4 test
674 674 | g
675 675 |
676 676 o 5:6343ca3eff20 test
677 677 | f
678 678 |
679 679 | o 4:e860deea161a test
680 680 | | e
681 681 | |
682 682 | o 3:055a42cdd887 test
683 683 | | d
684 684 | |
685 685 | o 2:177f92b77385 test
686 686 |/ c
687 687 |
688 688 o 1:d2ae7f538514 test
689 689 | b
690 690 |
691 691 o 0:cb9a9f314b8b test
692 692 a
693 693
694 694 Test a metadata-only in-memory merge
695 695 $ cd $TESTTMP
696 696 $ hg init no_exception
697 697 $ cd no_exception
698 698 # Produce the following graph:
699 699 # o 'add +x to foo.txt'
700 700 # | o r1 (adds bar.txt, just for something to rebase to)
701 701 # |/
702 702 # o r0 (adds foo.txt, no +x)
703 703 $ echo hi > foo.txt
704 704 $ hg ci -qAm r0
705 705 $ echo hi > bar.txt
706 706 $ hg ci -qAm r1
707 707 $ hg co -qr ".^"
708 708 $ chmod +x foo.txt
709 709 $ hg ci -qAm 'add +x to foo.txt'
710 710 issue5960: this was raising an AttributeError exception
711 711 $ hg rebase -r . -d 1
712 712 rebasing 2:539b93e77479 tip "add +x to foo.txt"
713 713 saved backup bundle to $TESTTMP/no_exception/.hg/strip-backup/*.hg (glob)
714 714 $ hg diff -c tip
715 715 diff --git a/foo.txt b/foo.txt
716 716 old mode 100644
717 717 new mode 100755
718 718
719 719 Test rebasing a commit with copy information, but no content changes
720 720
721 721 $ cd ..
722 722 $ hg clone -q repo1 merge-and-rename
723 723 $ cd merge-and-rename
724 724 $ cat << EOF >> .hg/hgrc
725 725 > [experimental]
726 726 > evolution.createmarkers=True
727 727 > evolution.allowunstable=True
728 728 > EOF
729 729 $ hg co -q 1
730 730 $ hg mv d e
731 731 $ hg ci -qm 'rename d to e'
732 732 $ hg co -q 3
733 733 $ hg merge -q 4
734 734 $ hg ci -m 'merge'
735 735 $ hg co -q 2
736 736 $ mv d e
737 737 $ hg addremove -qs 0
738 738 $ hg ci -qm 'untracked rename of d to e'
739 739 $ hg debugobsolete -q `hg log -T '{node}' -r 4` `hg log -T '{node}' -r .`
740 740 1 new orphan changesets
741 741 $ hg tglog
742 742 @ 6: 676538af172d 'untracked rename of d to e'
743 743 |
744 744 | * 5: 574d92ad16fc 'merge'
745 745 | |\
746 746 | | x 4: 2c8b5dad7956 'rename d to e'
747 747 | | |
748 748 | o | 3: ca58782ad1e4 'b'
749 749 |/ /
750 750 o / 2: 814f6bd05178 'c'
751 751 |/
752 752 o 1: 02952614a83d 'd'
753 753 |
754 754 o 0: b173517d0057 'a'
755 755
756 756 $ hg rebase -b 5 -d tip
757 757 rebasing 3:ca58782ad1e4 "b"
758 758 rebasing 5:574d92ad16fc "merge"
759 759 note: not rebasing 5:574d92ad16fc "merge", its destination already has all its changes
760 760
761 761 $ cd ..
762 762
763 763 Test rebasing a commit with copy information
764 764
765 765 $ hg init rebase-rename
766 766 $ cd rebase-rename
767 767 $ echo a > a
768 768 $ hg ci -Aqm 'add a'
769 769 $ echo a2 > a
770 770 $ hg ci -m 'modify a'
771 771 $ hg co -q 0
772 772 $ hg mv a b
773 773 $ hg ci -qm 'rename a to b'
774 774 $ hg rebase -d 1
775 775 rebasing 2:b977edf6f839 tip "rename a to b"
776 776 merging a and b to b
777 777 saved backup bundle to $TESTTMP/rebase-rename/.hg/strip-backup/b977edf6f839-0864f570-rebase.hg
778 778 $ hg st --copies --change .
779 779 A b
780 780 a
781 781 R a
782 782 $ cd ..
783 783
784 784 Test rebasing a commit with copy information, where the target is empty
785 785
786 786 $ hg init rebase-rename-empty
787 787 $ cd rebase-rename-empty
788 788 $ echo a > a
789 789 $ hg ci -Aqm 'add a'
790 790 $ cat > a
791 791 $ hg ci -m 'make a empty'
792 792 $ hg co -q 0
793 793 $ hg mv a b
794 794 $ hg ci -qm 'rename a to b'
795 795 $ hg rebase -d 1
796 796 rebasing 2:b977edf6f839 tip "rename a to b"
797 797 merging a and b to b
798 798 saved backup bundle to $TESTTMP/rebase-rename-empty/.hg/strip-backup/b977edf6f839-0864f570-rebase.hg
799 799 $ hg st --copies --change .
800 800 A b
801 801 a
802 802 R a
803 803 $ cd ..
804 804 Rebase across a copy with --collapse
805 805
806 806 $ hg init rebase-rename-collapse
807 807 $ cd rebase-rename-collapse
808 808 $ echo a > a
809 809 $ hg ci -Aqm 'add a'
810 810 $ hg mv a b
811 811 $ hg ci -m 'rename a to b'
812 812 $ hg co -q 0
813 813 $ echo a2 > a
814 814 $ hg ci -qm 'modify a'
815 815 $ hg rebase -r . -d 1 --collapse
816 816 rebasing 2:41c4ea50d4cf tip "modify a"
817 817 merging b and a to b
818 818 saved backup bundle to $TESTTMP/rebase-rename-collapse/.hg/strip-backup/41c4ea50d4cf-b90b7994-rebase.hg
819 819 $ cd ..
820 820
821 821 Test rebasing when the file we are merging in destination is empty
822 822
823 823 $ hg init test
824 824 $ cd test
825 825 $ echo a > foo
826 826 $ hg ci -Aqm 'added a to foo'
827 827
828 828 $ rm foo
829 829 $ touch foo
830 830 $ hg di
831 831 diff --git a/foo b/foo
832 832 --- a/foo
833 833 +++ b/foo
834 834 @@ -1,1 +0,0 @@
835 835 -a
836 836
837 837 $ hg ci -m "make foo an empty file"
838 838
839 839 $ hg up '.^'
840 840 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
841 841 $ echo b > foo
842 842 $ hg di
843 843 diff --git a/foo b/foo
844 844 --- a/foo
845 845 +++ b/foo
846 846 @@ -1,1 +1,1 @@
847 847 -a
848 848 +b
849 849 $ hg ci -m "add b to foo"
850 850 created new head
851 851
852 852 $ hg rebase -r . -d 1 --config ui.merge=internal:merge3
853 853 rebasing 2:fb62b706688e tip "add b to foo"
854 854 merging foo
855 855 hit merge conflicts; rebasing that commit again in the working copy
856 856 merging foo
857 857 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
858 858 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
859 859 [240]
860 860
861 861 $ cd $TESTTMP
862 862
863 863 Test rebasing when we're in the middle of a rebase already
864 864 $ hg init test_issue6214
865 865 $ cd test_issue6214
866 866 $ echo r0 > r0
867 867 $ hg ci -qAm 'r0'
868 868 $ echo hi > foo
869 869 $ hg ci -qAm 'hi from foo'
870 870 $ hg co -q '.^'
871 871 $ echo bye > foo
872 872 $ hg ci -qAm 'bye from foo'
873 873 $ hg co -q '.^'
874 874 $ echo unrelated > some_other_file
875 875 $ hg ci -qAm 'some unrelated changes'
876 876 $ hg log -G -T'{rev}: {desc}\n{files%"{file}\n"}'
877 877 @ 3: some unrelated changes
878 878 | some_other_file
879 879 | o 2: bye from foo
880 880 |/ foo
881 881 | o 1: hi from foo
882 882 |/ foo
883 883 o 0: r0
884 884 r0
885 885 $ hg rebase -r 2 -d 1 -t:merge3
886 886 rebasing 2:b4d249fbf8dd "bye from foo"
887 887 merging foo
888 888 hit merge conflicts; rebasing that commit again in the working copy
889 889 merging foo
890 890 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
891 891 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
892 892 [240]
893 893 $ hg rebase -r 3 -d 1 -t:merge3
894 894 abort: rebase in progress
895 895 (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop')
896 [255]
896 [20]
897 897 $ hg resolve --list
898 898 U foo
899 899 $ hg resolve --all --re-merge -t:other
900 900 (no more unresolved files)
901 901 continue: hg rebase --continue
902 902 $ hg rebase --continue
903 903 rebasing 2:b4d249fbf8dd "bye from foo"
904 904 saved backup bundle to $TESTTMP/test_issue6214/.hg/strip-backup/b4d249fbf8dd-299ec25c-rebase.hg
905 905 $ hg log -G -T'{rev}: {desc}\n{files%"{file}\n"}'
906 906 o 3: bye from foo
907 907 | foo
908 908 | @ 2: some unrelated changes
909 909 | | some_other_file
910 910 o | 1: hi from foo
911 911 |/ foo
912 912 o 0: r0
913 913 r0
914 914
915 915 $ cd ..
916 916
917 917 Changesets that become empty should not be committed. Merges are not empty by
918 918 definition.
919 919
920 920 $ hg init keep_merge
921 921 $ cd keep_merge
922 922 $ echo base > base; hg add base; hg ci -m base
923 923 $ echo test > test; hg add test; hg ci -m a
924 924 $ hg up 0 -q
925 925 $ echo test > test; hg add test; hg ci -m b -q
926 926 $ hg up 0 -q
927 927 $ echo test > test; hg add test; hg ci -m c -q
928 928 $ hg up 1 -q
929 929 $ hg merge 2 -q
930 930 $ hg ci -m merge
931 931 $ hg up null -q
932 932 $ hg tglog
933 933 o 4: 59c8292117b1 'merge'
934 934 |\
935 935 | | o 3: 531f80391e4a 'c'
936 936 | | |
937 937 | o | 2: 0194f1db184a 'b'
938 938 | |/
939 939 o / 1: 6f252845ea45 'a'
940 940 |/
941 941 o 0: d20a80d4def3 'base'
942 942
943 943 $ hg rebase -s 2 -d 3
944 944 rebasing 2:0194f1db184a "b"
945 945 note: not rebasing 2:0194f1db184a "b", its destination already has all its changes
946 946 rebasing 4:59c8292117b1 tip "merge"
947 947 saved backup bundle to $TESTTMP/keep_merge/.hg/strip-backup/0194f1db184a-aee31d03-rebase.hg
948 948 $ hg tglog
949 949 o 3: 506e2454484b 'merge'
950 950 |\
951 951 | o 2: 531f80391e4a 'c'
952 952 | |
953 953 o | 1: 6f252845ea45 'a'
954 954 |/
955 955 o 0: d20a80d4def3 'base'
956 956
957 957
958 958 $ cd ..
959 959
960 960 Test (virtual) working directory without changes, created by merge conflict
961 961 resolution. There was a regression where the file was incorrectly detected as
962 962 changed although the file contents were the same as in the parent.
963 963
964 964 $ hg init nofilechanges
965 965 $ cd nofilechanges
966 966 $ echo a > a; hg add a; hg ci -m a
967 967 $ echo foo > test; hg add test; hg ci -m b
968 968 $ hg up 0 -q
969 969 $ echo bar > test; hg add test; hg ci -m c
970 970 created new head
971 971 $ hg rebase -d 2 -d 1 --tool :local
972 972 rebasing 2:ca2749322ee5 tip "c"
973 973 note: not rebasing 2:ca2749322ee5 tip "c", its destination already has all its changes
974 974 saved backup bundle to $TESTTMP/nofilechanges/.hg/strip-backup/ca2749322ee5-6dc7e94b-rebase.hg
@@ -1,543 +1,543 b''
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extensions]
3 3 > rebase=
4 4 >
5 5 > [phases]
6 6 > publish=False
7 7 >
8 8 > [alias]
9 9 > tglog = log -G --template "{rev}: {node|short} '{desc}' {branches}\n"
10 10 > EOF
11 11
12 12 $ hg init a
13 13 $ cd a
14 14 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
15 15 adding changesets
16 16 adding manifests
17 17 adding file changes
18 18 added 8 changesets with 7 changes to 7 files (+2 heads)
19 19 new changesets cd010b8cd998:02de42196ebe (8 drafts)
20 20 (run 'hg heads' to see heads, 'hg merge' to merge)
21 21 $ hg up tip
22 22 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
23 23 $ cd ..
24 24
25 25 $ hg clone -q -u . a a1
26 26
27 27 $ cd a1
28 28
29 29 $ hg update 3
30 30 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
31 31 $ hg branch dev-one
32 32 marked working directory as branch dev-one
33 33 (branches are permanent and global, did you want a bookmark?)
34 34 $ hg ci -m 'dev-one named branch'
35 35
36 36 $ hg update 7
37 37 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
38 38 $ hg branch dev-two
39 39 marked working directory as branch dev-two
40 40
41 41 $ echo x > x
42 42
43 43 $ hg add x
44 44
45 45 $ hg ci -m 'dev-two named branch'
46 46
47 47 $ hg tglog
48 48 @ 9: cb039b7cae8e 'dev-two named branch' dev-two
49 49 |
50 50 | o 8: 643fc9128048 'dev-one named branch' dev-one
51 51 | |
52 52 o | 7: 02de42196ebe 'H'
53 53 | |
54 54 +---o 6: eea13746799a 'G'
55 55 | | |
56 56 o | | 5: 24b6387c8c8c 'F'
57 57 | | |
58 58 +---o 4: 9520eea781bc 'E'
59 59 | |
60 60 | o 3: 32af7686d403 'D'
61 61 | |
62 62 | o 2: 5fddd98957c8 'C'
63 63 | |
64 64 | o 1: 42ccdea3bb16 'B'
65 65 |/
66 66 o 0: cd010b8cd998 'A'
67 67
68 68
69 69 Branch name containing a dash (issue3181)
70 70
71 71 $ hg rebase -b dev-two -d dev-one --keepbranches
72 72 rebasing 5:24b6387c8c8c "F"
73 73 rebasing 6:eea13746799a "G"
74 74 rebasing 7:02de42196ebe "H"
75 75 rebasing 9:cb039b7cae8e tip "dev-two named branch"
76 76 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/24b6387c8c8c-24cb8001-rebase.hg
77 77
78 78 $ hg tglog
79 79 @ 9: 9e70cd31750f 'dev-two named branch' dev-two
80 80 |
81 81 o 8: 31d0e4ba75e6 'H'
82 82 |
83 83 | o 7: 4b988a958030 'G'
84 84 |/|
85 85 o | 6: 24de4aff8e28 'F'
86 86 | |
87 87 o | 5: 643fc9128048 'dev-one named branch' dev-one
88 88 | |
89 89 | o 4: 9520eea781bc 'E'
90 90 | |
91 91 o | 3: 32af7686d403 'D'
92 92 | |
93 93 o | 2: 5fddd98957c8 'C'
94 94 | |
95 95 o | 1: 42ccdea3bb16 'B'
96 96 |/
97 97 o 0: cd010b8cd998 'A'
98 98
99 99 $ hg rebase -s dev-one -d 0 --keepbranches
100 100 rebasing 5:643fc9128048 "dev-one named branch"
101 101 rebasing 6:24de4aff8e28 "F"
102 102 rebasing 7:4b988a958030 "G"
103 103 rebasing 8:31d0e4ba75e6 "H"
104 104 rebasing 9:9e70cd31750f tip "dev-two named branch"
105 105 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/643fc9128048-c4ee9ef5-rebase.hg
106 106
107 107 $ hg tglog
108 108 @ 9: 59c2e59309fe 'dev-two named branch' dev-two
109 109 |
110 110 o 8: 904590360559 'H'
111 111 |
112 112 | o 7: 1a1e6f72ec38 'G'
113 113 |/|
114 114 o | 6: 42aa3cf0fa7a 'F'
115 115 | |
116 116 o | 5: bc8139ee757c 'dev-one named branch' dev-one
117 117 | |
118 118 | o 4: 9520eea781bc 'E'
119 119 |/
120 120 | o 3: 32af7686d403 'D'
121 121 | |
122 122 | o 2: 5fddd98957c8 'C'
123 123 | |
124 124 | o 1: 42ccdea3bb16 'B'
125 125 |/
126 126 o 0: cd010b8cd998 'A'
127 127
128 128 $ hg update 3
129 129 3 files updated, 0 files merged, 3 files removed, 0 files unresolved
130 130 $ hg branch -f dev-one
131 131 marked working directory as branch dev-one
132 132 $ hg ci -m 'dev-one named branch'
133 133 created new head
134 134
135 135 $ hg tglog
136 136 @ 10: 643fc9128048 'dev-one named branch' dev-one
137 137 |
138 138 | o 9: 59c2e59309fe 'dev-two named branch' dev-two
139 139 | |
140 140 | o 8: 904590360559 'H'
141 141 | |
142 142 | | o 7: 1a1e6f72ec38 'G'
143 143 | |/|
144 144 | o | 6: 42aa3cf0fa7a 'F'
145 145 | | |
146 146 | o | 5: bc8139ee757c 'dev-one named branch' dev-one
147 147 | | |
148 148 | | o 4: 9520eea781bc 'E'
149 149 | |/
150 150 o | 3: 32af7686d403 'D'
151 151 | |
152 152 o | 2: 5fddd98957c8 'C'
153 153 | |
154 154 o | 1: 42ccdea3bb16 'B'
155 155 |/
156 156 o 0: cd010b8cd998 'A'
157 157
158 158 $ hg rebase -b 'max(branch("dev-two"))' -d dev-one --keepbranches
159 159 rebasing 5:bc8139ee757c "dev-one named branch"
160 160 note: not rebasing 5:bc8139ee757c "dev-one named branch", its destination already has all its changes
161 161 rebasing 6:42aa3cf0fa7a "F"
162 162 rebasing 7:1a1e6f72ec38 "G"
163 163 rebasing 8:904590360559 "H"
164 164 rebasing 9:59c2e59309fe "dev-two named branch"
165 165 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/bc8139ee757c-f11c1080-rebase.hg
166 166
167 167 $ hg tglog
168 168 o 9: 71325f8bc082 'dev-two named branch' dev-two
169 169 |
170 170 o 8: 12b2bc666e20 'H'
171 171 |
172 172 | o 7: 549f007a9f5f 'G'
173 173 |/|
174 174 o | 6: 679f28760620 'F'
175 175 | |
176 176 @ | 5: 643fc9128048 'dev-one named branch' dev-one
177 177 | |
178 178 | o 4: 9520eea781bc 'E'
179 179 | |
180 180 o | 3: 32af7686d403 'D'
181 181 | |
182 182 o | 2: 5fddd98957c8 'C'
183 183 | |
184 184 o | 1: 42ccdea3bb16 'B'
185 185 |/
186 186 o 0: cd010b8cd998 'A'
187 187
188 188 $ hg rebase -s 'max(branch("dev-one"))' -d 0 --keepbranches
189 189 rebasing 5:643fc9128048 "dev-one named branch"
190 190 rebasing 6:679f28760620 "F"
191 191 rebasing 7:549f007a9f5f "G"
192 192 rebasing 8:12b2bc666e20 "H"
193 193 rebasing 9:71325f8bc082 tip "dev-two named branch"
194 194 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/643fc9128048-6cdd1a52-rebase.hg
195 195
196 196 $ hg tglog
197 197 o 9: 3944801ae4ea 'dev-two named branch' dev-two
198 198 |
199 199 o 8: 8e279d293175 'H'
200 200 |
201 201 | o 7: aeefee77ab01 'G'
202 202 |/|
203 203 o | 6: e908b85f3729 'F'
204 204 | |
205 205 @ | 5: bc8139ee757c 'dev-one named branch' dev-one
206 206 | |
207 207 | o 4: 9520eea781bc 'E'
208 208 |/
209 209 | o 3: 32af7686d403 'D'
210 210 | |
211 211 | o 2: 5fddd98957c8 'C'
212 212 | |
213 213 | o 1: 42ccdea3bb16 'B'
214 214 |/
215 215 o 0: cd010b8cd998 'A'
216 216
217 217 $ hg up -r 0 > /dev/null
218 218
219 219 Rebasing descendant onto ancestor across different named branches
220 220
221 221 $ hg rebase -s 1 -d 9 --keepbranches
222 222 rebasing 1:42ccdea3bb16 "B"
223 223 rebasing 2:5fddd98957c8 "C"
224 224 rebasing 3:32af7686d403 "D"
225 225 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/42ccdea3bb16-3cb021d3-rebase.hg
226 226
227 227 $ hg tglog
228 228 o 9: e9f862ce8bad 'D'
229 229 |
230 230 o 8: a0d543090fa4 'C'
231 231 |
232 232 o 7: 3bdb949809d9 'B'
233 233 |
234 234 o 6: 3944801ae4ea 'dev-two named branch' dev-two
235 235 |
236 236 o 5: 8e279d293175 'H'
237 237 |
238 238 | o 4: aeefee77ab01 'G'
239 239 |/|
240 240 o | 3: e908b85f3729 'F'
241 241 | |
242 242 o | 2: bc8139ee757c 'dev-one named branch' dev-one
243 243 | |
244 244 | o 1: 9520eea781bc 'E'
245 245 |/
246 246 @ 0: cd010b8cd998 'A'
247 247
248 248 $ hg rebase -s 5 -d 6
249 249 abort: source and destination form a cycle
250 250 [255]
251 251
252 252 $ hg rebase -s 6 -d 5
253 253 rebasing 6:3944801ae4ea "dev-two named branch"
254 254 rebasing 7:3bdb949809d9 "B"
255 255 rebasing 8:a0d543090fa4 "C"
256 256 rebasing 9:e9f862ce8bad tip "D"
257 257 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/3944801ae4ea-fb46ed74-rebase.hg
258 258
259 259 $ hg tglog
260 260 o 9: e522577ccdbd 'D'
261 261 |
262 262 o 8: 810110211f50 'C'
263 263 |
264 264 o 7: 160b0930ccc6 'B'
265 265 |
266 266 o 6: c57724c84928 'dev-two named branch'
267 267 |
268 268 o 5: 8e279d293175 'H'
269 269 |
270 270 | o 4: aeefee77ab01 'G'
271 271 |/|
272 272 o | 3: e908b85f3729 'F'
273 273 | |
274 274 o | 2: bc8139ee757c 'dev-one named branch' dev-one
275 275 | |
276 276 | o 1: 9520eea781bc 'E'
277 277 |/
278 278 @ 0: cd010b8cd998 'A'
279 279
280 280
281 281 Reopen branch by rebase
282 282
283 283 $ hg up -qr3
284 284 $ hg branch -q b
285 285 $ hg ci -m 'create b'
286 286 $ hg ci -m 'close b' --close
287 287 $ hg rebase -b 8 -d b
288 288 reopening closed branch head 2b586e70108d
289 289 rebasing 5:8e279d293175 "H"
290 290 rebasing 6:c57724c84928 "dev-two named branch"
291 291 rebasing 7:160b0930ccc6 "B"
292 292 rebasing 8:810110211f50 "C"
293 293 rebasing 9:e522577ccdbd "D"
294 294 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/8e279d293175-b023e27c-rebase.hg
295 295
296 296 $ hg log -G -Tcompact
297 297 o 11[tip] be1dea60f2a6 2011-04-30 15:24 +0200 nicdumz
298 298 | D
299 299 |
300 300 o 10 ac34ce92632a 2011-04-30 15:24 +0200 nicdumz
301 301 | C
302 302 |
303 303 o 9 7bd665b6ce12 2011-04-30 15:24 +0200 nicdumz
304 304 | B
305 305 |
306 306 o 8 58e7c36e77f7 1970-01-01 00:00 +0000 test
307 307 | dev-two named branch
308 308 |
309 309 o 7 8e5a320651f3 2011-04-30 15:24 +0200 nicdumz
310 310 | H
311 311 |
312 312 @ 6 2b586e70108d 1970-01-01 00:00 +0000 test
313 313 | close b
314 314 |
315 315 o 5:3 3f9d5df8a707 1970-01-01 00:00 +0000 test
316 316 | create b
317 317 |
318 318 | o 4:3,1 aeefee77ab01 2011-04-30 15:24 +0200 nicdumz
319 319 |/| G
320 320 | |
321 321 o | 3 e908b85f3729 2011-04-30 15:24 +0200 nicdumz
322 322 | | F
323 323 | |
324 324 o | 2:0 bc8139ee757c 1970-01-01 00:00 +0000 test
325 325 | | dev-one named branch
326 326 | |
327 327 | o 1 9520eea781bc 2011-04-30 15:24 +0200 nicdumz
328 328 |/ E
329 329 |
330 330 o 0 cd010b8cd998 2011-04-30 15:24 +0200 nicdumz
331 331 A
332 332
333 333 $ echo A-mod > A
334 334 $ hg diff
335 335 diff -r 2b586e70108d A
336 336 --- a/A Thu Jan 01 00:00:00 1970 +0000
337 337 +++ b/A Thu Jan 01 00:00:00 1970 +0000
338 338 @@ -1,1 +1,1 @@
339 339 -A
340 340 +A-mod
341 341
342 342 --dry-run doesn't affect a dirty working directory that is unrelated to the
343 343 source or destination.
344 344
345 345 $ hg rebase -s tip -d 4 --dry-run
346 346 starting dry-run rebase; repository will not be changed
347 347 rebasing 11:be1dea60f2a6 tip "D"
348 348 dry-run rebase completed successfully; run without -n/--dry-run to perform this rebase
349 349 $ hg diff
350 350 diff -r 2b586e70108d A
351 351 --- a/A Thu Jan 01 00:00:00 1970 +0000
352 352 +++ b/A Thu Jan 01 00:00:00 1970 +0000
353 353 @@ -1,1 +1,1 @@
354 354 -A
355 355 +A-mod
356 356
357 357 Bailing out on --confirm doesn't affect a dirty working directory that is
358 358 unrelated to the source or destination.
359 359
360 360 $ echo A-mod > A
361 361 $ echo n | hg rebase -s tip -d 4 --confirm --config ui.interactive=True
362 362 starting in-memory rebase
363 363 rebasing 11:be1dea60f2a6 tip "D"
364 364 rebase completed successfully
365 365 apply changes (yn)? n
366 366 $ hg diff
367 367 diff -r 2b586e70108d A
368 368 --- a/A Thu Jan 01 00:00:00 1970 +0000
369 369 +++ b/A Thu Jan 01 00:00:00 1970 +0000
370 370 @@ -1,1 +1,1 @@
371 371 -A
372 372 +A-mod
373 373
374 374 $ echo A-mod > A
375 375 $ hg rebase -s tip -d 4 --confirm
376 376 starting in-memory rebase
377 377 rebasing 11:be1dea60f2a6 tip "D"
378 378 rebase completed successfully
379 379 apply changes (yn)? y
380 380 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/be1dea60f2a6-ca6d2dac-rebase.hg
381 381 $ hg diff
382 382 diff -r 2b586e70108d A
383 383 --- a/A Thu Jan 01 00:00:00 1970 +0000
384 384 +++ b/A Thu Jan 01 00:00:00 1970 +0000
385 385 @@ -1,1 +1,1 @@
386 386 -A
387 387 +A-mod
388 388
389 389 Attempting to rebase the parent of a dirty working directory will abort, without
390 390 mangling the working directory...
391 391
392 392 $ hg rebase -s 5 -d 4 --dry-run
393 393 starting dry-run rebase; repository will not be changed
394 394 abort: uncommitted changes
395 [255]
395 [20]
396 396 $ hg diff
397 397 diff -r 2b586e70108d A
398 398 --- a/A Thu Jan 01 00:00:00 1970 +0000
399 399 +++ b/A Thu Jan 01 00:00:00 1970 +0000
400 400 @@ -1,1 +1,1 @@
401 401 -A
402 402 +A-mod
403 403
404 404 ... ditto for --confirm
405 405
406 406 $ echo n | hg rebase -s 5 -d 4 --confirm --config ui.interactive=True
407 407 starting in-memory rebase
408 408 abort: uncommitted changes
409 [255]
409 [20]
410 410 $ hg diff
411 411 diff -r 2b586e70108d A
412 412 --- a/A Thu Jan 01 00:00:00 1970 +0000
413 413 +++ b/A Thu Jan 01 00:00:00 1970 +0000
414 414 @@ -1,1 +1,1 @@
415 415 -A
416 416 +A-mod
417 417 $ hg rebase -s 5 -d 4 --confirm
418 418 starting in-memory rebase
419 419 abort: uncommitted changes
420 [255]
420 [20]
421 421 $ hg diff
422 422 diff -r 2b586e70108d A
423 423 --- a/A Thu Jan 01 00:00:00 1970 +0000
424 424 +++ b/A Thu Jan 01 00:00:00 1970 +0000
425 425 @@ -1,1 +1,1 @@
426 426 -A
427 427 +A-mod
428 428
429 429 $ cd ..
430 430
431 431 Rebase to other head on branch
432 432
433 433 Set up a case:
434 434
435 435 $ hg init case1
436 436 $ cd case1
437 437 $ touch f
438 438 $ hg ci -qAm0
439 439 $ hg branch -q b
440 440 $ echo >> f
441 441 $ hg ci -qAm 'b1'
442 442 $ hg up -qr -2
443 443 $ hg branch -qf b
444 444 $ hg ci -qm 'b2'
445 445 $ hg up -qr -3
446 446 $ hg branch -q c
447 447 $ hg ci -m 'c1'
448 448
449 449 $ hg tglog
450 450 @ 3: c062e3ecd6c6 'c1' c
451 451 |
452 452 | o 2: 792845bb77ee 'b2' b
453 453 |/
454 454 | o 1: 40039acb7ca5 'b1' b
455 455 |/
456 456 o 0: d681519c3ea7 '0'
457 457
458 458 $ hg clone -q . ../case2
459 459
460 460 rebase 'b2' to another lower branch head
461 461
462 462 $ hg up -qr 2
463 463 $ hg rebase
464 464 rebasing 2:792845bb77ee "b2"
465 465 note: not rebasing 2:792845bb77ee "b2", its destination already has all its changes
466 466 saved backup bundle to $TESTTMP/case1/.hg/strip-backup/792845bb77ee-627120ee-rebase.hg
467 467 $ hg tglog
468 468 o 2: c062e3ecd6c6 'c1' c
469 469 |
470 470 | @ 1: 40039acb7ca5 'b1' b
471 471 |/
472 472 o 0: d681519c3ea7 '0'
473 473
474 474
475 475 rebase 'b1' on top of the tip of the branch ('b2') - ignoring the tip branch ('c1')
476 476
477 477 $ cd ../case2
478 478 $ hg up -qr 1
479 479 $ hg rebase
480 480 rebasing 1:40039acb7ca5 "b1"
481 481 saved backup bundle to $TESTTMP/case2/.hg/strip-backup/40039acb7ca5-342b72d1-rebase.hg
482 482 $ hg tglog
483 483 @ 3: 76abc1c6f8c7 'b1' b
484 484 |
485 485 | o 2: c062e3ecd6c6 'c1' c
486 486 | |
487 487 o | 1: 792845bb77ee 'b2' b
488 488 |/
489 489 o 0: d681519c3ea7 '0'
490 490
491 491
492 492 rebase 'c1' to the branch head 'c2' that is closed
493 493
494 494 $ hg branch -qf c
495 495 $ hg ci -qm 'c2 closed' --close
496 496 $ hg up -qr 2
497 497 $ hg tglog
498 498 _ 4: 8427af5d86f2 'c2 closed' c
499 499 |
500 500 o 3: 76abc1c6f8c7 'b1' b
501 501 |
502 502 | @ 2: c062e3ecd6c6 'c1' c
503 503 | |
504 504 o | 1: 792845bb77ee 'b2' b
505 505 |/
506 506 o 0: d681519c3ea7 '0'
507 507
508 508 $ hg rebase
509 509 abort: branch 'c' has one head - please rebase to an explicit rev
510 510 (run 'hg heads' to see all heads, specify destination with -d)
511 511 [255]
512 512 $ hg tglog
513 513 _ 4: 8427af5d86f2 'c2 closed' c
514 514 |
515 515 o 3: 76abc1c6f8c7 'b1' b
516 516 |
517 517 | @ 2: c062e3ecd6c6 'c1' c
518 518 | |
519 519 o | 1: 792845bb77ee 'b2' b
520 520 |/
521 521 o 0: d681519c3ea7 '0'
522 522
523 523
524 524 $ hg up -cr 1
525 525 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
526 526 $ hg branch x
527 527 marked working directory as branch x
528 528 $ hg rebase -r 3:: -d .
529 529 rebasing 3:76abc1c6f8c7 "b1"
530 530 rebasing 4:8427af5d86f2 tip "c2 closed"
531 531 note: not rebasing 4:8427af5d86f2 tip "c2 closed", its destination already has all its changes
532 532 saved backup bundle to $TESTTMP/case2/.hg/strip-backup/76abc1c6f8c7-cd698d13-rebase.hg
533 533 $ hg tglog
534 534 o 3: 117b0ed08075 'b1' x
535 535 |
536 536 | o 2: c062e3ecd6c6 'c1' c
537 537 | |
538 538 @ | 1: 792845bb77ee 'b2' b
539 539 |/
540 540 o 0: d681519c3ea7 '0'
541 541
542 542
543 543 $ cd ..
@@ -1,2140 +1,2140 b''
1 1 ==========================
2 2 Test rebase with obsolete
3 3 ==========================
4 4
5 5 Enable obsolete
6 6
7 7 $ cat >> $HGRCPATH << EOF
8 8 > [command-templates]
9 9 > log= {rev}:{node|short} {desc|firstline}{if(obsolete,' ({obsfate})')}
10 10 > [experimental]
11 11 > evolution.createmarkers=True
12 12 > evolution.allowunstable=True
13 13 > [phases]
14 14 > publish=False
15 15 > [extensions]
16 16 > rebase=
17 17 > drawdag=$TESTDIR/drawdag.py
18 18 > strip=
19 19 > EOF
20 20
21 21 Setup rebase canonical repo
22 22
23 23 $ hg init base
24 24 $ cd base
25 25 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
26 26 adding changesets
27 27 adding manifests
28 28 adding file changes
29 29 added 8 changesets with 7 changes to 7 files (+2 heads)
30 30 new changesets cd010b8cd998:02de42196ebe (8 drafts)
31 31 (run 'hg heads' to see heads, 'hg merge' to merge)
32 32 $ hg up tip
33 33 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
34 34 $ hg log -G
35 35 @ 7:02de42196ebe H
36 36 |
37 37 | o 6:eea13746799a G
38 38 |/|
39 39 o | 5:24b6387c8c8c F
40 40 | |
41 41 | o 4:9520eea781bc E
42 42 |/
43 43 | o 3:32af7686d403 D
44 44 | |
45 45 | o 2:5fddd98957c8 C
46 46 | |
47 47 | o 1:42ccdea3bb16 B
48 48 |/
49 49 o 0:cd010b8cd998 A
50 50
51 51 $ cd ..
52 52
53 53 simple rebase
54 54 ---------------------------------
55 55
56 56 $ hg clone base simple
57 57 updating to branch default
58 58 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
59 59 $ cd simple
60 60 $ hg up 32af7686d403
61 61 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
62 62 $ hg rebase -d eea13746799a
63 63 rebasing 1:42ccdea3bb16 "B"
64 64 rebasing 2:5fddd98957c8 "C"
65 65 rebasing 3:32af7686d403 "D"
66 66 $ hg log -G
67 67 @ 10:8eeb3c33ad33 D
68 68 |
69 69 o 9:2327fea05063 C
70 70 |
71 71 o 8:e4e5be0395b2 B
72 72 |
73 73 | o 7:02de42196ebe H
74 74 | |
75 75 o | 6:eea13746799a G
76 76 |\|
77 77 | o 5:24b6387c8c8c F
78 78 | |
79 79 o | 4:9520eea781bc E
80 80 |/
81 81 o 0:cd010b8cd998 A
82 82
83 83 $ hg log --hidden -G
84 84 @ 10:8eeb3c33ad33 D
85 85 |
86 86 o 9:2327fea05063 C
87 87 |
88 88 o 8:e4e5be0395b2 B
89 89 |
90 90 | o 7:02de42196ebe H
91 91 | |
92 92 o | 6:eea13746799a G
93 93 |\|
94 94 | o 5:24b6387c8c8c F
95 95 | |
96 96 o | 4:9520eea781bc E
97 97 |/
98 98 | x 3:32af7686d403 D (rewritten using rebase as 10:8eeb3c33ad33)
99 99 | |
100 100 | x 2:5fddd98957c8 C (rewritten using rebase as 9:2327fea05063)
101 101 | |
102 102 | x 1:42ccdea3bb16 B (rewritten using rebase as 8:e4e5be0395b2)
103 103 |/
104 104 o 0:cd010b8cd998 A
105 105
106 106 $ hg debugobsolete
107 107 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 e4e5be0395b2cbd471ed22a26b1b6a1a0658a794 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
108 108 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 2327fea05063f39961b14cb69435a9898dc9a245 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
109 109 32af7686d403cf45b5d95f2d70cebea587ac806a 8eeb3c33ad33d452c89e5dcf611c347f978fb42b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
110 110
111 111
112 112 $ cd ..
113 113
114 114 empty changeset
115 115 ---------------------------------
116 116
117 117 $ hg clone base empty
118 118 updating to branch default
119 119 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
120 120 $ cd empty
121 121 $ hg up eea13746799a
122 122 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
123 123
124 124 We make a copy of both the first changeset in the rebased and some other in the
125 125 set.
126 126
127 127 $ hg graft 42ccdea3bb16 32af7686d403
128 128 grafting 1:42ccdea3bb16 "B"
129 129 grafting 3:32af7686d403 "D"
130 130 $ hg rebase -s 42ccdea3bb16 -d .
131 131 rebasing 1:42ccdea3bb16 "B"
132 132 note: not rebasing 1:42ccdea3bb16 "B", its destination already has all its changes
133 133 rebasing 2:5fddd98957c8 "C"
134 134 rebasing 3:32af7686d403 "D"
135 135 note: not rebasing 3:32af7686d403 "D", its destination already has all its changes
136 136 $ hg log -G
137 137 o 10:5ae4c968c6ac C
138 138 |
139 139 @ 9:08483444fef9 D
140 140 |
141 141 o 8:8877864f1edb B
142 142 |
143 143 | o 7:02de42196ebe H
144 144 | |
145 145 o | 6:eea13746799a G
146 146 |\|
147 147 | o 5:24b6387c8c8c F
148 148 | |
149 149 o | 4:9520eea781bc E
150 150 |/
151 151 o 0:cd010b8cd998 A
152 152
153 153 $ hg log --hidden -G
154 154 o 10:5ae4c968c6ac C
155 155 |
156 156 @ 9:08483444fef9 D
157 157 |
158 158 o 8:8877864f1edb B
159 159 |
160 160 | o 7:02de42196ebe H
161 161 | |
162 162 o | 6:eea13746799a G
163 163 |\|
164 164 | o 5:24b6387c8c8c F
165 165 | |
166 166 o | 4:9520eea781bc E
167 167 |/
168 168 | x 3:32af7686d403 D (pruned using rebase)
169 169 | |
170 170 | x 2:5fddd98957c8 C (rewritten using rebase as 10:5ae4c968c6ac)
171 171 | |
172 172 | x 1:42ccdea3bb16 B (pruned using rebase)
173 173 |/
174 174 o 0:cd010b8cd998 A
175 175
176 176 $ hg debugobsolete
177 177 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'rebase', 'user': 'test'}
178 178 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
179 179 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'rebase', 'user': 'test'}
180 180
181 181
182 182 More complex case where part of the rebase set were already rebased
183 183
184 184 $ hg rebase --rev 'desc(D)' --dest 'desc(H)'
185 185 rebasing 9:08483444fef9 "D"
186 186 1 new orphan changesets
187 187 $ hg debugobsolete
188 188 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'rebase', 'user': 'test'}
189 189 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
190 190 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'rebase', 'user': 'test'}
191 191 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
192 192 $ hg log -G
193 193 @ 11:4596109a6a43 D
194 194 |
195 195 | * 10:5ae4c968c6ac C
196 196 | |
197 197 | x 9:08483444fef9 D (rewritten using rebase as 11:4596109a6a43)
198 198 | |
199 199 | o 8:8877864f1edb B
200 200 | |
201 201 o | 7:02de42196ebe H
202 202 | |
203 203 | o 6:eea13746799a G
204 204 |/|
205 205 o | 5:24b6387c8c8c F
206 206 | |
207 207 | o 4:9520eea781bc E
208 208 |/
209 209 o 0:cd010b8cd998 A
210 210
211 211 $ hg rebase --source 'desc(B)' --dest 'tip' --config experimental.rebaseskipobsolete=True
212 212 rebasing 8:8877864f1edb "B"
213 213 note: not rebasing 9:08483444fef9 "D", already in destination as 11:4596109a6a43 tip "D"
214 214 rebasing 10:5ae4c968c6ac "C"
215 215 $ hg debugobsolete
216 216 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 {cd010b8cd998f3981a5a8115f94f8da4ab506089} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'rebase', 'user': 'test'}
217 217 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 5ae4c968c6aca831df823664e706c9d4aa34473d 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
218 218 32af7686d403cf45b5d95f2d70cebea587ac806a 0 {5fddd98957c8a54a4d436dfe1da9d87f21a1b97b} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'rebase', 'user': 'test'}
219 219 08483444fef91d6224f6655ee586a65d263ad34c 4596109a6a4328c398bde3a4a3b6737cfade3003 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
220 220 8877864f1edb05d0e07dc4ba77b67a80a7b86672 462a34d07e599b87ea08676a449373fe4e2e1347 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
221 221 5ae4c968c6aca831df823664e706c9d4aa34473d 98f6af4ee9539e14da4465128f894c274900b6e5 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
222 222 $ hg log --rev 'contentdivergent()'
223 223 $ hg log -G
224 224 o 13:98f6af4ee953 C
225 225 |
226 226 o 12:462a34d07e59 B
227 227 |
228 228 @ 11:4596109a6a43 D
229 229 |
230 230 o 7:02de42196ebe H
231 231 |
232 232 | o 6:eea13746799a G
233 233 |/|
234 234 o | 5:24b6387c8c8c F
235 235 | |
236 236 | o 4:9520eea781bc E
237 237 |/
238 238 o 0:cd010b8cd998 A
239 239
240 240 $ hg log --style default --debug -r 4596109a6a4328c398bde3a4a3b6737cfade3003
241 241 changeset: 11:4596109a6a4328c398bde3a4a3b6737cfade3003
242 242 phase: draft
243 243 parent: 7:02de42196ebee42ef284b6780a87cdc96e8eaab6
244 244 parent: -1:0000000000000000000000000000000000000000
245 245 manifest: 11:a91006e3a02f1edf631f7018e6e5684cf27dd905
246 246 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
247 247 date: Sat Apr 30 15:24:48 2011 +0200
248 248 files+: D
249 249 extra: branch=default
250 250 extra: rebase_source=08483444fef91d6224f6655ee586a65d263ad34c
251 251 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
252 252 description:
253 253 D
254 254
255 255
256 256 $ hg up -qr 'desc(G)'
257 257 $ hg graft 4596109a6a4328c398bde3a4a3b6737cfade3003
258 258 grafting 11:4596109a6a43 "D"
259 259 $ hg up -qr 'desc(E)'
260 260 $ hg rebase -s tip -d .
261 261 rebasing 14:9e36056a46e3 tip "D"
262 262 $ hg log --style default --debug -r tip
263 263 changeset: 15:627d4614809036ba22b9e7cb31638ddc06ab99ab
264 264 tag: tip
265 265 phase: draft
266 266 parent: 4:9520eea781bcca16c1e15acc0ba14335a0e8e5ba
267 267 parent: -1:0000000000000000000000000000000000000000
268 268 manifest: 15:648e8ede73ae3e497d093d3a4c8fcc2daa864f42
269 269 user: Nicolas Dumazet <nicdumz.commits@gmail.com>
270 270 date: Sat Apr 30 15:24:48 2011 +0200
271 271 files+: D
272 272 extra: branch=default
273 273 extra: intermediate-source=4596109a6a4328c398bde3a4a3b6737cfade3003
274 274 extra: rebase_source=9e36056a46e37c9776168c7375734eebc70e294f
275 275 extra: source=32af7686d403cf45b5d95f2d70cebea587ac806a
276 276 description:
277 277 D
278 278
279 279
280 280 Start rebase from a commit that is obsolete but not hidden only because it's
281 281 a working copy parent. We should be moved back to the starting commit as usual
282 282 even though it is hidden (until we're moved there).
283 283
284 284 $ hg --hidden up -qr 'first(hidden())'
285 285 updated to hidden changeset 42ccdea3bb16
286 286 (hidden revision '42ccdea3bb16' is pruned)
287 287 $ hg rebase --rev 13 --dest 15
288 288 rebasing 13:98f6af4ee953 "C"
289 289 $ hg log -G
290 290 o 16:294a2b93eb4d C
291 291 |
292 292 o 15:627d46148090 D
293 293 |
294 294 | o 12:462a34d07e59 B
295 295 | |
296 296 | o 11:4596109a6a43 D
297 297 | |
298 298 | o 7:02de42196ebe H
299 299 | |
300 300 +---o 6:eea13746799a G
301 301 | |/
302 302 | o 5:24b6387c8c8c F
303 303 | |
304 304 o | 4:9520eea781bc E
305 305 |/
306 306 | @ 1:42ccdea3bb16 B (pruned using rebase)
307 307 |/
308 308 o 0:cd010b8cd998 A
309 309
310 310
311 311 $ cd ..
312 312
313 313 collapse rebase
314 314 ---------------------------------
315 315
316 316 $ hg clone base collapse
317 317 updating to branch default
318 318 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
319 319 $ cd collapse
320 320 $ hg rebase -s 42ccdea3bb16 -d eea13746799a --collapse
321 321 rebasing 1:42ccdea3bb16 "B"
322 322 rebasing 2:5fddd98957c8 "C"
323 323 rebasing 3:32af7686d403 "D"
324 324 $ hg log -G
325 325 o 8:4dc2197e807b Collapsed revision
326 326 |
327 327 | @ 7:02de42196ebe H
328 328 | |
329 329 o | 6:eea13746799a G
330 330 |\|
331 331 | o 5:24b6387c8c8c F
332 332 | |
333 333 o | 4:9520eea781bc E
334 334 |/
335 335 o 0:cd010b8cd998 A
336 336
337 337 $ hg log --hidden -G
338 338 o 8:4dc2197e807b Collapsed revision
339 339 |
340 340 | @ 7:02de42196ebe H
341 341 | |
342 342 o | 6:eea13746799a G
343 343 |\|
344 344 | o 5:24b6387c8c8c F
345 345 | |
346 346 o | 4:9520eea781bc E
347 347 |/
348 348 | x 3:32af7686d403 D (rewritten using rebase as 8:4dc2197e807b)
349 349 | |
350 350 | x 2:5fddd98957c8 C (rewritten using rebase as 8:4dc2197e807b)
351 351 | |
352 352 | x 1:42ccdea3bb16 B (rewritten using rebase as 8:4dc2197e807b)
353 353 |/
354 354 o 0:cd010b8cd998 A
355 355
356 356 $ hg id --debug -r tip
357 357 4dc2197e807bae9817f09905b50ab288be2dbbcf tip
358 358 $ hg debugobsolete
359 359 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'fold-id': '6fb65cdc', 'fold-idx': '1', 'fold-size': '3', 'operation': 'rebase', 'user': 'test'}
360 360 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'fold-id': '6fb65cdc', 'fold-idx': '2', 'fold-size': '3', 'operation': 'rebase', 'user': 'test'}
361 361 32af7686d403cf45b5d95f2d70cebea587ac806a 4dc2197e807bae9817f09905b50ab288be2dbbcf 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'fold-id': '6fb65cdc', 'fold-idx': '3', 'fold-size': '3', 'operation': 'rebase', 'user': 'test'}
362 362
363 363 $ cd ..
364 364
365 365 Rebase set has hidden descendants
366 366 ---------------------------------
367 367
368 368 We rebase a changeset which has hidden descendants. Hidden changesets must not
369 369 be rebased.
370 370
371 371 $ hg clone base hidden
372 372 updating to branch default
373 373 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
374 374 $ cd hidden
375 375 $ hg log -G
376 376 @ 7:02de42196ebe H
377 377 |
378 378 | o 6:eea13746799a G
379 379 |/|
380 380 o | 5:24b6387c8c8c F
381 381 | |
382 382 | o 4:9520eea781bc E
383 383 |/
384 384 | o 3:32af7686d403 D
385 385 | |
386 386 | o 2:5fddd98957c8 C
387 387 | |
388 388 | o 1:42ccdea3bb16 B
389 389 |/
390 390 o 0:cd010b8cd998 A
391 391
392 392 $ hg rebase -s 5fddd98957c8 -d eea13746799a
393 393 rebasing 2:5fddd98957c8 "C"
394 394 rebasing 3:32af7686d403 "D"
395 395 $ hg log -G
396 396 o 9:cf44d2f5a9f4 D
397 397 |
398 398 o 8:e273c5e7d2d2 C
399 399 |
400 400 | @ 7:02de42196ebe H
401 401 | |
402 402 o | 6:eea13746799a G
403 403 |\|
404 404 | o 5:24b6387c8c8c F
405 405 | |
406 406 o | 4:9520eea781bc E
407 407 |/
408 408 | o 1:42ccdea3bb16 B
409 409 |/
410 410 o 0:cd010b8cd998 A
411 411
412 412 $ hg rebase -s 42ccdea3bb16 -d 02de42196ebe
413 413 rebasing 1:42ccdea3bb16 "B"
414 414 $ hg log -G
415 415 o 10:7c6027df6a99 B
416 416 |
417 417 | o 9:cf44d2f5a9f4 D
418 418 | |
419 419 | o 8:e273c5e7d2d2 C
420 420 | |
421 421 @ | 7:02de42196ebe H
422 422 | |
423 423 | o 6:eea13746799a G
424 424 |/|
425 425 o | 5:24b6387c8c8c F
426 426 | |
427 427 | o 4:9520eea781bc E
428 428 |/
429 429 o 0:cd010b8cd998 A
430 430
431 431 $ hg log --hidden -G
432 432 o 10:7c6027df6a99 B
433 433 |
434 434 | o 9:cf44d2f5a9f4 D
435 435 | |
436 436 | o 8:e273c5e7d2d2 C
437 437 | |
438 438 @ | 7:02de42196ebe H
439 439 | |
440 440 | o 6:eea13746799a G
441 441 |/|
442 442 o | 5:24b6387c8c8c F
443 443 | |
444 444 | o 4:9520eea781bc E
445 445 |/
446 446 | x 3:32af7686d403 D (rewritten using rebase as 9:cf44d2f5a9f4)
447 447 | |
448 448 | x 2:5fddd98957c8 C (rewritten using rebase as 8:e273c5e7d2d2)
449 449 | |
450 450 | x 1:42ccdea3bb16 B (rewritten using rebase as 10:7c6027df6a99)
451 451 |/
452 452 o 0:cd010b8cd998 A
453 453
454 454 $ hg debugobsolete
455 455 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b e273c5e7d2d29df783dce9f9eaa3ac4adc69c15d 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
456 456 32af7686d403cf45b5d95f2d70cebea587ac806a cf44d2f5a9f4297a62be94cbdd3dff7c7dc54258 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
457 457 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 7c6027df6a99d93f461868e5433f63bde20b6dfb 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
458 458
459 459 Test that rewriting leaving instability behind is allowed
460 460 ---------------------------------------------------------------------
461 461
462 462 $ hg log -r 'children(8)'
463 463 9:cf44d2f5a9f4 D (no-eol)
464 464 $ hg rebase -r 8
465 465 rebasing 8:e273c5e7d2d2 "C"
466 466 1 new orphan changesets
467 467 $ hg log -G
468 468 o 11:0d8f238b634c C
469 469 |
470 470 o 10:7c6027df6a99 B
471 471 |
472 472 | * 9:cf44d2f5a9f4 D
473 473 | |
474 474 | x 8:e273c5e7d2d2 C (rewritten using rebase as 11:0d8f238b634c)
475 475 | |
476 476 @ | 7:02de42196ebe H
477 477 | |
478 478 | o 6:eea13746799a G
479 479 |/|
480 480 o | 5:24b6387c8c8c F
481 481 | |
482 482 | o 4:9520eea781bc E
483 483 |/
484 484 o 0:cd010b8cd998 A
485 485
486 486 $ cd ..
487 487 $ cp -R hidden stabilize
488 488 $ cd stabilize
489 489 $ hg rebase --auto-orphans '0::' -d 10
490 490 abort: cannot specify both --auto-orphans and --dest
491 491 [10]
492 492 $ hg rebase --auto-orphans '0::'
493 493 rebasing 9:cf44d2f5a9f4 "D"
494 494 $ hg log -G
495 495 o 12:7e3935feaa68 D
496 496 |
497 497 o 11:0d8f238b634c C
498 498 |
499 499 o 10:7c6027df6a99 B
500 500 |
501 501 @ 7:02de42196ebe H
502 502 |
503 503 | o 6:eea13746799a G
504 504 |/|
505 505 o | 5:24b6387c8c8c F
506 506 | |
507 507 | o 4:9520eea781bc E
508 508 |/
509 509 o 0:cd010b8cd998 A
510 510
511 511
512 512 $ cd ../hidden
513 513 $ rm -r ../stabilize
514 514
515 515 Test multiple root handling
516 516 ------------------------------------
517 517
518 518 $ hg rebase --dest 4 --rev '7+11+9'
519 519 rebasing 9:cf44d2f5a9f4 "D"
520 520 rebasing 7:02de42196ebe "H"
521 521 rebasing 11:0d8f238b634c tip "C"
522 522 $ hg log -G
523 523 o 14:1e8370e38cca C
524 524 |
525 525 @ 13:bfe264faf697 H
526 526 |
527 527 | o 12:102b4c1d889b D
528 528 |/
529 529 | * 10:7c6027df6a99 B
530 530 | |
531 531 | x 7:02de42196ebe H (rewritten using rebase as 13:bfe264faf697)
532 532 | |
533 533 +---o 6:eea13746799a G
534 534 | |/
535 535 | o 5:24b6387c8c8c F
536 536 | |
537 537 o | 4:9520eea781bc E
538 538 |/
539 539 o 0:cd010b8cd998 A
540 540
541 541 $ cd ..
542 542
543 543 Detach both parents
544 544
545 545 $ hg init double-detach
546 546 $ cd double-detach
547 547
548 548 $ hg debugdrawdag <<EOF
549 549 > F
550 550 > /|
551 551 > C E
552 552 > | |
553 553 > B D G
554 554 > \|/
555 555 > A
556 556 > EOF
557 557
558 558 $ hg rebase -d G -r 'B + D + F'
559 559 rebasing 1:112478962961 B "B"
560 560 rebasing 2:b18e25de2cf5 D "D"
561 561 rebasing 6:f15c3adaf214 F tip "F"
562 562 abort: cannot rebase 6:f15c3adaf214 without moving at least one of its parents
563 563 [255]
564 564
565 565 $ cd ..
566 566
567 567 test on rebase dropping a merge
568 568
569 569 (setup)
570 570
571 571 $ hg init dropmerge
572 572 $ cd dropmerge
573 573 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
574 574 adding changesets
575 575 adding manifests
576 576 adding file changes
577 577 added 8 changesets with 7 changes to 7 files (+2 heads)
578 578 new changesets cd010b8cd998:02de42196ebe (8 drafts)
579 579 (run 'hg heads' to see heads, 'hg merge' to merge)
580 580 $ hg up 3
581 581 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
582 582 $ hg merge 7
583 583 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
584 584 (branch merge, don't forget to commit)
585 585 $ hg ci -m 'M'
586 586 $ echo I > I
587 587 $ hg add I
588 588 $ hg ci -m I
589 589 $ hg log -G
590 590 @ 9:4bde274eefcf I
591 591 |
592 592 o 8:53a6a128b2b7 M
593 593 |\
594 594 | o 7:02de42196ebe H
595 595 | |
596 596 | | o 6:eea13746799a G
597 597 | |/|
598 598 | o | 5:24b6387c8c8c F
599 599 | | |
600 600 | | o 4:9520eea781bc E
601 601 | |/
602 602 o | 3:32af7686d403 D
603 603 | |
604 604 o | 2:5fddd98957c8 C
605 605 | |
606 606 o | 1:42ccdea3bb16 B
607 607 |/
608 608 o 0:cd010b8cd998 A
609 609
610 610 (actual test)
611 611
612 612 $ hg rebase --dest 6 --rev '((desc(H) + desc(D))::) - desc(M)'
613 613 rebasing 3:32af7686d403 "D"
614 614 rebasing 7:02de42196ebe "H"
615 615 rebasing 9:4bde274eefcf tip "I"
616 616 1 new orphan changesets
617 617 $ hg log -G
618 618 @ 12:acd174b7ab39 I
619 619 |
620 620 o 11:6c11a6218c97 H
621 621 |
622 622 | o 10:b5313c85b22e D
623 623 |/
624 624 | * 8:53a6a128b2b7 M
625 625 | |\
626 626 | | x 7:02de42196ebe H (rewritten using rebase as 11:6c11a6218c97)
627 627 | | |
628 628 o---+ 6:eea13746799a G
629 629 | | |
630 630 | | o 5:24b6387c8c8c F
631 631 | | |
632 632 o---+ 4:9520eea781bc E
633 633 / /
634 634 x | 3:32af7686d403 D (rewritten using rebase as 10:b5313c85b22e)
635 635 | |
636 636 o | 2:5fddd98957c8 C
637 637 | |
638 638 o | 1:42ccdea3bb16 B
639 639 |/
640 640 o 0:cd010b8cd998 A
641 641
642 642
643 643 Test hidden changesets in the rebase set (issue4504)
644 644
645 645 $ hg up --hidden 9
646 646 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
647 647 updated to hidden changeset 4bde274eefcf
648 648 (hidden revision '4bde274eefcf' was rewritten as: acd174b7ab39)
649 649 $ echo J > J
650 650 $ hg add J
651 651 $ hg commit -m J
652 652 1 new orphan changesets
653 653 $ hg debugobsolete `hg log --rev . -T '{node}'`
654 654 1 new obsolescence markers
655 655 obsoleted 1 changesets
656 656
657 657 $ hg rebase --rev .~1::. --dest 'max(desc(D))' --traceback --config experimental.rebaseskipobsolete=off
658 658 rebasing 9:4bde274eefcf "I"
659 659 rebasing 13:06edfc82198f tip "J"
660 660 2 new content-divergent changesets
661 661 $ hg log -G
662 662 @ 15:5ae8a643467b J
663 663 |
664 664 * 14:9ad579b4a5de I
665 665 |
666 666 | * 12:acd174b7ab39 I
667 667 | |
668 668 | o 11:6c11a6218c97 H
669 669 | |
670 670 o | 10:b5313c85b22e D
671 671 |/
672 672 | * 8:53a6a128b2b7 M
673 673 | |\
674 674 | | x 7:02de42196ebe H (rewritten using rebase as 11:6c11a6218c97)
675 675 | | |
676 676 o---+ 6:eea13746799a G
677 677 | | |
678 678 | | o 5:24b6387c8c8c F
679 679 | | |
680 680 o---+ 4:9520eea781bc E
681 681 / /
682 682 x | 3:32af7686d403 D (rewritten using rebase as 10:b5313c85b22e)
683 683 | |
684 684 o | 2:5fddd98957c8 C
685 685 | |
686 686 o | 1:42ccdea3bb16 B
687 687 |/
688 688 o 0:cd010b8cd998 A
689 689
690 690 $ hg up 14 -C
691 691 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
692 692 $ echo "K" > K
693 693 $ hg add K
694 694 $ hg commit --amend -m "K"
695 695 1 new orphan changesets
696 696 $ echo "L" > L
697 697 $ hg add L
698 698 $ hg commit -m "L"
699 699 $ hg up '.^'
700 700 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
701 701 $ echo "M" > M
702 702 $ hg add M
703 703 $ hg commit --amend -m "M"
704 704 1 new orphan changesets
705 705 $ hg log -G
706 706 @ 18:bfaedf8eb73b M
707 707 |
708 708 | * 17:97219452e4bd L
709 709 | |
710 710 | x 16:fc37a630c901 K (rewritten using amend as 18:bfaedf8eb73b)
711 711 |/
712 712 | * 15:5ae8a643467b J
713 713 | |
714 714 | x 14:9ad579b4a5de I (rewritten using amend as 16:fc37a630c901)
715 715 |/
716 716 | * 12:acd174b7ab39 I
717 717 | |
718 718 | o 11:6c11a6218c97 H
719 719 | |
720 720 o | 10:b5313c85b22e D
721 721 |/
722 722 | * 8:53a6a128b2b7 M
723 723 | |\
724 724 | | x 7:02de42196ebe H (rewritten using rebase as 11:6c11a6218c97)
725 725 | | |
726 726 o---+ 6:eea13746799a G
727 727 | | |
728 728 | | o 5:24b6387c8c8c F
729 729 | | |
730 730 o---+ 4:9520eea781bc E
731 731 / /
732 732 x | 3:32af7686d403 D (rewritten using rebase as 10:b5313c85b22e)
733 733 | |
734 734 o | 2:5fddd98957c8 C
735 735 | |
736 736 o | 1:42ccdea3bb16 B
737 737 |/
738 738 o 0:cd010b8cd998 A
739 739
740 740 $ hg rebase -s 14 -d 17 --config experimental.rebaseskipobsolete=True
741 741 note: not rebasing 14:9ad579b4a5de "I", already in destination as 16:fc37a630c901 "K"
742 742 rebasing 15:5ae8a643467b "J"
743 743 1 new orphan changesets
744 744
745 745 $ cd ..
746 746
747 747 Skip obsolete changeset even with multiple hops
748 748 -----------------------------------------------
749 749
750 750 setup
751 751
752 752 $ hg init obsskip
753 753 $ cd obsskip
754 754 $ cat << EOF >> .hg/hgrc
755 755 > [experimental]
756 756 > rebaseskipobsolete = True
757 757 > [extensions]
758 758 > strip =
759 759 > EOF
760 760 $ echo A > A
761 761 $ hg add A
762 762 $ hg commit -m A
763 763 $ echo B > B
764 764 $ hg add B
765 765 $ hg commit -m B0
766 766 $ hg commit --amend -m B1
767 767 $ hg commit --amend -m B2
768 768 $ hg up --hidden 'desc(B0)'
769 769 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
770 770 updated to hidden changeset a8b11f55fb19
771 771 (hidden revision 'a8b11f55fb19' was rewritten as: 261e70097290)
772 772 $ echo C > C
773 773 $ hg add C
774 774 $ hg commit -m C
775 775 1 new orphan changesets
776 776 $ hg log -G
777 777 @ 4:212cb178bcbb C
778 778 |
779 779 | o 3:261e70097290 B2
780 780 | |
781 781 x | 1:a8b11f55fb19 B0 (rewritten using amend as 3:261e70097290)
782 782 |/
783 783 o 0:4a2df7238c3b A
784 784
785 785
786 786 Rebase finds its way in a chain of marker
787 787
788 788 $ hg rebase -d 'desc(B2)'
789 789 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 3:261e70097290 "B2"
790 790 rebasing 4:212cb178bcbb tip "C"
791 791
792 792 Even when the chain include missing node
793 793
794 794 $ hg up --hidden 'desc(B0)'
795 795 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
796 796 updated to hidden changeset a8b11f55fb19
797 797 (hidden revision 'a8b11f55fb19' was rewritten as: 261e70097290)
798 798 $ echo D > D
799 799 $ hg add D
800 800 $ hg commit -m D
801 801 1 new orphan changesets
802 802 $ hg --hidden strip -r 'desc(B1)'
803 803 saved backup bundle to $TESTTMP/obsskip/.hg/strip-backup/86f6414ccda7-b1c452ee-backup.hg
804 804 1 new orphan changesets
805 805 $ hg log -G
806 806 @ 5:1a79b7535141 D
807 807 |
808 808 | o 4:ff2c4d47b71d C
809 809 | |
810 810 | o 2:261e70097290 B2
811 811 | |
812 812 x | 1:a8b11f55fb19 B0 (rewritten using amend as 2:261e70097290)
813 813 |/
814 814 o 0:4a2df7238c3b A
815 815
816 816
817 817 $ hg rebase -d 'desc(B2)'
818 818 note: not rebasing 1:a8b11f55fb19 "B0", already in destination as 2:261e70097290 "B2"
819 819 rebasing 5:1a79b7535141 tip "D"
820 820 $ hg up 4
821 821 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
822 822 $ echo "O" > O
823 823 $ hg add O
824 824 $ hg commit -m O
825 825 $ echo "P" > P
826 826 $ hg add P
827 827 $ hg commit -m P
828 828 $ hg log -G
829 829 @ 8:8d47583e023f P
830 830 |
831 831 o 7:360bbaa7d3ce O
832 832 |
833 833 | o 6:9c48361117de D
834 834 | |
835 835 o | 4:ff2c4d47b71d C
836 836 |/
837 837 o 2:261e70097290 B2
838 838 |
839 839 o 0:4a2df7238c3b A
840 840
841 841 $ hg debugobsolete `hg log -r 7 -T '{node}\n'` --config experimental.evolution=true
842 842 1 new obsolescence markers
843 843 obsoleted 1 changesets
844 844 1 new orphan changesets
845 845 $ hg rebase -d 6 -r "4::"
846 846 rebasing 4:ff2c4d47b71d "C"
847 847 note: not rebasing 7:360bbaa7d3ce "O", it has no successor
848 848 rebasing 8:8d47583e023f tip "P"
849 849
850 850 If all the changeset to be rebased are obsolete and present in the destination, we
851 851 should display a friendly error message
852 852
853 853 $ hg log -G
854 854 @ 10:121d9e3bc4c6 P
855 855 |
856 856 o 9:4be60e099a77 C
857 857 |
858 858 o 6:9c48361117de D
859 859 |
860 860 o 2:261e70097290 B2
861 861 |
862 862 o 0:4a2df7238c3b A
863 863
864 864
865 865 $ hg up 9
866 866 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
867 867 $ echo "non-relevant change" > nonrelevant
868 868 $ hg add nonrelevant
869 869 $ hg commit -m nonrelevant
870 870 created new head
871 871 $ hg debugobsolete `hg log -r 11 -T '{node}\n'` --config experimental.evolution=true
872 872 1 new obsolescence markers
873 873 obsoleted 1 changesets
874 874 $ hg log -G
875 875 @ 11:f44da1f4954c nonrelevant (pruned)
876 876 |
877 877 | o 10:121d9e3bc4c6 P
878 878 |/
879 879 o 9:4be60e099a77 C
880 880 |
881 881 o 6:9c48361117de D
882 882 |
883 883 o 2:261e70097290 B2
884 884 |
885 885 o 0:4a2df7238c3b A
886 886
887 887 $ hg rebase -r . -d 10
888 888 note: not rebasing 11:f44da1f4954c tip "nonrelevant", it has no successor
889 889
890 890 If a rebase is going to create divergence, it should abort
891 891
892 892 $ hg log -G
893 893 @ 10:121d9e3bc4c6 P
894 894 |
895 895 o 9:4be60e099a77 C
896 896 |
897 897 o 6:9c48361117de D
898 898 |
899 899 o 2:261e70097290 B2
900 900 |
901 901 o 0:4a2df7238c3b A
902 902
903 903
904 904 $ hg up 9
905 905 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
906 906 $ echo "john" > doe
907 907 $ hg add doe
908 908 $ hg commit -m "john doe"
909 909 created new head
910 910 $ hg up 10
911 911 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
912 912 $ echo "foo" > bar
913 913 $ hg add bar
914 914 $ hg commit --amend -m "10'"
915 915 $ hg up 10 --hidden
916 916 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
917 917 updated to hidden changeset 121d9e3bc4c6
918 918 (hidden revision '121d9e3bc4c6' was rewritten as: 77d874d096a2)
919 919 $ echo "bar" > foo
920 920 $ hg add foo
921 921 $ hg commit -m "bar foo"
922 922 1 new orphan changesets
923 923 $ hg log -G
924 924 @ 14:73568ab6879d bar foo
925 925 |
926 926 | o 13:77d874d096a2 10'
927 927 | |
928 928 | | o 12:3eb461388009 john doe
929 929 | |/
930 930 x | 10:121d9e3bc4c6 P (rewritten using amend as 13:77d874d096a2)
931 931 |/
932 932 o 9:4be60e099a77 C
933 933 |
934 934 o 6:9c48361117de D
935 935 |
936 936 o 2:261e70097290 B2
937 937 |
938 938 o 0:4a2df7238c3b A
939 939
940 940 $ hg summary
941 941 parent: 14:73568ab6879d tip (orphan)
942 942 bar foo
943 943 branch: default
944 944 commit: (clean)
945 945 update: 2 new changesets, 3 branch heads (merge)
946 946 phases: 8 draft
947 947 orphan: 1 changesets
948 948 $ hg rebase -s 10 -d 12
949 949 abort: this rebase will cause divergences from: 121d9e3bc4c6
950 950 (to force the rebase please set experimental.evolution.allowdivergence=True)
951 951 [255]
952 952 $ hg log -G
953 953 @ 14:73568ab6879d bar foo
954 954 |
955 955 | o 13:77d874d096a2 10'
956 956 | |
957 957 | | o 12:3eb461388009 john doe
958 958 | |/
959 959 x | 10:121d9e3bc4c6 P (rewritten using amend as 13:77d874d096a2)
960 960 |/
961 961 o 9:4be60e099a77 C
962 962 |
963 963 o 6:9c48361117de D
964 964 |
965 965 o 2:261e70097290 B2
966 966 |
967 967 o 0:4a2df7238c3b A
968 968
969 969 With experimental.evolution.allowdivergence=True, rebase can create divergence
970 970
971 971 $ hg rebase -s 10 -d 12 --config experimental.evolution.allowdivergence=True
972 972 rebasing 10:121d9e3bc4c6 "P"
973 973 rebasing 14:73568ab6879d tip "bar foo"
974 974 2 new content-divergent changesets
975 975 $ hg summary
976 976 parent: 16:61bd55f69bc4 tip
977 977 bar foo
978 978 branch: default
979 979 commit: (clean)
980 980 update: 1 new changesets, 2 branch heads (merge)
981 981 phases: 8 draft
982 982 content-divergent: 2 changesets
983 983
984 984 rebase --continue + skipped rev because their successors are in destination
985 985 we make a change in trunk and work on conflicting changes to make rebase abort.
986 986
987 987 $ hg log -G -r 16::
988 988 @ 16:61bd55f69bc4 bar foo
989 989 |
990 990 ~
991 991
992 992 Create the two changes in trunk
993 993 $ printf "a" > willconflict
994 994 $ hg add willconflict
995 995 $ hg commit -m "willconflict first version"
996 996
997 997 $ printf "dummy" > C
998 998 $ hg commit -m "dummy change successor"
999 999
1000 1000 Create the changes that we will rebase
1001 1001 $ hg update -C 16 -q
1002 1002 $ printf "b" > willconflict
1003 1003 $ hg add willconflict
1004 1004 $ hg commit -m "willconflict second version"
1005 1005 created new head
1006 1006 $ printf "dummy" > K
1007 1007 $ hg add K
1008 1008 $ hg commit -m "dummy change"
1009 1009 $ printf "dummy" > L
1010 1010 $ hg add L
1011 1011 $ hg commit -m "dummy change"
1012 1012 $ hg debugobsolete `hg log -r ".^" -T '{node}'` `hg log -r 18 -T '{node}'` --config experimental.evolution=true
1013 1013 1 new obsolescence markers
1014 1014 obsoleted 1 changesets
1015 1015 1 new orphan changesets
1016 1016
1017 1017 $ hg log -G -r 16::
1018 1018 @ 21:7bdc8a87673d dummy change
1019 1019 |
1020 1020 x 20:8b31da3c4919 dummy change (rewritten as 18:601db7a18f51)
1021 1021 |
1022 1022 o 19:b82fb57ea638 willconflict second version
1023 1023 |
1024 1024 | o 18:601db7a18f51 dummy change successor
1025 1025 | |
1026 1026 | o 17:357ddf1602d5 willconflict first version
1027 1027 |/
1028 1028 o 16:61bd55f69bc4 bar foo
1029 1029 |
1030 1030 ~
1031 1031 $ hg rebase -r ".^^ + .^ + ." -d 18
1032 1032 rebasing 19:b82fb57ea638 "willconflict second version"
1033 1033 merging willconflict
1034 1034 warning: conflicts while merging willconflict! (edit, then use 'hg resolve --mark')
1035 1035 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
1036 1036 [240]
1037 1037
1038 1038 $ hg resolve --mark willconflict
1039 1039 (no more unresolved files)
1040 1040 continue: hg rebase --continue
1041 1041 $ hg rebase --continue
1042 1042 rebasing 19:b82fb57ea638 "willconflict second version"
1043 1043 note: not rebasing 20:8b31da3c4919 "dummy change", already in destination as 18:601db7a18f51 "dummy change successor"
1044 1044 rebasing 21:7bdc8a87673d tip "dummy change"
1045 1045 $ cd ..
1046 1046
1047 1047 Divergence cases due to obsolete changesets
1048 1048 -------------------------------------------
1049 1049
1050 1050 We should ignore branches with unstable changesets when they are based on an
1051 1051 obsolete changeset which successor is in rebase set.
1052 1052
1053 1053 $ hg init divergence
1054 1054 $ cd divergence
1055 1055 $ cat >> .hg/hgrc << EOF
1056 1056 > [extensions]
1057 1057 > strip =
1058 1058 > [alias]
1059 1059 > strip = strip --no-backup --quiet
1060 1060 > [templates]
1061 1061 > instabilities = '{rev}:{node|short} {desc|firstline}{if(instabilities," ({instabilities})")}\n'
1062 1062 > EOF
1063 1063
1064 1064 $ hg debugdrawdag <<EOF
1065 1065 > e f
1066 1066 > | |
1067 1067 > d' d # replace: d -> d'
1068 1068 > \ /
1069 1069 > c
1070 1070 > |
1071 1071 > x b
1072 1072 > \|
1073 1073 > a
1074 1074 > EOF
1075 1075 1 new orphan changesets
1076 1076 $ hg log -G -r 'a'::
1077 1077 * 7:1143e9adc121 f
1078 1078 |
1079 1079 | o 6:d60ebfa0f1cb e
1080 1080 | |
1081 1081 | o 5:027ad6c5830d d'
1082 1082 | |
1083 1083 x | 4:76be324c128b d (rewritten using replace as 5:027ad6c5830d)
1084 1084 |/
1085 1085 o 3:a82ac2b38757 c
1086 1086 |
1087 1087 | o 2:630d7c95eff7 x
1088 1088 | |
1089 1089 o | 1:488e1b7e7341 b
1090 1090 |/
1091 1091 o 0:b173517d0057 a
1092 1092
1093 1093
1094 1094 Changeset d and its descendants are excluded to avoid divergence of d, which
1095 1095 would occur because the successor of d (d') is also in rebaseset. As a
1096 1096 consequence f (descendant of d) is left behind.
1097 1097
1098 1098 $ hg rebase -b 'e' -d 'x'
1099 1099 rebasing 1:488e1b7e7341 b "b"
1100 1100 rebasing 3:a82ac2b38757 c "c"
1101 1101 rebasing 5:027ad6c5830d d' "d'"
1102 1102 rebasing 6:d60ebfa0f1cb e "e"
1103 1103 note: not rebasing 4:76be324c128b d "d" and its descendants as this would cause divergence
1104 1104 $ hg log -G -r 'a'::
1105 1105 o 11:eb6d63fc4ed5 e
1106 1106 |
1107 1107 o 10:44d8c724a70c d'
1108 1108 |
1109 1109 o 9:d008e6b4d3fd c
1110 1110 |
1111 1111 o 8:67e8f4a16c49 b
1112 1112 |
1113 1113 | * 7:1143e9adc121 f
1114 1114 | |
1115 1115 | | x 6:d60ebfa0f1cb e (rewritten using rebase as 11:eb6d63fc4ed5)
1116 1116 | | |
1117 1117 | | x 5:027ad6c5830d d' (rewritten using rebase as 10:44d8c724a70c)
1118 1118 | | |
1119 1119 | x | 4:76be324c128b d (rewritten using replace as 5:027ad6c5830d)
1120 1120 | |/
1121 1121 | x 3:a82ac2b38757 c (rewritten using rebase as 9:d008e6b4d3fd)
1122 1122 | |
1123 1123 o | 2:630d7c95eff7 x
1124 1124 | |
1125 1125 | x 1:488e1b7e7341 b (rewritten using rebase as 8:67e8f4a16c49)
1126 1126 |/
1127 1127 o 0:b173517d0057 a
1128 1128
1129 1129 $ hg strip -r 8:
1130 1130 $ hg log -G -r 'a'::
1131 1131 * 7:1143e9adc121 f
1132 1132 |
1133 1133 | o 6:d60ebfa0f1cb e
1134 1134 | |
1135 1135 | o 5:027ad6c5830d d'
1136 1136 | |
1137 1137 x | 4:76be324c128b d (rewritten using replace as 5:027ad6c5830d)
1138 1138 |/
1139 1139 o 3:a82ac2b38757 c
1140 1140 |
1141 1141 | o 2:630d7c95eff7 x
1142 1142 | |
1143 1143 o | 1:488e1b7e7341 b
1144 1144 |/
1145 1145 o 0:b173517d0057 a
1146 1146
1147 1147
1148 1148 If the rebase set has an obsolete (d) with a successor (d') outside the rebase
1149 1149 set and none in destination, we still get the divergence warning.
1150 1150 By allowing divergence, we can perform the rebase.
1151 1151
1152 1152 $ hg rebase -r 'c'::'f' -d 'x'
1153 1153 abort: this rebase will cause divergences from: 76be324c128b
1154 1154 (to force the rebase please set experimental.evolution.allowdivergence=True)
1155 1155 [255]
1156 1156 $ hg rebase --config experimental.evolution.allowdivergence=true -r 'c'::'f' -d 'x'
1157 1157 rebasing 3:a82ac2b38757 c "c"
1158 1158 rebasing 4:76be324c128b d "d"
1159 1159 rebasing 7:1143e9adc121 f tip "f"
1160 1160 1 new orphan changesets
1161 1161 2 new content-divergent changesets
1162 1162 $ hg log -G -r 'a':: -T instabilities
1163 1163 o 10:e1744ea07510 f
1164 1164 |
1165 1165 * 9:e2b36ea9a0a0 d (content-divergent)
1166 1166 |
1167 1167 o 8:6a0376de376e c
1168 1168 |
1169 1169 | x 7:1143e9adc121 f
1170 1170 | |
1171 1171 | | * 6:d60ebfa0f1cb e (orphan)
1172 1172 | | |
1173 1173 | | * 5:027ad6c5830d d' (orphan content-divergent)
1174 1174 | | |
1175 1175 | x | 4:76be324c128b d
1176 1176 | |/
1177 1177 | x 3:a82ac2b38757 c
1178 1178 | |
1179 1179 o | 2:630d7c95eff7 x
1180 1180 | |
1181 1181 | o 1:488e1b7e7341 b
1182 1182 |/
1183 1183 o 0:b173517d0057 a
1184 1184
1185 1185 $ hg strip -r 8:
1186 1186
1187 1187 (Not skipping obsoletes means that divergence is allowed.)
1188 1188
1189 1189 $ hg rebase --config experimental.rebaseskipobsolete=false -r 'c'::'f' -d 'x'
1190 1190 rebasing 3:a82ac2b38757 c "c"
1191 1191 rebasing 4:76be324c128b d "d"
1192 1192 rebasing 7:1143e9adc121 f tip "f"
1193 1193 1 new orphan changesets
1194 1194 2 new content-divergent changesets
1195 1195
1196 1196 $ hg strip -r 0:
1197 1197
1198 1198 Similar test on a more complex graph
1199 1199
1200 1200 $ hg debugdrawdag <<EOF
1201 1201 > g
1202 1202 > |
1203 1203 > f e
1204 1204 > | |
1205 1205 > e' d # replace: e -> e'
1206 1206 > \ /
1207 1207 > c
1208 1208 > |
1209 1209 > x b
1210 1210 > \|
1211 1211 > a
1212 1212 > EOF
1213 1213 1 new orphan changesets
1214 1214 $ hg log -G -r 'a':
1215 1215 * 8:2876ce66c6eb g
1216 1216 |
1217 1217 | o 7:3ffec603ab53 f
1218 1218 | |
1219 1219 x | 6:e36fae928aec e (rewritten using replace as 5:63324dc512ea)
1220 1220 | |
1221 1221 | o 5:63324dc512ea e'
1222 1222 | |
1223 1223 o | 4:76be324c128b d
1224 1224 |/
1225 1225 o 3:a82ac2b38757 c
1226 1226 |
1227 1227 | o 2:630d7c95eff7 x
1228 1228 | |
1229 1229 o | 1:488e1b7e7341 b
1230 1230 |/
1231 1231 o 0:b173517d0057 a
1232 1232
1233 1233 $ hg rebase -b 'f' -d 'x'
1234 1234 rebasing 1:488e1b7e7341 b "b"
1235 1235 rebasing 3:a82ac2b38757 c "c"
1236 1236 rebasing 5:63324dc512ea e' "e'"
1237 1237 rebasing 7:3ffec603ab53 f "f"
1238 1238 rebasing 4:76be324c128b d "d"
1239 1239 note: not rebasing 6:e36fae928aec e "e" and its descendants as this would cause divergence
1240 1240 $ hg log -G -r 'a':
1241 1241 o 13:a1707a5b7c2c d
1242 1242 |
1243 1243 | o 12:ef6251596616 f
1244 1244 | |
1245 1245 | o 11:b6f172e64af9 e'
1246 1246 |/
1247 1247 o 10:d008e6b4d3fd c
1248 1248 |
1249 1249 o 9:67e8f4a16c49 b
1250 1250 |
1251 1251 | * 8:2876ce66c6eb g
1252 1252 | |
1253 1253 | | x 7:3ffec603ab53 f (rewritten using rebase as 12:ef6251596616)
1254 1254 | | |
1255 1255 | x | 6:e36fae928aec e (rewritten using replace as 5:63324dc512ea)
1256 1256 | | |
1257 1257 | | x 5:63324dc512ea e' (rewritten using rebase as 11:b6f172e64af9)
1258 1258 | | |
1259 1259 | x | 4:76be324c128b d (rewritten using rebase as 13:a1707a5b7c2c)
1260 1260 | |/
1261 1261 | x 3:a82ac2b38757 c (rewritten using rebase as 10:d008e6b4d3fd)
1262 1262 | |
1263 1263 o | 2:630d7c95eff7 x
1264 1264 | |
1265 1265 | x 1:488e1b7e7341 b (rewritten using rebase as 9:67e8f4a16c49)
1266 1266 |/
1267 1267 o 0:b173517d0057 a
1268 1268
1269 1269
1270 1270 issue5782
1271 1271 $ hg strip -r 0:
1272 1272 $ hg debugdrawdag <<EOF
1273 1273 > d
1274 1274 > |
1275 1275 > c1 c # replace: c -> c1
1276 1276 > \ /
1277 1277 > b
1278 1278 > |
1279 1279 > a
1280 1280 > EOF
1281 1281 1 new orphan changesets
1282 1282 $ hg debugobsolete `hg log -T "{node}" --hidden -r 'desc("c1")'`
1283 1283 1 new obsolescence markers
1284 1284 obsoleted 1 changesets
1285 1285 $ hg log -G -r 'a': --hidden
1286 1286 * 4:76be324c128b d
1287 1287 |
1288 1288 | x 3:ef8a456de8fa c1 (pruned)
1289 1289 | |
1290 1290 x | 2:a82ac2b38757 c (rewritten using replace as 3:ef8a456de8fa)
1291 1291 |/
1292 1292 o 1:488e1b7e7341 b
1293 1293 |
1294 1294 o 0:b173517d0057 a
1295 1295
1296 1296 $ hg rebase -d 0 -r 2
1297 1297 rebasing 2:a82ac2b38757 c "c"
1298 1298 $ hg log -G -r 'a': --hidden
1299 1299 o 5:69ad416a4a26 c
1300 1300 |
1301 1301 | * 4:76be324c128b d
1302 1302 | |
1303 1303 | | x 3:ef8a456de8fa c1 (pruned)
1304 1304 | | |
1305 1305 | x | 2:a82ac2b38757 c (rewritten using replace as 3:ef8a456de8fa rewritten using rebase as 5:69ad416a4a26)
1306 1306 | |/
1307 1307 | o 1:488e1b7e7341 b
1308 1308 |/
1309 1309 o 0:b173517d0057 a
1310 1310
1311 1311 $ cd ..
1312 1312
1313 1313 Rebase merge where successor of one parent is equal to destination (issue5198)
1314 1314
1315 1315 $ hg init p1-succ-is-dest
1316 1316 $ cd p1-succ-is-dest
1317 1317
1318 1318 $ hg debugdrawdag <<EOF
1319 1319 > F
1320 1320 > /|
1321 1321 > E D B # replace: D -> B
1322 1322 > \|/
1323 1323 > A
1324 1324 > EOF
1325 1325 1 new orphan changesets
1326 1326
1327 1327 $ hg rebase -d B -s D
1328 1328 note: not rebasing 2:b18e25de2cf5 D "D", already in destination as 1:112478962961 B "B"
1329 1329 rebasing 4:66f1a38021c9 F tip "F"
1330 1330 $ hg log -G
1331 1331 o 5:50e9d60b99c6 F
1332 1332 |\
1333 1333 | | x 4:66f1a38021c9 F (rewritten using rebase as 5:50e9d60b99c6)
1334 1334 | |/|
1335 1335 | o | 3:7fb047a69f22 E
1336 1336 | | |
1337 1337 | | x 2:b18e25de2cf5 D (rewritten using replace as 1:112478962961)
1338 1338 | |/
1339 1339 o | 1:112478962961 B
1340 1340 |/
1341 1341 o 0:426bada5c675 A
1342 1342
1343 1343 $ cd ..
1344 1344
1345 1345 Rebase merge where successor of other parent is equal to destination
1346 1346
1347 1347 $ hg init p2-succ-is-dest
1348 1348 $ cd p2-succ-is-dest
1349 1349
1350 1350 $ hg debugdrawdag <<EOF
1351 1351 > F
1352 1352 > /|
1353 1353 > E D B # replace: E -> B
1354 1354 > \|/
1355 1355 > A
1356 1356 > EOF
1357 1357 1 new orphan changesets
1358 1358
1359 1359 $ hg rebase -d B -s E
1360 1360 note: not rebasing 3:7fb047a69f22 E "E", already in destination as 1:112478962961 B "B"
1361 1361 rebasing 4:66f1a38021c9 F tip "F"
1362 1362 $ hg log -G
1363 1363 o 5:aae1787dacee F
1364 1364 |\
1365 1365 | | x 4:66f1a38021c9 F (rewritten using rebase as 5:aae1787dacee)
1366 1366 | |/|
1367 1367 | | x 3:7fb047a69f22 E (rewritten using replace as 1:112478962961)
1368 1368 | | |
1369 1369 | o | 2:b18e25de2cf5 D
1370 1370 | |/
1371 1371 o / 1:112478962961 B
1372 1372 |/
1373 1373 o 0:426bada5c675 A
1374 1374
1375 1375 $ cd ..
1376 1376
1377 1377 Rebase merge where successor of one parent is ancestor of destination
1378 1378
1379 1379 $ hg init p1-succ-in-dest
1380 1380 $ cd p1-succ-in-dest
1381 1381
1382 1382 $ hg debugdrawdag <<EOF
1383 1383 > F C
1384 1384 > /| |
1385 1385 > E D B # replace: D -> B
1386 1386 > \|/
1387 1387 > A
1388 1388 > EOF
1389 1389 1 new orphan changesets
1390 1390
1391 1391 $ hg rebase -d C -s D
1392 1392 note: not rebasing 2:b18e25de2cf5 D "D", already in destination as 1:112478962961 B "B"
1393 1393 rebasing 5:66f1a38021c9 F tip "F"
1394 1394
1395 1395 $ hg log -G
1396 1396 o 6:0913febf6439 F
1397 1397 |\
1398 1398 +---x 5:66f1a38021c9 F (rewritten using rebase as 6:0913febf6439)
1399 1399 | | |
1400 1400 | o | 4:26805aba1e60 C
1401 1401 | | |
1402 1402 o | | 3:7fb047a69f22 E
1403 1403 | | |
1404 1404 +---x 2:b18e25de2cf5 D (rewritten using replace as 1:112478962961)
1405 1405 | |
1406 1406 | o 1:112478962961 B
1407 1407 |/
1408 1408 o 0:426bada5c675 A
1409 1409
1410 1410 $ cd ..
1411 1411
1412 1412 Rebase merge where successor of other parent is ancestor of destination
1413 1413
1414 1414 $ hg init p2-succ-in-dest
1415 1415 $ cd p2-succ-in-dest
1416 1416
1417 1417 $ hg debugdrawdag <<EOF
1418 1418 > F C
1419 1419 > /| |
1420 1420 > E D B # replace: E -> B
1421 1421 > \|/
1422 1422 > A
1423 1423 > EOF
1424 1424 1 new orphan changesets
1425 1425
1426 1426 $ hg rebase -d C -s E
1427 1427 note: not rebasing 3:7fb047a69f22 E "E", already in destination as 1:112478962961 B "B"
1428 1428 rebasing 5:66f1a38021c9 F tip "F"
1429 1429 $ hg log -G
1430 1430 o 6:c6ab0cc6d220 F
1431 1431 |\
1432 1432 +---x 5:66f1a38021c9 F (rewritten using rebase as 6:c6ab0cc6d220)
1433 1433 | | |
1434 1434 | o | 4:26805aba1e60 C
1435 1435 | | |
1436 1436 | | x 3:7fb047a69f22 E (rewritten using replace as 1:112478962961)
1437 1437 | | |
1438 1438 o---+ 2:b18e25de2cf5 D
1439 1439 / /
1440 1440 o / 1:112478962961 B
1441 1441 |/
1442 1442 o 0:426bada5c675 A
1443 1443
1444 1444 $ cd ..
1445 1445
1446 1446 Rebase merge where successor of one parent is ancestor of destination
1447 1447
1448 1448 $ hg init p1-succ-in-dest-b
1449 1449 $ cd p1-succ-in-dest-b
1450 1450
1451 1451 $ hg debugdrawdag <<EOF
1452 1452 > F C
1453 1453 > /| |
1454 1454 > E D B # replace: E -> B
1455 1455 > \|/
1456 1456 > A
1457 1457 > EOF
1458 1458 1 new orphan changesets
1459 1459
1460 1460 $ hg rebase -d C -b F
1461 1461 rebasing 2:b18e25de2cf5 D "D"
1462 1462 note: not rebasing 3:7fb047a69f22 E "E", already in destination as 1:112478962961 B "B"
1463 1463 rebasing 5:66f1a38021c9 F tip "F"
1464 1464 note: not rebasing 5:66f1a38021c9 F tip "F", its destination already has all its changes
1465 1465 $ hg log -G
1466 1466 o 6:8f47515dda15 D
1467 1467 |
1468 1468 | x 5:66f1a38021c9 F (pruned using rebase)
1469 1469 | |\
1470 1470 o | | 4:26805aba1e60 C
1471 1471 | | |
1472 1472 | | x 3:7fb047a69f22 E (rewritten using replace as 1:112478962961)
1473 1473 | | |
1474 1474 | x | 2:b18e25de2cf5 D (rewritten using rebase as 6:8f47515dda15)
1475 1475 | |/
1476 1476 o / 1:112478962961 B
1477 1477 |/
1478 1478 o 0:426bada5c675 A
1479 1479
1480 1480 $ cd ..
1481 1481
1482 1482 Rebase merge where successor of other parent is ancestor of destination
1483 1483
1484 1484 $ hg init p2-succ-in-dest-b
1485 1485 $ cd p2-succ-in-dest-b
1486 1486
1487 1487 $ hg debugdrawdag <<EOF
1488 1488 > F C
1489 1489 > /| |
1490 1490 > E D B # replace: D -> B
1491 1491 > \|/
1492 1492 > A
1493 1493 > EOF
1494 1494 1 new orphan changesets
1495 1495
1496 1496 $ hg rebase -d C -b F
1497 1497 note: not rebasing 2:b18e25de2cf5 D "D", already in destination as 1:112478962961 B "B"
1498 1498 rebasing 3:7fb047a69f22 E "E"
1499 1499 rebasing 5:66f1a38021c9 F tip "F"
1500 1500 note: not rebasing 5:66f1a38021c9 F tip "F", its destination already has all its changes
1501 1501
1502 1502 $ hg log -G
1503 1503 o 6:533690786a86 E
1504 1504 |
1505 1505 | x 5:66f1a38021c9 F (pruned using rebase)
1506 1506 | |\
1507 1507 o | | 4:26805aba1e60 C
1508 1508 | | |
1509 1509 | | x 3:7fb047a69f22 E (rewritten using rebase as 6:533690786a86)
1510 1510 | | |
1511 1511 | x | 2:b18e25de2cf5 D (rewritten using replace as 1:112478962961)
1512 1512 | |/
1513 1513 o / 1:112478962961 B
1514 1514 |/
1515 1515 o 0:426bada5c675 A
1516 1516
1517 1517 $ cd ..
1518 1518
1519 1519 Rebase merge where extinct node has successor that is not an ancestor of
1520 1520 destination
1521 1521
1522 1522 $ hg init extinct-with-succ-not-in-dest
1523 1523 $ cd extinct-with-succ-not-in-dest
1524 1524
1525 1525 $ hg debugdrawdag <<EOF
1526 1526 > E C # replace: C -> E
1527 1527 > | |
1528 1528 > D B
1529 1529 > |/
1530 1530 > A
1531 1531 > EOF
1532 1532
1533 1533 $ hg rebase -d D -s B
1534 1534 rebasing 1:112478962961 B "B"
1535 1535 note: not rebasing 3:26805aba1e60 C "C" and its descendants as this would cause divergence
1536 1536
1537 1537 $ cd ..
1538 1538
1539 1539 $ hg init p2-succ-in-dest-c
1540 1540 $ cd p2-succ-in-dest-c
1541 1541
1542 1542 The scenario here was that B::D were developed on default. B was queued on
1543 1543 stable, but amended before being push to hg-committed. C was queued on default,
1544 1544 along with unrelated J.
1545 1545
1546 1546 $ hg debugdrawdag <<EOF
1547 1547 > J
1548 1548 > |
1549 1549 > F
1550 1550 > |
1551 1551 > E
1552 1552 > | D
1553 1553 > | |
1554 1554 > | C # replace: C -> F
1555 1555 > | | H I # replace: B -> H -> I
1556 1556 > | B |/
1557 1557 > |/ G
1558 1558 > A
1559 1559 > EOF
1560 1560 1 new orphan changesets
1561 1561
1562 1562 This strip seems to be the key to avoid an early divergence warning.
1563 1563 $ hg --config extensions.strip= --hidden strip -qr H
1564 1564 1 new orphan changesets
1565 1565
1566 1566 $ hg rebase -b 'desc("D")' -d 'desc("J")'
1567 1567 abort: this rebase will cause divergences from: 112478962961
1568 1568 (to force the rebase please set experimental.evolution.allowdivergence=True)
1569 1569 [255]
1570 1570
1571 1571 Rebase merge where both parents have successors in destination
1572 1572
1573 1573 $ hg init p12-succ-in-dest
1574 1574 $ cd p12-succ-in-dest
1575 1575 $ hg debugdrawdag <<'EOS'
1576 1576 > E F
1577 1577 > /| /| # replace: A -> C
1578 1578 > A B C D # replace: B -> D
1579 1579 > | |
1580 1580 > X Y
1581 1581 > EOS
1582 1582 1 new orphan changesets
1583 1583 $ hg rebase -r A+B+E -d F
1584 1584 note: not rebasing 4:a3d17304151f A "A", already in destination as 0:96cc3511f894 C "C"
1585 1585 note: not rebasing 5:b23a2cc00842 B "B", already in destination as 1:058c1e1fb10a D "D"
1586 1586 rebasing 7:dac5d11c5a7d E tip "E"
1587 1587 abort: rebasing 7:dac5d11c5a7d will include unwanted changes from 3:59c792af609c, 5:b23a2cc00842 or 2:ba2b7fa7166d, 4:a3d17304151f
1588 1588 [255]
1589 1589 $ cd ..
1590 1590
1591 1591 Rebase a non-clean merge. One parent has successor in destination, the other
1592 1592 parent moves as requested.
1593 1593
1594 1594 $ hg init p1-succ-p2-move
1595 1595 $ cd p1-succ-p2-move
1596 1596 $ hg debugdrawdag <<'EOS'
1597 1597 > D Z
1598 1598 > /| | # replace: A -> C
1599 1599 > A B C # D/D = D
1600 1600 > EOS
1601 1601 1 new orphan changesets
1602 1602 $ hg rebase -r A+B+D -d Z
1603 1603 note: not rebasing 0:426bada5c675 A "A", already in destination as 2:96cc3511f894 C "C"
1604 1604 rebasing 1:fc2b737bb2e5 B "B"
1605 1605 rebasing 3:b8ed089c80ad D "D"
1606 1606
1607 1607 $ rm .hg/localtags
1608 1608 $ hg log -G
1609 1609 o 6:e4f78693cc88 D
1610 1610 |
1611 1611 o 5:76840d832e98 B
1612 1612 |
1613 1613 o 4:50e41c1f3950 Z
1614 1614 |
1615 1615 o 2:96cc3511f894 C
1616 1616
1617 1617 $ hg files -r tip
1618 1618 B
1619 1619 C
1620 1620 D
1621 1621 Z
1622 1622
1623 1623 $ cd ..
1624 1624
1625 1625 $ hg init p1-move-p2-succ
1626 1626 $ cd p1-move-p2-succ
1627 1627 $ hg debugdrawdag <<'EOS'
1628 1628 > D Z
1629 1629 > /| | # replace: B -> C
1630 1630 > A B C # D/D = D
1631 1631 > EOS
1632 1632 1 new orphan changesets
1633 1633 $ hg rebase -r B+A+D -d Z
1634 1634 rebasing 0:426bada5c675 A "A"
1635 1635 note: not rebasing 1:fc2b737bb2e5 B "B", already in destination as 2:96cc3511f894 C "C"
1636 1636 rebasing 3:b8ed089c80ad D "D"
1637 1637
1638 1638 $ rm .hg/localtags
1639 1639 $ hg log -G
1640 1640 o 6:1b355ed94d82 D
1641 1641 |
1642 1642 o 5:a81a74d764a6 A
1643 1643 |
1644 1644 o 4:50e41c1f3950 Z
1645 1645 |
1646 1646 o 2:96cc3511f894 C
1647 1647
1648 1648 $ hg files -r tip
1649 1649 A
1650 1650 C
1651 1651 D
1652 1652 Z
1653 1653
1654 1654 $ cd ..
1655 1655
1656 1656 Test that bookmark is moved and working dir is updated when all changesets have
1657 1657 equivalents in destination
1658 1658 $ hg init rbsrepo && cd rbsrepo
1659 1659 $ echo "[experimental]" > .hg/hgrc
1660 1660 $ echo "evolution=true" >> .hg/hgrc
1661 1661 $ echo "rebaseskipobsolete=on" >> .hg/hgrc
1662 1662 $ echo root > root && hg ci -Am root
1663 1663 adding root
1664 1664 $ echo a > a && hg ci -Am a
1665 1665 adding a
1666 1666 $ hg up 0
1667 1667 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1668 1668 $ echo b > b && hg ci -Am b
1669 1669 adding b
1670 1670 created new head
1671 1671 $ hg rebase -r 2 -d 1
1672 1672 rebasing 2:1e9a3c00cbe9 tip "b"
1673 1673 $ hg log -r . # working dir is at rev 3 (successor of 2)
1674 1674 3:be1832deae9a b (no-eol)
1675 1675 $ hg book -r 2 mybook --hidden # rev 2 has a bookmark on it now
1676 1676 bookmarking hidden changeset 1e9a3c00cbe9
1677 1677 (hidden revision '1e9a3c00cbe9' was rewritten as: be1832deae9a)
1678 1678 $ hg up 2 && hg log -r . # working dir is at rev 2 again
1679 1679 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1680 1680 2:1e9a3c00cbe9 b (rewritten using rebase as 3:be1832deae9a) (no-eol)
1681 1681 $ hg rebase -r 2 -d 3 --config experimental.evolution.track-operation=1
1682 1682 note: not rebasing 2:1e9a3c00cbe9 mybook "b", already in destination as 3:be1832deae9a tip "b"
1683 1683 Check that working directory and bookmark was updated to rev 3 although rev 2
1684 1684 was skipped
1685 1685 $ hg log -r .
1686 1686 3:be1832deae9a b (no-eol)
1687 1687 $ hg bookmarks
1688 1688 mybook 3:be1832deae9a
1689 1689 $ hg debugobsolete --rev tip
1690 1690 1e9a3c00cbe90d236ac05ef61efcc5e40b7412bc be1832deae9ac531caa7438b8dcf6055a122cd8e 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
1691 1691
1692 1692 Obsoleted working parent and bookmark could be moved if an ancestor of working
1693 1693 parent gets moved:
1694 1694
1695 1695 $ hg init $TESTTMP/ancestor-wd-move
1696 1696 $ cd $TESTTMP/ancestor-wd-move
1697 1697 $ hg debugdrawdag <<'EOS'
1698 1698 > E D1 # rebase: D1 -> D2
1699 1699 > | |
1700 1700 > | C
1701 1701 > D2 |
1702 1702 > | B
1703 1703 > |/
1704 1704 > A
1705 1705 > EOS
1706 1706 $ hg update D1 -q
1707 1707 $ hg bookmark book -i
1708 1708 $ hg rebase -r B+D1 -d E
1709 1709 rebasing 1:112478962961 B "B"
1710 1710 note: not rebasing 5:15ecf15e0114 book D1 tip "D1", already in destination as 2:0807738e0be9 D2 "D2"
1711 1711 1 new orphan changesets
1712 1712 $ hg log -G -T '{desc} {bookmarks}'
1713 1713 @ B book
1714 1714 |
1715 1715 | x D1
1716 1716 | |
1717 1717 o | E
1718 1718 | |
1719 1719 | * C
1720 1720 | |
1721 1721 o | D2
1722 1722 | |
1723 1723 | x B
1724 1724 |/
1725 1725 o A
1726 1726
1727 1727 Rebasing a merge with one of its parent having a hidden successor
1728 1728
1729 1729 $ hg init $TESTTMP/merge-p1-hidden-successor
1730 1730 $ cd $TESTTMP/merge-p1-hidden-successor
1731 1731
1732 1732 $ hg debugdrawdag <<'EOS'
1733 1733 > E
1734 1734 > |
1735 1735 > B3 B2 # amend: B1 -> B2 -> B3
1736 1736 > |/ # B2 is hidden
1737 1737 > | D
1738 1738 > | |\
1739 1739 > | B1 C
1740 1740 > |/
1741 1741 > A
1742 1742 > EOS
1743 1743 1 new orphan changesets
1744 1744
1745 1745 $ eval `hg tags -T '{tag}={node}\n'`
1746 1746 $ rm .hg/localtags
1747 1747
1748 1748 $ hg rebase -r $D -d $E
1749 1749 rebasing 5:9e62094e4d94 "D"
1750 1750
1751 1751 $ hg log -G
1752 1752 o 7:a699d059adcf D
1753 1753 |\
1754 1754 | o 6:ecc93090a95c E
1755 1755 | |
1756 1756 | o 4:0dc878468a23 B3
1757 1757 | |
1758 1758 o | 1:96cc3511f894 C
1759 1759 /
1760 1760 o 0:426bada5c675 A
1761 1761
1762 1762 For some reasons (--hidden, rebaseskipobsolete=0, directaccess, etc.),
1763 1763 rebasestate may contain hidden hashes. "rebase --abort" should work regardless.
1764 1764
1765 1765 $ hg init $TESTTMP/hidden-state1
1766 1766 $ cd $TESTTMP/hidden-state1
1767 1767 $ cat >> .hg/hgrc <<EOF
1768 1768 > [experimental]
1769 1769 > rebaseskipobsolete=0
1770 1770 > EOF
1771 1771
1772 1772 $ hg debugdrawdag <<'EOS'
1773 1773 > C
1774 1774 > |
1775 1775 > D B # prune: B, C
1776 1776 > |/ # B/D=B
1777 1777 > A
1778 1778 > EOS
1779 1779
1780 1780 $ eval `hg tags -T '{tag}={node}\n'`
1781 1781 $ rm .hg/localtags
1782 1782
1783 1783 $ hg update -q $C --hidden
1784 1784 updated to hidden changeset 7829726be4dc
1785 1785 (hidden revision '7829726be4dc' is pruned)
1786 1786 $ hg rebase -s $B -d $D
1787 1787 rebasing 1:2ec65233581b "B"
1788 1788 merging D
1789 1789 warning: conflicts while merging D! (edit, then use 'hg resolve --mark')
1790 1790 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
1791 1791 [240]
1792 1792
1793 1793 $ cp -R . $TESTTMP/hidden-state2
1794 1794
1795 1795 $ hg log -G
1796 1796 @ 2:b18e25de2cf5 D
1797 1797 |
1798 1798 | % 1:2ec65233581b B (pruned using prune)
1799 1799 |/
1800 1800 o 0:426bada5c675 A
1801 1801
1802 1802 $ hg summary
1803 1803 parent: 2:b18e25de2cf5 tip
1804 1804 D
1805 1805 branch: default
1806 1806 commit: 1 modified, 1 added, 1 unknown, 1 unresolved
1807 1807 update: 1 new changesets, 2 branch heads (merge)
1808 1808 phases: 3 draft
1809 1809 rebase: 0 rebased, 2 remaining (rebase --continue)
1810 1810
1811 1811 $ hg rebase --abort
1812 1812 rebase aborted
1813 1813
1814 1814 Also test --continue for the above case
1815 1815
1816 1816 $ cd $TESTTMP/hidden-state2
1817 1817 $ hg resolve -m
1818 1818 (no more unresolved files)
1819 1819 continue: hg rebase --continue
1820 1820 $ hg rebase --continue
1821 1821 rebasing 1:2ec65233581b "B"
1822 1822 rebasing 3:7829726be4dc tip "C"
1823 1823 $ hg log -G
1824 1824 @ 5:1964d5d5b547 C
1825 1825 |
1826 1826 o 4:68deb90c12a2 B
1827 1827 |
1828 1828 o 2:b18e25de2cf5 D
1829 1829 |
1830 1830 o 0:426bada5c675 A
1831 1831
1832 1832 ====================
1833 1833 Test --stop option |
1834 1834 ====================
1835 1835 $ cd ..
1836 1836 $ hg init rbstop
1837 1837 $ cd rbstop
1838 1838 $ echo a>a
1839 1839 $ hg ci -Aqma
1840 1840 $ echo b>b
1841 1841 $ hg ci -Aqmb
1842 1842 $ echo c>c
1843 1843 $ hg ci -Aqmc
1844 1844 $ echo d>d
1845 1845 $ hg ci -Aqmd
1846 1846 $ hg up 0 -q
1847 1847 $ echo f>f
1848 1848 $ hg ci -Aqmf
1849 1849 $ echo D>d
1850 1850 $ hg ci -Aqm "conflict with d"
1851 1851 $ hg up 3 -q
1852 1852 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
1853 1853 o 5:00bfc9898aeb test
1854 1854 | conflict with d
1855 1855 |
1856 1856 o 4:dafd40200f93 test
1857 1857 | f
1858 1858 |
1859 1859 | @ 3:055a42cdd887 test
1860 1860 | | d
1861 1861 | |
1862 1862 | o 2:177f92b77385 test
1863 1863 | | c
1864 1864 | |
1865 1865 | o 1:d2ae7f538514 test
1866 1866 |/ b
1867 1867 |
1868 1868 o 0:cb9a9f314b8b test
1869 1869 a
1870 1870
1871 1871 $ hg rebase -s 1 -d 5
1872 1872 rebasing 1:d2ae7f538514 "b"
1873 1873 rebasing 2:177f92b77385 "c"
1874 1874 rebasing 3:055a42cdd887 "d"
1875 1875 merging d
1876 1876 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
1877 1877 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
1878 1878 [240]
1879 1879 $ hg rebase --stop
1880 1880 1 new orphan changesets
1881 1881 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
1882 1882 o 7:7fffad344617 test
1883 1883 | c
1884 1884 |
1885 1885 o 6:b15528633407 test
1886 1886 | b
1887 1887 |
1888 1888 o 5:00bfc9898aeb test
1889 1889 | conflict with d
1890 1890 |
1891 1891 o 4:dafd40200f93 test
1892 1892 | f
1893 1893 |
1894 1894 | @ 3:055a42cdd887 test
1895 1895 | | d
1896 1896 | |
1897 1897 | x 2:177f92b77385 test
1898 1898 | | c
1899 1899 | |
1900 1900 | x 1:d2ae7f538514 test
1901 1901 |/ b
1902 1902 |
1903 1903 o 0:cb9a9f314b8b test
1904 1904 a
1905 1905
1906 1906 Test it aborts if unstable csets is not allowed:
1907 1907 ===============================================
1908 1908 $ cat >> $HGRCPATH << EOF
1909 1909 > [experimental]
1910 1910 > evolution.allowunstable=False
1911 1911 > EOF
1912 1912
1913 1913 $ hg strip 6 --no-backup -q
1914 1914 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
1915 1915 o 5:00bfc9898aeb test
1916 1916 | conflict with d
1917 1917 |
1918 1918 o 4:dafd40200f93 test
1919 1919 | f
1920 1920 |
1921 1921 | @ 3:055a42cdd887 test
1922 1922 | | d
1923 1923 | |
1924 1924 | o 2:177f92b77385 test
1925 1925 | | c
1926 1926 | |
1927 1927 | o 1:d2ae7f538514 test
1928 1928 |/ b
1929 1929 |
1930 1930 o 0:cb9a9f314b8b test
1931 1931 a
1932 1932
1933 1933 $ hg rebase -s 1 -d 5
1934 1934 rebasing 1:d2ae7f538514 "b"
1935 1935 rebasing 2:177f92b77385 "c"
1936 1936 rebasing 3:055a42cdd887 "d"
1937 1937 merging d
1938 1938 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
1939 1939 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
1940 1940 [240]
1941 1941 $ hg rebase --stop
1942 1942 abort: cannot remove original changesets with unrebased descendants
1943 1943 (either enable obsmarkers to allow unstable revisions or use --keep to keep original changesets)
1944 1944 [255]
1945 1945 $ hg rebase --abort
1946 1946 saved backup bundle to $TESTTMP/rbstop/.hg/strip-backup/b15528633407-6eb72b6f-backup.hg
1947 1947 rebase aborted
1948 1948
1949 1949 Test --stop when --keep is passed:
1950 1950 ==================================
1951 1951 $ hg rebase -s 1 -d 5 --keep
1952 1952 rebasing 1:d2ae7f538514 "b"
1953 1953 rebasing 2:177f92b77385 "c"
1954 1954 rebasing 3:055a42cdd887 "d"
1955 1955 merging d
1956 1956 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
1957 1957 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
1958 1958 [240]
1959 1959 $ hg rebase --stop
1960 1960 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
1961 1961 o 7:7fffad344617 test
1962 1962 | c
1963 1963 |
1964 1964 o 6:b15528633407 test
1965 1965 | b
1966 1966 |
1967 1967 o 5:00bfc9898aeb test
1968 1968 | conflict with d
1969 1969 |
1970 1970 o 4:dafd40200f93 test
1971 1971 | f
1972 1972 |
1973 1973 | @ 3:055a42cdd887 test
1974 1974 | | d
1975 1975 | |
1976 1976 | o 2:177f92b77385 test
1977 1977 | | c
1978 1978 | |
1979 1979 | o 1:d2ae7f538514 test
1980 1980 |/ b
1981 1981 |
1982 1982 o 0:cb9a9f314b8b test
1983 1983 a
1984 1984
1985 1985 Test --stop aborts when --collapse was passed:
1986 1986 =============================================
1987 1987 $ cat >> $HGRCPATH << EOF
1988 1988 > [experimental]
1989 1989 > evolution.allowunstable=True
1990 1990 > EOF
1991 1991
1992 1992 $ hg strip 6
1993 1993 saved backup bundle to $TESTTMP/rbstop/.hg/strip-backup/b15528633407-6eb72b6f-backup.hg
1994 1994 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
1995 1995 o 5:00bfc9898aeb test
1996 1996 | conflict with d
1997 1997 |
1998 1998 o 4:dafd40200f93 test
1999 1999 | f
2000 2000 |
2001 2001 | @ 3:055a42cdd887 test
2002 2002 | | d
2003 2003 | |
2004 2004 | o 2:177f92b77385 test
2005 2005 | | c
2006 2006 | |
2007 2007 | o 1:d2ae7f538514 test
2008 2008 |/ b
2009 2009 |
2010 2010 o 0:cb9a9f314b8b test
2011 2011 a
2012 2012
2013 2013 $ hg rebase -s 1 -d 5 --collapse -m "collapsed b c d"
2014 2014 rebasing 1:d2ae7f538514 "b"
2015 2015 rebasing 2:177f92b77385 "c"
2016 2016 rebasing 3:055a42cdd887 "d"
2017 2017 merging d
2018 2018 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
2019 2019 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
2020 2020 [240]
2021 2021 $ hg rebase --stop
2022 2022 abort: cannot stop in --collapse session
2023 2023 [255]
2024 2024 $ hg rebase --abort
2025 2025 rebase aborted
2026 2026 $ hg diff
2027 2027 $ hg log -G --template "{rev}:{short(node)} {person(author)}\n{firstline(desc)} {topic}\n\n"
2028 2028 o 5:00bfc9898aeb test
2029 2029 | conflict with d
2030 2030 |
2031 2031 o 4:dafd40200f93 test
2032 2032 | f
2033 2033 |
2034 2034 | @ 3:055a42cdd887 test
2035 2035 | | d
2036 2036 | |
2037 2037 | o 2:177f92b77385 test
2038 2038 | | c
2039 2039 | |
2040 2040 | o 1:d2ae7f538514 test
2041 2041 |/ b
2042 2042 |
2043 2043 o 0:cb9a9f314b8b test
2044 2044 a
2045 2045
2046 2046 Test --stop raise errors with conflicting options:
2047 2047 =================================================
2048 2048 $ hg rebase -s 3 -d 5
2049 2049 rebasing 3:055a42cdd887 "d"
2050 2050 merging d
2051 2051 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
2052 2052 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
2053 2053 [240]
2054 2054 $ hg rebase --stop --dry-run
2055 2055 abort: cannot specify both --stop and --dry-run
2056 2056 [10]
2057 2057
2058 2058 $ hg rebase -s 3 -d 5
2059 2059 abort: rebase in progress
2060 2060 (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop')
2061 [255]
2061 [20]
2062 2062 $ hg rebase --stop --continue
2063 2063 abort: cannot specify both --stop and --continue
2064 2064 [10]
2065 2065
2066 2066 Test --stop moves bookmarks of original revisions to new rebased nodes:
2067 2067 ======================================================================
2068 2068 $ cd ..
2069 2069 $ hg init repo
2070 2070 $ cd repo
2071 2071
2072 2072 $ echo a > a
2073 2073 $ hg ci -Am A
2074 2074 adding a
2075 2075
2076 2076 $ echo b > b
2077 2077 $ hg ci -Am B
2078 2078 adding b
2079 2079 $ hg book X
2080 2080 $ hg book Y
2081 2081
2082 2082 $ echo c > c
2083 2083 $ hg ci -Am C
2084 2084 adding c
2085 2085 $ hg book Z
2086 2086
2087 2087 $ echo d > d
2088 2088 $ hg ci -Am D
2089 2089 adding d
2090 2090
2091 2091 $ hg up 0 -q
2092 2092 $ echo e > e
2093 2093 $ hg ci -Am E
2094 2094 adding e
2095 2095 created new head
2096 2096
2097 2097 $ echo doubt > d
2098 2098 $ hg ci -Am "conflict with d"
2099 2099 adding d
2100 2100
2101 2101 $ hg log -GT "{rev}: {node|short} '{desc}' bookmarks: {bookmarks}\n"
2102 2102 @ 5: 39adf30bc1be 'conflict with d' bookmarks:
2103 2103 |
2104 2104 o 4: 9c1e55f411b6 'E' bookmarks:
2105 2105 |
2106 2106 | o 3: 67a385d4e6f2 'D' bookmarks: Z
2107 2107 | |
2108 2108 | o 2: 49cb3485fa0c 'C' bookmarks: Y
2109 2109 | |
2110 2110 | o 1: 6c81ed0049f8 'B' bookmarks: X
2111 2111 |/
2112 2112 o 0: 1994f17a630e 'A' bookmarks:
2113 2113
2114 2114 $ hg rebase -s 1 -d 5
2115 2115 rebasing 1:6c81ed0049f8 X "B"
2116 2116 rebasing 2:49cb3485fa0c Y "C"
2117 2117 rebasing 3:67a385d4e6f2 Z "D"
2118 2118 merging d
2119 2119 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
2120 2120 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
2121 2121 [240]
2122 2122 $ hg rebase --stop
2123 2123 1 new orphan changesets
2124 2124 $ hg log -GT "{rev}: {node|short} '{desc}' bookmarks: {bookmarks}\n"
2125 2125 o 7: 9c86c650b686 'C' bookmarks: Y
2126 2126 |
2127 2127 o 6: 9b87b54e5fd8 'B' bookmarks: X
2128 2128 |
2129 2129 @ 5: 39adf30bc1be 'conflict with d' bookmarks:
2130 2130 |
2131 2131 o 4: 9c1e55f411b6 'E' bookmarks:
2132 2132 |
2133 2133 | * 3: 67a385d4e6f2 'D' bookmarks: Z
2134 2134 | |
2135 2135 | x 2: 49cb3485fa0c 'C' bookmarks:
2136 2136 | |
2137 2137 | x 1: 6c81ed0049f8 'B' bookmarks:
2138 2138 |/
2139 2139 o 0: 1994f17a630e 'A' bookmarks:
2140 2140
@@ -1,531 +1,531 b''
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extensions]
3 3 > rebase=
4 4 >
5 5 > [phases]
6 6 > publish=False
7 7 >
8 8 > [alias]
9 9 > tglog = log -G --template "{rev}: {node|short} '{desc}' {branches}\n"
10 10 > EOF
11 11
12 12
13 13 $ hg init a
14 14 $ cd a
15 15 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
16 16 adding changesets
17 17 adding manifests
18 18 adding file changes
19 19 added 8 changesets with 7 changes to 7 files (+2 heads)
20 20 new changesets cd010b8cd998:02de42196ebe (8 drafts)
21 21 (run 'hg heads' to see heads, 'hg merge' to merge)
22 22 $ hg up tip
23 23 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
24 24
25 25 $ echo I > I
26 26 $ hg ci -AmI
27 27 adding I
28 28
29 29 $ hg tglog
30 30 @ 8: e7ec4e813ba6 'I'
31 31 |
32 32 o 7: 02de42196ebe 'H'
33 33 |
34 34 | o 6: eea13746799a 'G'
35 35 |/|
36 36 o | 5: 24b6387c8c8c 'F'
37 37 | |
38 38 | o 4: 9520eea781bc 'E'
39 39 |/
40 40 | o 3: 32af7686d403 'D'
41 41 | |
42 42 | o 2: 5fddd98957c8 'C'
43 43 | |
44 44 | o 1: 42ccdea3bb16 'B'
45 45 |/
46 46 o 0: cd010b8cd998 'A'
47 47
48 48 $ cd ..
49 49
50 50 Version with only two heads (to allow default destination to work)
51 51
52 52 $ hg clone -q -u . a a2heads -r 3 -r 8
53 53
54 54 These fail:
55 55
56 56 $ hg clone -q -u . a a0
57 57 $ cd a0
58 58
59 59 $ hg rebase -s 8 -d 7
60 60 nothing to rebase
61 61 [1]
62 62
63 63 $ hg rebase --continue --abort
64 64 abort: cannot specify both --abort and --continue
65 65 [10]
66 66
67 67 $ hg rebase --continue --collapse
68 68 abort: cannot use collapse with continue or abort
69 69 [255]
70 70
71 71 $ hg rebase --continue --dest 4
72 72 abort: cannot specify both --continue and --dest
73 73 [10]
74 74
75 75 $ hg rebase --base 5 --source 4
76 76 abort: cannot specify both --source and --base
77 77 [10]
78 78
79 79 $ hg rebase --rev 5 --source 4
80 80 abort: cannot specify both --rev and --source
81 81 [10]
82 82 $ hg rebase --base 5 --rev 4
83 83 abort: cannot specify both --rev and --base
84 84 [10]
85 85
86 86 $ hg rebase --base 6
87 87 abort: branch 'default' has 3 heads - please rebase to an explicit rev
88 88 (run 'hg heads .' to see heads, specify destination with -d)
89 89 [255]
90 90
91 91 $ hg rebase --rev '1 & !1' --dest 8
92 92 empty "rev" revision set - nothing to rebase
93 93 [1]
94 94
95 95 $ hg rebase --rev 'wdir()' --dest 6
96 96 abort: cannot rebase the working copy
97 97 [255]
98 98
99 99 $ hg rebase --source 'wdir()' --dest 6
100 100 abort: cannot rebase the working copy
101 101 [255]
102 102
103 103 $ hg rebase --source 1 --source 'wdir()' --dest 6
104 104 abort: cannot rebase the working copy
105 105 [255]
106 106
107 107 $ hg rebase --source '1 & !1' --dest 8
108 108 empty "source" revision set - nothing to rebase
109 109 [1]
110 110
111 111 $ hg rebase --base '1 & !1' --dest 8
112 112 empty "base" revision set - can't compute rebase set
113 113 [1]
114 114
115 115 $ hg rebase --dest 8
116 116 nothing to rebase - working directory parent is also destination
117 117 [1]
118 118
119 119 $ hg rebase -b . --dest 8
120 120 nothing to rebase - e7ec4e813ba6 is both "base" and destination
121 121 [1]
122 122
123 123 $ hg up -q 7
124 124
125 125 $ hg rebase --dest 8 --traceback
126 126 nothing to rebase - working directory parent is already an ancestor of destination e7ec4e813ba6
127 127 [1]
128 128
129 129 $ hg rebase --dest 8 -b.
130 130 nothing to rebase - "base" 02de42196ebe is already an ancestor of destination e7ec4e813ba6
131 131 [1]
132 132
133 133 $ hg rebase --dest '1 & !1'
134 134 abort: empty revision set
135 135 [255]
136 136
137 137 These work:
138 138
139 139 Rebase with no arguments (from 3 onto 8):
140 140
141 141 $ cd ..
142 142 $ hg clone -q -u . a2heads a1
143 143 $ cd a1
144 144 $ hg up -q -C 3
145 145
146 146 $ hg rebase
147 147 rebasing 1:42ccdea3bb16 "B"
148 148 rebasing 2:5fddd98957c8 "C"
149 149 rebasing 3:32af7686d403 "D"
150 150 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/42ccdea3bb16-3cb021d3-rebase.hg
151 151
152 152 $ hg tglog
153 153 @ 6: ed65089c18f8 'D'
154 154 |
155 155 o 5: 7621bf1a2f17 'C'
156 156 |
157 157 o 4: 9430a62369c6 'B'
158 158 |
159 159 o 3: e7ec4e813ba6 'I'
160 160 |
161 161 o 2: 02de42196ebe 'H'
162 162 |
163 163 o 1: 24b6387c8c8c 'F'
164 164 |
165 165 o 0: cd010b8cd998 'A'
166 166
167 167 Try to rollback after a rebase (fail):
168 168
169 169 $ hg rollback
170 170 no rollback information available
171 171 [1]
172 172
173 173 $ cd ..
174 174
175 175 Rebase with base == '.' => same as no arguments (from 3 onto 8):
176 176
177 177 $ hg clone -q -u 3 a2heads a2
178 178 $ cd a2
179 179
180 180 $ hg rebase --base .
181 181 rebasing 1:42ccdea3bb16 "B"
182 182 rebasing 2:5fddd98957c8 "C"
183 183 rebasing 3:32af7686d403 "D"
184 184 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/42ccdea3bb16-3cb021d3-rebase.hg
185 185
186 186 $ hg tglog
187 187 @ 6: ed65089c18f8 'D'
188 188 |
189 189 o 5: 7621bf1a2f17 'C'
190 190 |
191 191 o 4: 9430a62369c6 'B'
192 192 |
193 193 o 3: e7ec4e813ba6 'I'
194 194 |
195 195 o 2: 02de42196ebe 'H'
196 196 |
197 197 o 1: 24b6387c8c8c 'F'
198 198 |
199 199 o 0: cd010b8cd998 'A'
200 200
201 201 $ cd ..
202 202
203 203
204 204 Rebase with dest == branch(.) => same as no arguments (from 3 onto 8):
205 205
206 206 $ hg clone -q -u 3 a a3
207 207 $ cd a3
208 208
209 209 $ hg rebase --dest 'branch(.)'
210 210 rebasing 1:42ccdea3bb16 "B"
211 211 rebasing 2:5fddd98957c8 "C"
212 212 rebasing 3:32af7686d403 "D"
213 213 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/42ccdea3bb16-3cb021d3-rebase.hg
214 214
215 215 $ hg tglog
216 216 @ 8: ed65089c18f8 'D'
217 217 |
218 218 o 7: 7621bf1a2f17 'C'
219 219 |
220 220 o 6: 9430a62369c6 'B'
221 221 |
222 222 o 5: e7ec4e813ba6 'I'
223 223 |
224 224 o 4: 02de42196ebe 'H'
225 225 |
226 226 | o 3: eea13746799a 'G'
227 227 |/|
228 228 o | 2: 24b6387c8c8c 'F'
229 229 | |
230 230 | o 1: 9520eea781bc 'E'
231 231 |/
232 232 o 0: cd010b8cd998 'A'
233 233
234 234 $ cd ..
235 235
236 236
237 237 Specify only source (from 2 onto 8):
238 238
239 239 $ hg clone -q -u . a2heads a4
240 240 $ cd a4
241 241
242 242 $ hg rebase --source 'desc("C")'
243 243 rebasing 2:5fddd98957c8 "C"
244 244 rebasing 3:32af7686d403 "D"
245 245 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/5fddd98957c8-f9244fa1-rebase.hg
246 246
247 247 $ hg tglog
248 248 o 6: 7726e9fd58f7 'D'
249 249 |
250 250 o 5: 72c8333623d0 'C'
251 251 |
252 252 @ 4: e7ec4e813ba6 'I'
253 253 |
254 254 o 3: 02de42196ebe 'H'
255 255 |
256 256 o 2: 24b6387c8c8c 'F'
257 257 |
258 258 | o 1: 42ccdea3bb16 'B'
259 259 |/
260 260 o 0: cd010b8cd998 'A'
261 261
262 262 $ cd ..
263 263
264 264
265 265 Specify only dest (from 3 onto 6):
266 266
267 267 $ hg clone -q -u 3 a a5
268 268 $ cd a5
269 269
270 270 $ hg rebase --dest 6
271 271 rebasing 1:42ccdea3bb16 "B"
272 272 rebasing 2:5fddd98957c8 "C"
273 273 rebasing 3:32af7686d403 "D"
274 274 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/42ccdea3bb16-3cb021d3-rebase.hg
275 275
276 276 $ hg tglog
277 277 @ 8: 8eeb3c33ad33 'D'
278 278 |
279 279 o 7: 2327fea05063 'C'
280 280 |
281 281 o 6: e4e5be0395b2 'B'
282 282 |
283 283 | o 5: e7ec4e813ba6 'I'
284 284 | |
285 285 | o 4: 02de42196ebe 'H'
286 286 | |
287 287 o | 3: eea13746799a 'G'
288 288 |\|
289 289 | o 2: 24b6387c8c8c 'F'
290 290 | |
291 291 o | 1: 9520eea781bc 'E'
292 292 |/
293 293 o 0: cd010b8cd998 'A'
294 294
295 295 $ cd ..
296 296
297 297
298 298 Specify only base (from 1 onto 8):
299 299
300 300 $ hg clone -q -u . a2heads a6
301 301 $ cd a6
302 302
303 303 $ hg rebase --base 'desc("D")'
304 304 rebasing 1:42ccdea3bb16 "B"
305 305 rebasing 2:5fddd98957c8 "C"
306 306 rebasing 3:32af7686d403 "D"
307 307 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/42ccdea3bb16-3cb021d3-rebase.hg
308 308
309 309 $ hg tglog
310 310 o 6: ed65089c18f8 'D'
311 311 |
312 312 o 5: 7621bf1a2f17 'C'
313 313 |
314 314 o 4: 9430a62369c6 'B'
315 315 |
316 316 @ 3: e7ec4e813ba6 'I'
317 317 |
318 318 o 2: 02de42196ebe 'H'
319 319 |
320 320 o 1: 24b6387c8c8c 'F'
321 321 |
322 322 o 0: cd010b8cd998 'A'
323 323
324 324 $ cd ..
325 325
326 326
327 327 Specify source and dest (from 2 onto 7):
328 328
329 329 $ hg clone -q -u . a a7
330 330 $ cd a7
331 331
332 332 $ hg rebase --source 2 --dest 7
333 333 rebasing 2:5fddd98957c8 "C"
334 334 rebasing 3:32af7686d403 "D"
335 335 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/5fddd98957c8-f9244fa1-rebase.hg
336 336
337 337 $ hg tglog
338 338 o 8: 668acadedd30 'D'
339 339 |
340 340 o 7: 09eb682ba906 'C'
341 341 |
342 342 | @ 6: e7ec4e813ba6 'I'
343 343 |/
344 344 o 5: 02de42196ebe 'H'
345 345 |
346 346 | o 4: eea13746799a 'G'
347 347 |/|
348 348 o | 3: 24b6387c8c8c 'F'
349 349 | |
350 350 | o 2: 9520eea781bc 'E'
351 351 |/
352 352 | o 1: 42ccdea3bb16 'B'
353 353 |/
354 354 o 0: cd010b8cd998 'A'
355 355
356 356 $ cd ..
357 357
358 358
359 359 Specify base and dest (from 1 onto 7):
360 360
361 361 $ hg clone -q -u . a a8
362 362 $ cd a8
363 363
364 364 $ hg rebase --base 3 --dest 7
365 365 rebasing 1:42ccdea3bb16 "B"
366 366 rebasing 2:5fddd98957c8 "C"
367 367 rebasing 3:32af7686d403 "D"
368 368 saved backup bundle to $TESTTMP/a8/.hg/strip-backup/42ccdea3bb16-3cb021d3-rebase.hg
369 369
370 370 $ hg tglog
371 371 o 8: 287cc92ba5a4 'D'
372 372 |
373 373 o 7: 6824f610a250 'C'
374 374 |
375 375 o 6: 7c6027df6a99 'B'
376 376 |
377 377 | @ 5: e7ec4e813ba6 'I'
378 378 |/
379 379 o 4: 02de42196ebe 'H'
380 380 |
381 381 | o 3: eea13746799a 'G'
382 382 |/|
383 383 o | 2: 24b6387c8c8c 'F'
384 384 | |
385 385 | o 1: 9520eea781bc 'E'
386 386 |/
387 387 o 0: cd010b8cd998 'A'
388 388
389 389 $ cd ..
390 390
391 391
392 392 Specify only revs (from 2 onto 8)
393 393
394 394 $ hg clone -q -u . a2heads a9
395 395 $ cd a9
396 396
397 397 $ hg rebase --rev 'desc("C")::'
398 398 rebasing 2:5fddd98957c8 "C"
399 399 rebasing 3:32af7686d403 "D"
400 400 saved backup bundle to $TESTTMP/a9/.hg/strip-backup/5fddd98957c8-f9244fa1-rebase.hg
401 401
402 402 $ hg tglog
403 403 o 6: 7726e9fd58f7 'D'
404 404 |
405 405 o 5: 72c8333623d0 'C'
406 406 |
407 407 @ 4: e7ec4e813ba6 'I'
408 408 |
409 409 o 3: 02de42196ebe 'H'
410 410 |
411 411 o 2: 24b6387c8c8c 'F'
412 412 |
413 413 | o 1: 42ccdea3bb16 'B'
414 414 |/
415 415 o 0: cd010b8cd998 'A'
416 416
417 417 $ cd ..
418 418
419 419 Rebasing both a single revision and a merge in one command
420 420
421 421 $ hg clone -q -u . a aX
422 422 $ cd aX
423 423 $ hg rebase -r 3 -r 6 --dest 8
424 424 rebasing 3:32af7686d403 "D"
425 425 rebasing 6:eea13746799a "G"
426 426 saved backup bundle to $TESTTMP/aX/.hg/strip-backup/eea13746799a-ad273fd6-rebase.hg
427 427 $ cd ..
428 428
429 429 Test --tool parameter:
430 430
431 431 $ hg init b
432 432 $ cd b
433 433
434 434 $ echo c1 > c1
435 435 $ hg ci -Am c1
436 436 adding c1
437 437
438 438 $ echo c2 > c2
439 439 $ hg ci -Am c2
440 440 adding c2
441 441
442 442 $ hg up -q 0
443 443 $ echo c2b > c2
444 444 $ hg ci -Am c2b
445 445 adding c2
446 446 created new head
447 447
448 448 $ cd ..
449 449
450 450 $ hg clone -q -u . b b1
451 451 $ cd b1
452 452
453 453 $ hg rebase -s 2 -d 1 --tool internal:local
454 454 rebasing 2:e4e3f3546619 tip "c2b"
455 455 note: not rebasing 2:e4e3f3546619 tip "c2b", its destination already has all its changes
456 456 saved backup bundle to $TESTTMP/b1/.hg/strip-backup/e4e3f3546619-b0841178-rebase.hg
457 457
458 458 $ hg cat c2
459 459 c2
460 460
461 461 $ cd ..
462 462
463 463
464 464 $ hg clone -q -u . b b2
465 465 $ cd b2
466 466
467 467 $ hg rebase -s 2 -d 1 --tool internal:other
468 468 rebasing 2:e4e3f3546619 tip "c2b"
469 469 saved backup bundle to $TESTTMP/b2/.hg/strip-backup/e4e3f3546619-b0841178-rebase.hg
470 470
471 471 $ hg cat c2
472 472 c2b
473 473
474 474 $ cd ..
475 475
476 476
477 477 $ hg clone -q -u . b b3
478 478 $ cd b3
479 479
480 480 $ hg rebase -s 2 -d 1 --tool internal:fail
481 481 rebasing 2:e4e3f3546619 tip "c2b"
482 482 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
483 483 [240]
484 484
485 485 $ hg summary
486 486 parent: 1:56daeba07f4b
487 487 c2
488 488 branch: default
489 489 commit: 1 unresolved (clean)
490 490 update: 1 new changesets, 2 branch heads (merge)
491 491 phases: 3 draft
492 492 rebase: 0 rebased, 1 remaining (rebase --continue)
493 493
494 494 $ hg resolve -l
495 495 U c2
496 496
497 497 $ hg resolve -m c2
498 498 (no more unresolved files)
499 499 continue: hg rebase --continue
500 500 $ hg graft --continue
501 501 abort: no graft in progress
502 502 (continue: hg rebase --continue)
503 [255]
503 [20]
504 504 $ hg rebase -c --tool internal:fail
505 505 rebasing 2:e4e3f3546619 tip "c2b"
506 506 note: not rebasing 2:e4e3f3546619 tip "c2b", its destination already has all its changes
507 507 saved backup bundle to $TESTTMP/b3/.hg/strip-backup/e4e3f3546619-b0841178-rebase.hg
508 508
509 509 $ hg rebase -i
510 510 abort: interactive history editing is supported by the 'histedit' extension (see "hg --config extensions.histedit= help -e histedit")
511 511 [255]
512 512
513 513 $ hg rebase --interactive
514 514 abort: interactive history editing is supported by the 'histedit' extension (see "hg --config extensions.histedit= help -e histedit")
515 515 [255]
516 516
517 517 $ cd ..
518 518
519 519 No common ancestor
520 520
521 521 $ hg init separaterepo
522 522 $ cd separaterepo
523 523 $ touch a
524 524 $ hg commit -Aqm a
525 525 $ hg up -q null
526 526 $ touch b
527 527 $ hg commit -Aqm b
528 528 $ hg rebase -d 0
529 529 nothing to rebase from d7486e00c6f1 to 3903775176ed
530 530 [1]
531 531 $ cd ..
@@ -1,460 +1,460 b''
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extensions]
3 3 > rebase=
4 4 > histedit=
5 5 >
6 6 > [alias]
7 7 > tglog = log -G --template "{rev}: {node|short} '{desc}' {branches}\n"
8 8 > EOF
9 9
10 10
11 11 $ hg init a
12 12 $ cd a
13 13
14 14 $ echo C1 > C1
15 15 $ hg ci -Am C1
16 16 adding C1
17 17
18 18 $ echo C2 > C2
19 19 $ hg ci -Am C2
20 20 adding C2
21 21
22 22 $ cd ..
23 23
24 24 $ hg clone a b
25 25 updating to branch default
26 26 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
27 27
28 28 $ hg clone a c
29 29 updating to branch default
30 30 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
31 31
32 32 $ cd b
33 33
34 34 $ echo L1 > L1
35 35 $ hg ci -Am L1
36 36 adding L1
37 37
38 38
39 39 $ cd ../a
40 40
41 41 $ echo R1 > R1
42 42 $ hg ci -Am R1
43 43 adding R1
44 44
45 45
46 46 $ cd ../b
47 47
48 48 Now b has one revision to be pulled from a:
49 49
50 50 $ hg pull --rebase
51 51 pulling from $TESTTMP/a
52 52 searching for changes
53 53 adding changesets
54 54 adding manifests
55 55 adding file changes
56 56 added 1 changesets with 1 changes to 1 files (+1 heads)
57 57 new changesets 77ae9631bcca
58 58 rebasing 2:ff8d69a621f9 "L1"
59 59 saved backup bundle to $TESTTMP/b/.hg/strip-backup/ff8d69a621f9-160fa373-rebase.hg
60 60
61 61 $ hg tglog
62 62 @ 3: d80cc2da061e 'L1'
63 63 |
64 64 o 2: 77ae9631bcca 'R1'
65 65 |
66 66 o 1: 783333faa078 'C2'
67 67 |
68 68 o 0: 05d58a0c15dd 'C1'
69 69
70 70 Re-run:
71 71
72 72 $ hg pull --rebase
73 73 pulling from $TESTTMP/a
74 74 searching for changes
75 75 no changes found
76 76
77 77 Abort pull early if working dir is not clean:
78 78
79 79 $ echo L1-mod > L1
80 80 $ hg pull --rebase
81 81 abort: uncommitted changes
82 82 (cannot pull with rebase: please commit or shelve your changes first)
83 [255]
83 [20]
84 84 $ hg update --clean --quiet
85 85
86 86 Abort pull early if another operation (histedit) is in progress:
87 87
88 88 $ hg histedit . -q --commands - << EOF
89 89 > edit d80cc2da061e histedit: generate unfinished state
90 90 > EOF
91 91 Editing (d80cc2da061e), you may commit or record as needed now.
92 92 (hg histedit --continue to resume)
93 93 [240]
94 94 $ hg pull --rebase
95 95 abort: histedit in progress
96 96 (use 'hg histedit --continue' or 'hg histedit --abort')
97 [255]
97 [20]
98 98 $ hg histedit --abort --quiet
99 99
100 100 Abort pull early with pending uncommitted merge:
101 101
102 102 $ cd ..
103 103 $ hg clone --noupdate c d
104 104 $ cd d
105 105 $ hg tglog
106 106 o 1: 783333faa078 'C2'
107 107 |
108 108 o 0: 05d58a0c15dd 'C1'
109 109
110 110 $ hg update --quiet 0
111 111 $ echo M1 > M1
112 112 $ hg commit --quiet -Am M1
113 113 $ hg update --quiet 1
114 114 $ hg merge 2
115 115 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
116 116 (branch merge, don't forget to commit)
117 117 $ hg pull --rebase
118 118 abort: outstanding uncommitted merge
119 119 (cannot pull with rebase: please commit or shelve your changes first)
120 [255]
120 [20]
121 121 $ hg update --clean --quiet
122 122
123 123 Abort pull early with unclean subrepo:
124 124 $ echo s = s > .hgsub
125 125 $ hg add .hgsub
126 126 $ hg init s
127 127 $ hg commit -m "generated a subrepo"
128 128 $ echo a > s/a
129 129 $ hg -R s add s/a
130 130 $ hg pull --rebase
131 131 abort: uncommitted changes in subrepository "s"
132 132 (cannot pull with rebase: please commit or shelve your changes first)
133 133 [255]
134 134
135 135 Invoke pull --rebase and nothing to rebase:
136 136
137 137 $ cd ../c
138 138
139 139 $ hg book norebase
140 140 $ hg pull --rebase
141 141 pulling from $TESTTMP/a
142 142 searching for changes
143 143 adding changesets
144 144 adding manifests
145 145 adding file changes
146 146 added 1 changesets with 1 changes to 1 files
147 147 new changesets 77ae9631bcca
148 148 nothing to rebase - updating instead
149 149 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 150 updating bookmark norebase
151 151
152 152 $ hg tglog -l 1
153 153 @ 2: 77ae9631bcca 'R1'
154 154 |
155 155 ~
156 156
157 157 pull --rebase --update should ignore --update:
158 158
159 159 $ hg pull --rebase --update
160 160 pulling from $TESTTMP/a
161 161 searching for changes
162 162 no changes found
163 163
164 164 pull --rebase doesn't update if nothing has been pulled:
165 165
166 166 $ hg up -q 1
167 167
168 168 $ hg pull --rebase
169 169 pulling from $TESTTMP/a
170 170 searching for changes
171 171 no changes found
172 172
173 173 $ hg tglog -l 1
174 174 o 2: 77ae9631bcca 'R1'
175 175 |
176 176 ~
177 177
178 178 $ cd ..
179 179
180 180 pull --rebase works when a specific revision is pulled (issue3619)
181 181
182 182 $ cd a
183 183 $ hg tglog
184 184 @ 2: 77ae9631bcca 'R1'
185 185 |
186 186 o 1: 783333faa078 'C2'
187 187 |
188 188 o 0: 05d58a0c15dd 'C1'
189 189
190 190 $ echo R2 > R2
191 191 $ hg ci -Am R2
192 192 adding R2
193 193 $ echo R3 > R3
194 194 $ hg ci -Am R3
195 195 adding R3
196 196 $ cd ../c
197 197 $ hg tglog
198 198 o 2: 77ae9631bcca 'R1'
199 199 |
200 200 @ 1: 783333faa078 'C2'
201 201 |
202 202 o 0: 05d58a0c15dd 'C1'
203 203
204 204 $ echo L1 > L1
205 205 $ hg ci -Am L1
206 206 adding L1
207 207 created new head
208 208 $ hg pull --rev tip --rebase
209 209 pulling from $TESTTMP/a
210 210 searching for changes
211 211 adding changesets
212 212 adding manifests
213 213 adding file changes
214 214 added 2 changesets with 2 changes to 2 files
215 215 new changesets 31cd3a05214e:770a61882ace
216 216 rebasing 3:ff8d69a621f9 "L1"
217 217 saved backup bundle to $TESTTMP/c/.hg/strip-backup/ff8d69a621f9-160fa373-rebase.hg
218 218 $ hg tglog
219 219 @ 5: 518d153c0ba3 'L1'
220 220 |
221 221 o 4: 770a61882ace 'R3'
222 222 |
223 223 o 3: 31cd3a05214e 'R2'
224 224 |
225 225 o 2: 77ae9631bcca 'R1'
226 226 |
227 227 o 1: 783333faa078 'C2'
228 228 |
229 229 o 0: 05d58a0c15dd 'C1'
230 230
231 231 pull --rebase works with bundle2 turned on
232 232
233 233 $ cd ../a
234 234 $ echo R4 > R4
235 235 $ hg ci -Am R4
236 236 adding R4
237 237 $ hg tglog
238 238 @ 5: 00e3b7781125 'R4'
239 239 |
240 240 o 4: 770a61882ace 'R3'
241 241 |
242 242 o 3: 31cd3a05214e 'R2'
243 243 |
244 244 o 2: 77ae9631bcca 'R1'
245 245 |
246 246 o 1: 783333faa078 'C2'
247 247 |
248 248 o 0: 05d58a0c15dd 'C1'
249 249
250 250 $ cd ../c
251 251 $ hg pull --rebase
252 252 pulling from $TESTTMP/a
253 253 searching for changes
254 254 adding changesets
255 255 adding manifests
256 256 adding file changes
257 257 added 1 changesets with 1 changes to 1 files (+1 heads)
258 258 new changesets 00e3b7781125
259 259 rebasing 5:518d153c0ba3 "L1"
260 260 saved backup bundle to $TESTTMP/c/.hg/strip-backup/518d153c0ba3-73407f14-rebase.hg
261 261 $ hg tglog
262 262 @ 6: 0d0727eb7ce0 'L1'
263 263 |
264 264 o 5: 00e3b7781125 'R4'
265 265 |
266 266 o 4: 770a61882ace 'R3'
267 267 |
268 268 o 3: 31cd3a05214e 'R2'
269 269 |
270 270 o 2: 77ae9631bcca 'R1'
271 271 |
272 272 o 1: 783333faa078 'C2'
273 273 |
274 274 o 0: 05d58a0c15dd 'C1'
275 275
276 276
277 277 pull --rebase only update if there is nothing to rebase
278 278
279 279 $ cd ../a
280 280 $ echo R5 > R5
281 281 $ hg ci -Am R5
282 282 adding R5
283 283 $ hg tglog
284 284 @ 6: 88dd24261747 'R5'
285 285 |
286 286 o 5: 00e3b7781125 'R4'
287 287 |
288 288 o 4: 770a61882ace 'R3'
289 289 |
290 290 o 3: 31cd3a05214e 'R2'
291 291 |
292 292 o 2: 77ae9631bcca 'R1'
293 293 |
294 294 o 1: 783333faa078 'C2'
295 295 |
296 296 o 0: 05d58a0c15dd 'C1'
297 297
298 298 $ cd ../c
299 299 $ echo L2 > L2
300 300 $ hg ci -Am L2
301 301 adding L2
302 302 $ hg up 'desc(L1)'
303 303 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
304 304 $ hg pull --rebase
305 305 pulling from $TESTTMP/a
306 306 searching for changes
307 307 adding changesets
308 308 adding manifests
309 309 adding file changes
310 310 added 1 changesets with 1 changes to 1 files (+1 heads)
311 311 new changesets 88dd24261747
312 312 rebasing 6:0d0727eb7ce0 "L1"
313 313 rebasing 7:c1f58876e3bf "L2"
314 314 saved backup bundle to $TESTTMP/c/.hg/strip-backup/0d0727eb7ce0-ef61ccb2-rebase.hg
315 315 $ hg tglog
316 316 o 8: 6dc0ea5dcf55 'L2'
317 317 |
318 318 @ 7: 864e0a2d2614 'L1'
319 319 |
320 320 o 6: 88dd24261747 'R5'
321 321 |
322 322 o 5: 00e3b7781125 'R4'
323 323 |
324 324 o 4: 770a61882ace 'R3'
325 325 |
326 326 o 3: 31cd3a05214e 'R2'
327 327 |
328 328 o 2: 77ae9631bcca 'R1'
329 329 |
330 330 o 1: 783333faa078 'C2'
331 331 |
332 332 o 0: 05d58a0c15dd 'C1'
333 333
334 334
335 335 pull --rebase update (no rebase) use proper update:
336 336
337 337 - warn about other head.
338 338
339 339 $ cd ../a
340 340 $ echo R6 > R6
341 341 $ hg ci -Am R6
342 342 adding R6
343 343 $ cd ../c
344 344 $ hg up 'desc(R5)'
345 345 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
346 346 $ hg pull --rebase
347 347 pulling from $TESTTMP/a
348 348 searching for changes
349 349 adding changesets
350 350 adding manifests
351 351 adding file changes
352 352 added 1 changesets with 1 changes to 1 files (+1 heads)
353 353 new changesets 65bc164c1d9b
354 354 nothing to rebase - updating instead
355 355 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
356 356 updated to "65bc164c1d9b: R6"
357 357 1 other heads for branch "default"
358 358 $ hg tglog
359 359 @ 9: 65bc164c1d9b 'R6'
360 360 |
361 361 | o 8: 6dc0ea5dcf55 'L2'
362 362 | |
363 363 | o 7: 864e0a2d2614 'L1'
364 364 |/
365 365 o 6: 88dd24261747 'R5'
366 366 |
367 367 o 5: 00e3b7781125 'R4'
368 368 |
369 369 o 4: 770a61882ace 'R3'
370 370 |
371 371 o 3: 31cd3a05214e 'R2'
372 372 |
373 373 o 2: 77ae9631bcca 'R1'
374 374 |
375 375 o 1: 783333faa078 'C2'
376 376 |
377 377 o 0: 05d58a0c15dd 'C1'
378 378
379 379
380 380 Multiple pre-existing heads on the branch
381 381 -----------------------------------------
382 382
383 383 Pull bring content, but nothing on the current branch, we should not consider
384 384 pre-existing heads.
385 385
386 386 $ cd ../a
387 387 $ hg branch unrelatedbranch
388 388 marked working directory as branch unrelatedbranch
389 389 (branches are permanent and global, did you want a bookmark?)
390 390 $ echo B1 > B1
391 391 $ hg commit -Am B1
392 392 adding B1
393 393 $ cd ../c
394 394 $ hg up 'desc(L2)'
395 395 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
396 396 $ hg pull --rebase
397 397 pulling from $TESTTMP/a
398 398 searching for changes
399 399 adding changesets
400 400 adding manifests
401 401 adding file changes
402 402 added 1 changesets with 1 changes to 1 files
403 403 new changesets 39c381359968
404 404 nothing to rebase
405 405
406 406 There is two local heads and we pull a third one.
407 407 The second local head should not confuse the `hg pull rebase`.
408 408
409 409 $ hg up 'desc(R6)'
410 410 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
411 411 $ echo M1 > M1
412 412 $ hg commit -Am M1
413 413 adding M1
414 414 $ cd ../a
415 415 $ hg up 'desc(R6)'
416 416 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
417 417 $ echo R7 > R7
418 418 $ hg commit -Am R7
419 419 adding R7
420 420 $ cd ../c
421 421 $ hg up 'desc(L2)'
422 422 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
423 423 $ hg pull --rebase
424 424 pulling from $TESTTMP/a
425 425 searching for changes
426 426 adding changesets
427 427 adding manifests
428 428 adding file changes
429 429 added 1 changesets with 1 changes to 1 files (+1 heads)
430 430 new changesets f7d3e42052f9
431 431 rebasing 7:864e0a2d2614 "L1"
432 432 rebasing 8:6dc0ea5dcf55 "L2"
433 433 saved backup bundle to $TESTTMP/c/.hg/strip-backup/864e0a2d2614-2f72c89c-rebase.hg
434 434 $ hg tglog
435 435 @ 12: 3603a865eea0 'L2'
436 436 |
437 437 o 11: bcc8a9cd04bf 'L1'
438 438 |
439 439 o 10: f7d3e42052f9 'R7'
440 440 |
441 441 | o 9: 41fab4eef82f 'M1'
442 442 |/
443 443 | o 8: 39c381359968 'B1' unrelatedbranch
444 444 |/
445 445 o 7: 65bc164c1d9b 'R6'
446 446 |
447 447 o 6: 88dd24261747 'R5'
448 448 |
449 449 o 5: 00e3b7781125 'R4'
450 450 |
451 451 o 4: 770a61882ace 'R3'
452 452 |
453 453 o 3: 31cd3a05214e 'R2'
454 454 |
455 455 o 2: 77ae9631bcca 'R1'
456 456 |
457 457 o 1: 783333faa078 'C2'
458 458 |
459 459 o 0: 05d58a0c15dd 'C1'
460 460
@@ -1,738 +1,738 b''
1 1 test that a commit clears the merge state.
2 2
3 3 $ hg init repo
4 4 $ cd repo
5 5
6 6 $ echo foo > file1
7 7 $ echo foo > file2
8 8 $ hg commit -Am 'add files'
9 9 adding file1
10 10 adding file2
11 11
12 12 $ echo bar >> file1
13 13 $ echo bar >> file2
14 14 $ hg commit -Am 'append bar to files'
15 15
16 16 create a second head with conflicting edits
17 17
18 18 $ hg up -C 0
19 19 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
20 20 $ echo baz >> file1
21 21 $ echo baz >> file2
22 22 $ hg commit -Am 'append baz to files'
23 23 created new head
24 24
25 25 create a third head with no conflicting edits
26 26 $ hg up -qC 0
27 27 $ echo foo > file3
28 28 $ hg commit -Am 'add non-conflicting file'
29 29 adding file3
30 30 created new head
31 31
32 32 failing merge
33 33
34 34 $ hg up -qC 2
35 35 $ hg merge --tool=internal:fail 1
36 36 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
37 37 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
38 38 [1]
39 39
40 40 resolve -l should contain unresolved entries
41 41
42 42 $ hg resolve -l
43 43 U file1
44 44 U file2
45 45
46 46 $ hg resolve -l --no-status
47 47 file1
48 48 file2
49 49
50 50 resolving an unknown path should emit a warning, but not for -l
51 51
52 52 $ hg resolve -m does-not-exist
53 53 arguments do not match paths that need resolving
54 54 $ hg resolve -l does-not-exist
55 55
56 56 tell users how they could have used resolve
57 57
58 58 $ mkdir nested
59 59 $ cd nested
60 60 $ hg resolve -m file1
61 61 arguments do not match paths that need resolving
62 62 (try: hg resolve -m path:file1)
63 63 $ hg resolve -m file1 filez
64 64 arguments do not match paths that need resolving
65 65 (try: hg resolve -m path:file1 path:filez)
66 66 $ hg resolve -m path:file1 path:filez
67 67 $ hg resolve -l
68 68 R file1
69 69 U file2
70 70 $ hg resolve -l --config ui.relative-paths=yes
71 71 R ../file1
72 72 U ../file2
73 73 $ hg resolve --re-merge filez file2
74 74 arguments do not match paths that need resolving
75 75 (try: hg resolve --re-merge path:filez path:file2)
76 76 $ hg resolve -m filez file2
77 77 arguments do not match paths that need resolving
78 78 (try: hg resolve -m path:filez path:file2)
79 79 $ hg resolve -m path:filez path:file2
80 80 (no more unresolved files)
81 81 $ hg resolve -l
82 82 R file1
83 83 R file2
84 84
85 85 cleanup
86 86 $ hg resolve -u
87 87 $ cd ..
88 88 $ rmdir nested
89 89
90 90 resolve the failure
91 91
92 92 $ echo resolved > file1
93 93 $ hg resolve -m file1
94 94
95 95 resolve -l should show resolved file as resolved
96 96
97 97 $ hg resolve -l
98 98 R file1
99 99 U file2
100 100
101 101 $ hg resolve -l -Tjson
102 102 [
103 103 {
104 104 "mergestatus": "R",
105 105 "path": "file1"
106 106 },
107 107 {
108 108 "mergestatus": "U",
109 109 "path": "file2"
110 110 }
111 111 ]
112 112
113 113 $ hg resolve -l -T '{path} {mergestatus} {status} {p1rev} {p2rev}\n'
114 114 file1 R M 2 1
115 115 file2 U M 2 1
116 116
117 117 resolve -m without paths should mark all resolved
118 118
119 119 $ hg resolve -m
120 120 (no more unresolved files)
121 121 $ hg commit -m 'resolved'
122 122
123 123 resolve -l should be empty after commit
124 124
125 125 $ hg resolve -l
126 126
127 127 $ hg resolve -l -Tjson
128 128 [
129 129 ]
130 130
131 131 resolve --all should abort when no merge in progress
132 132
133 133 $ hg resolve --all
134 134 abort: resolve command not applicable when not merging
135 [255]
135 [20]
136 136
137 137 resolve -m should abort when no merge in progress
138 138
139 139 $ hg resolve -m
140 140 abort: resolve command not applicable when not merging
141 [255]
141 [20]
142 142
143 143 can not update or merge when there are unresolved conflicts
144 144
145 145 $ hg up -qC 0
146 146 $ echo quux >> file1
147 147 $ hg up 1
148 148 merging file1
149 149 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
150 150 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
151 151 use 'hg resolve' to retry unresolved file merges
152 152 [1]
153 153 $ hg up 0
154 154 abort: outstanding merge conflicts
155 155 (use 'hg resolve' to resolve)
156 156 [255]
157 157 $ hg merge 2
158 158 abort: outstanding merge conflicts
159 159 (use 'hg resolve' to resolve)
160 160 [255]
161 161 $ hg merge --force 2
162 162 abort: outstanding merge conflicts
163 163 (use 'hg resolve' to resolve)
164 164 [255]
165 165
166 166 set up conflict-free merge
167 167
168 168 $ hg up -qC 3
169 169 $ hg merge 1
170 170 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
171 171 (branch merge, don't forget to commit)
172 172
173 173 resolve --all should do nothing in merge without conflicts
174 174 $ hg resolve --all
175 175 (no more unresolved files)
176 176
177 177 resolve -m should do nothing in merge without conflicts
178 178
179 179 $ hg resolve -m
180 180 (no more unresolved files)
181 181
182 182 get back to conflicting state
183 183
184 184 $ hg up -qC 2
185 185 $ hg merge --tool=internal:fail 1
186 186 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
187 187 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
188 188 [1]
189 189
190 190 resolve without arguments should suggest --all
191 191 $ hg resolve
192 192 abort: no files or directories specified
193 193 (use --all to re-merge all unresolved files)
194 194 [10]
195 195
196 196 resolve --all should re-merge all unresolved files
197 197 $ hg resolve --all
198 198 merging file1
199 199 merging file2
200 200 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
201 201 warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
202 202 [1]
203 203 $ cat file1.orig
204 204 foo
205 205 baz
206 206 $ cat file2.orig
207 207 foo
208 208 baz
209 209
210 210 .orig files should exists where specified
211 211 $ hg resolve --all --verbose --config 'ui.origbackuppath=.hg/origbackups'
212 212 merging file1
213 213 creating directory: $TESTTMP/repo/.hg/origbackups
214 214 merging file2
215 215 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
216 216 warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
217 217 [1]
218 218 $ ls .hg/origbackups
219 219 file1
220 220 file2
221 221 $ grep '<<<' file1 > /dev/null
222 222 $ grep '<<<' file2 > /dev/null
223 223
224 224 resolve <file> should re-merge file
225 225 $ echo resolved > file1
226 226 $ hg resolve -q file1
227 227 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
228 228 [1]
229 229 $ grep '<<<' file1 > /dev/null
230 230
231 231 test .orig behavior with resolve
232 232
233 233 $ hg resolve -q file1 --tool "sh -c 'f --dump \"$TESTTMP/repo/file1.orig\"'"
234 234 $TESTTMP/repo/file1.orig:
235 235 >>>
236 236 foo
237 237 baz
238 238 <<<
239 239
240 240 resolve <file> should do nothing if 'file' was marked resolved
241 241 $ echo resolved > file1
242 242 $ hg resolve -m file1
243 243 $ hg resolve -q file1
244 244 $ cat file1
245 245 resolved
246 246
247 247 insert unsupported advisory merge record
248 248
249 249 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -x
250 250 $ hg debugmergestate
251 251 local (working copy): 57653b9f834a4493f7240b0681efcb9ae7cab745
252 252 other (merge rev): dc77451844e37f03f5c559e3b8529b2b48d381d1
253 253 file: file1 (state "r")
254 254 local path: file1 (hash 60b27f004e454aca81b0480209cce5081ec52390, flags "")
255 255 ancestor path: file1 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
256 256 other path: file1 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
257 257 extra: ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac
258 258 file: file2 (state "u")
259 259 local path: file2 (hash cb99b709a1978bd205ab9dfd4c5aaa1fc91c7523, flags "")
260 260 ancestor path: file2 (node 2ed2a3912a0b24502043eae84ee4b279c18b90dd)
261 261 other path: file2 (node 6f4310b00b9a147241b071a60c28a650827fb03d)
262 262 extra: ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac
263 263 $ hg resolve -l
264 264 R file1
265 265 U file2
266 266
267 267 test json output
268 268
269 269 $ hg debugmergestate -T json
270 270 [
271 271 {
272 272 "commits": [{"label": "working copy", "name": "local", "node": "57653b9f834a4493f7240b0681efcb9ae7cab745"}, {"label": "merge rev", "name": "other", "node": "dc77451844e37f03f5c559e3b8529b2b48d381d1"}],
273 273 "extras": [],
274 274 "files": [{"ancestor_node": "2ed2a3912a0b24502043eae84ee4b279c18b90dd", "ancestor_path": "file1", "extras": [{"key": "ancestorlinknode", "value": "99726c03216e233810a2564cbc0adfe395007eac"}], "local_flags": "", "local_key": "60b27f004e454aca81b0480209cce5081ec52390", "local_path": "file1", "other_node": "6f4310b00b9a147241b071a60c28a650827fb03d", "other_path": "file1", "path": "file1", "state": "r"}, {"ancestor_node": "2ed2a3912a0b24502043eae84ee4b279c18b90dd", "ancestor_path": "file2", "extras": [{"key": "ancestorlinknode", "value": "99726c03216e233810a2564cbc0adfe395007eac"}], "local_flags": "", "local_key": "cb99b709a1978bd205ab9dfd4c5aaa1fc91c7523", "local_path": "file2", "other_node": "6f4310b00b9a147241b071a60c28a650827fb03d", "other_path": "file2", "path": "file2", "state": "u"}]
275 275 }
276 276 ]
277 277
278 278
279 279 insert unsupported mandatory merge record
280 280
281 281 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -X
282 282 $ hg debugmergestate
283 283 abort: unsupported merge state records: X
284 284 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
285 285 [255]
286 286 $ hg resolve -l
287 287 abort: unsupported merge state records: X
288 288 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
289 289 [255]
290 290 $ hg resolve -ma
291 291 abort: unsupported merge state records: X
292 292 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
293 293 [255]
294 294 $ hg summary
295 295 warning: merge state has unsupported record types: X
296 296 parent: 2:57653b9f834a
297 297 append baz to files
298 298 parent: 1:dc77451844e3
299 299 append bar to files
300 300 branch: default
301 301 commit: 2 modified, 2 unknown (merge)
302 302 update: 2 new changesets (update)
303 303 phases: 5 draft
304 304
305 305 update --clean shouldn't abort on unsupported records
306 306
307 307 $ hg up -qC 1
308 308 $ hg debugmergestate
309 309 no merge state found
310 310
311 311 test crashed merge with empty mergestate
312 312
313 313 $ mkdir .hg/merge
314 314 $ touch .hg/merge/state
315 315
316 316 resolve -l should be empty
317 317
318 318 $ hg resolve -l
319 319
320 320 resolve -m can be configured to look for remaining conflict markers
321 321 $ hg up -qC 2
322 322 $ hg merge -q --tool=internal:merge 1
323 323 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
324 324 warning: conflicts while merging file2! (edit, then use 'hg resolve --mark')
325 325 [1]
326 326 $ hg resolve -l
327 327 U file1
328 328 U file2
329 329 $ echo 'remove markers' > file1
330 330 $ hg --config commands.resolve.mark-check=abort resolve -m
331 331 warning: the following files still have conflict markers:
332 332 file2
333 333 abort: conflict markers detected
334 334 (use --all to mark anyway)
335 [255]
335 [20]
336 336 $ hg resolve -l
337 337 U file1
338 338 U file2
339 339 Try with --all from the hint
340 340 $ hg --config commands.resolve.mark-check=abort resolve -m --all
341 341 warning: the following files still have conflict markers:
342 342 file2
343 343 (no more unresolved files)
344 344 $ hg resolve -l
345 345 R file1
346 346 R file2
347 347 Test option value 'warn'
348 348 $ hg resolve --unmark
349 349 $ hg resolve -l
350 350 U file1
351 351 U file2
352 352 $ hg --config commands.resolve.mark-check=warn resolve -m
353 353 warning: the following files still have conflict markers:
354 354 file2
355 355 (no more unresolved files)
356 356 $ hg resolve -l
357 357 R file1
358 358 R file2
359 359 If the file is already marked as resolved, we don't warn about it
360 360 $ hg resolve --unmark file1
361 361 $ hg resolve -l
362 362 U file1
363 363 R file2
364 364 $ hg --config commands.resolve.mark-check=warn resolve -m
365 365 (no more unresolved files)
366 366 $ hg resolve -l
367 367 R file1
368 368 R file2
369 369 If the user passes an invalid value, we treat it as 'none'.
370 370 $ hg resolve --unmark
371 371 $ hg resolve -l
372 372 U file1
373 373 U file2
374 374 $ hg --config commands.resolve.mark-check=nope resolve -m
375 375 (no more unresolved files)
376 376 $ hg resolve -l
377 377 R file1
378 378 R file2
379 379 Test explicitly setting the option to 'none'
380 380 $ hg resolve --unmark
381 381 $ hg resolve -l
382 382 U file1
383 383 U file2
384 384 $ hg --config commands.resolve.mark-check=none resolve -m
385 385 (no more unresolved files)
386 386 $ hg resolve -l
387 387 R file1
388 388 R file2
389 389 Test with marking an explicit file as resolved, this should not abort (since
390 390 there's no --force flag, we have no way of combining --all with a filename)
391 391 $ hg resolve --unmark
392 392 $ hg resolve -l
393 393 U file1
394 394 U file2
395 395 (This downgrades to a warning since an explicit file was specified).
396 396 $ hg --config commands.resolve.mark-check=abort resolve -m file2
397 397 warning: the following files still have conflict markers:
398 398 file2
399 399 $ hg resolve -l
400 400 U file1
401 401 R file2
402 402 Testing the --re-merge flag
403 403 $ hg resolve --unmark file1
404 404 $ hg resolve -l
405 405 U file1
406 406 R file2
407 407 $ hg resolve --mark --re-merge
408 408 abort: too many actions specified
409 409 [10]
410 410 $ hg resolve --re-merge --all
411 411 merging file1
412 412 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
413 413 [1]
414 414 Explicit re-merge
415 415 $ hg resolve --unmark file1
416 416 $ hg resolve --config commands.resolve.explicit-re-merge=1 --all
417 417 abort: no action specified
418 418 (use --mark, --unmark, --list or --re-merge)
419 419 [10]
420 420 $ hg resolve --config commands.resolve.explicit-re-merge=1 --re-merge --all
421 421 merging file1
422 422 warning: conflicts while merging file1! (edit, then use 'hg resolve --mark')
423 423 [1]
424 424
425 425 $ cd ..
426 426
427 427 ======================================================
428 428 Test 'hg resolve' confirm config option functionality |
429 429 ======================================================
430 430 $ cat >> $HGRCPATH << EOF
431 431 > [extensions]
432 432 > rebase=
433 433 > EOF
434 434
435 435 $ hg init repo2
436 436 $ cd repo2
437 437
438 438 $ echo boss > boss
439 439 $ hg ci -Am "add boss"
440 440 adding boss
441 441
442 442 $ for emp in emp1 emp2 emp3; do echo work > $emp; done;
443 443 $ hg ci -Aqm "added emp1 emp2 emp3"
444 444
445 445 $ hg up 0
446 446 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
447 447
448 448 $ for emp in emp1 emp2 emp3; do echo nowork > $emp; done;
449 449 $ hg ci -Aqm "added lazy emp1 emp2 emp3"
450 450
451 451 $ hg log -GT "{rev} {node|short} {firstline(desc)}\n"
452 452 @ 2 0acfd4a49af0 added lazy emp1 emp2 emp3
453 453 |
454 454 | o 1 f30f98a8181f added emp1 emp2 emp3
455 455 |/
456 456 o 0 88660038d466 add boss
457 457
458 458 $ hg rebase -s 1 -d 2
459 459 rebasing 1:f30f98a8181f "added emp1 emp2 emp3"
460 460 merging emp1
461 461 merging emp2
462 462 merging emp3
463 463 warning: conflicts while merging emp1! (edit, then use 'hg resolve --mark')
464 464 warning: conflicts while merging emp2! (edit, then use 'hg resolve --mark')
465 465 warning: conflicts while merging emp3! (edit, then use 'hg resolve --mark')
466 466 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
467 467 [240]
468 468
469 469 Test when commands.resolve.confirm config option is not set:
470 470 ===========================================================
471 471 $ hg resolve --all
472 472 merging emp1
473 473 merging emp2
474 474 merging emp3
475 475 warning: conflicts while merging emp1! (edit, then use 'hg resolve --mark')
476 476 warning: conflicts while merging emp2! (edit, then use 'hg resolve --mark')
477 477 warning: conflicts while merging emp3! (edit, then use 'hg resolve --mark')
478 478 [1]
479 479
480 480 Test when config option is set:
481 481 ==============================
482 482 $ cat >> .hg/hgrc << EOF
483 483 > [ui]
484 484 > interactive = True
485 485 > [commands]
486 486 > resolve.confirm = True
487 487 > EOF
488 488
489 489 $ hg resolve
490 490 abort: no files or directories specified
491 491 (use --all to re-merge all unresolved files)
492 492 [10]
493 493 $ hg resolve --all << EOF
494 494 > n
495 495 > EOF
496 496 re-merge all unresolved files (yn)? n
497 497 abort: user quit
498 498 [255]
499 499
500 500 $ hg resolve --all << EOF
501 501 > y
502 502 > EOF
503 503 re-merge all unresolved files (yn)? y
504 504 merging emp1
505 505 merging emp2
506 506 merging emp3
507 507 warning: conflicts while merging emp1! (edit, then use 'hg resolve --mark')
508 508 warning: conflicts while merging emp2! (edit, then use 'hg resolve --mark')
509 509 warning: conflicts while merging emp3! (edit, then use 'hg resolve --mark')
510 510 [1]
511 511
512 512 Test that commands.resolve.confirm respect --mark option (only when no patterns args are given):
513 513 ===============================================================================================
514 514
515 515 $ hg resolve -m emp1
516 516 $ hg resolve -l
517 517 R emp1
518 518 U emp2
519 519 U emp3
520 520
521 521 $ hg resolve -m << EOF
522 522 > n
523 523 > EOF
524 524 mark all unresolved files as resolved (yn)? n
525 525 abort: user quit
526 526 [255]
527 527
528 528 $ hg resolve -m << EOF
529 529 > y
530 530 > EOF
531 531 mark all unresolved files as resolved (yn)? y
532 532 (no more unresolved files)
533 533 continue: hg rebase --continue
534 534 $ hg resolve -l
535 535 R emp1
536 536 R emp2
537 537 R emp3
538 538
539 539 Test that commands.resolve.confirm respect --unmark option (only when no patterns args are given):
540 540 =================================================================================================
541 541
542 542 $ hg resolve -u emp1
543 543
544 544 $ hg resolve -l
545 545 U emp1
546 546 R emp2
547 547 R emp3
548 548
549 549 $ hg resolve -u << EOF
550 550 > n
551 551 > EOF
552 552 mark all resolved files as unresolved (yn)? n
553 553 abort: user quit
554 554 [255]
555 555
556 556 $ hg resolve -m << EOF
557 557 > y
558 558 > EOF
559 559 mark all unresolved files as resolved (yn)? y
560 560 (no more unresolved files)
561 561 continue: hg rebase --continue
562 562
563 563 $ hg resolve -l
564 564 R emp1
565 565 R emp2
566 566 R emp3
567 567
568 568 $ hg rebase --abort
569 569 rebase aborted
570 570
571 571 Done with commands.resolve.confirm tests:
572 572 $ cd ..
573 573
574 574 Test that commands.resolve.mark-check works even if there are deleted files:
575 575 $ hg init resolve-deleted
576 576 $ cd resolve-deleted
577 577 $ echo r0 > file1
578 578 $ hg ci -qAm r0
579 579 $ echo r1 > file1
580 580 $ hg ci -qm r1
581 581 $ hg co -qr 0
582 582 $ hg rm file1
583 583 $ hg ci -qm "r2 (delete file1)"
584 584
585 585 (At this point we have r0 creating file1, and sibling commits r1 and r2, which
586 586 modify and delete file1, respectively)
587 587
588 588 $ hg merge -r 1
589 589 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
590 590 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
591 591 What do you want to do? u
592 592 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
593 593 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
594 594 [1]
595 595 $ hg resolve --list
596 596 U file1
597 597 Because we left it as 'unresolved' the file should still exist.
598 598 $ [ -f file1 ] || echo "File does not exist?"
599 599 BC behavior: `hg resolve --mark` accepts that the file is still there, and
600 600 doesn't have a problem with this situation.
601 601 $ hg resolve --mark --config commands.resolve.mark-check=abort
602 602 (no more unresolved files)
603 603 $ hg resolve --list
604 604 R file1
605 605 The file is still there:
606 606 $ [ -f file1 ] || echo "File does not exist?"
607 607 Let's check mark-check=warn:
608 608 $ hg resolve --unmark file1
609 609 $ hg resolve --mark --config commands.resolve.mark-check=warn
610 610 (no more unresolved files)
611 611 $ hg resolve --list
612 612 R file1
613 613 The file is still there:
614 614 $ [ -f file1 ] || echo "File does not exist?"
615 615 Let's resolve the issue by deleting the file via `hg resolve`
616 616 $ hg resolve --unmark file1
617 617 $ echo 'd' | hg resolve file1 --config ui.interactive=1
618 618 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
619 619 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
620 620 What do you want to do? d
621 621 (no more unresolved files)
622 622 $ hg resolve --list
623 623 R file1
624 624 The file is deleted:
625 625 $ [ -f file1 ] && echo "File still exists?" || true
626 626 Doing `hg resolve --mark` doesn't break now that the file is missing:
627 627 $ hg resolve --mark --config commands.resolve.mark-check=abort
628 628 (no more unresolved files)
629 629 $ hg resolve --mark --config commands.resolve.mark-check=warn
630 630 (no more unresolved files)
631 631 Resurrect the file, and delete it outside of hg:
632 632 $ hg resolve --unmark file1
633 633 $ hg resolve file1
634 634 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
635 635 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
636 636 What do you want to do? u
637 637 [1]
638 638 $ [ -f file1 ] || echo "File does not exist?"
639 639 $ hg resolve --list
640 640 U file1
641 641 $ rm file1
642 642 $ hg resolve --mark --config commands.resolve.mark-check=abort
643 643 (no more unresolved files)
644 644 $ hg resolve --list
645 645 R file1
646 646 $ hg resolve --unmark file1
647 647 $ hg resolve file1
648 648 file 'file1' was deleted in local [working copy] but was modified in other [merge rev].
649 649 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
650 650 What do you want to do? u
651 651 [1]
652 652 $ [ -f file1 ] || echo "File does not exist?"
653 653 $ hg resolve --list
654 654 U file1
655 655 $ rm file1
656 656 $ hg resolve --mark --config commands.resolve.mark-check=warn
657 657 (no more unresolved files)
658 658 $ hg resolve --list
659 659 R file1
660 660
661 661
662 662 For completeness, let's try that in the opposite direction (merging r2 into r1,
663 663 instead of r1 into r2):
664 664 $ hg update -qCr 1
665 665 $ hg merge -r 2
666 666 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
667 667 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
668 668 What do you want to do? u
669 669 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
670 670 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
671 671 [1]
672 672 $ hg resolve --list
673 673 U file1
674 674 Because we left it as 'unresolved' the file should still exist.
675 675 $ [ -f file1 ] || echo "File does not exist?"
676 676 BC behavior: `hg resolve --mark` accepts that the file is still there, and
677 677 doesn't have a problem with this situation.
678 678 $ hg resolve --mark --config commands.resolve.mark-check=abort
679 679 (no more unresolved files)
680 680 $ hg resolve --list
681 681 R file1
682 682 The file is still there:
683 683 $ [ -f file1 ] || echo "File does not exist?"
684 684 Let's check mark-check=warn:
685 685 $ hg resolve --unmark file1
686 686 $ hg resolve --mark --config commands.resolve.mark-check=warn
687 687 (no more unresolved files)
688 688 $ hg resolve --list
689 689 R file1
690 690 The file is still there:
691 691 $ [ -f file1 ] || echo "File does not exist?"
692 692 Let's resolve the issue by deleting the file via `hg resolve`
693 693 $ hg resolve --unmark file1
694 694 $ echo 'd' | hg resolve file1 --config ui.interactive=1
695 695 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
696 696 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
697 697 What do you want to do? d
698 698 (no more unresolved files)
699 699 $ hg resolve --list
700 700 R file1
701 701 The file is deleted:
702 702 $ [ -f file1 ] && echo "File still exists?" || true
703 703 Doing `hg resolve --mark` doesn't break now that the file is missing:
704 704 $ hg resolve --mark --config commands.resolve.mark-check=abort
705 705 (no more unresolved files)
706 706 $ hg resolve --mark --config commands.resolve.mark-check=warn
707 707 (no more unresolved files)
708 708 Resurrect the file, and delete it outside of hg:
709 709 $ hg resolve --unmark file1
710 710 $ hg resolve file1
711 711 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
712 712 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
713 713 What do you want to do? u
714 714 [1]
715 715 $ [ -f file1 ] || echo "File does not exist?"
716 716 $ hg resolve --list
717 717 U file1
718 718 $ rm file1
719 719 $ hg resolve --mark --config commands.resolve.mark-check=abort
720 720 (no more unresolved files)
721 721 $ hg resolve --list
722 722 R file1
723 723 $ hg resolve --unmark file1
724 724 $ hg resolve file1
725 725 file 'file1' was deleted in other [merge rev] but was modified in local [working copy].
726 726 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
727 727 What do you want to do? u
728 728 [1]
729 729 $ [ -f file1 ] || echo "File does not exist?"
730 730 $ hg resolve --list
731 731 U file1
732 732 $ rm file1
733 733 $ hg resolve --mark --config commands.resolve.mark-check=warn
734 734 (no more unresolved files)
735 735 $ hg resolve --list
736 736 R file1
737 737
738 738 $ cd ..
@@ -1,1512 +1,1512 b''
1 1 #testcases stripbased phasebased
2 2
3 3 $ cat <<EOF >> $HGRCPATH
4 4 > [extensions]
5 5 > mq =
6 6 > [defaults]
7 7 > diff = --nodates --git
8 8 > qnew = --date '0 0'
9 9 > [shelve]
10 10 > maxbackups = 2
11 11 > EOF
12 12
13 13 #if phasebased
14 14
15 15 $ cat <<EOF >> $HGRCPATH
16 16 > [format]
17 17 > internal-phase = yes
18 18 > EOF
19 19
20 20 #endif
21 21
22 22 $ hg init repo
23 23 $ cd repo
24 24 $ mkdir a b
25 25 $ echo a > a/a
26 26 $ echo b > b/b
27 27 $ echo c > c
28 28 $ echo d > d
29 29 $ echo x > x
30 30 $ hg addremove -q
31 31
32 32 shelve has a help message
33 33 $ hg shelve -h
34 34 hg shelve [OPTION]... [FILE]...
35 35
36 36 save and set aside changes from the working directory
37 37
38 38 Shelving takes files that "hg status" reports as not clean, saves the
39 39 modifications to a bundle (a shelved change), and reverts the files so
40 40 that their state in the working directory becomes clean.
41 41
42 42 To restore these changes to the working directory, using "hg unshelve";
43 43 this will work even if you switch to a different commit.
44 44
45 45 When no files are specified, "hg shelve" saves all not-clean files. If
46 46 specific files or directories are named, only changes to those files are
47 47 shelved.
48 48
49 49 In bare shelve (when no files are specified, without interactive, include
50 50 and exclude option), shelving remembers information if the working
51 51 directory was on newly created branch, in other words working directory
52 52 was on different branch than its first parent. In this situation
53 53 unshelving restores branch information to the working directory.
54 54
55 55 Each shelved change has a name that makes it easier to find later. The
56 56 name of a shelved change defaults to being based on the active bookmark,
57 57 or if there is no active bookmark, the current named branch. To specify a
58 58 different name, use "--name".
59 59
60 60 To see a list of existing shelved changes, use the "--list" option. For
61 61 each shelved change, this will print its name, age, and description; use "
62 62 --patch" or "--stat" for more details.
63 63
64 64 To delete specific shelved changes, use "--delete". To delete all shelved
65 65 changes, use "--cleanup".
66 66
67 67 options ([+] can be repeated):
68 68
69 69 -A --addremove mark new/missing files as added/removed before
70 70 shelving
71 71 -u --unknown store unknown files in the shelve
72 72 --cleanup delete all shelved changes
73 73 --date DATE shelve with the specified commit date
74 74 -d --delete delete the named shelved change(s)
75 75 -e --edit invoke editor on commit messages
76 76 -k --keep shelve, but keep changes in the working directory
77 77 -l --list list current shelves
78 78 -m --message TEXT use text as shelve message
79 79 -n --name NAME use the given name for the shelved commit
80 80 -p --patch output patches for changes (provide the names of the
81 81 shelved changes as positional arguments)
82 82 -i --interactive interactive mode
83 83 --stat output diffstat-style summary of changes (provide
84 84 the names of the shelved changes as positional
85 85 arguments)
86 86 -I --include PATTERN [+] include names matching the given patterns
87 87 -X --exclude PATTERN [+] exclude names matching the given patterns
88 88 --mq operate on patch repository
89 89
90 90 (some details hidden, use --verbose to show complete help)
91 91
92 92 shelving in an empty repo should be possible
93 93 (this tests also that editor is not invoked, if '--edit' is not
94 94 specified)
95 95
96 96 $ HGEDITOR=cat hg shelve
97 97 shelved as default
98 98 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
99 99
100 100 $ hg unshelve
101 101 unshelving change 'default'
102 102
103 103 $ hg commit -q -m 'initial commit'
104 104
105 105 $ hg shelve
106 106 nothing changed
107 107 [1]
108 108
109 109 make sure shelve files were backed up
110 110
111 111 $ ls .hg/shelve-backup
112 112 default.hg
113 113 default.patch
114 114 default.shelve
115 115
116 116 checks to make sure we dont create a directory or
117 117 hidden file while choosing a new shelve name
118 118
119 119 when we are given a name
120 120
121 121 $ hg shelve -n foo/bar
122 122 abort: shelved change names can not contain slashes
123 123 [255]
124 124 $ hg shelve -n .baz
125 125 abort: shelved change names can not start with '.'
126 126 [255]
127 127 $ hg shelve -n foo\\bar
128 128 abort: shelved change names can not contain slashes
129 129 [255]
130 130
131 131 when shelve has to choose itself
132 132
133 133 $ hg branch x/y -q
134 134 $ hg commit -q -m "Branch commit 0"
135 135 $ hg shelve
136 136 nothing changed
137 137 [1]
138 138 $ hg branch .x -q
139 139 $ hg commit -q -m "Branch commit 1"
140 140 $ hg shelve
141 141 nothing changed
142 142 [1]
143 143 $ hg branch x\\y -q
144 144 $ hg commit -q -m "Branch commit 2"
145 145 $ hg shelve
146 146 nothing changed
147 147 [1]
148 148
149 149 cleaning the branches made for name checking tests
150 150
151 151 $ hg up default -q
152 152 $ hg strip e9177275307e+6a6d231f43d+882bae7c62c2 -q
153 153
154 154 create an mq patch - shelving should work fine with a patch applied
155 155
156 156 $ echo n > n
157 157 $ hg add n
158 158 $ hg commit n -m second
159 159 $ hg qnew second.patch
160 160
161 161 shelve a change that we will delete later
162 162
163 163 $ echo a >> a/a
164 164 $ hg shelve
165 165 shelved as default
166 166 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
167 167
168 168 set up some more complex changes to shelve
169 169
170 170 $ echo a >> a/a
171 171 $ hg mv b b.rename
172 172 moving b/b to b.rename/b
173 173 $ hg cp c c.copy
174 174 $ hg mv d ghost
175 175 $ rm ghost
176 176 $ hg status -C
177 177 M a/a
178 178 A b.rename/b
179 179 b/b
180 180 A c.copy
181 181 c
182 182 R b/b
183 183 R d
184 184 ! ghost
185 185 d
186 186
187 187 the common case - no options or filenames
188 188
189 189 $ hg shelve
190 190 shelved as default-01
191 191 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
192 192 $ hg status -C
193 193
194 194 ensure that our shelved changes exist
195 195
196 196 $ hg shelve -l
197 197 default-01 (*)* changes to: [mq]: second.patch (glob)
198 198 default (*)* changes to: [mq]: second.patch (glob)
199 199
200 200 $ hg shelve -l -p default
201 201 default (*)* changes to: [mq]: second.patch (glob)
202 202
203 203 diff --git a/a/a b/a/a
204 204 --- a/a/a
205 205 +++ b/a/a
206 206 @@ -1,1 +1,2 @@
207 207 a
208 208 +a
209 209
210 210 $ hg shelve --list --addremove
211 211 abort: options '--list' and '--addremove' may not be used together
212 212 [10]
213 213
214 214 delete our older shelved change
215 215
216 216 $ hg shelve -d default
217 217 $ hg qfinish -a -q
218 218
219 219 ensure shelve backups aren't overwritten
220 220
221 221 $ ls .hg/shelve-backup/
222 222 default-1.hg
223 223 default-1.patch
224 224 default-1.shelve
225 225 default.hg
226 226 default.patch
227 227 default.shelve
228 228
229 229 local edits should not prevent a shelved change from applying
230 230
231 231 $ printf "z\na\n" > a/a
232 232 $ hg unshelve --keep
233 233 unshelving change 'default-01'
234 234 temporarily committing pending changes (restore with 'hg unshelve --abort')
235 235 rebasing shelved changes
236 236 merging a/a
237 237
238 238 $ hg revert --all -q
239 239 $ rm a/a.orig b.rename/b c.copy
240 240
241 241 apply it and make sure our state is as expected
242 242
243 243 (this also tests that same timestamp prevents backups from being
244 244 removed, even though there are more than 'maxbackups' backups)
245 245
246 246 $ f -t .hg/shelve-backup/default.patch
247 247 .hg/shelve-backup/default.patch: file
248 248 $ touch -t 200001010000 .hg/shelve-backup/default.patch
249 249 $ f -t .hg/shelve-backup/default-1.patch
250 250 .hg/shelve-backup/default-1.patch: file
251 251 $ touch -t 200001010000 .hg/shelve-backup/default-1.patch
252 252
253 253 $ hg unshelve
254 254 unshelving change 'default-01'
255 255 $ hg status -C
256 256 M a/a
257 257 A b.rename/b
258 258 b/b
259 259 A c.copy
260 260 c
261 261 R b/b
262 262 R d
263 263 $ hg shelve -l
264 264
265 265 (both of default.hg and default-1.hg should be still kept, because it
266 266 is difficult to decide actual order of them from same timestamp)
267 267
268 268 $ ls .hg/shelve-backup/
269 269 default-01.hg
270 270 default-01.patch
271 271 default-01.shelve
272 272 default-1.hg
273 273 default-1.patch
274 274 default-1.shelve
275 275 default.hg
276 276 default.patch
277 277 default.shelve
278 278
279 279 $ hg unshelve
280 280 abort: no shelved changes to apply!
281 281 [255]
282 282 $ hg unshelve foo
283 283 abort: shelved change 'foo' not found
284 284 [255]
285 285
286 286 named shelves, specific filenames, and "commit messages" should all work
287 287 (this tests also that editor is invoked, if '--edit' is specified)
288 288
289 289 $ hg status -C
290 290 M a/a
291 291 A b.rename/b
292 292 b/b
293 293 A c.copy
294 294 c
295 295 R b/b
296 296 R d
297 297 $ HGEDITOR=cat hg shelve -q -n wibble -m wat -e a
298 298 wat
299 299
300 300
301 301 HG: Enter commit message. Lines beginning with 'HG:' are removed.
302 302 HG: Leave message empty to abort commit.
303 303 HG: --
304 304 HG: user: shelve@localhost
305 305 HG: branch 'default'
306 306 HG: changed a/a
307 307
308 308 expect "a" to no longer be present, but status otherwise unchanged
309 309
310 310 $ hg status -C
311 311 A b.rename/b
312 312 b/b
313 313 A c.copy
314 314 c
315 315 R b/b
316 316 R d
317 317 $ hg shelve -l --stat
318 318 wibble (*) wat (glob)
319 319 a/a | 1 +
320 320 1 files changed, 1 insertions(+), 0 deletions(-)
321 321
322 322 and now "a/a" should reappear
323 323
324 324 $ cd a
325 325 $ hg unshelve -q wibble
326 326 $ cd ..
327 327 $ hg status -C
328 328 M a/a
329 329 A b.rename/b
330 330 b/b
331 331 A c.copy
332 332 c
333 333 R b/b
334 334 R d
335 335
336 336 ensure old shelve backups are being deleted automatically
337 337
338 338 $ ls .hg/shelve-backup/
339 339 default-01.hg
340 340 default-01.patch
341 341 default-01.shelve
342 342 wibble.hg
343 343 wibble.patch
344 344 wibble.shelve
345 345
346 346 cause unshelving to result in a merge with 'a' conflicting
347 347
348 348 $ hg shelve -q
349 349 $ echo c>>a/a
350 350 $ hg commit -m second
351 351 $ hg tip --template '{files}\n'
352 352 a/a
353 353
354 354 add an unrelated change that should be preserved
355 355
356 356 $ mkdir foo
357 357 $ echo foo > foo/foo
358 358 $ hg add foo/foo
359 359
360 360 force a conflicted merge to occur
361 361
362 362 $ hg unshelve
363 363 unshelving change 'default'
364 364 temporarily committing pending changes (restore with 'hg unshelve --abort')
365 365 rebasing shelved changes
366 366 merging a/a
367 367 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
368 368 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
369 369 [240]
370 370 $ hg status -v
371 371 M a/a
372 372 M b.rename/b
373 373 M c.copy
374 374 R b/b
375 375 R d
376 376 ? a/a.orig
377 377 # The repository is in an unfinished *unshelve* state.
378 378
379 379 # Unresolved merge conflicts:
380 380 #
381 381 # a/a
382 382 #
383 383 # To mark files as resolved: hg resolve --mark FILE
384 384
385 385 # To continue: hg unshelve --continue
386 386 # To abort: hg unshelve --abort
387 387
388 388
389 389 ensure that we have a merge with unresolved conflicts
390 390
391 391 #if phasebased
392 392 $ hg heads -q --template '{rev}\n'
393 393 8
394 394 5
395 395 $ hg parents -q --template '{rev}\n'
396 396 8
397 397 5
398 398 #endif
399 399
400 400 #if stripbased
401 401 $ hg heads -q --template '{rev}\n'
402 402 5
403 403 4
404 404 $ hg parents -q --template '{rev}\n'
405 405 4
406 406 5
407 407 #endif
408 408
409 409 $ hg status
410 410 M a/a
411 411 M b.rename/b
412 412 M c.copy
413 413 R b/b
414 414 R d
415 415 ? a/a.orig
416 416 $ hg diff
417 417 diff --git a/a/a b/a/a
418 418 --- a/a/a
419 419 +++ b/a/a
420 420 @@ -1,2 +1,6 @@
421 421 a
422 422 +<<<<<<< working-copy: 2377350b6337 - shelve: pending changes temporary commit
423 423 c
424 424 +=======
425 425 +a
426 426 +>>>>>>> shelve: 203c9f771d2b - shelve: changes to: [mq]: second.patch
427 427 diff --git a/b/b b/b.rename/b
428 428 rename from b/b
429 429 rename to b.rename/b
430 430 diff --git a/c b/c.copy
431 431 copy from c
432 432 copy to c.copy
433 433 diff --git a/d b/d
434 434 deleted file mode 100644
435 435 --- a/d
436 436 +++ /dev/null
437 437 @@ -1,1 +0,0 @@
438 438 -d
439 439 $ hg resolve -l
440 440 U a/a
441 441
442 442 $ hg shelve
443 443 abort: unshelve already in progress
444 444 (use 'hg unshelve --continue' or 'hg unshelve --abort')
445 [255]
445 [20]
446 446
447 447 abort the unshelve and be happy
448 448
449 449 $ hg status
450 450 M a/a
451 451 M b.rename/b
452 452 M c.copy
453 453 R b/b
454 454 R d
455 455 ? a/a.orig
456 456 $ hg unshelve -a
457 457 unshelve of 'default' aborted
458 458 $ hg heads -q
459 459 [37]:2e69b451d1ea (re)
460 460 $ hg parents
461 461 changeset: [37]:2e69b451d1ea (re)
462 462 tag: tip
463 463 parent: 3:509104101065 (?)
464 464 user: test
465 465 date: Thu Jan 01 00:00:00 1970 +0000
466 466 summary: second
467 467
468 468 $ hg resolve -l
469 469 $ hg status
470 470 A foo/foo
471 471 ? a/a.orig
472 472
473 473 try to continue with no unshelve underway
474 474
475 475 $ hg unshelve -c
476 476 abort: no unshelve in progress
477 [255]
477 [20]
478 478 $ hg status
479 479 A foo/foo
480 480 ? a/a.orig
481 481
482 482 redo the unshelve to get a conflict
483 483
484 484 $ hg unshelve -q
485 485 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
486 486 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
487 487 [240]
488 488
489 489 attempt to continue
490 490
491 491 $ hg unshelve -c
492 492 abort: unresolved conflicts, can't continue
493 493 (see 'hg resolve', then 'hg unshelve --continue')
494 494 [255]
495 495
496 496 $ hg revert -r . a/a
497 497 $ hg resolve -m a/a
498 498 (no more unresolved files)
499 499 continue: hg unshelve --continue
500 500
501 501 $ hg commit -m 'commit while unshelve in progress'
502 502 abort: unshelve already in progress
503 503 (use 'hg unshelve --continue' or 'hg unshelve --abort')
504 [255]
504 [20]
505 505
506 506 $ hg graft --continue
507 507 abort: no graft in progress
508 508 (continue: hg unshelve --continue)
509 [255]
509 [20]
510 510 $ hg unshelve -c
511 511 unshelve of 'default' complete
512 512
513 513 ensure the repo is as we hope
514 514
515 515 $ hg parents
516 516 changeset: [37]:2e69b451d1ea (re)
517 517 tag: tip
518 518 parent: 3:509104101065 (?)
519 519 user: test
520 520 date: Thu Jan 01 00:00:00 1970 +0000
521 521 summary: second
522 522
523 523 $ hg heads -q
524 524 [37]:2e69b451d1ea (re)
525 525
526 526 $ hg status -C
527 527 A b.rename/b
528 528 b/b
529 529 A c.copy
530 530 c
531 531 A foo/foo
532 532 R b/b
533 533 R d
534 534 ? a/a.orig
535 535
536 536 there should be no shelves left
537 537
538 538 $ hg shelve -l
539 539
540 540 #if execbit
541 541
542 542 ensure that metadata-only changes are shelved
543 543
544 544 $ chmod +x a/a
545 545 $ hg shelve -q -n execbit a/a
546 546 $ hg status a/a
547 547 $ hg unshelve -q execbit
548 548 $ hg status a/a
549 549 M a/a
550 550 $ hg revert a/a
551 551
552 552 #else
553 553
554 554 Dummy shelve op, to keep rev numbers aligned
555 555
556 556 $ echo foo > a/a
557 557 $ hg shelve -q -n dummy a/a
558 558 $ hg unshelve -q dummy
559 559 $ hg revert a/a
560 560
561 561 #endif
562 562
563 563 #if symlink
564 564
565 565 $ rm a/a
566 566 $ ln -s foo a/a
567 567 $ hg shelve -q -n symlink a/a
568 568 $ hg status a/a
569 569 $ hg unshelve -q -n symlink
570 570 $ hg status a/a
571 571 M a/a
572 572 $ hg revert a/a
573 573
574 574 #else
575 575
576 576 Dummy shelve op, to keep rev numbers aligned
577 577
578 578 $ echo bar > a/a
579 579 $ hg shelve -q -n dummy a/a
580 580 $ hg unshelve -q dummy
581 581 $ hg revert a/a
582 582
583 583 #endif
584 584
585 585 set up another conflict between a commit and a shelved change
586 586
587 587 $ hg revert -q -C -a
588 588 $ rm a/a.orig b.rename/b c.copy
589 589 $ echo a >> a/a
590 590 $ hg shelve -q
591 591 $ echo x >> a/a
592 592 $ hg ci -m 'create conflict'
593 593 $ hg add foo/foo
594 594
595 595 if we resolve a conflict while unshelving, the unshelve should succeed
596 596
597 597 $ hg unshelve --tool :merge-other --keep
598 598 unshelving change 'default'
599 599 temporarily committing pending changes (restore with 'hg unshelve --abort')
600 600 rebasing shelved changes
601 601 merging a/a
602 602 $ hg parents -q
603 603 (4|13):33f7f61e6c5e (re)
604 604 $ hg shelve -l
605 605 default (*)* changes to: second (glob)
606 606 $ hg status
607 607 M a/a
608 608 A foo/foo
609 609 $ cat a/a
610 610 a
611 611 c
612 612 a
613 613 $ cat > a/a << EOF
614 614 > a
615 615 > c
616 616 > x
617 617 > EOF
618 618
619 619 $ HGMERGE=true hg unshelve
620 620 unshelving change 'default'
621 621 temporarily committing pending changes (restore with 'hg unshelve --abort')
622 622 rebasing shelved changes
623 623 merging a/a
624 624 note: unshelved changes already existed in the working copy
625 625 $ hg parents -q
626 626 (4|13):33f7f61e6c5e (re)
627 627 $ hg shelve -l
628 628 $ hg status
629 629 A foo/foo
630 630 $ cat a/a
631 631 a
632 632 c
633 633 x
634 634
635 635 test keep and cleanup
636 636
637 637 $ hg shelve
638 638 shelved as default
639 639 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
640 640 $ hg shelve --list
641 641 default (*)* changes to: create conflict (glob)
642 642 $ hg unshelve -k
643 643 unshelving change 'default'
644 644 $ hg shelve --list
645 645 default (*)* changes to: create conflict (glob)
646 646 $ hg shelve --cleanup
647 647 $ hg shelve --list
648 648
649 649 $ hg shelve --cleanup --delete
650 650 abort: options '--cleanup' and '--delete' may not be used together
651 651 [10]
652 652 $ hg shelve --cleanup --patch
653 653 abort: options '--cleanup' and '--patch' may not be used together
654 654 [10]
655 655 $ hg shelve --cleanup --message MESSAGE
656 656 abort: options '--cleanup' and '--message' may not be used together
657 657 [10]
658 658
659 659 test bookmarks
660 660
661 661 $ hg bookmark test
662 662 $ hg bookmark
663 663 \* test (4|13):33f7f61e6c5e (re)
664 664 $ hg shelve
665 665 shelved as test
666 666 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
667 667 $ hg bookmark
668 668 \* test (4|13):33f7f61e6c5e (re)
669 669 $ hg unshelve
670 670 unshelving change 'test'
671 671 $ hg bookmark
672 672 \* test (4|13):33f7f61e6c5e (re)
673 673
674 674 shelve should still work even if mq is disabled
675 675
676 676 $ hg --config extensions.mq=! shelve
677 677 shelved as test
678 678 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
679 679 $ hg --config extensions.mq=! shelve --list
680 680 test (*)* changes to: create conflict (glob)
681 681 $ hg bookmark
682 682 \* test (4|13):33f7f61e6c5e (re)
683 683 $ hg --config extensions.mq=! unshelve
684 684 unshelving change 'test'
685 685 $ hg bookmark
686 686 \* test (4|13):33f7f61e6c5e (re)
687 687
688 688 Recreate some conflict again
689 689
690 690 $ hg up -C -r 2e69b451d1ea
691 691 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
692 692 (leaving bookmark test)
693 693 $ echo y >> a/a
694 694 $ hg shelve
695 695 shelved as default
696 696 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
697 697 $ hg up test
698 698 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
699 699 (activating bookmark test)
700 700 $ hg bookmark
701 701 \* test (4|13):33f7f61e6c5e (re)
702 702 $ hg unshelve
703 703 unshelving change 'default'
704 704 rebasing shelved changes
705 705 merging a/a
706 706 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
707 707 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
708 708 [240]
709 709 $ hg bookmark
710 710 test (4|13):33f7f61e6c5e (re)
711 711
712 712 Test that resolving all conflicts in one direction (so that the rebase
713 713 is a no-op), works (issue4398)
714 714
715 715 $ hg revert -a -r .
716 716 reverting a/a
717 717 $ hg resolve -m a/a
718 718 (no more unresolved files)
719 719 continue: hg unshelve --continue
720 720 $ hg unshelve -c
721 721 note: unshelved changes already existed in the working copy
722 722 unshelve of 'default' complete
723 723 $ hg bookmark
724 724 \* test (4|13):33f7f61e6c5e (re)
725 725 $ hg diff
726 726 $ hg status
727 727 ? a/a.orig
728 728 ? foo/foo
729 729 $ hg summary
730 730 parent: (4|13):33f7f61e6c5e tip (re)
731 731 create conflict
732 732 branch: default
733 733 bookmarks: *test
734 734 commit: 2 unknown (clean)
735 735 update: (current)
736 736 phases: 5 draft
737 737
738 738 $ hg shelve --delete --stat
739 739 abort: options '--delete' and '--stat' may not be used together
740 740 [10]
741 741 $ hg shelve --delete --name NAME
742 742 abort: options '--delete' and '--name' may not be used together
743 743 [10]
744 744
745 745 Test interactive shelve
746 746 $ cat <<EOF >> $HGRCPATH
747 747 > [ui]
748 748 > interactive = true
749 749 > EOF
750 750 $ echo 'a' >> a/b
751 751 $ cat a/a >> a/b
752 752 $ echo 'x' >> a/b
753 753 $ mv a/b a/a
754 754 $ echo 'a' >> foo/foo
755 755 $ hg st
756 756 M a/a
757 757 ? a/a.orig
758 758 ? foo/foo
759 759 $ cat a/a
760 760 a
761 761 a
762 762 c
763 763 x
764 764 x
765 765 $ cat foo/foo
766 766 foo
767 767 a
768 768 $ hg shelve --interactive --config ui.interactive=false
769 769 abort: running non-interactively
770 770 [10]
771 771 $ hg shelve --interactive << EOF
772 772 > y
773 773 > y
774 774 > n
775 775 > EOF
776 776 diff --git a/a/a b/a/a
777 777 2 hunks, 2 lines changed
778 778 examine changes to 'a/a'?
779 779 (enter ? for help) [Ynesfdaq?] y
780 780
781 781 @@ -1,3 +1,4 @@
782 782 +a
783 783 a
784 784 c
785 785 x
786 786 record change 1/2 to 'a/a'?
787 787 (enter ? for help) [Ynesfdaq?] y
788 788
789 789 @@ -1,3 +2,4 @@
790 790 a
791 791 c
792 792 x
793 793 +x
794 794 record change 2/2 to 'a/a'?
795 795 (enter ? for help) [Ynesfdaq?] n
796 796
797 797 shelved as test
798 798 merging a/a
799 799 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
800 800 $ cat a/a
801 801 a
802 802 c
803 803 x
804 804 x
805 805 $ cat foo/foo
806 806 foo
807 807 a
808 808 $ hg st
809 809 M a/a
810 810 ? foo/foo
811 811 $ hg bookmark
812 812 \* test (4|13):33f7f61e6c5e (re)
813 813 $ hg unshelve
814 814 unshelving change 'test'
815 815 temporarily committing pending changes (restore with 'hg unshelve --abort')
816 816 rebasing shelved changes
817 817 merging a/a
818 818 $ hg bookmark
819 819 \* test (4|13):33f7f61e6c5e (re)
820 820 $ cat a/a
821 821 a
822 822 a
823 823 c
824 824 x
825 825 x
826 826
827 827 shelve --patch and shelve --stat should work with valid shelfnames
828 828
829 829 $ hg up --clean .
830 830 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
831 831 (leaving bookmark test)
832 832 $ hg shelve --list
833 833 $ echo 'patch a' > shelf-patch-a
834 834 $ hg add shelf-patch-a
835 835 $ hg shelve
836 836 shelved as default
837 837 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
838 838 $ echo 'patch b' > shelf-patch-b
839 839 $ hg add shelf-patch-b
840 840 $ hg shelve
841 841 shelved as default-01
842 842 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
843 843 $ hg shelve --patch default default-01
844 844 default-01 (*)* changes to: create conflict (glob)
845 845
846 846 diff --git a/shelf-patch-b b/shelf-patch-b
847 847 new file mode 100644
848 848 --- /dev/null
849 849 +++ b/shelf-patch-b
850 850 @@ -0,0 +1,1 @@
851 851 +patch b
852 852 default (*)* changes to: create conflict (glob)
853 853
854 854 diff --git a/shelf-patch-a b/shelf-patch-a
855 855 new file mode 100644
856 856 --- /dev/null
857 857 +++ b/shelf-patch-a
858 858 @@ -0,0 +1,1 @@
859 859 +patch a
860 860 $ hg shelve --stat default default-01
861 861 default-01 (*)* changes to: create conflict (glob)
862 862 shelf-patch-b | 1 +
863 863 1 files changed, 1 insertions(+), 0 deletions(-)
864 864 default (*)* changes to: create conflict (glob)
865 865 shelf-patch-a | 1 +
866 866 1 files changed, 1 insertions(+), 0 deletions(-)
867 867 $ hg shelve --patch default
868 868 default (*)* changes to: create conflict (glob)
869 869
870 870 diff --git a/shelf-patch-a b/shelf-patch-a
871 871 new file mode 100644
872 872 --- /dev/null
873 873 +++ b/shelf-patch-a
874 874 @@ -0,0 +1,1 @@
875 875 +patch a
876 876 $ hg shelve --stat default
877 877 default (*)* changes to: create conflict (glob)
878 878 shelf-patch-a | 1 +
879 879 1 files changed, 1 insertions(+), 0 deletions(-)
880 880 $ hg shelve --patch nonexistentshelf
881 881 abort: cannot find shelf nonexistentshelf
882 882 [255]
883 883 $ hg shelve --stat nonexistentshelf
884 884 abort: cannot find shelf nonexistentshelf
885 885 [255]
886 886 $ hg shelve --patch default nonexistentshelf
887 887 abort: cannot find shelf nonexistentshelf
888 888 [255]
889 889
890 890 when the user asks for a patch, we assume they want the most recent shelve if
891 891 they don't provide a shelve name
892 892
893 893 $ hg shelve --patch
894 894 default-01 (*)* changes to: create conflict (glob)
895 895
896 896 diff --git a/shelf-patch-b b/shelf-patch-b
897 897 new file mode 100644
898 898 --- /dev/null
899 899 +++ b/shelf-patch-b
900 900 @@ -0,0 +1,1 @@
901 901 +patch b
902 902
903 903 $ cd ..
904 904
905 905 Shelve from general delta repo uses bundle2 on disk
906 906 --------------------------------------------------
907 907
908 908 no general delta
909 909
910 910 $ hg clone --pull repo bundle1 --config format.usegeneraldelta=0
911 911 requesting all changes
912 912 adding changesets
913 913 adding manifests
914 914 adding file changes
915 915 added 5 changesets with 8 changes to 6 files
916 916 new changesets cc01e2b0c59f:33f7f61e6c5e
917 917 updating to branch default
918 918 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
919 919 $ cd bundle1
920 920 $ echo babar > jungle
921 921 $ hg add jungle
922 922 $ hg shelve
923 923 shelved as default
924 924 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
925 925 $ hg debugbundle .hg/shelved/*.hg
926 926 330882a04d2ce8487636b1fb292e5beea77fa1e3
927 927 $ cd ..
928 928
929 929 with general delta
930 930
931 931 $ hg clone --pull repo bundle2 --config format.usegeneraldelta=1
932 932 requesting all changes
933 933 adding changesets
934 934 adding manifests
935 935 adding file changes
936 936 added 5 changesets with 8 changes to 6 files
937 937 new changesets cc01e2b0c59f:33f7f61e6c5e
938 938 updating to branch default
939 939 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
940 940 $ cd bundle2
941 941 $ echo babar > jungle
942 942 $ hg add jungle
943 943 $ hg shelve
944 944 shelved as default
945 945 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
946 946 $ hg debugbundle .hg/shelved/*.hg
947 947 Stream params: {Compression: BZ}
948 948 changegroup -- {nbchanges: 1, version: 02} (mandatory: True)
949 949 330882a04d2ce8487636b1fb292e5beea77fa1e3
950 950
951 951 Test shelve --keep
952 952
953 953 $ hg unshelve
954 954 unshelving change 'default'
955 955 $ hg shelve --keep --list
956 956 abort: options '--list' and '--keep' may not be used together
957 957 [10]
958 958 $ hg shelve --keep --patch
959 959 abort: options '--patch' and '--keep' may not be used together
960 960 [10]
961 961 $ hg shelve --keep --delete
962 962 abort: options '--delete' and '--keep' may not be used together
963 963 [10]
964 964 $ hg shelve --keep
965 965 shelved as default
966 966 $ hg diff
967 967 diff --git a/jungle b/jungle
968 968 new file mode 100644
969 969 --- /dev/null
970 970 +++ b/jungle
971 971 @@ -0,0 +1,1 @@
972 972 +babar
973 973
974 974 Test shelve --delete
975 975
976 976 $ hg shelve --list
977 977 default (*s ago) changes to: create conflict (glob)
978 978 $ hg shelve --delete doesnotexist
979 979 abort: shelved change 'doesnotexist' not found
980 980 [255]
981 981 $ hg shelve --delete default
982 982
983 983 $ cd ..
984 984
985 985 Test visibility of in-memory changes inside transaction to external hook
986 986 ------------------------------------------------------------------------
987 987
988 988 $ cd repo
989 989
990 990 $ echo xxxx >> x
991 991 $ hg commit -m "#5: changes to invoke rebase"
992 992
993 993 $ cat > $TESTTMP/checkvisibility.sh <<EOF
994 994 > echo "==== \$1:"
995 995 > hg parents --template "VISIBLE {rev}:{node|short}\n"
996 996 > # test that pending changes are hidden
997 997 > unset HG_PENDING
998 998 > hg parents --template "ACTUAL {rev}:{node|short}\n"
999 999 > echo "===="
1000 1000 > EOF
1001 1001
1002 1002 $ cat >> .hg/hgrc <<EOF
1003 1003 > [defaults]
1004 1004 > # to fix hash id of temporary revisions
1005 1005 > unshelve = --date '0 0'
1006 1006 > EOF
1007 1007
1008 1008 "hg unshelve" at REV5 implies steps below:
1009 1009
1010 1010 (1) commit changes in the working directory (REV6)
1011 1011 (2) unbundle shelved revision (REV7)
1012 1012 (3) rebase: merge REV7 into REV6 (REV6 => REV6, REV7)
1013 1013 (4) rebase: commit merged revision (REV8)
1014 1014 (5) rebase: update to REV6 (REV8 => REV6)
1015 1015 (6) update to REV5 (REV6 => REV5)
1016 1016 (7) abort transaction
1017 1017
1018 1018 == test visibility to external preupdate hook
1019 1019
1020 1020 $ cat >> .hg/hgrc <<EOF
1021 1021 > [hooks]
1022 1022 > preupdate.visibility = sh $TESTTMP/checkvisibility.sh preupdate
1023 1023 > EOF
1024 1024
1025 1025 $ echo nnnn >> n
1026 1026
1027 1027 $ sh $TESTTMP/checkvisibility.sh before-unshelving
1028 1028 ==== before-unshelving:
1029 1029 VISIBLE (5|19):703117a2acfb (re)
1030 1030 ACTUAL (5|19):703117a2acfb (re)
1031 1031 ====
1032 1032
1033 1033 $ hg unshelve --keep default
1034 1034 temporarily committing pending changes (restore with 'hg unshelve --abort')
1035 1035 rebasing shelved changes
1036 1036 ==== preupdate:
1037 1037 VISIBLE (6|20):54c00d20fb3f (re)
1038 1038 ACTUAL (5|19):703117a2acfb (re)
1039 1039 ====
1040 1040 ==== preupdate:
1041 1041 VISIBLE (8|21):8efe6f7537dc (re)
1042 1042 ACTUAL (5|19):703117a2acfb (re)
1043 1043 ====
1044 1044 ==== preupdate:
1045 1045 VISIBLE (6|20):54c00d20fb3f (re)
1046 1046 ACTUAL (5|19):703117a2acfb (re)
1047 1047 ====
1048 1048
1049 1049 $ cat >> .hg/hgrc <<EOF
1050 1050 > [hooks]
1051 1051 > preupdate.visibility =
1052 1052 > EOF
1053 1053
1054 1054 $ sh $TESTTMP/checkvisibility.sh after-unshelving
1055 1055 ==== after-unshelving:
1056 1056 VISIBLE (5|19):703117a2acfb (re)
1057 1057 ACTUAL (5|19):703117a2acfb (re)
1058 1058 ====
1059 1059
1060 1060 == test visibility to external update hook
1061 1061
1062 1062 $ hg update -q -C 703117a2acfb
1063 1063
1064 1064 $ cat >> .hg/hgrc <<EOF
1065 1065 > [hooks]
1066 1066 > update.visibility = sh $TESTTMP/checkvisibility.sh update
1067 1067 > EOF
1068 1068
1069 1069 $ echo nnnn >> n
1070 1070
1071 1071 $ sh $TESTTMP/checkvisibility.sh before-unshelving
1072 1072 ==== before-unshelving:
1073 1073 VISIBLE (5|19):703117a2acfb (re)
1074 1074 ACTUAL (5|19):703117a2acfb (re)
1075 1075 ====
1076 1076
1077 1077 $ hg unshelve --keep default
1078 1078 temporarily committing pending changes (restore with 'hg unshelve --abort')
1079 1079 rebasing shelved changes
1080 1080 ==== update:
1081 1081 VISIBLE (6|20):54c00d20fb3f (re)
1082 1082 VISIBLE 1?7:492ed9d705e5 (re)
1083 1083 ACTUAL (5|19):703117a2acfb (re)
1084 1084 ====
1085 1085 ==== update:
1086 1086 VISIBLE (6|20):54c00d20fb3f (re)
1087 1087 ACTUAL (5|19):703117a2acfb (re)
1088 1088 ====
1089 1089 ==== update:
1090 1090 VISIBLE (5|19):703117a2acfb (re)
1091 1091 ACTUAL (5|19):703117a2acfb (re)
1092 1092 ====
1093 1093
1094 1094 $ cat >> .hg/hgrc <<EOF
1095 1095 > [hooks]
1096 1096 > update.visibility =
1097 1097 > EOF
1098 1098
1099 1099 $ sh $TESTTMP/checkvisibility.sh after-unshelving
1100 1100 ==== after-unshelving:
1101 1101 VISIBLE (5|19):703117a2acfb (re)
1102 1102 ACTUAL (5|19):703117a2acfb (re)
1103 1103 ====
1104 1104
1105 1105 $ cd ..
1106 1106
1107 1107 Keep active bookmark while (un)shelving even on shared repo (issue4940)
1108 1108 -----------------------------------------------------------------------
1109 1109
1110 1110 $ cat <<EOF >> $HGRCPATH
1111 1111 > [extensions]
1112 1112 > share =
1113 1113 > EOF
1114 1114
1115 1115 $ hg bookmarks -R repo
1116 1116 test (4|13):33f7f61e6c5e (re)
1117 1117 $ hg share -B repo share
1118 1118 updating working directory
1119 1119 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
1120 1120 $ cd share
1121 1121
1122 1122 $ hg bookmarks
1123 1123 test (4|13):33f7f61e6c5e (re)
1124 1124 $ hg bookmarks foo
1125 1125 $ hg bookmarks
1126 1126 \* foo (5|19):703117a2acfb (re)
1127 1127 test (4|13):33f7f61e6c5e (re)
1128 1128 $ echo x >> x
1129 1129 $ hg shelve
1130 1130 shelved as foo
1131 1131 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1132 1132 $ hg bookmarks
1133 1133 \* foo (5|19):703117a2acfb (re)
1134 1134 test (4|13):33f7f61e6c5e (re)
1135 1135
1136 1136 $ hg unshelve
1137 1137 unshelving change 'foo'
1138 1138 $ hg bookmarks
1139 1139 \* foo (5|19):703117a2acfb (re)
1140 1140 test (4|13):33f7f61e6c5e (re)
1141 1141
1142 1142 $ cd ..
1143 1143
1144 1144 Abort unshelve while merging (issue5123)
1145 1145 ----------------------------------------
1146 1146
1147 1147 $ hg init issue5123
1148 1148 $ cd issue5123
1149 1149 $ echo > a
1150 1150 $ hg ci -Am a
1151 1151 adding a
1152 1152 $ hg co null
1153 1153 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1154 1154 $ echo > b
1155 1155 $ hg ci -Am b
1156 1156 adding b
1157 1157 created new head
1158 1158 $ echo > c
1159 1159 $ hg add c
1160 1160 $ hg shelve
1161 1161 shelved as default
1162 1162 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1163 1163 $ hg co 1
1164 1164 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1165 1165 $ hg merge 0
1166 1166 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1167 1167 (branch merge, don't forget to commit)
1168 1168 -- successful merge with two parents
1169 1169 $ hg log -G
1170 1170 @ changeset: 1:406bf70c274f
1171 1171 tag: tip
1172 1172 parent: -1:000000000000
1173 1173 user: test
1174 1174 date: Thu Jan 01 00:00:00 1970 +0000
1175 1175 summary: b
1176 1176
1177 1177 @ changeset: 0:ada8c9eb8252
1178 1178 user: test
1179 1179 date: Thu Jan 01 00:00:00 1970 +0000
1180 1180 summary: a
1181 1181
1182 1182 -- trying to pull in the shelve bits
1183 1183 -- unshelve should abort otherwise, it'll eat my second parent.
1184 1184 $ hg unshelve
1185 1185 abort: outstanding uncommitted merge
1186 1186 (use 'hg commit' or 'hg merge --abort')
1187 [255]
1187 [20]
1188 1188
1189 1189 $ cd ..
1190 1190
1191 1191 -- test for interactive mode on unshelve
1192 1192
1193 1193 $ hg init a
1194 1194 $ cd a
1195 1195 $ echo > b
1196 1196 $ hg ci -Am b
1197 1197 adding b
1198 1198 $ echo > c
1199 1199 $ echo > d
1200 1200 $ hg add .
1201 1201 adding c
1202 1202 adding d
1203 1203 $ hg shelve
1204 1204 shelved as default
1205 1205 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1206 1206 $ echo > e
1207 1207 $ hg add e
1208 1208 $ hg ci -m e
1209 1209 $ hg shelve --patch
1210 1210 default (*s ago) changes to: b (glob)
1211 1211
1212 1212 diff --git a/c b/c
1213 1213 new file mode 100644
1214 1214 --- /dev/null
1215 1215 +++ b/c
1216 1216 @@ -0,0 +1,1 @@
1217 1217 +
1218 1218 diff --git a/d b/d
1219 1219 new file mode 100644
1220 1220 --- /dev/null
1221 1221 +++ b/d
1222 1222 @@ -0,0 +1,1 @@
1223 1223 +
1224 1224 $ hg unshelve -i <<EOF
1225 1225 > y
1226 1226 > y
1227 1227 > y
1228 1228 > n
1229 1229 > EOF
1230 1230 unshelving change 'default'
1231 1231 rebasing shelved changes
1232 1232 diff --git a/c b/c
1233 1233 new file mode 100644
1234 1234 examine changes to 'c'?
1235 1235 (enter ? for help) [Ynesfdaq?] y
1236 1236
1237 1237 @@ -0,0 +1,1 @@
1238 1238 +
1239 1239 record change 1/2 to 'c'?
1240 1240 (enter ? for help) [Ynesfdaq?] y
1241 1241
1242 1242 diff --git a/d b/d
1243 1243 new file mode 100644
1244 1244 examine changes to 'd'?
1245 1245 (enter ? for help) [Ynesfdaq?] y
1246 1246
1247 1247 @@ -0,0 +1,1 @@
1248 1248 +
1249 1249 record change 2/2 to 'd'?
1250 1250 (enter ? for help) [Ynesfdaq?] n
1251 1251
1252 1252 $ ls -A
1253 1253 .hg
1254 1254 b
1255 1255 c
1256 1256 e
1257 1257 -- shelve should not contain `c` now
1258 1258 $ hg shelve --patch
1259 1259 default (*s ago) changes to: b (glob)
1260 1260
1261 1261 diff --git a/d b/d
1262 1262 new file mode 100644
1263 1263 --- /dev/null
1264 1264 +++ b/d
1265 1265 @@ -0,0 +1,1 @@
1266 1266 +
1267 1267 $ hg unshelve -i <<EOF
1268 1268 > y
1269 1269 > y
1270 1270 > EOF
1271 1271 unshelving change 'default'
1272 1272 temporarily committing pending changes (restore with 'hg unshelve --abort')
1273 1273 rebasing shelved changes
1274 1274 diff --git a/d b/d
1275 1275 new file mode 100644
1276 1276 examine changes to 'd'?
1277 1277 (enter ? for help) [Ynesfdaq?] y
1278 1278
1279 1279 @@ -0,0 +1,1 @@
1280 1280 +
1281 1281 record this change to 'd'?
1282 1282 (enter ? for help) [Ynesfdaq?] y
1283 1283
1284 1284
1285 1285 $ hg status -v
1286 1286 A c
1287 1287 A d
1288 1288 $ ls -A
1289 1289 .hg
1290 1290 b
1291 1291 c
1292 1292 d
1293 1293 e
1294 1294 $ hg shelve --list
1295 1295
1296 1296 -- now, unshelve selected changes from a file
1297 1297
1298 1298 $ echo B > foo
1299 1299 $ hg add foo
1300 1300 $ hg ci -m 'add B to foo'
1301 1301 $ cat > foo <<EOF
1302 1302 > A
1303 1303 > B
1304 1304 > C
1305 1305 > EOF
1306 1306 $ echo > garbage
1307 1307 $ hg st
1308 1308 M foo
1309 1309 ? garbage
1310 1310 $ hg shelve --unknown
1311 1311 shelved as default
1312 1312 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1313 1313 $ cat foo
1314 1314 B
1315 1315 $ hg unshelve -i <<EOF
1316 1316 > y
1317 1317 > y
1318 1318 > n
1319 1319 > y
1320 1320 > y
1321 1321 > EOF
1322 1322 unshelving change 'default'
1323 1323 rebasing shelved changes
1324 1324 diff --git a/foo b/foo
1325 1325 2 hunks, 2 lines changed
1326 1326 examine changes to 'foo'?
1327 1327 (enter ? for help) [Ynesfdaq?] y
1328 1328
1329 1329 @@ -1,1 +1,2 @@
1330 1330 +A
1331 1331 B
1332 1332 record change 1/3 to 'foo'?
1333 1333 (enter ? for help) [Ynesfdaq?] y
1334 1334
1335 1335 @@ -1,1 +2,2 @@
1336 1336 B
1337 1337 +C
1338 1338 record change 2/3 to 'foo'?
1339 1339 (enter ? for help) [Ynesfdaq?] n
1340 1340
1341 1341 diff --git a/garbage b/garbage
1342 1342 new file mode 100644
1343 1343 examine changes to 'garbage'?
1344 1344 (enter ? for help) [Ynesfdaq?] y
1345 1345
1346 1346 @@ -0,0 +1,1 @@
1347 1347 +
1348 1348 record change 3/3 to 'garbage'?
1349 1349 (enter ? for help) [Ynesfdaq?] y
1350 1350
1351 1351 $ hg st
1352 1352 M foo
1353 1353 ? garbage
1354 1354 $ cat foo
1355 1355 A
1356 1356 B
1357 1357 $ hg shelve --patch
1358 1358 default (*s ago) changes to: add B to foo (glob)
1359 1359
1360 1360 diff --git a/foo b/foo
1361 1361 --- a/foo
1362 1362 +++ b/foo
1363 1363 @@ -1,2 +1,3 @@
1364 1364 A
1365 1365 B
1366 1366 +C
1367 1367
1368 1368 -- unshelve interactive on conflicts
1369 1369
1370 1370 $ echo A >> bar1
1371 1371 $ echo A >> bar2
1372 1372 $ hg add bar1 bar2
1373 1373 $ hg ci -m 'add A to bars'
1374 1374 $ echo B >> bar1
1375 1375 $ echo B >> bar2
1376 1376 $ hg shelve
1377 1377 shelved as default-01
1378 1378 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1379 1379 $ echo C >> bar1
1380 1380 $ echo C >> bar2
1381 1381 $ hg ci -m 'add C to bars'
1382 1382 $ hg unshelve -i
1383 1383 unshelving change 'default-01'
1384 1384 rebasing shelved changes
1385 1385 merging bar1
1386 1386 merging bar2
1387 1387 warning: conflicts while merging bar1! (edit, then use 'hg resolve --mark')
1388 1388 warning: conflicts while merging bar2! (edit, then use 'hg resolve --mark')
1389 1389 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
1390 1390 [240]
1391 1391
1392 1392 $ cat > bar1 <<EOF
1393 1393 > A
1394 1394 > B
1395 1395 > C
1396 1396 > EOF
1397 1397 $ cat > bar2 <<EOF
1398 1398 > A
1399 1399 > B
1400 1400 > C
1401 1401 > EOF
1402 1402 $ hg resolve -m bar1 bar2
1403 1403 (no more unresolved files)
1404 1404 continue: hg unshelve --continue
1405 1405
1406 1406 -- using --continue with --interactive should throw an error
1407 1407 $ hg unshelve --continue -i
1408 1408 abort: cannot use both continue and interactive
1409 1409 [255]
1410 1410
1411 1411 $ cat bar1
1412 1412 A
1413 1413 B
1414 1414 C
1415 1415
1416 1416 #if stripbased
1417 1417 $ hg log -r 3:: -G
1418 1418 @ changeset: 5:f1d5f53e397b
1419 1419 | tag: tip
1420 1420 | parent: 3:e28fd7fa7938
1421 1421 | user: shelve@localhost
1422 1422 | date: Thu Jan 01 00:00:00 1970 +0000
1423 1423 | summary: changes to: add A to bars
1424 1424 |
1425 1425 | @ changeset: 4:fe451a778c81
1426 1426 |/ user: test
1427 1427 | date: Thu Jan 01 00:00:00 1970 +0000
1428 1428 | summary: add C to bars
1429 1429 |
1430 1430 o changeset: 3:e28fd7fa7938
1431 1431 | user: test
1432 1432 ~ date: Thu Jan 01 00:00:00 1970 +0000
1433 1433 summary: add A to bars
1434 1434
1435 1435 #endif
1436 1436
1437 1437 $ hg unshelve --continue <<EOF
1438 1438 > y
1439 1439 > y
1440 1440 > y
1441 1441 > n
1442 1442 > EOF
1443 1443 diff --git a/bar1 b/bar1
1444 1444 1 hunks, 1 lines changed
1445 1445 examine changes to 'bar1'?
1446 1446 (enter ? for help) [Ynesfdaq?] y
1447 1447
1448 1448 @@ -1,2 +1,3 @@
1449 1449 A
1450 1450 +B
1451 1451 C
1452 1452 record change 1/2 to 'bar1'?
1453 1453 (enter ? for help) [Ynesfdaq?] y
1454 1454
1455 1455 diff --git a/bar2 b/bar2
1456 1456 1 hunks, 1 lines changed
1457 1457 examine changes to 'bar2'?
1458 1458 (enter ? for help) [Ynesfdaq?] y
1459 1459
1460 1460 @@ -1,2 +1,3 @@
1461 1461 A
1462 1462 +B
1463 1463 C
1464 1464 record change 2/2 to 'bar2'?
1465 1465 (enter ? for help) [Ynesfdaq?] n
1466 1466
1467 1467 unshelve of 'default-01' complete
1468 1468
1469 1469 #if stripbased
1470 1470 $ hg log -r 3:: -G
1471 1471 @ changeset: 4:fe451a778c81
1472 1472 | tag: tip
1473 1473 | user: test
1474 1474 | date: Thu Jan 01 00:00:00 1970 +0000
1475 1475 | summary: add C to bars
1476 1476 |
1477 1477 o changeset: 3:e28fd7fa7938
1478 1478 | user: test
1479 1479 ~ date: Thu Jan 01 00:00:00 1970 +0000
1480 1480 summary: add A to bars
1481 1481
1482 1482 #endif
1483 1483
1484 1484 $ hg unshelve --continue
1485 1485 abort: no unshelve in progress
1486 [255]
1486 [20]
1487 1487
1488 1488 $ hg shelve --list
1489 1489 default-01 (*)* changes to: add A to bars (glob)
1490 1490 default (*)* changes to: add B to foo (glob)
1491 1491 $ hg unshelve -n default-01 -i <<EOF
1492 1492 > y
1493 1493 > y
1494 1494 > EOF
1495 1495 temporarily committing pending changes (restore with 'hg unshelve --abort')
1496 1496 rebasing shelved changes
1497 1497 diff --git a/bar2 b/bar2
1498 1498 1 hunks, 1 lines changed
1499 1499 examine changes to 'bar2'?
1500 1500 (enter ? for help) [Ynesfdaq?] y
1501 1501
1502 1502 @@ -1,2 +1,3 @@
1503 1503 A
1504 1504 +B
1505 1505 C
1506 1506 record this change to 'bar2'?
1507 1507 (enter ? for help) [Ynesfdaq?] y
1508 1508
1509 1509 -- test for --interactive --keep
1510 1510 $ hg unshelve -i --keep
1511 1511 abort: --keep on --interactive is not yet supported
1512 1512 [255]
@@ -1,945 +1,945 b''
1 1 #testcases stripbased phasebased
2 2 #testcases abortflag abortcommand
3 3 #testcases continueflag continuecommand
4 4
5 5 $ cat <<EOF >> $HGRCPATH
6 6 > [extensions]
7 7 > mq =
8 8 > [defaults]
9 9 > diff = --nodates --git
10 10 > qnew = --date '0 0'
11 11 > [shelve]
12 12 > maxbackups = 2
13 13 > EOF
14 14
15 15 #if phasebased
16 16
17 17 $ cat <<EOF >> $HGRCPATH
18 18 > [format]
19 19 > internal-phase = yes
20 20 > EOF
21 21
22 22 #endif
23 23
24 24 #if abortflag
25 25 $ cat >> $HGRCPATH <<EOF
26 26 > [alias]
27 27 > abort = unshelve --abort
28 28 > EOF
29 29 #endif
30 30
31 31 #if continueflag
32 32 $ cat >> $HGRCPATH <<EOF
33 33 > [alias]
34 34 > continue = unshelve --continue
35 35 > EOF
36 36 #endif
37 37
38 38 shelve should leave dirstate clean (issue4055)
39 39
40 40 $ hg init shelverebase
41 41 $ cd shelverebase
42 42 $ printf 'x\ny\n' > x
43 43 $ echo z > z
44 44 $ hg commit -Aqm xy
45 45 $ echo z >> x
46 46 $ hg commit -Aqm z
47 47 $ hg up 5c4c67fb7dce
48 48 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
49 49 $ printf 'a\nx\ny\nz\n' > x
50 50 $ hg commit -Aqm xyz
51 51 $ echo c >> z
52 52 $ hg shelve
53 53 shelved as default
54 54 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
55 55
56 56 $ hg rebase -d 6c103be8f4e4 --config extensions.rebase=
57 57 rebasing 2:323bfa07f744( tip)? "xyz" (re)
58 58 merging x
59 59 saved backup bundle to \$TESTTMP/shelverebase/.hg/strip-backup/323bfa07f744-(78114325|7ae538ef)-rebase.hg (re)
60 60 $ hg unshelve
61 61 unshelving change 'default'
62 62 rebasing shelved changes
63 63 $ hg status
64 64 M z
65 65
66 66 $ cd ..
67 67
68 68 shelve should only unshelve pending changes (issue4068)
69 69
70 70 $ hg init onlypendingchanges
71 71 $ cd onlypendingchanges
72 72 $ touch a
73 73 $ hg ci -Aqm a
74 74 $ touch b
75 75 $ hg ci -Aqm b
76 76 $ hg up -q 3903775176ed
77 77 $ touch c
78 78 $ hg ci -Aqm c
79 79
80 80 $ touch d
81 81 $ hg add d
82 82 $ hg shelve
83 83 shelved as default
84 84 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
85 85 $ hg up -q 0e067c57feba
86 86 $ hg unshelve
87 87 unshelving change 'default'
88 88 rebasing shelved changes
89 89 $ hg status
90 90 A d
91 91
92 92 unshelve should work on an ancestor of the original commit
93 93
94 94 $ hg shelve
95 95 shelved as default
96 96 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
97 97 $ hg up 3903775176ed
98 98 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
99 99 $ hg unshelve
100 100 unshelving change 'default'
101 101 rebasing shelved changes
102 102 $ hg status
103 103 A d
104 104
105 105 test bug 4073 we need to enable obsolete markers for it
106 106
107 107 $ cat >> $HGRCPATH << EOF
108 108 > [experimental]
109 109 > evolution.createmarkers=True
110 110 > EOF
111 111 $ hg shelve
112 112 shelved as default
113 113 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
114 114 $ hg debugobsolete `hg log -r 0e067c57feba -T '{node}'`
115 115 1 new obsolescence markers
116 116 obsoleted 1 changesets
117 117 $ hg unshelve
118 118 unshelving change 'default'
119 119
120 120 unshelve should leave unknown files alone (issue4113)
121 121
122 122 $ echo e > e
123 123 $ hg shelve
124 124 shelved as default
125 125 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
126 126 $ hg status
127 127 ? e
128 128 $ hg unshelve
129 129 unshelving change 'default'
130 130 $ hg status
131 131 A d
132 132 ? e
133 133 $ cat e
134 134 e
135 135
136 136 unshelve should keep a copy of unknown files
137 137
138 138 $ hg add e
139 139 $ hg shelve
140 140 shelved as default
141 141 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
142 142 $ echo z > e
143 143 $ hg unshelve
144 144 unshelving change 'default'
145 145 $ cat e
146 146 e
147 147 $ cat e.orig
148 148 z
149 149 $ rm e.orig
150 150
151 151 restores backup of unknown file to right directory
152 152
153 153 $ hg shelve
154 154 shelved as default
155 155 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
156 156 $ echo z > e
157 157 $ mkdir dir
158 158 $ hg unshelve --cwd dir
159 159 unshelving change 'default'
160 160 $ rmdir dir
161 161 $ cat e
162 162 e
163 163 $ cat e.orig
164 164 z
165 165
166 166 unshelve and conflicts with tracked and untracked files
167 167
168 168 preparing:
169 169
170 170 $ rm -f *.orig
171 171 $ hg ci -qm 'commit stuff'
172 172 $ hg phase -p null:
173 173
174 174 no other changes - no merge:
175 175
176 176 $ echo f > f
177 177 $ hg add f
178 178 $ hg shelve
179 179 shelved as default
180 180 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
181 181 $ echo g > f
182 182 $ hg unshelve
183 183 unshelving change 'default'
184 184 $ hg st
185 185 A f
186 186 ? f.orig
187 187 $ cat f
188 188 f
189 189 $ cat f.orig
190 190 g
191 191
192 192 other uncommitted changes - merge:
193 193
194 194 $ hg st
195 195 A f
196 196 ? f.orig
197 197 $ hg shelve
198 198 shelved as default
199 199 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
200 200 #if repobundlerepo
201 201 $ hg log -G --template '{rev} {desc|firstline} {author}' -R bundle://.hg/shelved/default.hg -r 'bundle()' --hidden
202 202 o [48] changes to: commit stuff shelve@localhost (re)
203 203 |
204 204 ~
205 205 #endif
206 206 $ hg log -G --template '{rev} {desc|firstline} {author}'
207 207 @ [37] commit stuff test (re)
208 208 |
209 209 | o 2 c test
210 210 |/
211 211 o 0 a test
212 212
213 213 $ mv f.orig f
214 214 $ echo 1 > a
215 215 $ hg unshelve --date '1073741824 0'
216 216 unshelving change 'default'
217 217 temporarily committing pending changes (restore with 'hg unshelve --abort')
218 218 rebasing shelved changes
219 219 merging f
220 220 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
221 221 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
222 222 [240]
223 223
224 224 #if phasebased
225 225 $ hg log -G --template '{rev} {desc|firstline} {author} {date|isodate}'
226 226 @ 9 pending changes temporary commit shelve@localhost 2004-01-10 13:37 +0000
227 227 |
228 228 | @ 8 changes to: commit stuff shelve@localhost 1970-01-01 00:00 +0000
229 229 |/
230 230 o 7 commit stuff test 1970-01-01 00:00 +0000
231 231 |
232 232 | o 2 c test 1970-01-01 00:00 +0000
233 233 |/
234 234 o 0 a test 1970-01-01 00:00 +0000
235 235
236 236 #endif
237 237
238 238 #if stripbased
239 239 $ hg log -G --template '{rev} {desc|firstline} {author} {date|isodate}'
240 240 @ 5 changes to: commit stuff shelve@localhost 1970-01-01 00:00 +0000
241 241 |
242 242 | @ 4 pending changes temporary commit shelve@localhost 2004-01-10 13:37 +0000
243 243 |/
244 244 o 3 commit stuff test 1970-01-01 00:00 +0000
245 245 |
246 246 | o 2 c test 1970-01-01 00:00 +0000
247 247 |/
248 248 o 0 a test 1970-01-01 00:00 +0000
249 249
250 250 #endif
251 251
252 252 $ hg st
253 253 M f
254 254 ? f.orig
255 255 $ cat f
256 256 <<<<<<< working-copy: d44eae5c3d33 - shelve: pending changes temporary commit
257 257 g
258 258 =======
259 259 f
260 260 >>>>>>> shelve: aef214a5229c - shelve: changes to: commit stuff
261 261 $ cat f.orig
262 262 g
263 263 $ hg unshelve --abort -t false
264 264 tool option will be ignored
265 265 unshelve of 'default' aborted
266 266 $ hg st
267 267 M a
268 268 ? f.orig
269 269 $ cat f.orig
270 270 g
271 271 $ hg unshelve
272 272 unshelving change 'default'
273 273 temporarily committing pending changes (restore with 'hg unshelve --abort')
274 274 rebasing shelved changes
275 275 $ hg st
276 276 M a
277 277 A f
278 278 ? f.orig
279 279
280 280 other committed changes - merge:
281 281
282 282 $ hg shelve f
283 283 shelved as default
284 284 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
285 285 $ hg ci a -m 'intermediate other change'
286 286 $ mv f.orig f
287 287 $ hg unshelve
288 288 unshelving change 'default'
289 289 rebasing shelved changes
290 290 merging f
291 291 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
292 292 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
293 293 [240]
294 294 $ hg st
295 295 M f
296 296 ? f.orig
297 297 $ cat f
298 298 <<<<<<< working-copy: 6b563750f973 - test: intermediate other change
299 299 g
300 300 =======
301 301 f
302 302 >>>>>>> shelve: aef214a5229c - shelve: changes to: commit stuff
303 303 $ cat f.orig
304 304 g
305 305
306 306 #if abortcommand
307 307 when in dry-run mode
308 308 $ hg abort --dry-run
309 309 unshelve in progress, will be aborted
310 310 #endif
311 311
312 312 $ hg abort
313 313 unshelve of 'default' aborted
314 314 $ hg st
315 315 ? f.orig
316 316 $ cat f.orig
317 317 g
318 318 $ hg shelve --delete default
319 319 $ cd ..
320 320
321 321 you shouldn't be able to ask for the patch/stats of the most recent shelve if
322 322 there are no shelves
323 323
324 324 $ hg init noshelves
325 325 $ cd noshelves
326 326
327 327 $ hg shelve --patch
328 328 abort: there are no shelves to show
329 329 [255]
330 330 $ hg shelve --stat
331 331 abort: there are no shelves to show
332 332 [255]
333 333
334 334 $ cd ..
335 335
336 336 test .orig files go where the user wants them to
337 337 ---------------------------------------------------------------
338 338 $ hg init salvage
339 339 $ cd salvage
340 340 $ echo 'content' > root
341 341 $ hg commit -A -m 'root' -q
342 342 $ echo '' > root
343 343 $ hg shelve -q
344 344 $ echo 'contADDent' > root
345 345 $ hg unshelve -q --config 'ui.origbackuppath=.hg/origbackups'
346 346 warning: conflicts while merging root! (edit, then use 'hg resolve --mark')
347 347 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
348 348 [240]
349 349 $ ls .hg/origbackups
350 350 root
351 351 $ rm -rf .hg/origbackups
352 352
353 353 test Abort unshelve always gets user out of the unshelved state
354 354 ---------------------------------------------------------------
355 355
356 356 with a corrupted shelve state file
357 357 $ sed 's/ae8c668541e8/123456789012/' .hg/shelvedstate > ../corrupt-shelvedstate
358 358 $ mv ../corrupt-shelvedstate .hg/shelvestate
359 359 $ hg unshelve --abort 2>&1 | grep 'aborted'
360 360 unshelve of 'default' aborted
361 361 $ hg summary
362 362 parent: 0:ae8c668541e8 tip
363 363 root
364 364 branch: default
365 365 commit: 1 modified
366 366 update: (current)
367 367 phases: 1 draft
368 368 $ hg up -C .
369 369 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
370 370
371 371 $ cd ..
372 372
373 373 Shelve and unshelve unknown files. For the purposes of unshelve, a shelved
374 374 unknown file is the same as a shelved added file, except that it will be in
375 375 unknown state after unshelve if and only if it was either absent or unknown
376 376 before the unshelve operation.
377 377
378 378 $ hg init unknowns
379 379 $ cd unknowns
380 380
381 381 The simplest case is if I simply have an unknown file that I shelve and unshelve
382 382
383 383 $ echo unknown > unknown
384 384 $ hg status
385 385 ? unknown
386 386 $ hg shelve --unknown
387 387 shelved as default
388 388 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
389 389 $ hg status
390 390 $ hg unshelve
391 391 unshelving change 'default'
392 392 $ hg status
393 393 ? unknown
394 394 $ rm unknown
395 395
396 396 If I shelve, add the file, and unshelve, does it stay added?
397 397
398 398 $ echo unknown > unknown
399 399 $ hg shelve -u
400 400 shelved as default
401 401 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
402 402 $ hg status
403 403 $ touch unknown
404 404 $ hg add unknown
405 405 $ hg status
406 406 A unknown
407 407 $ hg unshelve
408 408 unshelving change 'default'
409 409 temporarily committing pending changes (restore with 'hg unshelve --abort')
410 410 rebasing shelved changes
411 411 merging unknown
412 412 $ hg status
413 413 A unknown
414 414 $ hg forget unknown
415 415 $ rm unknown
416 416
417 417 And if I shelve, commit, then unshelve, does it become modified?
418 418
419 419 $ echo unknown > unknown
420 420 $ hg shelve -u
421 421 shelved as default
422 422 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
423 423 $ hg status
424 424 $ touch unknown
425 425 $ hg add unknown
426 426 $ hg commit -qm "Add unknown"
427 427 $ hg status
428 428 $ hg unshelve
429 429 unshelving change 'default'
430 430 rebasing shelved changes
431 431 merging unknown
432 432 $ hg status
433 433 M unknown
434 434 $ hg remove --force unknown
435 435 $ hg commit -qm "Remove unknown"
436 436
437 437 $ cd ..
438 438
439 439 We expects that non-bare shelve keeps newly created branch in
440 440 working directory.
441 441
442 442 $ hg init shelve-preserve-new-branch
443 443 $ cd shelve-preserve-new-branch
444 444 $ echo "a" >> a
445 445 $ hg add a
446 446 $ echo "b" >> b
447 447 $ hg add b
448 448 $ hg commit -m "ab"
449 449 $ echo "aa" >> a
450 450 $ echo "bb" >> b
451 451 $ hg branch new-branch
452 452 marked working directory as branch new-branch
453 453 (branches are permanent and global, did you want a bookmark?)
454 454 $ hg status
455 455 M a
456 456 M b
457 457 $ hg branch
458 458 new-branch
459 459 $ hg shelve a
460 460 shelved as default
461 461 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
462 462 $ hg branch
463 463 new-branch
464 464 $ hg status
465 465 M b
466 466 $ touch "c" >> c
467 467 $ hg add c
468 468 $ hg status
469 469 M b
470 470 A c
471 471 $ hg shelve --exclude c
472 472 shelved as default-01
473 473 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
474 474 $ hg branch
475 475 new-branch
476 476 $ hg status
477 477 A c
478 478 $ hg shelve --include c
479 479 shelved as default-02
480 480 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
481 481 $ hg branch
482 482 new-branch
483 483 $ hg status
484 484 $ echo "d" >> d
485 485 $ hg add d
486 486 $ hg status
487 487 A d
488 488
489 489 We expect that bare-shelve will not keep branch in current working directory.
490 490
491 491 $ hg shelve
492 492 shelved as default-03
493 493 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
494 494 $ hg branch
495 495 default
496 496 $ cd ..
497 497
498 498 When i shelve commit on newly created branch i expect
499 499 that after unshelve newly created branch will be preserved.
500 500
501 501 $ hg init shelve_on_new_branch_simple
502 502 $ cd shelve_on_new_branch_simple
503 503 $ echo "aaa" >> a
504 504 $ hg commit -A -m "a"
505 505 adding a
506 506 $ hg branch
507 507 default
508 508 $ hg branch test
509 509 marked working directory as branch test
510 510 (branches are permanent and global, did you want a bookmark?)
511 511 $ echo "bbb" >> a
512 512 $ hg status
513 513 M a
514 514 $ hg shelve
515 515 shelved as default
516 516 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
517 517 $ hg branch
518 518 default
519 519 $ echo "bbb" >> b
520 520 $ hg status
521 521 ? b
522 522 $ hg unshelve
523 523 unshelving change 'default'
524 524 marked working directory as branch test
525 525 $ hg status
526 526 M a
527 527 ? b
528 528 $ hg branch
529 529 test
530 530 $ cd ..
531 531
532 532 When i shelve commit on newly created branch, make
533 533 some changes, unshelve it and running into merge
534 534 conflicts i expect that after fixing them and
535 535 running unshelve --continue newly created branch
536 536 will be preserved.
537 537
538 538 $ hg init shelve_on_new_branch_conflict
539 539 $ cd shelve_on_new_branch_conflict
540 540 $ echo "aaa" >> a
541 541 $ hg commit -A -m "a"
542 542 adding a
543 543 $ hg branch
544 544 default
545 545 $ hg branch test
546 546 marked working directory as branch test
547 547 (branches are permanent and global, did you want a bookmark?)
548 548 $ echo "bbb" >> a
549 549 $ hg status
550 550 M a
551 551 $ hg shelve
552 552 shelved as default
553 553 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
554 554 $ hg branch
555 555 default
556 556 $ echo "ccc" >> a
557 557 $ hg status
558 558 M a
559 559 $ hg unshelve
560 560 unshelving change 'default'
561 561 temporarily committing pending changes (restore with 'hg unshelve --abort')
562 562 rebasing shelved changes
563 563 merging a
564 564 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
565 565 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
566 566 [240]
567 567 $ echo "aaabbbccc" > a
568 568 $ rm a.orig
569 569 $ hg resolve --mark a
570 570 (no more unresolved files)
571 571 continue: hg unshelve --continue
572 572 $ hg continue
573 573 marked working directory as branch test
574 574 unshelve of 'default' complete
575 575 $ cat a
576 576 aaabbbccc
577 577 $ hg status
578 578 M a
579 579 $ hg branch
580 580 test
581 581 $ hg commit -m "test-commit"
582 582
583 583 When i shelve on test branch, update to default branch
584 584 and unshelve i expect that it will not preserve previous
585 585 test branch.
586 586
587 587 $ echo "xxx" > b
588 588 $ hg add b
589 589 $ hg shelve
590 590 shelved as test
591 591 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
592 592 $ hg update -r 7049e48789d7
593 593 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
594 594 $ hg unshelve
595 595 unshelving change 'test'
596 596 rebasing shelved changes
597 597 $ hg status
598 598 A b
599 599 $ hg branch
600 600 default
601 601 $ cd ..
602 602
603 603 When i unshelve resulting in merge conflicts and makes saved
604 604 file shelvedstate looks like in previous versions in
605 605 mercurial(without restore branch information in 7th line) i
606 606 expect that after resolving conflicts and successfully
607 607 running 'shelve --continue' the branch information won't be
608 608 restored and branch will be unchanged.
609 609
610 610 shelve on new branch, conflict with previous shelvedstate
611 611
612 612 $ hg init conflict
613 613 $ cd conflict
614 614 $ echo "aaa" >> a
615 615 $ hg commit -A -m "a"
616 616 adding a
617 617 $ hg branch
618 618 default
619 619 $ hg branch test
620 620 marked working directory as branch test
621 621 (branches are permanent and global, did you want a bookmark?)
622 622 $ echo "bbb" >> a
623 623 $ hg status
624 624 M a
625 625 $ hg shelve
626 626 shelved as default
627 627 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
628 628 $ hg branch
629 629 default
630 630 $ echo "ccc" >> a
631 631 $ hg status
632 632 M a
633 633 $ hg unshelve
634 634 unshelving change 'default'
635 635 temporarily committing pending changes (restore with 'hg unshelve --abort')
636 636 rebasing shelved changes
637 637 merging a
638 638 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
639 639 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
640 640 [240]
641 641
642 642 Removing restore branch information from shelvedstate file(making it looks like
643 643 in previous versions) and running unshelve --continue
644 644
645 645 $ cp .hg/shelvedstate .hg/shelvedstate_old
646 646 $ cat .hg/shelvedstate_old | grep -v 'branchtorestore' > .hg/shelvedstate
647 647
648 648 $ echo "aaabbbccc" > a
649 649 $ rm a.orig
650 650 $ hg resolve --mark a
651 651 (no more unresolved files)
652 652 continue: hg unshelve --continue
653 653
654 654 #if continuecommand
655 655 $ hg continue --dry-run
656 656 unshelve in progress, will be resumed
657 657 #endif
658 658
659 659 $ hg continue
660 660 unshelve of 'default' complete
661 661 $ cat a
662 662 aaabbbccc
663 663 $ hg status
664 664 M a
665 665 $ hg branch
666 666 default
667 667 $ cd ..
668 668
669 669 On non bare shelve the branch information shouldn't be restored
670 670
671 671 $ hg init bare_shelve_on_new_branch
672 672 $ cd bare_shelve_on_new_branch
673 673 $ echo "aaa" >> a
674 674 $ hg commit -A -m "a"
675 675 adding a
676 676 $ hg branch
677 677 default
678 678 $ hg branch test
679 679 marked working directory as branch test
680 680 (branches are permanent and global, did you want a bookmark?)
681 681 $ echo "bbb" >> a
682 682 $ hg status
683 683 M a
684 684 $ hg shelve a
685 685 shelved as default
686 686 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
687 687 $ hg branch
688 688 test
689 689 $ hg branch default
690 690 marked working directory as branch default
691 691 (branches are permanent and global, did you want a bookmark?)
692 692 $ echo "bbb" >> b
693 693 $ hg status
694 694 ? b
695 695 $ hg unshelve
696 696 unshelving change 'default'
697 697 $ hg status
698 698 M a
699 699 ? b
700 700 $ hg branch
701 701 default
702 702 $ cd ..
703 703
704 704 Prepare unshelve with a corrupted shelvedstate
705 705 $ hg init r1 && cd r1
706 706 $ echo text1 > file && hg add file
707 707 $ hg shelve
708 708 shelved as default
709 709 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
710 710 $ echo text2 > file && hg ci -Am text1
711 711 adding file
712 712 $ hg unshelve
713 713 unshelving change 'default'
714 714 rebasing shelved changes
715 715 merging file
716 716 warning: conflicts while merging file! (edit, then use 'hg resolve --mark')
717 717 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
718 718 [240]
719 719 $ echo somethingsomething > .hg/shelvedstate
720 720
721 721 Unshelve --continue fails with appropriate message if shelvedstate is corrupted
722 722 $ hg continue
723 723 abort: corrupted shelved state file
724 724 (please run hg unshelve --abort to abort unshelve operation)
725 725 [255]
726 726
727 727 Unshelve --abort works with a corrupted shelvedstate
728 728 $ hg abort
729 729 abort: could not read shelved state file, your working copy may be in an unexpected state
730 730 please update to some commit
731 731
732 732 [255]
733 733
734 734 Unshelve --abort fails with appropriate message if there's no unshelve in
735 735 progress
736 736
737 737 #if abortflag
738 738 $ hg unshelve --abort
739 739 abort: no unshelve in progress
740 [255]
740 [20]
741 741 #else
742 742 $ hg abort
743 743 aborting the merge, updating back to 9451eaa6eee3
744 744 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
745 745 #endif
746 746 $ cd ..
747 747
748 748 Unshelve respects --keep even if user intervention is needed
749 749 $ hg init unshelvekeep && cd unshelvekeep
750 750 $ echo 1 > file && hg ci -Am 1
751 751 adding file
752 752 $ echo 2 >> file
753 753 $ hg shelve
754 754 shelved as default
755 755 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
756 756 $ echo 3 >> file && hg ci -Am 13
757 757 $ hg shelve --list
758 758 default (*s ago) * changes to: 1 (glob)
759 759 $ hg unshelve --keep
760 760 unshelving change 'default'
761 761 rebasing shelved changes
762 762 merging file
763 763 warning: conflicts while merging file! (edit, then use 'hg resolve --mark')
764 764 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
765 765 [240]
766 766 $ hg resolve --mark file
767 767 (no more unresolved files)
768 768 continue: hg unshelve --continue
769 769 $ hg continue
770 770 unshelve of 'default' complete
771 771 $ hg shelve --list
772 772 default (*s ago) * changes to: 1 (glob)
773 773 $ cd ..
774 774
775 775 Unshelving when there are deleted files does not crash (issue4176)
776 776 $ hg init unshelve-deleted-file && cd unshelve-deleted-file
777 777 $ echo a > a && echo b > b && hg ci -Am ab
778 778 adding a
779 779 adding b
780 780 $ echo aa > a && hg shelve
781 781 shelved as default
782 782 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
783 783 $ rm b
784 784 $ hg st
785 785 ! b
786 786 $ hg unshelve
787 787 unshelving change 'default'
788 788 $ hg shelve
789 789 shelved as default
790 790 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
791 791 $ rm a && echo b > b
792 792 $ hg st
793 793 ! a
794 794 $ hg unshelve
795 795 unshelving change 'default'
796 796 abort: shelved change touches missing files
797 797 (run hg status to see which files are missing)
798 798 [255]
799 799 $ hg st
800 800 ! a
801 801 $ cd ..
802 802
803 803 New versions of Mercurial know how to read onld shelvedstate files
804 804 $ hg init oldshelvedstate
805 805 $ cd oldshelvedstate
806 806 $ echo root > root && hg ci -Am root
807 807 adding root
808 808 $ echo 1 > a
809 809 $ hg add a
810 810 $ hg shelve --name ashelve
811 811 shelved as ashelve
812 812 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
813 813 $ echo 2 > a
814 814 $ hg ci -Am a
815 815 adding a
816 816 $ hg unshelve
817 817 unshelving change 'ashelve'
818 818 rebasing shelved changes
819 819 merging a
820 820 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
821 821 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
822 822 [240]
823 823 putting v1 shelvedstate file in place of a created v2
824 824 $ cat << EOF > .hg/shelvedstate
825 825 > 1
826 826 > ashelve
827 827 > 8b058dae057a5a78f393f4535d9e363dd5efac9d
828 828 > 8b058dae057a5a78f393f4535d9e363dd5efac9d
829 829 > 8b058dae057a5a78f393f4535d9e363dd5efac9d f543b27db2cdb41737e2e0008dc524c471da1446
830 830 > f543b27db2cdb41737e2e0008dc524c471da1446
831 831 >
832 832 > nokeep
833 833 > :no-active-bookmark
834 834 > EOF
835 835 $ echo 1 > a
836 836 $ hg resolve --mark a
837 837 (no more unresolved files)
838 838 continue: hg unshelve --continue
839 839 mercurial does not crash
840 840 $ hg continue
841 841 unshelve of 'ashelve' complete
842 842
843 843 #if phasebased
844 844
845 845 Unshelve with some metadata file missing
846 846 ----------------------------------------
847 847
848 848 $ hg shelve
849 849 shelved as default
850 850 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
851 851 $ echo 3 > a
852 852
853 853 Test with the `.shelve` missing, but the changeset still in the repo (non-natural case)
854 854
855 855 $ rm .hg/shelved/default.shelve
856 856 $ hg unshelve
857 857 unshelving change 'default'
858 858 temporarily committing pending changes (restore with 'hg unshelve --abort')
859 859 rebasing shelved changes
860 860 merging a
861 861 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
862 862 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
863 863 [240]
864 864 $ hg abort
865 865 unshelve of 'default' aborted
866 866
867 867 Unshelve without .shelve metadata (can happen when upgrading a repository with old shelve)
868 868
869 869 $ cat .hg/shelved/default.shelve
870 870 node=82e0cb9893247d12667017593ce1e5655860f1ac
871 871 $ hg strip --hidden --rev 82e0cb989324 --no-backup
872 872 $ rm .hg/shelved/default.shelve
873 873 $ hg unshelve
874 874 unshelving change 'default'
875 875 temporarily committing pending changes (restore with 'hg unshelve --abort')
876 876 rebasing shelved changes
877 877 merging a
878 878 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
879 879 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
880 880 [240]
881 881 $ cat .hg/shelved/default.shelve
882 882 node=82e0cb9893247d12667017593ce1e5655860f1ac
883 883 $ hg abort
884 884 unshelve of 'default' aborted
885 885
886 886 #endif
887 887
888 888 $ cd ..
889 889
890 890 Block merge abort when unshelve in progress(issue6160)
891 891 ------------------------------------------------------
892 892
893 893 $ hg init a
894 894 $ cd a
895 895 $ echo foo > a ; hg commit -qAm "initial commit"
896 896 $ echo bar > a
897 897 $ hg shelve
898 898 shelved as default
899 899 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
900 900 $ echo foobar > a
901 901 $ hg unshelve
902 902 unshelving change 'default'
903 903 temporarily committing pending changes (restore with 'hg unshelve --abort')
904 904 rebasing shelved changes
905 905 merging a
906 906 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
907 907 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
908 908 [240]
909 909
910 910 $ hg log --template '{desc|firstline} {author} {date|isodate} \n' -r .
911 911 pending changes temporary commit shelve@localhost 1970-01-01 00:00 +0000
912 912 $ hg merge --abort
913 913 abort: cannot abort merge with unshelve in progress
914 914 (use 'hg unshelve --continue' or 'hg unshelve --abort')
915 [255]
915 [20]
916 916
917 917 $ hg unshelve --abort
918 918 unshelve of 'default' aborted
919 919
920 920 $ hg log -G --template '{desc|firstline} {author} {date|isodate} \n' -r .
921 921 @ initial commit test 1970-01-01 00:00 +0000
922 922
923 923 $ cd ..
924 924
925 925 Demonstrate that the labels are correct in the merge conflict
926 926 -------------------------------------------------------------
927 927 $ hg init labels
928 928 $ cd labels
929 929 $ echo r0 > foo
930 930 $ hg ci -qAm r0
931 931 $ echo "this will be shelved" >> foo
932 932 $ hg shelve -q
933 933 $ echo "this is in wdir, conflicts with shelve" >> foo
934 934 $ hg unshelve -q
935 935 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
936 936 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
937 937 [240]
938 938 $ cat foo
939 939 r0
940 940 <<<<<<< working-copy: 0b2fcf2a90e9 - shelve: pending changes temporary commit
941 941 this is in wdir, conflicts with shelve
942 942 =======
943 943 this will be shelved
944 944 >>>>>>> shelve: 9c072a2163db - shelve: changes to: r0
945 945 $ cd ..
@@ -1,978 +1,978 b''
1 1 #testcases obsstore-on obsstore-off
2 2
3 3 $ cat > $TESTTMP/editor.py <<EOF
4 4 > #!"$PYTHON"
5 5 > import os
6 6 > import sys
7 7 > path = os.path.join(os.environ['TESTTMP'], 'messages')
8 8 > messages = open(path).read().split('--\n')
9 9 > prompt = open(sys.argv[1]).read()
10 10 > sys.stdout.write(''.join('EDITOR: %s' % l for l in prompt.splitlines(True)))
11 11 > sys.stdout.flush()
12 12 > with open(sys.argv[1], 'w') as f:
13 13 > f.write(messages[0])
14 14 > with open(path, 'w') as f:
15 15 > f.write('--\n'.join(messages[1:]))
16 16 > EOF
17 17
18 18 $ cat >> $HGRCPATH <<EOF
19 19 > [extensions]
20 20 > drawdag=$TESTDIR/drawdag.py
21 21 > split=
22 22 > [ui]
23 23 > interactive=1
24 24 > color=no
25 25 > paginate=never
26 26 > [diff]
27 27 > git=1
28 28 > unified=0
29 29 > [commands]
30 30 > commit.interactive.unified=0
31 31 > [alias]
32 32 > glog=log -G -T '{rev}:{node|short} {desc} {bookmarks}\n'
33 33 > EOF
34 34
35 35 #if obsstore-on
36 36 $ cat >> $HGRCPATH <<EOF
37 37 > [experimental]
38 38 > evolution=all
39 39 > EOF
40 40 #endif
41 41
42 42 $ hg init a
43 43 $ cd a
44 44
45 45 Nothing to split
46 46
47 47 $ hg split
48 48 nothing to split
49 49 [1]
50 50
51 51 $ hg commit -m empty --config ui.allowemptycommit=1
52 52 $ hg split
53 53 abort: cannot split an empty revision
54 54 [255]
55 55
56 56 $ rm -rf .hg
57 57 $ hg init
58 58
59 59 Cannot split working directory
60 60
61 61 $ hg split -r 'wdir()'
62 62 abort: cannot split working directory
63 63 [255]
64 64
65 65 Generate some content. The sed filter drop CR on Windows, which is dropped in
66 66 the a > b line.
67 67
68 68 $ $TESTDIR/seq.py 1 5 | sed 's/\r$//' >> a
69 69 $ hg ci -m a1 -A a -q
70 70 $ hg bookmark -i r1
71 71 $ sed 's/1/11/;s/3/33/;s/5/55/' a > b
72 72 $ mv b a
73 73 $ hg ci -m a2 -q
74 74 $ hg bookmark -i r2
75 75
76 76 Cannot split a public changeset
77 77
78 78 $ hg phase --public -r 'all()'
79 79 $ hg split .
80 80 abort: cannot split public changesets
81 81 (see 'hg help phases' for details)
82 82 [255]
83 83
84 84 $ hg phase --draft -f -r 'all()'
85 85
86 86 Cannot split while working directory is dirty
87 87
88 88 $ touch dirty
89 89 $ hg add dirty
90 90 $ hg split .
91 91 abort: uncommitted changes
92 [255]
92 [20]
93 93 $ hg forget dirty
94 94 $ rm dirty
95 95
96 96 Make a clean directory for future tests to build off of
97 97
98 98 $ cp -R . ../clean
99 99
100 100 Split a head
101 101
102 102 $ hg bookmark r3
103 103
104 104 $ hg split 'all()'
105 105 abort: cannot split multiple revisions
106 106 [255]
107 107
108 108 This function splits a bit strangely primarily to avoid changing the behavior of
109 109 the test after a bug was fixed with how split/commit --interactive handled
110 110 `commands.commit.interactive.unified=0`: when there were no context lines,
111 111 it kept only the last diff hunk. When running split, this meant that runsplit
112 112 was always recording three commits, one for each diff hunk, in reverse order
113 113 (the base commit was the last diff hunk in the file).
114 114 $ runsplit() {
115 115 > cat > $TESTTMP/messages <<EOF
116 116 > split 1
117 117 > --
118 118 > split 2
119 119 > --
120 120 > split 3
121 121 > EOF
122 122 > cat <<EOF | hg split "$@"
123 123 > y
124 124 > n
125 125 > n
126 126 > y
127 127 > y
128 128 > n
129 129 > y
130 130 > y
131 131 > y
132 132 > EOF
133 133 > }
134 134
135 135 $ HGEDITOR=false runsplit
136 136 diff --git a/a b/a
137 137 3 hunks, 3 lines changed
138 138 examine changes to 'a'?
139 139 (enter ? for help) [Ynesfdaq?] y
140 140
141 141 @@ -1,1 +1,1 @@
142 142 -1
143 143 +11
144 144 record change 1/3 to 'a'?
145 145 (enter ? for help) [Ynesfdaq?] n
146 146
147 147 @@ -3,1 +3,1 @@ 2
148 148 -3
149 149 +33
150 150 record change 2/3 to 'a'?
151 151 (enter ? for help) [Ynesfdaq?] n
152 152
153 153 @@ -5,1 +5,1 @@ 4
154 154 -5
155 155 +55
156 156 record change 3/3 to 'a'?
157 157 (enter ? for help) [Ynesfdaq?] y
158 158
159 159 transaction abort!
160 160 rollback completed
161 161 abort: edit failed: false exited with status 1
162 162 [255]
163 163 $ hg status
164 164
165 165 $ HGEDITOR="\"$PYTHON\" $TESTTMP/editor.py"
166 166 $ runsplit
167 167 diff --git a/a b/a
168 168 3 hunks, 3 lines changed
169 169 examine changes to 'a'?
170 170 (enter ? for help) [Ynesfdaq?] y
171 171
172 172 @@ -1,1 +1,1 @@
173 173 -1
174 174 +11
175 175 record change 1/3 to 'a'?
176 176 (enter ? for help) [Ynesfdaq?] n
177 177
178 178 @@ -3,1 +3,1 @@ 2
179 179 -3
180 180 +33
181 181 record change 2/3 to 'a'?
182 182 (enter ? for help) [Ynesfdaq?] n
183 183
184 184 @@ -5,1 +5,1 @@ 4
185 185 -5
186 186 +55
187 187 record change 3/3 to 'a'?
188 188 (enter ? for help) [Ynesfdaq?] y
189 189
190 190 EDITOR: HG: Splitting 1df0d5c5a3ab. Write commit message for the first split changeset.
191 191 EDITOR: a2
192 192 EDITOR:
193 193 EDITOR:
194 194 EDITOR: HG: Enter commit message. Lines beginning with 'HG:' are removed.
195 195 EDITOR: HG: Leave message empty to abort commit.
196 196 EDITOR: HG: --
197 197 EDITOR: HG: user: test
198 198 EDITOR: HG: branch 'default'
199 199 EDITOR: HG: changed a
200 200 created new head
201 201 diff --git a/a b/a
202 202 2 hunks, 2 lines changed
203 203 examine changes to 'a'?
204 204 (enter ? for help) [Ynesfdaq?] y
205 205
206 206 @@ -1,1 +1,1 @@
207 207 -1
208 208 +11
209 209 record change 1/2 to 'a'?
210 210 (enter ? for help) [Ynesfdaq?] n
211 211
212 212 @@ -3,1 +3,1 @@ 2
213 213 -3
214 214 +33
215 215 record change 2/2 to 'a'?
216 216 (enter ? for help) [Ynesfdaq?] y
217 217
218 218 EDITOR: HG: Splitting 1df0d5c5a3ab. So far it has been split into:
219 219 EDITOR: HG: - 2:e704349bd21b tip "split 1"
220 220 EDITOR: HG: Write commit message for the next split changeset.
221 221 EDITOR: a2
222 222 EDITOR:
223 223 EDITOR:
224 224 EDITOR: HG: Enter commit message. Lines beginning with 'HG:' are removed.
225 225 EDITOR: HG: Leave message empty to abort commit.
226 226 EDITOR: HG: --
227 227 EDITOR: HG: user: test
228 228 EDITOR: HG: branch 'default'
229 229 EDITOR: HG: changed a
230 230 diff --git a/a b/a
231 231 1 hunks, 1 lines changed
232 232 examine changes to 'a'?
233 233 (enter ? for help) [Ynesfdaq?] y
234 234
235 235 @@ -1,1 +1,1 @@
236 236 -1
237 237 +11
238 238 record this change to 'a'?
239 239 (enter ? for help) [Ynesfdaq?] y
240 240
241 241 EDITOR: HG: Splitting 1df0d5c5a3ab. So far it has been split into:
242 242 EDITOR: HG: - 2:e704349bd21b tip "split 1"
243 243 EDITOR: HG: - 3:a09ad58faae3 "split 2"
244 244 EDITOR: HG: Write commit message for the next split changeset.
245 245 EDITOR: a2
246 246 EDITOR:
247 247 EDITOR:
248 248 EDITOR: HG: Enter commit message. Lines beginning with 'HG:' are removed.
249 249 EDITOR: HG: Leave message empty to abort commit.
250 250 EDITOR: HG: --
251 251 EDITOR: HG: user: test
252 252 EDITOR: HG: branch 'default'
253 253 EDITOR: HG: changed a
254 254 saved backup bundle to $TESTTMP/a/.hg/strip-backup/1df0d5c5a3ab-8341b760-split.hg (obsstore-off !)
255 255
256 256 #if obsstore-off
257 257 $ hg bookmark
258 258 r1 0:a61bcde8c529
259 259 r2 3:00eebaf8d2e2
260 260 * r3 3:00eebaf8d2e2
261 261 $ hg glog -p
262 262 @ 3:00eebaf8d2e2 split 3 r2 r3
263 263 | diff --git a/a b/a
264 264 | --- a/a
265 265 | +++ b/a
266 266 | @@ -1,1 +1,1 @@
267 267 | -1
268 268 | +11
269 269 |
270 270 o 2:a09ad58faae3 split 2
271 271 | diff --git a/a b/a
272 272 | --- a/a
273 273 | +++ b/a
274 274 | @@ -3,1 +3,1 @@
275 275 | -3
276 276 | +33
277 277 |
278 278 o 1:e704349bd21b split 1
279 279 | diff --git a/a b/a
280 280 | --- a/a
281 281 | +++ b/a
282 282 | @@ -5,1 +5,1 @@
283 283 | -5
284 284 | +55
285 285 |
286 286 o 0:a61bcde8c529 a1 r1
287 287 diff --git a/a b/a
288 288 new file mode 100644
289 289 --- /dev/null
290 290 +++ b/a
291 291 @@ -0,0 +1,5 @@
292 292 +1
293 293 +2
294 294 +3
295 295 +4
296 296 +5
297 297
298 298 #else
299 299 $ hg bookmark
300 300 r1 0:a61bcde8c529
301 301 r2 4:00eebaf8d2e2
302 302 * r3 4:00eebaf8d2e2
303 303 $ hg glog
304 304 @ 4:00eebaf8d2e2 split 3 r2 r3
305 305 |
306 306 o 3:a09ad58faae3 split 2
307 307 |
308 308 o 2:e704349bd21b split 1
309 309 |
310 310 o 0:a61bcde8c529 a1 r1
311 311
312 312 #endif
313 313
314 314 Split a head while working parent is not that head
315 315
316 316 $ cp -R $TESTTMP/clean $TESTTMP/b
317 317 $ cd $TESTTMP/b
318 318
319 319 $ hg up 0 -q
320 320 $ hg bookmark r3
321 321
322 322 $ runsplit tip >/dev/null
323 323
324 324 #if obsstore-off
325 325 $ hg bookmark
326 326 r1 0:a61bcde8c529
327 327 r2 3:00eebaf8d2e2
328 328 * r3 0:a61bcde8c529
329 329 $ hg glog
330 330 o 3:00eebaf8d2e2 split 3 r2
331 331 |
332 332 o 2:a09ad58faae3 split 2
333 333 |
334 334 o 1:e704349bd21b split 1
335 335 |
336 336 @ 0:a61bcde8c529 a1 r1 r3
337 337
338 338 #else
339 339 $ hg bookmark
340 340 r1 0:a61bcde8c529
341 341 r2 4:00eebaf8d2e2
342 342 * r3 0:a61bcde8c529
343 343 $ hg glog
344 344 o 4:00eebaf8d2e2 split 3 r2
345 345 |
346 346 o 3:a09ad58faae3 split 2
347 347 |
348 348 o 2:e704349bd21b split 1
349 349 |
350 350 @ 0:a61bcde8c529 a1 r1 r3
351 351
352 352 #endif
353 353
354 354 Split a non-head
355 355
356 356 $ cp -R $TESTTMP/clean $TESTTMP/c
357 357 $ cd $TESTTMP/c
358 358 $ echo d > d
359 359 $ hg ci -m d1 -A d
360 360 $ hg bookmark -i d1
361 361 $ echo 2 >> d
362 362 $ hg ci -m d2
363 363 $ echo 3 >> d
364 364 $ hg ci -m d3
365 365 $ hg bookmark -i d3
366 366 $ hg up '.^' -q
367 367 $ hg bookmark d2
368 368 $ cp -R . ../d
369 369
370 370 $ runsplit -r 1 | grep rebasing
371 371 rebasing 2:b5c5ea414030 d1 "d1"
372 372 rebasing 3:f4a0a8d004cc d2 "d2"
373 373 rebasing 4:777940761eba d3 "d3"
374 374 #if obsstore-off
375 375 $ hg bookmark
376 376 d1 4:c4b449ef030e
377 377 * d2 5:c9dd00ab36a3
378 378 d3 6:19f476bc865c
379 379 r1 0:a61bcde8c529
380 380 r2 3:00eebaf8d2e2
381 381 $ hg glog -p
382 382 o 6:19f476bc865c d3 d3
383 383 | diff --git a/d b/d
384 384 | --- a/d
385 385 | +++ b/d
386 386 | @@ -2,0 +3,1 @@
387 387 | +3
388 388 |
389 389 @ 5:c9dd00ab36a3 d2 d2
390 390 | diff --git a/d b/d
391 391 | --- a/d
392 392 | +++ b/d
393 393 | @@ -1,0 +2,1 @@
394 394 | +2
395 395 |
396 396 o 4:c4b449ef030e d1 d1
397 397 | diff --git a/d b/d
398 398 | new file mode 100644
399 399 | --- /dev/null
400 400 | +++ b/d
401 401 | @@ -0,0 +1,1 @@
402 402 | +d
403 403 |
404 404 o 3:00eebaf8d2e2 split 3 r2
405 405 | diff --git a/a b/a
406 406 | --- a/a
407 407 | +++ b/a
408 408 | @@ -1,1 +1,1 @@
409 409 | -1
410 410 | +11
411 411 |
412 412 o 2:a09ad58faae3 split 2
413 413 | diff --git a/a b/a
414 414 | --- a/a
415 415 | +++ b/a
416 416 | @@ -3,1 +3,1 @@
417 417 | -3
418 418 | +33
419 419 |
420 420 o 1:e704349bd21b split 1
421 421 | diff --git a/a b/a
422 422 | --- a/a
423 423 | +++ b/a
424 424 | @@ -5,1 +5,1 @@
425 425 | -5
426 426 | +55
427 427 |
428 428 o 0:a61bcde8c529 a1 r1
429 429 diff --git a/a b/a
430 430 new file mode 100644
431 431 --- /dev/null
432 432 +++ b/a
433 433 @@ -0,0 +1,5 @@
434 434 +1
435 435 +2
436 436 +3
437 437 +4
438 438 +5
439 439
440 440 #else
441 441 $ hg bookmark
442 442 d1 8:c4b449ef030e
443 443 * d2 9:c9dd00ab36a3
444 444 d3 10:19f476bc865c
445 445 r1 0:a61bcde8c529
446 446 r2 7:00eebaf8d2e2
447 447 $ hg glog
448 448 o 10:19f476bc865c d3 d3
449 449 |
450 450 @ 9:c9dd00ab36a3 d2 d2
451 451 |
452 452 o 8:c4b449ef030e d1 d1
453 453 |
454 454 o 7:00eebaf8d2e2 split 3 r2
455 455 |
456 456 o 6:a09ad58faae3 split 2
457 457 |
458 458 o 5:e704349bd21b split 1
459 459 |
460 460 o 0:a61bcde8c529 a1 r1
461 461
462 462 #endif
463 463
464 464 Split a non-head without rebase
465 465
466 466 $ cd $TESTTMP/d
467 467 #if obsstore-off
468 468 $ runsplit -r 1 --no-rebase
469 469 abort: cannot split changeset with children
470 470 [255]
471 471 #else
472 472 $ runsplit -r 1 --no-rebase >/dev/null
473 473 3 new orphan changesets
474 474 $ hg bookmark
475 475 d1 2:b5c5ea414030
476 476 * d2 3:f4a0a8d004cc
477 477 d3 4:777940761eba
478 478 r1 0:a61bcde8c529
479 479 r2 7:00eebaf8d2e2
480 480
481 481 $ hg glog
482 482 o 7:00eebaf8d2e2 split 3 r2
483 483 |
484 484 o 6:a09ad58faae3 split 2
485 485 |
486 486 o 5:e704349bd21b split 1
487 487 |
488 488 | * 4:777940761eba d3 d3
489 489 | |
490 490 | @ 3:f4a0a8d004cc d2 d2
491 491 | |
492 492 | * 2:b5c5ea414030 d1 d1
493 493 | |
494 494 | x 1:1df0d5c5a3ab a2
495 495 |/
496 496 o 0:a61bcde8c529 a1 r1
497 497
498 498 #endif
499 499
500 500 Split a non-head with obsoleted descendants
501 501
502 502 #if obsstore-on
503 503 $ hg init $TESTTMP/e
504 504 $ cd $TESTTMP/e
505 505 $ hg debugdrawdag <<'EOS'
506 506 > H I J
507 507 > | | |
508 508 > F G1 G2 # amend: G1 -> G2
509 509 > | | / # prune: F
510 510 > C D E
511 511 > \|/
512 512 > B
513 513 > |
514 514 > A
515 515 > EOS
516 516 2 new orphan changesets
517 517 $ eval `hg tags -T '{tag}={node}\n'`
518 518 $ rm .hg/localtags
519 519 $ hg split $B --config experimental.evolution=createmarkers
520 520 abort: cannot split changeset with children
521 521 [255]
522 522 $ cat > $TESTTMP/messages <<EOF
523 523 > Split B
524 524 > EOF
525 525 $ cat <<EOF | hg split $B
526 526 > y
527 527 > y
528 528 > EOF
529 529 diff --git a/B b/B
530 530 new file mode 100644
531 531 examine changes to 'B'?
532 532 (enter ? for help) [Ynesfdaq?] y
533 533
534 534 @@ -0,0 +1,1 @@
535 535 +B
536 536 \ No newline at end of file
537 537 record this change to 'B'?
538 538 (enter ? for help) [Ynesfdaq?] y
539 539
540 540 EDITOR: HG: Splitting 112478962961. Write commit message for the first split changeset.
541 541 EDITOR: B
542 542 EDITOR:
543 543 EDITOR:
544 544 EDITOR: HG: Enter commit message. Lines beginning with 'HG:' are removed.
545 545 EDITOR: HG: Leave message empty to abort commit.
546 546 EDITOR: HG: --
547 547 EDITOR: HG: user: test
548 548 EDITOR: HG: branch 'default'
549 549 EDITOR: HG: added B
550 550 created new head
551 551 rebasing 2:26805aba1e60 "C"
552 552 rebasing 3:be0ef73c17ad "D"
553 553 rebasing 4:49cb92066bfd "E"
554 554 rebasing 7:97a6268cc7ef "G2"
555 555 rebasing 10:e2f1e425c0db "J"
556 556 $ hg glog -r 'sort(all(), topo)'
557 557 o 16:556c085f8b52 J
558 558 |
559 559 o 15:8761f6c9123f G2
560 560 |
561 561 o 14:a7aeffe59b65 E
562 562 |
563 563 | o 13:e1e914ede9ab D
564 564 |/
565 565 | o 12:01947e9b98aa C
566 566 |/
567 567 o 11:0947baa74d47 Split B
568 568 |
569 569 | * 9:88ede1d5ee13 I
570 570 | |
571 571 | x 6:af8cbf225b7b G1
572 572 | |
573 573 | x 3:be0ef73c17ad D
574 574 | |
575 575 | | * 8:74863e5b5074 H
576 576 | | |
577 577 | | x 5:ee481a2a1e69 F
578 578 | | |
579 579 | | x 2:26805aba1e60 C
580 580 | |/
581 581 | x 1:112478962961 B
582 582 |/
583 583 o 0:426bada5c675 A
584 584
585 585 #endif
586 586
587 587 Preserve secret phase in split
588 588
589 589 $ cp -R $TESTTMP/clean $TESTTMP/phases1
590 590 $ cd $TESTTMP/phases1
591 591 $ hg phase --secret -fr tip
592 592 $ hg log -T '{short(node)} {phase}\n'
593 593 1df0d5c5a3ab secret
594 594 a61bcde8c529 draft
595 595 $ runsplit tip >/dev/null
596 596 $ hg log -T '{short(node)} {phase}\n'
597 597 00eebaf8d2e2 secret
598 598 a09ad58faae3 secret
599 599 e704349bd21b secret
600 600 a61bcde8c529 draft
601 601
602 602 Do not move things to secret even if phases.new-commit=secret
603 603
604 604 $ cp -R $TESTTMP/clean $TESTTMP/phases2
605 605 $ cd $TESTTMP/phases2
606 606 $ cat >> .hg/hgrc <<EOF
607 607 > [phases]
608 608 > new-commit=secret
609 609 > EOF
610 610 $ hg log -T '{short(node)} {phase}\n'
611 611 1df0d5c5a3ab draft
612 612 a61bcde8c529 draft
613 613 $ runsplit tip >/dev/null
614 614 $ hg log -T '{short(node)} {phase}\n'
615 615 00eebaf8d2e2 draft
616 616 a09ad58faae3 draft
617 617 e704349bd21b draft
618 618 a61bcde8c529 draft
619 619
620 620 `hg split` with ignoreblanklines=1 does not infinite loop
621 621
622 622 $ mkdir $TESTTMP/f
623 623 $ hg init $TESTTMP/f/a
624 624 $ cd $TESTTMP/f/a
625 625 $ printf '1\n2\n3\n4\n5\n' > foo
626 626 $ cp foo bar
627 627 $ hg ci -qAm initial
628 628 $ printf '1\n\n2\n3\ntest\n4\n5\n' > bar
629 629 $ printf '1\n2\n3\ntest\n4\n5\n' > foo
630 630 $ hg ci -qm splitme
631 631 $ cat > $TESTTMP/messages <<EOF
632 632 > split 1
633 633 > --
634 634 > split 2
635 635 > EOF
636 636 $ printf 'f\nn\nf\n' | hg --config extensions.split= --config diff.ignoreblanklines=1 split
637 637 diff --git a/bar b/bar
638 638 2 hunks, 2 lines changed
639 639 examine changes to 'bar'?
640 640 (enter ? for help) [Ynesfdaq?] f
641 641
642 642 diff --git a/foo b/foo
643 643 1 hunks, 1 lines changed
644 644 examine changes to 'foo'?
645 645 (enter ? for help) [Ynesfdaq?] n
646 646
647 647 EDITOR: HG: Splitting dd3c45017cbf. Write commit message for the first split changeset.
648 648 EDITOR: splitme
649 649 EDITOR:
650 650 EDITOR:
651 651 EDITOR: HG: Enter commit message. Lines beginning with 'HG:' are removed.
652 652 EDITOR: HG: Leave message empty to abort commit.
653 653 EDITOR: HG: --
654 654 EDITOR: HG: user: test
655 655 EDITOR: HG: branch 'default'
656 656 EDITOR: HG: changed bar
657 657 created new head
658 658 diff --git a/foo b/foo
659 659 1 hunks, 1 lines changed
660 660 examine changes to 'foo'?
661 661 (enter ? for help) [Ynesfdaq?] f
662 662
663 663 EDITOR: HG: Splitting dd3c45017cbf. So far it has been split into:
664 664 EDITOR: HG: - 2:f205aea1c624 tip "split 1"
665 665 EDITOR: HG: Write commit message for the next split changeset.
666 666 EDITOR: splitme
667 667 EDITOR:
668 668 EDITOR:
669 669 EDITOR: HG: Enter commit message. Lines beginning with 'HG:' are removed.
670 670 EDITOR: HG: Leave message empty to abort commit.
671 671 EDITOR: HG: --
672 672 EDITOR: HG: user: test
673 673 EDITOR: HG: branch 'default'
674 674 EDITOR: HG: changed foo
675 675 saved backup bundle to $TESTTMP/f/a/.hg/strip-backup/dd3c45017cbf-463441b5-split.hg (obsstore-off !)
676 676
677 677 Let's try that again, with a slightly different set of patches, to ensure that
678 678 the ignoreblanklines thing isn't somehow position dependent.
679 679
680 680 $ hg init $TESTTMP/f/b
681 681 $ cd $TESTTMP/f/b
682 682 $ printf '1\n2\n3\n4\n5\n' > foo
683 683 $ cp foo bar
684 684 $ hg ci -qAm initial
685 685 $ printf '1\n2\n3\ntest\n4\n5\n' > bar
686 686 $ printf '1\n2\n3\ntest\n4\n\n5\n' > foo
687 687 $ hg ci -qm splitme
688 688 $ cat > $TESTTMP/messages <<EOF
689 689 > split 1
690 690 > --
691 691 > split 2
692 692 > EOF
693 693 $ printf 'f\nn\nf\n' | hg --config extensions.split= --config diff.ignoreblanklines=1 split
694 694 diff --git a/bar b/bar
695 695 1 hunks, 1 lines changed
696 696 examine changes to 'bar'?
697 697 (enter ? for help) [Ynesfdaq?] f
698 698
699 699 diff --git a/foo b/foo
700 700 2 hunks, 2 lines changed
701 701 examine changes to 'foo'?
702 702 (enter ? for help) [Ynesfdaq?] n
703 703
704 704 EDITOR: HG: Splitting 904c80b40a4a. Write commit message for the first split changeset.
705 705 EDITOR: splitme
706 706 EDITOR:
707 707 EDITOR:
708 708 EDITOR: HG: Enter commit message. Lines beginning with 'HG:' are removed.
709 709 EDITOR: HG: Leave message empty to abort commit.
710 710 EDITOR: HG: --
711 711 EDITOR: HG: user: test
712 712 EDITOR: HG: branch 'default'
713 713 EDITOR: HG: changed bar
714 714 created new head
715 715 diff --git a/foo b/foo
716 716 2 hunks, 2 lines changed
717 717 examine changes to 'foo'?
718 718 (enter ? for help) [Ynesfdaq?] f
719 719
720 720 EDITOR: HG: Splitting 904c80b40a4a. So far it has been split into:
721 721 EDITOR: HG: - 2:ffecf40fa954 tip "split 1"
722 722 EDITOR: HG: Write commit message for the next split changeset.
723 723 EDITOR: splitme
724 724 EDITOR:
725 725 EDITOR:
726 726 EDITOR: HG: Enter commit message. Lines beginning with 'HG:' are removed.
727 727 EDITOR: HG: Leave message empty to abort commit.
728 728 EDITOR: HG: --
729 729 EDITOR: HG: user: test
730 730 EDITOR: HG: branch 'default'
731 731 EDITOR: HG: changed foo
732 732 saved backup bundle to $TESTTMP/f/b/.hg/strip-backup/904c80b40a4a-47fb907f-split.hg (obsstore-off !)
733 733
734 734
735 735 Testing the case in split when commiting flag-only file changes (issue5864)
736 736 ---------------------------------------------------------------------------
737 737 $ hg init $TESTTMP/issue5864
738 738 $ cd $TESTTMP/issue5864
739 739 $ echo foo > foo
740 740 $ hg add foo
741 741 $ hg ci -m "initial"
742 742 $ hg import -q --bypass -m "make executable" - <<EOF
743 743 > diff --git a/foo b/foo
744 744 > old mode 100644
745 745 > new mode 100755
746 746 > EOF
747 747 $ hg up -q
748 748
749 749 $ hg glog
750 750 @ 1:3a2125f0f4cb make executable
751 751 |
752 752 o 0:51f273a58d82 initial
753 753
754 754
755 755 #if no-windows
756 756 $ cat > $TESTTMP/messages <<EOF
757 757 > split 1
758 758 > EOF
759 759 $ printf 'y\n' | hg split
760 760 diff --git a/foo b/foo
761 761 old mode 100644
762 762 new mode 100755
763 763 examine changes to 'foo'?
764 764 (enter ? for help) [Ynesfdaq?] y
765 765
766 766 EDITOR: HG: Splitting 3a2125f0f4cb. Write commit message for the first split changeset.
767 767 EDITOR: make executable
768 768 EDITOR:
769 769 EDITOR:
770 770 EDITOR: HG: Enter commit message. Lines beginning with 'HG:' are removed.
771 771 EDITOR: HG: Leave message empty to abort commit.
772 772 EDITOR: HG: --
773 773 EDITOR: HG: user: test
774 774 EDITOR: HG: branch 'default'
775 775 EDITOR: HG: changed foo
776 776 created new head
777 777 saved backup bundle to $TESTTMP/issue5864/.hg/strip-backup/3a2125f0f4cb-629e4432-split.hg (obsstore-off !)
778 778
779 779 $ hg log -G -T "{node|short} {desc}\n"
780 780 @ b154670c87da split 1
781 781 |
782 782 o 51f273a58d82 initial
783 783
784 784 #else
785 785
786 786 TODO: Fix this on Windows. See issue 2020 and 5883
787 787
788 788 $ printf 'y\ny\ny\n' | hg split
789 789 abort: cannot split an empty revision
790 790 [255]
791 791 #endif
792 792
793 793 Test that splitting moves works properly (issue5723)
794 794 ----------------------------------------------------
795 795
796 796 $ hg init $TESTTMP/issue5723-mv
797 797 $ cd $TESTTMP/issue5723-mv
798 798 $ printf '1\n2\n' > file
799 799 $ hg ci -qAm initial
800 800 $ hg mv file file2
801 801 $ printf 'a\nb\n1\n2\n3\n4\n' > file2
802 802 $ cat > $TESTTMP/messages <<EOF
803 803 > split1, keeping only the numbered lines
804 804 > --
805 805 > split2, keeping the lettered lines
806 806 > EOF
807 807 $ hg ci -m 'move and modify'
808 808 $ printf 'y\nn\na\na\n' | hg split
809 809 diff --git a/file b/file2
810 810 rename from file
811 811 rename to file2
812 812 2 hunks, 4 lines changed
813 813 examine changes to 'file' and 'file2'?
814 814 (enter ? for help) [Ynesfdaq?] y
815 815
816 816 @@ -0,0 +1,2 @@
817 817 +a
818 818 +b
819 819 record change 1/2 to 'file2'?
820 820 (enter ? for help) [Ynesfdaq?] n
821 821
822 822 @@ -2,0 +5,2 @@ 2
823 823 +3
824 824 +4
825 825 record change 2/2 to 'file2'?
826 826 (enter ? for help) [Ynesfdaq?] a
827 827
828 828 EDITOR: HG: Splitting 8c42fa635116. Write commit message for the first split changeset.
829 829 EDITOR: move and modify
830 830 EDITOR:
831 831 EDITOR:
832 832 EDITOR: HG: Enter commit message. Lines beginning with 'HG:' are removed.
833 833 EDITOR: HG: Leave message empty to abort commit.
834 834 EDITOR: HG: --
835 835 EDITOR: HG: user: test
836 836 EDITOR: HG: branch 'default'
837 837 EDITOR: HG: added file2
838 838 EDITOR: HG: removed file
839 839 created new head
840 840 diff --git a/file2 b/file2
841 841 1 hunks, 2 lines changed
842 842 examine changes to 'file2'?
843 843 (enter ? for help) [Ynesfdaq?] a
844 844
845 845 EDITOR: HG: Splitting 8c42fa635116. So far it has been split into:
846 846 EDITOR: HG: - 2:478be2a70c27 tip "split1, keeping only the numbered lines"
847 847 EDITOR: HG: Write commit message for the next split changeset.
848 848 EDITOR: move and modify
849 849 EDITOR:
850 850 EDITOR:
851 851 EDITOR: HG: Enter commit message. Lines beginning with 'HG:' are removed.
852 852 EDITOR: HG: Leave message empty to abort commit.
853 853 EDITOR: HG: --
854 854 EDITOR: HG: user: test
855 855 EDITOR: HG: branch 'default'
856 856 EDITOR: HG: changed file2
857 857 saved backup bundle to $TESTTMP/issue5723-mv/.hg/strip-backup/8c42fa635116-a38044d4-split.hg (obsstore-off !)
858 858 $ hg log -T '{desc}: {files%"{file} "}\n'
859 859 split2, keeping the lettered lines: file2
860 860 split1, keeping only the numbered lines: file file2
861 861 initial: file
862 862 $ cat file2
863 863 a
864 864 b
865 865 1
866 866 2
867 867 3
868 868 4
869 869 $ hg cat -r ".^" file2
870 870 1
871 871 2
872 872 3
873 873 4
874 874 $ hg cat -r . file2
875 875 a
876 876 b
877 877 1
878 878 2
879 879 3
880 880 4
881 881
882 882
883 883 Test that splitting copies works properly (issue5723)
884 884 ----------------------------------------------------
885 885
886 886 $ hg init $TESTTMP/issue5723-cp
887 887 $ cd $TESTTMP/issue5723-cp
888 888 $ printf '1\n2\n' > file
889 889 $ hg ci -qAm initial
890 890 $ hg cp file file2
891 891 $ printf 'a\nb\n1\n2\n3\n4\n' > file2
892 892 Also modify 'file' to prove that the changes aren't being pulled in
893 893 accidentally.
894 894 $ printf 'this is the new contents of "file"' > file
895 895 $ cat > $TESTTMP/messages <<EOF
896 896 > split1, keeping "file" and only the numbered lines in file2
897 897 > --
898 898 > split2, keeping the lettered lines in file2
899 899 > EOF
900 900 $ hg ci -m 'copy file->file2, modify both'
901 901 $ printf 'f\ny\nn\na\na\n' | hg split
902 902 diff --git a/file b/file
903 903 1 hunks, 2 lines changed
904 904 examine changes to 'file'?
905 905 (enter ? for help) [Ynesfdaq?] f
906 906
907 907 diff --git a/file b/file2
908 908 copy from file
909 909 copy to file2
910 910 2 hunks, 4 lines changed
911 911 examine changes to 'file' and 'file2'?
912 912 (enter ? for help) [Ynesfdaq?] y
913 913
914 914 @@ -0,0 +1,2 @@
915 915 +a
916 916 +b
917 917 record change 2/3 to 'file2'?
918 918 (enter ? for help) [Ynesfdaq?] n
919 919
920 920 @@ -2,0 +5,2 @@ 2
921 921 +3
922 922 +4
923 923 record change 3/3 to 'file2'?
924 924 (enter ? for help) [Ynesfdaq?] a
925 925
926 926 EDITOR: HG: Splitting 41c861dfa61e. Write commit message for the first split changeset.
927 927 EDITOR: copy file->file2, modify both
928 928 EDITOR:
929 929 EDITOR:
930 930 EDITOR: HG: Enter commit message. Lines beginning with 'HG:' are removed.
931 931 EDITOR: HG: Leave message empty to abort commit.
932 932 EDITOR: HG: --
933 933 EDITOR: HG: user: test
934 934 EDITOR: HG: branch 'default'
935 935 EDITOR: HG: added file2
936 936 EDITOR: HG: changed file
937 937 created new head
938 938 diff --git a/file2 b/file2
939 939 1 hunks, 2 lines changed
940 940 examine changes to 'file2'?
941 941 (enter ? for help) [Ynesfdaq?] a
942 942
943 943 EDITOR: HG: Splitting 41c861dfa61e. So far it has been split into:
944 944 EDITOR: HG: - 2:4b19e06610eb tip "split1, keeping "file" and only the numbered lines in file2"
945 945 EDITOR: HG: Write commit message for the next split changeset.
946 946 EDITOR: copy file->file2, modify both
947 947 EDITOR:
948 948 EDITOR:
949 949 EDITOR: HG: Enter commit message. Lines beginning with 'HG:' are removed.
950 950 EDITOR: HG: Leave message empty to abort commit.
951 951 EDITOR: HG: --
952 952 EDITOR: HG: user: test
953 953 EDITOR: HG: branch 'default'
954 954 EDITOR: HG: changed file2
955 955 saved backup bundle to $TESTTMP/issue5723-cp/.hg/strip-backup/41c861dfa61e-467e8d3c-split.hg (obsstore-off !)
956 956 $ hg log -T '{desc}: {files%"{file} "}\n'
957 957 split2, keeping the lettered lines in file2: file2
958 958 split1, keeping "file" and only the numbered lines in file2: file file2
959 959 initial: file
960 960 $ cat file2
961 961 a
962 962 b
963 963 1
964 964 2
965 965 3
966 966 4
967 967 $ hg cat -r ".^" file2
968 968 1
969 969 2
970 970 3
971 971 4
972 972 $ hg cat -r . file2
973 973 a
974 974 b
975 975 1
976 976 2
977 977 3
978 978 4
@@ -1,1461 +1,1461 b''
1 1 $ echo "[extensions]" >> $HGRCPATH
2 2 $ echo "strip=" >> $HGRCPATH
3 3 $ echo "drawdag=$TESTDIR/drawdag.py" >> $HGRCPATH
4 4
5 5 $ restore() {
6 6 > hg unbundle -q .hg/strip-backup/*
7 7 > rm .hg/strip-backup/*
8 8 > }
9 9 $ teststrip() {
10 10 > hg up -C $1
11 11 > echo % before update $1, strip $2
12 12 > hg log -G -T '{rev}:{node}'
13 13 > hg --traceback strip $2
14 14 > echo % after update $1, strip $2
15 15 > hg log -G -T '{rev}:{node}'
16 16 > restore
17 17 > }
18 18
19 19 $ hg init test
20 20 $ cd test
21 21
22 22 $ echo foo > bar
23 23 $ hg ci -Ama
24 24 adding bar
25 25
26 26 $ echo more >> bar
27 27 $ hg ci -Amb
28 28
29 29 $ echo blah >> bar
30 30 $ hg ci -Amc
31 31
32 32 $ hg up 1
33 33 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
34 34 $ echo blah >> bar
35 35 $ hg ci -Amd
36 36 created new head
37 37
38 38 $ echo final >> bar
39 39 $ hg ci -Ame
40 40
41 41 $ hg log
42 42 changeset: 4:443431ffac4f
43 43 tag: tip
44 44 user: test
45 45 date: Thu Jan 01 00:00:00 1970 +0000
46 46 summary: e
47 47
48 48 changeset: 3:65bd5f99a4a3
49 49 parent: 1:ef3a871183d7
50 50 user: test
51 51 date: Thu Jan 01 00:00:00 1970 +0000
52 52 summary: d
53 53
54 54 changeset: 2:264128213d29
55 55 user: test
56 56 date: Thu Jan 01 00:00:00 1970 +0000
57 57 summary: c
58 58
59 59 changeset: 1:ef3a871183d7
60 60 user: test
61 61 date: Thu Jan 01 00:00:00 1970 +0000
62 62 summary: b
63 63
64 64 changeset: 0:9ab35a2d17cb
65 65 user: test
66 66 date: Thu Jan 01 00:00:00 1970 +0000
67 67 summary: a
68 68
69 69
70 70 $ teststrip 4 4
71 71 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
72 72 % before update 4, strip 4
73 73 @ 4:443431ffac4f5b5a19b0b6c298a21b7ba736bcce
74 74 |
75 75 o 3:65bd5f99a4a376cdea23a1153f07856b0d881d64
76 76 |
77 77 | o 2:264128213d290d868c54642d13aeaa3675551a78
78 78 |/
79 79 o 1:ef3a871183d7199c541cc140218298bbfcc6c28a
80 80 |
81 81 o 0:9ab35a2d17cb64271241ea881efcc19dd953215b
82 82
83 83 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 84 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
85 85 % after update 4, strip 4
86 86 @ 3:65bd5f99a4a376cdea23a1153f07856b0d881d64
87 87 |
88 88 | o 2:264128213d290d868c54642d13aeaa3675551a78
89 89 |/
90 90 o 1:ef3a871183d7199c541cc140218298bbfcc6c28a
91 91 |
92 92 o 0:9ab35a2d17cb64271241ea881efcc19dd953215b
93 93
94 94 $ teststrip 4 3
95 95 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
96 96 % before update 4, strip 3
97 97 @ 4:443431ffac4f5b5a19b0b6c298a21b7ba736bcce
98 98 |
99 99 o 3:65bd5f99a4a376cdea23a1153f07856b0d881d64
100 100 |
101 101 | o 2:264128213d290d868c54642d13aeaa3675551a78
102 102 |/
103 103 o 1:ef3a871183d7199c541cc140218298bbfcc6c28a
104 104 |
105 105 o 0:9ab35a2d17cb64271241ea881efcc19dd953215b
106 106
107 107 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
108 108 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
109 109 % after update 4, strip 3
110 110 o 2:264128213d290d868c54642d13aeaa3675551a78
111 111 |
112 112 @ 1:ef3a871183d7199c541cc140218298bbfcc6c28a
113 113 |
114 114 o 0:9ab35a2d17cb64271241ea881efcc19dd953215b
115 115
116 116 $ teststrip 1 4
117 117 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 118 % before update 1, strip 4
119 119 o 4:443431ffac4f5b5a19b0b6c298a21b7ba736bcce
120 120 |
121 121 o 3:65bd5f99a4a376cdea23a1153f07856b0d881d64
122 122 |
123 123 | o 2:264128213d290d868c54642d13aeaa3675551a78
124 124 |/
125 125 @ 1:ef3a871183d7199c541cc140218298bbfcc6c28a
126 126 |
127 127 o 0:9ab35a2d17cb64271241ea881efcc19dd953215b
128 128
129 129 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
130 130 % after update 1, strip 4
131 131 o 3:65bd5f99a4a376cdea23a1153f07856b0d881d64
132 132 |
133 133 | o 2:264128213d290d868c54642d13aeaa3675551a78
134 134 |/
135 135 @ 1:ef3a871183d7199c541cc140218298bbfcc6c28a
136 136 |
137 137 o 0:9ab35a2d17cb64271241ea881efcc19dd953215b
138 138
139 139 $ teststrip 4 2
140 140 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 141 % before update 4, strip 2
142 142 @ 4:443431ffac4f5b5a19b0b6c298a21b7ba736bcce
143 143 |
144 144 o 3:65bd5f99a4a376cdea23a1153f07856b0d881d64
145 145 |
146 146 | o 2:264128213d290d868c54642d13aeaa3675551a78
147 147 |/
148 148 o 1:ef3a871183d7199c541cc140218298bbfcc6c28a
149 149 |
150 150 o 0:9ab35a2d17cb64271241ea881efcc19dd953215b
151 151
152 152 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
153 153 % after update 4, strip 2
154 154 @ 3:443431ffac4f5b5a19b0b6c298a21b7ba736bcce
155 155 |
156 156 o 2:65bd5f99a4a376cdea23a1153f07856b0d881d64
157 157 |
158 158 o 1:ef3a871183d7199c541cc140218298bbfcc6c28a
159 159 |
160 160 o 0:9ab35a2d17cb64271241ea881efcc19dd953215b
161 161
162 162 $ teststrip 4 1
163 163 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
164 164 % before update 4, strip 1
165 165 @ 4:264128213d290d868c54642d13aeaa3675551a78
166 166 |
167 167 | o 3:443431ffac4f5b5a19b0b6c298a21b7ba736bcce
168 168 | |
169 169 | o 2:65bd5f99a4a376cdea23a1153f07856b0d881d64
170 170 |/
171 171 o 1:ef3a871183d7199c541cc140218298bbfcc6c28a
172 172 |
173 173 o 0:9ab35a2d17cb64271241ea881efcc19dd953215b
174 174
175 175 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
176 176 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
177 177 % after update 4, strip 1
178 178 @ 0:9ab35a2d17cb64271241ea881efcc19dd953215b
179 179
180 180 $ teststrip null 4
181 181 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
182 182 % before update null, strip 4
183 183 o 4:264128213d290d868c54642d13aeaa3675551a78
184 184 |
185 185 | o 3:443431ffac4f5b5a19b0b6c298a21b7ba736bcce
186 186 | |
187 187 | o 2:65bd5f99a4a376cdea23a1153f07856b0d881d64
188 188 |/
189 189 o 1:ef3a871183d7199c541cc140218298bbfcc6c28a
190 190 |
191 191 o 0:9ab35a2d17cb64271241ea881efcc19dd953215b
192 192
193 193 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
194 194 % after update null, strip 4
195 195 o 3:443431ffac4f5b5a19b0b6c298a21b7ba736bcce
196 196 |
197 197 o 2:65bd5f99a4a376cdea23a1153f07856b0d881d64
198 198 |
199 199 o 1:ef3a871183d7199c541cc140218298bbfcc6c28a
200 200 |
201 201 o 0:9ab35a2d17cb64271241ea881efcc19dd953215b
202 202
203 203
204 204 $ hg log
205 205 changeset: 4:264128213d29
206 206 tag: tip
207 207 parent: 1:ef3a871183d7
208 208 user: test
209 209 date: Thu Jan 01 00:00:00 1970 +0000
210 210 summary: c
211 211
212 212 changeset: 3:443431ffac4f
213 213 user: test
214 214 date: Thu Jan 01 00:00:00 1970 +0000
215 215 summary: e
216 216
217 217 changeset: 2:65bd5f99a4a3
218 218 user: test
219 219 date: Thu Jan 01 00:00:00 1970 +0000
220 220 summary: d
221 221
222 222 changeset: 1:ef3a871183d7
223 223 user: test
224 224 date: Thu Jan 01 00:00:00 1970 +0000
225 225 summary: b
226 226
227 227 changeset: 0:9ab35a2d17cb
228 228 user: test
229 229 date: Thu Jan 01 00:00:00 1970 +0000
230 230 summary: a
231 231
232 232 $ hg up -C 4
233 233 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
234 234 $ hg parents
235 235 changeset: 4:264128213d29
236 236 tag: tip
237 237 parent: 1:ef3a871183d7
238 238 user: test
239 239 date: Thu Jan 01 00:00:00 1970 +0000
240 240 summary: c
241 241
242 242
243 243 $ hg --traceback strip 4
244 244 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
245 245 saved backup bundle to $TESTTMP/test/.hg/strip-backup/264128213d29-0b39d6bf-backup.hg
246 246 $ hg parents
247 247 changeset: 1:ef3a871183d7
248 248 user: test
249 249 date: Thu Jan 01 00:00:00 1970 +0000
250 250 summary: b
251 251
252 252 $ hg debugbundle .hg/strip-backup/*
253 253 Stream params: {Compression: BZ}
254 254 changegroup -- {nbchanges: 1, version: 02} (mandatory: True)
255 255 264128213d290d868c54642d13aeaa3675551a78
256 256 cache:rev-branch-cache -- {} (mandatory: False)
257 257 phase-heads -- {} (mandatory: True)
258 258 264128213d290d868c54642d13aeaa3675551a78 draft
259 259 $ hg unbundle .hg/strip-backup/*
260 260 adding changesets
261 261 adding manifests
262 262 adding file changes
263 263 added 1 changesets with 0 changes to 1 files (+1 heads)
264 264 new changesets 264128213d29 (1 drafts)
265 265 (run 'hg heads' to see heads, 'hg merge' to merge)
266 266 $ rm .hg/strip-backup/*
267 267 $ hg log --graph
268 268 o changeset: 4:264128213d29
269 269 | tag: tip
270 270 | parent: 1:ef3a871183d7
271 271 | user: test
272 272 | date: Thu Jan 01 00:00:00 1970 +0000
273 273 | summary: c
274 274 |
275 275 | o changeset: 3:443431ffac4f
276 276 | | user: test
277 277 | | date: Thu Jan 01 00:00:00 1970 +0000
278 278 | | summary: e
279 279 | |
280 280 | o changeset: 2:65bd5f99a4a3
281 281 |/ user: test
282 282 | date: Thu Jan 01 00:00:00 1970 +0000
283 283 | summary: d
284 284 |
285 285 @ changeset: 1:ef3a871183d7
286 286 | user: test
287 287 | date: Thu Jan 01 00:00:00 1970 +0000
288 288 | summary: b
289 289 |
290 290 o changeset: 0:9ab35a2d17cb
291 291 user: test
292 292 date: Thu Jan 01 00:00:00 1970 +0000
293 293 summary: a
294 294
295 295 $ hg up -C 2
296 296 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
297 297 $ hg merge 4
298 298 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
299 299 (branch merge, don't forget to commit)
300 300
301 301 before strip of merge parent
302 302
303 303 $ hg parents
304 304 changeset: 2:65bd5f99a4a3
305 305 user: test
306 306 date: Thu Jan 01 00:00:00 1970 +0000
307 307 summary: d
308 308
309 309 changeset: 4:264128213d29
310 310 tag: tip
311 311 parent: 1:ef3a871183d7
312 312 user: test
313 313 date: Thu Jan 01 00:00:00 1970 +0000
314 314 summary: c
315 315
316 316 ##strip not allowed with merge in progress
317 317 $ hg strip 4
318 318 abort: outstanding uncommitted merge
319 319 (use 'hg commit' or 'hg merge --abort')
320 [255]
320 [20]
321 321 ##strip allowed --force with merge in progress
322 322 $ hg strip 4 --force
323 323 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
324 324 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
325 325
326 326 after strip of merge parent
327 327
328 328 $ hg parents
329 329 changeset: 1:ef3a871183d7
330 330 user: test
331 331 date: Thu Jan 01 00:00:00 1970 +0000
332 332 summary: b
333 333
334 334 $ restore
335 335
336 336 $ hg up
337 337 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
338 338 updated to "264128213d29: c"
339 339 1 other heads for branch "default"
340 340 $ hg log -G
341 341 @ changeset: 4:264128213d29
342 342 | tag: tip
343 343 | parent: 1:ef3a871183d7
344 344 | user: test
345 345 | date: Thu Jan 01 00:00:00 1970 +0000
346 346 | summary: c
347 347 |
348 348 | o changeset: 3:443431ffac4f
349 349 | | user: test
350 350 | | date: Thu Jan 01 00:00:00 1970 +0000
351 351 | | summary: e
352 352 | |
353 353 | o changeset: 2:65bd5f99a4a3
354 354 |/ user: test
355 355 | date: Thu Jan 01 00:00:00 1970 +0000
356 356 | summary: d
357 357 |
358 358 o changeset: 1:ef3a871183d7
359 359 | user: test
360 360 | date: Thu Jan 01 00:00:00 1970 +0000
361 361 | summary: b
362 362 |
363 363 o changeset: 0:9ab35a2d17cb
364 364 user: test
365 365 date: Thu Jan 01 00:00:00 1970 +0000
366 366 summary: a
367 367
368 368
369 369 2 is parent of 3, only one strip should happen
370 370
371 371 $ hg strip "roots(2)" 3
372 372 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
373 373 $ hg log -G
374 374 @ changeset: 2:264128213d29
375 375 | tag: tip
376 376 | user: test
377 377 | date: Thu Jan 01 00:00:00 1970 +0000
378 378 | summary: c
379 379 |
380 380 o changeset: 1:ef3a871183d7
381 381 | user: test
382 382 | date: Thu Jan 01 00:00:00 1970 +0000
383 383 | summary: b
384 384 |
385 385 o changeset: 0:9ab35a2d17cb
386 386 user: test
387 387 date: Thu Jan 01 00:00:00 1970 +0000
388 388 summary: a
389 389
390 390 $ restore
391 391 $ hg log -G
392 392 o changeset: 4:443431ffac4f
393 393 | tag: tip
394 394 | user: test
395 395 | date: Thu Jan 01 00:00:00 1970 +0000
396 396 | summary: e
397 397 |
398 398 o changeset: 3:65bd5f99a4a3
399 399 | parent: 1:ef3a871183d7
400 400 | user: test
401 401 | date: Thu Jan 01 00:00:00 1970 +0000
402 402 | summary: d
403 403 |
404 404 | @ changeset: 2:264128213d29
405 405 |/ user: test
406 406 | date: Thu Jan 01 00:00:00 1970 +0000
407 407 | summary: c
408 408 |
409 409 o changeset: 1:ef3a871183d7
410 410 | user: test
411 411 | date: Thu Jan 01 00:00:00 1970 +0000
412 412 | summary: b
413 413 |
414 414 o changeset: 0:9ab35a2d17cb
415 415 user: test
416 416 date: Thu Jan 01 00:00:00 1970 +0000
417 417 summary: a
418 418
419 419 Failed hook while applying "saveheads" bundle.
420 420
421 421 $ hg strip 2 --config hooks.pretxnchangegroup.bad=false
422 422 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
423 423 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
424 424 transaction abort!
425 425 rollback completed
426 426 strip failed, backup bundle stored in '$TESTTMP/test/.hg/strip-backup/*-backup.hg' (glob)
427 427 strip failed, unrecovered changes stored in '$TESTTMP/test/.hg/strip-backup/*-temp.hg' (glob)
428 428 (fix the problem, then recover the changesets with "hg unbundle '$TESTTMP/test/.hg/strip-backup/*-temp.hg'") (glob)
429 429 abort: pretxnchangegroup.bad hook exited with status 1
430 430 [255]
431 431 $ restore
432 432 $ hg log -G
433 433 o changeset: 4:443431ffac4f
434 434 | tag: tip
435 435 | user: test
436 436 | date: Thu Jan 01 00:00:00 1970 +0000
437 437 | summary: e
438 438 |
439 439 o changeset: 3:65bd5f99a4a3
440 440 | parent: 1:ef3a871183d7
441 441 | user: test
442 442 | date: Thu Jan 01 00:00:00 1970 +0000
443 443 | summary: d
444 444 |
445 445 | o changeset: 2:264128213d29
446 446 |/ user: test
447 447 | date: Thu Jan 01 00:00:00 1970 +0000
448 448 | summary: c
449 449 |
450 450 @ changeset: 1:ef3a871183d7
451 451 | user: test
452 452 | date: Thu Jan 01 00:00:00 1970 +0000
453 453 | summary: b
454 454 |
455 455 o changeset: 0:9ab35a2d17cb
456 456 user: test
457 457 date: Thu Jan 01 00:00:00 1970 +0000
458 458 summary: a
459 459
460 460
461 461 2 different branches: 2 strips
462 462
463 463 $ hg strip 2 4
464 464 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
465 465 $ hg log -G
466 466 o changeset: 2:65bd5f99a4a3
467 467 | tag: tip
468 468 | user: test
469 469 | date: Thu Jan 01 00:00:00 1970 +0000
470 470 | summary: d
471 471 |
472 472 @ changeset: 1:ef3a871183d7
473 473 | user: test
474 474 | date: Thu Jan 01 00:00:00 1970 +0000
475 475 | summary: b
476 476 |
477 477 o changeset: 0:9ab35a2d17cb
478 478 user: test
479 479 date: Thu Jan 01 00:00:00 1970 +0000
480 480 summary: a
481 481
482 482 $ restore
483 483
484 484 2 different branches and a common ancestor: 1 strip
485 485
486 486 $ hg strip 1 "2|4"
487 487 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
488 488 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
489 489 $ restore
490 490
491 491 verify fncache is kept up-to-date
492 492
493 493 $ touch a
494 494 $ hg ci -qAm a
495 495 #if repofncache
496 496 $ cat .hg/store/fncache | sort
497 497 data/a.i
498 498 data/bar.i
499 499 #endif
500 500
501 501 $ hg strip tip
502 502 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
503 503 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
504 504 #if repofncache
505 505 $ cat .hg/store/fncache
506 506 data/bar.i
507 507 #endif
508 508
509 509 stripping an empty revset
510 510
511 511 $ hg strip "1 and not 1"
512 512 abort: empty revision set
513 513 [255]
514 514
515 515 remove branchy history for qimport tests
516 516
517 517 $ hg strip 3
518 518 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
519 519
520 520
521 521 strip of applied mq should cleanup status file
522 522
523 523 $ echo "mq=" >> $HGRCPATH
524 524 $ hg up -C 3
525 525 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
526 526 $ echo fooagain >> bar
527 527 $ hg ci -mf
528 528 $ hg qimport -r tip:2
529 529
530 530 applied patches before strip
531 531
532 532 $ hg qapplied
533 533 d
534 534 e
535 535 f
536 536
537 537 stripping revision in queue
538 538
539 539 $ hg strip 3
540 540 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
541 541 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
542 542
543 543 applied patches after stripping rev in queue
544 544
545 545 $ hg qapplied
546 546 d
547 547
548 548 stripping ancestor of queue
549 549
550 550 $ hg strip 1
551 551 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
552 552 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
553 553
554 554 applied patches after stripping ancestor of queue
555 555
556 556 $ hg qapplied
557 557
558 558 Verify strip protects against stripping wc parent when there are uncommitted mods
559 559
560 560 $ echo b > b
561 561 $ echo bb > bar
562 562 $ hg add b
563 563 $ hg ci -m 'b'
564 564 $ hg log --graph
565 565 @ changeset: 1:76dcf9fab855
566 566 | tag: tip
567 567 | user: test
568 568 | date: Thu Jan 01 00:00:00 1970 +0000
569 569 | summary: b
570 570 |
571 571 o changeset: 0:9ab35a2d17cb
572 572 user: test
573 573 date: Thu Jan 01 00:00:00 1970 +0000
574 574 summary: a
575 575
576 576 $ hg up 0
577 577 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
578 578 $ echo c > bar
579 579 $ hg up -t false
580 580 merging bar
581 581 merging bar failed!
582 582 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
583 583 use 'hg resolve' to retry unresolved file merges
584 584 [1]
585 585 $ hg sum
586 586 parent: 1:76dcf9fab855 tip
587 587 b
588 588 branch: default
589 589 commit: 1 modified, 1 unknown, 1 unresolved
590 590 update: (current)
591 591 phases: 2 draft
592 592 mq: 3 unapplied
593 593
594 594 $ hg log --graph
595 595 @ changeset: 1:76dcf9fab855
596 596 | tag: tip
597 597 | user: test
598 598 | date: Thu Jan 01 00:00:00 1970 +0000
599 599 | summary: b
600 600 |
601 601 % changeset: 0:9ab35a2d17cb
602 602 user: test
603 603 date: Thu Jan 01 00:00:00 1970 +0000
604 604 summary: a
605 605
606 606 $ echo c > b
607 607 $ hg strip tip
608 608 abort: uncommitted changes
609 [255]
609 [20]
610 610 $ hg strip tip --keep
611 611 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
612 612 $ hg log --graph
613 613 @ changeset: 0:9ab35a2d17cb
614 614 tag: tip
615 615 user: test
616 616 date: Thu Jan 01 00:00:00 1970 +0000
617 617 summary: a
618 618
619 619 $ hg status
620 620 M bar
621 621 ? b
622 622 ? bar.orig
623 623
624 624 $ rm bar.orig
625 625 $ hg sum
626 626 parent: 0:9ab35a2d17cb tip
627 627 a
628 628 branch: default
629 629 commit: 1 modified, 1 unknown
630 630 update: (current)
631 631 phases: 1 draft
632 632 mq: 3 unapplied
633 633
634 634 Strip adds, removes, modifies with --keep
635 635
636 636 $ touch b
637 637 $ hg add b
638 638 $ hg commit -mb
639 639 $ touch c
640 640
641 641 ... with a clean working dir
642 642
643 643 $ hg add c
644 644 $ hg rm bar
645 645 $ hg commit -mc
646 646 $ hg status
647 647 $ hg strip --keep tip
648 648 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
649 649 $ hg status
650 650 ! bar
651 651 ? c
652 652
653 653 ... with a dirty working dir
654 654
655 655 $ hg add c
656 656 $ hg rm bar
657 657 $ hg commit -mc
658 658 $ hg status
659 659 $ echo b > b
660 660 $ echo d > d
661 661 $ hg strip --keep tip
662 662 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
663 663 $ hg status
664 664 M b
665 665 ! bar
666 666 ? c
667 667 ? d
668 668
669 669 ... after updating the dirstate
670 670 $ hg add c
671 671 $ hg commit -mc
672 672 $ hg rm c
673 673 $ hg commit -mc
674 674 $ hg strip --keep '.^' -q
675 675 $ cd ..
676 676
677 677 stripping many nodes on a complex graph (issue3299)
678 678
679 679 $ hg init issue3299
680 680 $ cd issue3299
681 681 $ hg debugbuilddag '@a.:a@b.:b.:x<a@a.:a<b@b.:b<a@a.:a'
682 682 $ hg strip 'not ancestors(x)'
683 683 saved backup bundle to $TESTTMP/issue3299/.hg/strip-backup/*-backup.hg (glob)
684 684
685 685 test hg strip -B bookmark
686 686
687 687 $ cd ..
688 688 $ hg init bookmarks
689 689 $ cd bookmarks
690 690 $ hg debugbuilddag '..<2.*1/2:m<2+3:c<m+3:a<2.:b<m+2:d<2.:e<m+1:f'
691 691 $ hg bookmark -r 'a' 'todelete'
692 692 $ hg bookmark -r 'b' 'B'
693 693 $ hg bookmark -r 'b' 'nostrip'
694 694 $ hg bookmark -r 'c' 'delete'
695 695 $ hg bookmark -r 'd' 'multipledelete1'
696 696 $ hg bookmark -r 'e' 'multipledelete2'
697 697 $ hg bookmark -r 'f' 'singlenode1'
698 698 $ hg bookmark -r 'f' 'singlenode2'
699 699 $ hg up -C todelete
700 700 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
701 701 (activating bookmark todelete)
702 702 $ hg strip -B nostrip
703 703 bookmark 'nostrip' deleted
704 704 abort: empty revision set
705 705 [255]
706 706 $ hg strip -B todelete
707 707 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
708 708 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob)
709 709 bookmark 'todelete' deleted
710 710 $ hg id -ir dcbb326fdec2
711 711 abort: unknown revision 'dcbb326fdec2'!
712 712 [255]
713 713 $ hg id -ir d62d843c9a01
714 714 d62d843c9a01
715 715 $ hg bookmarks
716 716 B 9:ff43616e5d0f
717 717 delete 6:2702dd0c91e7
718 718 multipledelete1 11:e46a4836065c
719 719 multipledelete2 12:b4594d867745
720 720 singlenode1 13:43227190fef8
721 721 singlenode2 13:43227190fef8
722 722 $ hg strip -B multipledelete1 -B multipledelete2
723 723 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/e46a4836065c-89ec65c2-backup.hg
724 724 bookmark 'multipledelete1' deleted
725 725 bookmark 'multipledelete2' deleted
726 726 $ hg id -ir e46a4836065c
727 727 abort: unknown revision 'e46a4836065c'!
728 728 [255]
729 729 $ hg id -ir b4594d867745
730 730 abort: unknown revision 'b4594d867745'!
731 731 [255]
732 732 $ hg strip -B singlenode1 -B singlenode2
733 733 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/43227190fef8-8da858f2-backup.hg
734 734 bookmark 'singlenode1' deleted
735 735 bookmark 'singlenode2' deleted
736 736 $ hg id -ir 43227190fef8
737 737 abort: unknown revision '43227190fef8'!
738 738 [255]
739 739 $ hg strip -B unknownbookmark
740 740 abort: bookmark 'unknownbookmark' not found
741 741 [255]
742 742 $ hg strip -B unknownbookmark1 -B unknownbookmark2
743 743 abort: bookmark 'unknownbookmark1,unknownbookmark2' not found
744 744 [255]
745 745 $ hg strip -B delete -B unknownbookmark
746 746 abort: bookmark 'unknownbookmark' not found
747 747 [255]
748 748 $ hg strip -B delete
749 749 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob)
750 750 bookmark 'delete' deleted
751 751 $ hg id -ir 6:2702dd0c91e7
752 752 abort: unknown revision '2702dd0c91e7'!
753 753 [255]
754 754 $ hg update B
755 755 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
756 756 (activating bookmark B)
757 757 $ echo a > a
758 758 $ hg add a
759 759 $ hg strip -B B
760 760 abort: uncommitted changes
761 [255]
761 [20]
762 762 $ hg bookmarks
763 763 * B 6:ff43616e5d0f
764 764
765 765 Make sure no one adds back a -b option:
766 766
767 767 $ hg strip -b tip
768 768 hg strip: option -b not recognized
769 769 hg strip [-k] [-f] [-B bookmark] [-r] REV...
770 770
771 771 strip changesets and all their descendants from the repository
772 772
773 773 (use 'hg help -e strip' to show help for the strip extension)
774 774
775 775 options ([+] can be repeated):
776 776
777 777 -r --rev REV [+] strip specified revision (optional, can specify
778 778 revisions without this option)
779 779 -f --force force removal of changesets, discard uncommitted
780 780 changes (no backup)
781 781 --no-backup do not save backup bundle
782 782 -k --keep do not modify working directory during strip
783 783 -B --bookmark BOOKMARK [+] remove revs only reachable from given bookmark
784 784 --mq operate on patch repository
785 785
786 786 (use 'hg strip -h' to show more help)
787 787 [255]
788 788
789 789 $ cd ..
790 790
791 791 Verify bundles don't get overwritten:
792 792
793 793 $ hg init doublebundle
794 794 $ cd doublebundle
795 795 $ touch a
796 796 $ hg commit -Aqm a
797 797 $ touch b
798 798 $ hg commit -Aqm b
799 799 $ hg strip -r 0
800 800 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
801 801 saved backup bundle to $TESTTMP/doublebundle/.hg/strip-backup/3903775176ed-e68910bd-backup.hg
802 802 $ ls .hg/strip-backup
803 803 3903775176ed-e68910bd-backup.hg
804 804 #if repobundlerepo
805 805 $ hg pull -q -r 3903775176ed .hg/strip-backup/3903775176ed-e68910bd-backup.hg
806 806 $ hg strip -r 0
807 807 saved backup bundle to $TESTTMP/doublebundle/.hg/strip-backup/3903775176ed-54390173-backup.hg
808 808 $ ls .hg/strip-backup
809 809 3903775176ed-54390173-backup.hg
810 810 3903775176ed-e68910bd-backup.hg
811 811 #endif
812 812 $ cd ..
813 813
814 814 Test that we only bundle the stripped changesets (issue4736)
815 815 ------------------------------------------------------------
816 816
817 817 initialization (previous repo is empty anyway)
818 818
819 819 $ hg init issue4736
820 820 $ cd issue4736
821 821 $ echo a > a
822 822 $ hg add a
823 823 $ hg commit -m commitA
824 824 $ echo b > b
825 825 $ hg add b
826 826 $ hg commit -m commitB
827 827 $ echo c > c
828 828 $ hg add c
829 829 $ hg commit -m commitC
830 830 $ hg up 'desc(commitB)'
831 831 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
832 832 $ echo d > d
833 833 $ hg add d
834 834 $ hg commit -m commitD
835 835 created new head
836 836 $ hg up 'desc(commitC)'
837 837 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
838 838 $ hg merge 'desc(commitD)'
839 839 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
840 840 (branch merge, don't forget to commit)
841 841 $ hg ci -m 'mergeCD'
842 842 $ hg log -G
843 843 @ changeset: 4:d8db9d137221
844 844 |\ tag: tip
845 845 | | parent: 2:5c51d8d6557d
846 846 | | parent: 3:6625a5168474
847 847 | | user: test
848 848 | | date: Thu Jan 01 00:00:00 1970 +0000
849 849 | | summary: mergeCD
850 850 | |
851 851 | o changeset: 3:6625a5168474
852 852 | | parent: 1:eca11cf91c71
853 853 | | user: test
854 854 | | date: Thu Jan 01 00:00:00 1970 +0000
855 855 | | summary: commitD
856 856 | |
857 857 o | changeset: 2:5c51d8d6557d
858 858 |/ user: test
859 859 | date: Thu Jan 01 00:00:00 1970 +0000
860 860 | summary: commitC
861 861 |
862 862 o changeset: 1:eca11cf91c71
863 863 | user: test
864 864 | date: Thu Jan 01 00:00:00 1970 +0000
865 865 | summary: commitB
866 866 |
867 867 o changeset: 0:105141ef12d0
868 868 user: test
869 869 date: Thu Jan 01 00:00:00 1970 +0000
870 870 summary: commitA
871 871
872 872
873 873 Check bundle behavior:
874 874
875 875 $ hg bundle -r 'desc(mergeCD)' --base 'desc(commitC)' ../issue4736.hg
876 876 2 changesets found
877 877 #if repobundlerepo
878 878 $ hg log -r 'bundle()' -R ../issue4736.hg
879 879 changeset: 3:6625a5168474
880 880 parent: 1:eca11cf91c71
881 881 user: test
882 882 date: Thu Jan 01 00:00:00 1970 +0000
883 883 summary: commitD
884 884
885 885 changeset: 4:d8db9d137221
886 886 tag: tip
887 887 parent: 2:5c51d8d6557d
888 888 parent: 3:6625a5168474
889 889 user: test
890 890 date: Thu Jan 01 00:00:00 1970 +0000
891 891 summary: mergeCD
892 892
893 893 #endif
894 894
895 895 check strip behavior
896 896
897 897 $ hg --config extensions.strip= strip 'desc(commitD)' --debug
898 898 resolving manifests
899 899 branchmerge: False, force: True, partial: False
900 900 ancestor: d8db9d137221+, local: d8db9d137221+, remote: eca11cf91c71
901 901 c: other deleted -> r
902 902 removing c
903 903 d: other deleted -> r
904 904 removing d
905 905 starting 4 threads for background file closing (?)
906 906 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
907 907 2 changesets found
908 908 list of changesets:
909 909 6625a516847449b6f0fa3737b9ba56e9f0f3032c
910 910 d8db9d1372214336d2b5570f20ee468d2c72fa8b
911 911 bundle2-output-bundle: "HG20", (1 params) 3 parts total
912 912 bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload
913 913 bundle2-output-part: "cache:rev-branch-cache" (advisory) streamed payload
914 914 bundle2-output-part: "phase-heads" 24 bytes payload
915 915 saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/6625a5168474-345bb43d-backup.hg
916 916 updating the branch cache
917 917 invalid branch cache (served): tip differs
918 918 $ hg log -G
919 919 o changeset: 2:5c51d8d6557d
920 920 | tag: tip
921 921 | user: test
922 922 | date: Thu Jan 01 00:00:00 1970 +0000
923 923 | summary: commitC
924 924 |
925 925 @ changeset: 1:eca11cf91c71
926 926 | user: test
927 927 | date: Thu Jan 01 00:00:00 1970 +0000
928 928 | summary: commitB
929 929 |
930 930 o changeset: 0:105141ef12d0
931 931 user: test
932 932 date: Thu Jan 01 00:00:00 1970 +0000
933 933 summary: commitA
934 934
935 935
936 936 strip backup content
937 937
938 938 #if repobundlerepo
939 939 $ hg log -r 'bundle()' -R .hg/strip-backup/6625a5168474-*-backup.hg
940 940 changeset: 3:6625a5168474
941 941 parent: 1:eca11cf91c71
942 942 user: test
943 943 date: Thu Jan 01 00:00:00 1970 +0000
944 944 summary: commitD
945 945
946 946 changeset: 4:d8db9d137221
947 947 tag: tip
948 948 parent: 2:5c51d8d6557d
949 949 parent: 3:6625a5168474
950 950 user: test
951 951 date: Thu Jan 01 00:00:00 1970 +0000
952 952 summary: mergeCD
953 953
954 954
955 955 #endif
956 956
957 957 Check that the phase cache is properly invalidated after a strip with bookmark.
958 958
959 959 $ cat > ../stripstalephasecache.py << EOF
960 960 > from mercurial import extensions, localrepo
961 961 > def transactioncallback(orig, repo, desc, *args, **kwargs):
962 962 > def test(transaction):
963 963 > # observe cache inconsistency
964 964 > try:
965 965 > [repo.changelog.node(r) for r in repo.revs(b"not public()")]
966 966 > except IndexError:
967 967 > repo.ui.status(b"Index error!\n")
968 968 > transaction = orig(repo, desc, *args, **kwargs)
969 969 > # warm up the phase cache
970 970 > list(repo.revs(b"not public()"))
971 971 > if desc != b'strip':
972 972 > transaction.addpostclose(b"phase invalidation test", test)
973 973 > return transaction
974 974 > def extsetup(ui):
975 975 > extensions.wrapfunction(localrepo.localrepository, b"transaction",
976 976 > transactioncallback)
977 977 > EOF
978 978 $ hg up -C 2
979 979 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
980 980 $ echo k > k
981 981 $ hg add k
982 982 $ hg commit -m commitK
983 983 $ echo l > l
984 984 $ hg add l
985 985 $ hg commit -m commitL
986 986 $ hg book -r tip blah
987 987 $ hg strip ".^" --config extensions.crash=$TESTTMP/stripstalephasecache.py
988 988 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
989 989 saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/8f0b4384875c-4fa10deb-backup.hg
990 990 $ hg up -C 1
991 991 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
992 992
993 993 Error during post-close callback of the strip transaction
994 994 (They should be gracefully handled and reported)
995 995
996 996 $ cat > ../crashstrip.py << EOF
997 997 > from mercurial import error
998 998 > def reposetup(ui, repo):
999 999 > class crashstriprepo(repo.__class__):
1000 1000 > def transaction(self, desc, *args, **kwargs):
1001 1001 > tr = super(crashstriprepo, self).transaction(desc, *args, **kwargs)
1002 1002 > if desc == b'strip':
1003 1003 > def crash(tra): raise error.Abort(b'boom')
1004 1004 > tr.addpostclose(b'crash', crash)
1005 1005 > return tr
1006 1006 > repo.__class__ = crashstriprepo
1007 1007 > EOF
1008 1008 $ hg strip tip --config extensions.crash=$TESTTMP/crashstrip.py
1009 1009 saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/5c51d8d6557d-70daef06-backup.hg
1010 1010 strip failed, backup bundle stored in '$TESTTMP/issue4736/.hg/strip-backup/5c51d8d6557d-70daef06-backup.hg'
1011 1011 abort: boom
1012 1012 [255]
1013 1013
1014 1014 test stripping a working directory parent doesn't switch named branches
1015 1015
1016 1016 $ hg log -G
1017 1017 @ changeset: 1:eca11cf91c71
1018 1018 | tag: tip
1019 1019 | user: test
1020 1020 | date: Thu Jan 01 00:00:00 1970 +0000
1021 1021 | summary: commitB
1022 1022 |
1023 1023 o changeset: 0:105141ef12d0
1024 1024 user: test
1025 1025 date: Thu Jan 01 00:00:00 1970 +0000
1026 1026 summary: commitA
1027 1027
1028 1028
1029 1029 $ hg branch new-branch
1030 1030 marked working directory as branch new-branch
1031 1031 (branches are permanent and global, did you want a bookmark?)
1032 1032 $ hg ci -m "start new branch"
1033 1033 $ echo 'foo' > foo.txt
1034 1034 $ hg ci -Aqm foo
1035 1035 $ hg up default
1036 1036 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1037 1037 $ echo 'bar' > bar.txt
1038 1038 $ hg ci -Aqm bar
1039 1039 $ hg up new-branch
1040 1040 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1041 1041 $ hg merge default
1042 1042 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1043 1043 (branch merge, don't forget to commit)
1044 1044 $ hg log -G
1045 1045 @ changeset: 4:35358f982181
1046 1046 | tag: tip
1047 1047 | parent: 1:eca11cf91c71
1048 1048 | user: test
1049 1049 | date: Thu Jan 01 00:00:00 1970 +0000
1050 1050 | summary: bar
1051 1051 |
1052 1052 | @ changeset: 3:f62c6c09b707
1053 1053 | | branch: new-branch
1054 1054 | | user: test
1055 1055 | | date: Thu Jan 01 00:00:00 1970 +0000
1056 1056 | | summary: foo
1057 1057 | |
1058 1058 | o changeset: 2:b1d33a8cadd9
1059 1059 |/ branch: new-branch
1060 1060 | user: test
1061 1061 | date: Thu Jan 01 00:00:00 1970 +0000
1062 1062 | summary: start new branch
1063 1063 |
1064 1064 o changeset: 1:eca11cf91c71
1065 1065 | user: test
1066 1066 | date: Thu Jan 01 00:00:00 1970 +0000
1067 1067 | summary: commitB
1068 1068 |
1069 1069 o changeset: 0:105141ef12d0
1070 1070 user: test
1071 1071 date: Thu Jan 01 00:00:00 1970 +0000
1072 1072 summary: commitA
1073 1073
1074 1074
1075 1075 $ hg strip --force -r 35358f982181
1076 1076 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1077 1077 saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/35358f982181-50d992d4-backup.hg
1078 1078 $ hg log -G
1079 1079 @ changeset: 3:f62c6c09b707
1080 1080 | branch: new-branch
1081 1081 | tag: tip
1082 1082 | user: test
1083 1083 | date: Thu Jan 01 00:00:00 1970 +0000
1084 1084 | summary: foo
1085 1085 |
1086 1086 o changeset: 2:b1d33a8cadd9
1087 1087 | branch: new-branch
1088 1088 | user: test
1089 1089 | date: Thu Jan 01 00:00:00 1970 +0000
1090 1090 | summary: start new branch
1091 1091 |
1092 1092 o changeset: 1:eca11cf91c71
1093 1093 | user: test
1094 1094 | date: Thu Jan 01 00:00:00 1970 +0000
1095 1095 | summary: commitB
1096 1096 |
1097 1097 o changeset: 0:105141ef12d0
1098 1098 user: test
1099 1099 date: Thu Jan 01 00:00:00 1970 +0000
1100 1100 summary: commitA
1101 1101
1102 1102
1103 1103 $ hg up default
1104 1104 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1105 1105 $ echo 'bar' > bar.txt
1106 1106 $ hg ci -Aqm bar
1107 1107 $ hg up new-branch
1108 1108 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1109 1109 $ hg merge default
1110 1110 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1111 1111 (branch merge, don't forget to commit)
1112 1112 $ hg ci -m merge
1113 1113 $ hg log -G
1114 1114 @ changeset: 5:4cf5e92caec2
1115 1115 |\ branch: new-branch
1116 1116 | | tag: tip
1117 1117 | | parent: 3:f62c6c09b707
1118 1118 | | parent: 4:35358f982181
1119 1119 | | user: test
1120 1120 | | date: Thu Jan 01 00:00:00 1970 +0000
1121 1121 | | summary: merge
1122 1122 | |
1123 1123 | o changeset: 4:35358f982181
1124 1124 | | parent: 1:eca11cf91c71
1125 1125 | | user: test
1126 1126 | | date: Thu Jan 01 00:00:00 1970 +0000
1127 1127 | | summary: bar
1128 1128 | |
1129 1129 o | changeset: 3:f62c6c09b707
1130 1130 | | branch: new-branch
1131 1131 | | user: test
1132 1132 | | date: Thu Jan 01 00:00:00 1970 +0000
1133 1133 | | summary: foo
1134 1134 | |
1135 1135 o | changeset: 2:b1d33a8cadd9
1136 1136 |/ branch: new-branch
1137 1137 | user: test
1138 1138 | date: Thu Jan 01 00:00:00 1970 +0000
1139 1139 | summary: start new branch
1140 1140 |
1141 1141 o changeset: 1:eca11cf91c71
1142 1142 | user: test
1143 1143 | date: Thu Jan 01 00:00:00 1970 +0000
1144 1144 | summary: commitB
1145 1145 |
1146 1146 o changeset: 0:105141ef12d0
1147 1147 user: test
1148 1148 date: Thu Jan 01 00:00:00 1970 +0000
1149 1149 summary: commitA
1150 1150
1151 1151
1152 1152 $ hg strip -r 35358f982181
1153 1153 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1154 1154 saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/35358f982181-a6f020aa-backup.hg
1155 1155 $ hg log -G
1156 1156 @ changeset: 3:f62c6c09b707
1157 1157 | branch: new-branch
1158 1158 | tag: tip
1159 1159 | user: test
1160 1160 | date: Thu Jan 01 00:00:00 1970 +0000
1161 1161 | summary: foo
1162 1162 |
1163 1163 o changeset: 2:b1d33a8cadd9
1164 1164 | branch: new-branch
1165 1165 | user: test
1166 1166 | date: Thu Jan 01 00:00:00 1970 +0000
1167 1167 | summary: start new branch
1168 1168 |
1169 1169 o changeset: 1:eca11cf91c71
1170 1170 | user: test
1171 1171 | date: Thu Jan 01 00:00:00 1970 +0000
1172 1172 | summary: commitB
1173 1173 |
1174 1174 o changeset: 0:105141ef12d0
1175 1175 user: test
1176 1176 date: Thu Jan 01 00:00:00 1970 +0000
1177 1177 summary: commitA
1178 1178
1179 1179
1180 1180 stripping a set containing a merge properly reset file content, including items on other branches
1181 1181
1182 1182 The added file is moved to unknown, which is the behavior we have been seeing for other `hg strip --keep` call.
1183 1183
1184 1184 stripping a set containing a merge properly reset file content, including items on other branches
1185 1185
1186 1186 The added file is moved to unknown, which is the behavior we have been seeing for other `hg strip --keep` call.
1187 1187
1188 1188 $ hg unbundle -u $TESTTMP/issue4736/.hg/strip-backup/35358f982181-a6f020aa-backup.hg
1189 1189 adding changesets
1190 1190 adding manifests
1191 1191 adding file changes
1192 1192 added 2 changesets with 1 changes to 1 files
1193 1193 new changesets 35358f982181:4cf5e92caec2 (2 drafts)
1194 1194 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1195 1195
1196 1196 $ hg id
1197 1197 4cf5e92caec2 (new-branch) tip
1198 1198 $ hg status --rev "f62c6c09b707"
1199 1199 A bar.txt
1200 1200 $ hg diff --rev "f62c6c09b707"
1201 1201 diff -r f62c6c09b707 bar.txt
1202 1202 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1203 1203 +++ b/bar.txt Thu Jan 01 00:00:00 1970 +0000
1204 1204 @@ -0,0 +1,1 @@
1205 1205 +bar
1206 1206 $ hg log -G -v --rev 35358f982181:: --patch
1207 1207 @ changeset: 5:4cf5e92caec2
1208 1208 |\ branch: new-branch
1209 1209 | ~ tag: tip
1210 1210 | parent: 3:f62c6c09b707
1211 1211 | parent: 4:35358f982181
1212 1212 | user: test
1213 1213 | date: Thu Jan 01 00:00:00 1970 +0000
1214 1214 | description:
1215 1215 | merge
1216 1216 |
1217 1217 |
1218 1218 | diff -r f62c6c09b707 -r 4cf5e92caec2 bar.txt
1219 1219 | --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1220 1220 | +++ b/bar.txt Thu Jan 01 00:00:00 1970 +0000
1221 1221 | @@ -0,0 +1,1 @@
1222 1222 | +bar
1223 1223 |
1224 1224 o changeset: 4:35358f982181
1225 1225 | parent: 1:eca11cf91c71
1226 1226 ~ user: test
1227 1227 date: Thu Jan 01 00:00:00 1970 +0000
1228 1228 files: bar.txt
1229 1229 description:
1230 1230 bar
1231 1231
1232 1232
1233 1233 diff -r eca11cf91c71 -r 35358f982181 bar.txt
1234 1234 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1235 1235 +++ b/bar.txt Thu Jan 01 00:00:00 1970 +0000
1236 1236 @@ -0,0 +1,1 @@
1237 1237 +bar
1238 1238
1239 1239
1240 1240 $ hg strip -k -r 35358f982181
1241 1241 saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/35358f982181-a6f020aa-backup.hg
1242 1242 $ hg log -G
1243 1243 @ changeset: 3:f62c6c09b707
1244 1244 | branch: new-branch
1245 1245 | tag: tip
1246 1246 | user: test
1247 1247 | date: Thu Jan 01 00:00:00 1970 +0000
1248 1248 | summary: foo
1249 1249 |
1250 1250 o changeset: 2:b1d33a8cadd9
1251 1251 | branch: new-branch
1252 1252 | user: test
1253 1253 | date: Thu Jan 01 00:00:00 1970 +0000
1254 1254 | summary: start new branch
1255 1255 |
1256 1256 o changeset: 1:eca11cf91c71
1257 1257 | user: test
1258 1258 | date: Thu Jan 01 00:00:00 1970 +0000
1259 1259 | summary: commitB
1260 1260 |
1261 1261 o changeset: 0:105141ef12d0
1262 1262 user: test
1263 1263 date: Thu Jan 01 00:00:00 1970 +0000
1264 1264 summary: commitA
1265 1265
1266 1266
1267 1267 $ hg status -A
1268 1268 ? bar.txt
1269 1269 C a
1270 1270 C b
1271 1271 C foo.txt
1272 1272 $ cat bar.txt
1273 1273 bar
1274 1274
1275 1275 Use delayedstrip to strip inside a transaction
1276 1276
1277 1277 $ cd $TESTTMP
1278 1278 $ hg init delayedstrip
1279 1279 $ cd delayedstrip
1280 1280 $ hg debugdrawdag <<'EOS'
1281 1281 > D
1282 1282 > |
1283 1283 > C F H # Commit on top of "I",
1284 1284 > | |/| # Strip B+D+I+E+G+H+Z
1285 1285 > I B E G
1286 1286 > \|/
1287 1287 > A Z
1288 1288 > EOS
1289 1289 $ cp -R . ../scmutilcleanup
1290 1290
1291 1291 $ hg up -C I
1292 1292 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1293 1293 $ echo 3 >> I
1294 1294 $ cat > $TESTTMP/delayedstrip.py <<EOF
1295 1295 > from __future__ import absolute_import
1296 1296 > from mercurial import commands, registrar, repair
1297 1297 > cmdtable = {}
1298 1298 > command = registrar.command(cmdtable)
1299 1299 > @command(b'testdelayedstrip')
1300 1300 > def testdelayedstrip(ui, repo):
1301 1301 > def getnodes(expr):
1302 1302 > return [repo.changelog.node(r) for r in repo.revs(expr)]
1303 1303 > with repo.wlock():
1304 1304 > with repo.lock():
1305 1305 > with repo.transaction(b'delayedstrip'):
1306 1306 > repair.delayedstrip(ui, repo, getnodes(b'B+I+Z+D+E'), b'J')
1307 1307 > repair.delayedstrip(ui, repo, getnodes(b'G+H+Z'), b'I')
1308 1308 > commands.commit(ui, repo, message=b'J', date=b'0 0')
1309 1309 > EOF
1310 1310 $ hg testdelayedstrip --config extensions.t=$TESTTMP/delayedstrip.py
1311 1311 warning: orphaned descendants detected, not stripping 08ebfeb61bac, 112478962961, 7fb047a69f22
1312 1312 saved backup bundle to $TESTTMP/delayedstrip/.hg/strip-backup/f585351a92f8-17475721-I.hg
1313 1313
1314 1314 $ hg log -G -T '{rev}:{node|short} {desc}' -r 'sort(all(), topo)'
1315 1315 @ 6:2f2d51af6205 J
1316 1316 |
1317 1317 o 3:08ebfeb61bac I
1318 1318 |
1319 1319 | o 5:64a8289d2492 F
1320 1320 | |
1321 1321 | o 2:7fb047a69f22 E
1322 1322 |/
1323 1323 | o 4:26805aba1e60 C
1324 1324 | |
1325 1325 | o 1:112478962961 B
1326 1326 |/
1327 1327 o 0:426bada5c675 A
1328 1328
1329 1329 Test high-level scmutil.cleanupnodes API
1330 1330
1331 1331 $ cd $TESTTMP/scmutilcleanup
1332 1332 $ hg debugdrawdag <<'EOS'
1333 1333 > D2 F2 G2 # D2, F2, G2 are replacements for D, F, G
1334 1334 > | | |
1335 1335 > C H G
1336 1336 > EOS
1337 1337 $ for i in B C D F G I Z; do
1338 1338 > hg bookmark -i -r $i b-$i
1339 1339 > done
1340 1340 $ hg bookmark -i -r E 'b-F@divergent1'
1341 1341 $ hg bookmark -i -r H 'b-F@divergent2'
1342 1342 $ hg bookmark -i -r G 'b-F@divergent3'
1343 1343 $ cp -R . ../scmutilcleanup.obsstore
1344 1344
1345 1345 $ cat > $TESTTMP/scmutilcleanup.py <<EOF
1346 1346 > from mercurial import registrar, scmutil
1347 1347 > cmdtable = {}
1348 1348 > command = registrar.command(cmdtable)
1349 1349 > @command(b'testnodescleanup')
1350 1350 > def testnodescleanup(ui, repo):
1351 1351 > def nodes(expr):
1352 1352 > return [repo.changelog.node(r) for r in repo.revs(expr)]
1353 1353 > def node(expr):
1354 1354 > return nodes(expr)[0]
1355 1355 > with repo.wlock():
1356 1356 > with repo.lock():
1357 1357 > with repo.transaction(b'delayedstrip'):
1358 1358 > mapping = {node(b'F'): [node(b'F2')],
1359 1359 > node(b'D'): [node(b'D2')],
1360 1360 > node(b'G'): [node(b'G2')]}
1361 1361 > scmutil.cleanupnodes(repo, mapping, b'replace')
1362 1362 > scmutil.cleanupnodes(repo, nodes(b'((B::)+I+Z)-D2-obsolete()'),
1363 1363 > b'replace')
1364 1364 > EOF
1365 1365 $ hg testnodescleanup --config extensions.t=$TESTTMP/scmutilcleanup.py
1366 1366 warning: orphaned descendants detected, not stripping 112478962961, 1fc8102cda62, 26805aba1e60
1367 1367 saved backup bundle to $TESTTMP/scmutilcleanup/.hg/strip-backup/f585351a92f8-73fb7c03-replace.hg
1368 1368
1369 1369 $ hg log -G -T '{rev}:{node|short} {desc} {bookmarks}' -r 'sort(all(), topo)'
1370 1370 o 8:1473d4b996d1 G2 b-F@divergent3 b-G
1371 1371 |
1372 1372 | o 7:d11b3456a873 F2 b-F
1373 1373 | |
1374 1374 | o 5:5cb05ba470a7 H
1375 1375 |/|
1376 1376 | o 3:7fb047a69f22 E b-F@divergent1
1377 1377 | |
1378 1378 | | o 6:7c78f703e465 D2 b-D
1379 1379 | | |
1380 1380 | | o 4:26805aba1e60 C
1381 1381 | | |
1382 1382 | | o 2:112478962961 B
1383 1383 | |/
1384 1384 o | 1:1fc8102cda62 G
1385 1385 /
1386 1386 o 0:426bada5c675 A b-B b-C b-I
1387 1387
1388 1388 $ hg bookmark
1389 1389 b-B 0:426bada5c675
1390 1390 b-C 0:426bada5c675
1391 1391 b-D 6:7c78f703e465
1392 1392 b-F 7:d11b3456a873
1393 1393 b-F@divergent1 3:7fb047a69f22
1394 1394 b-F@divergent3 8:1473d4b996d1
1395 1395 b-G 8:1473d4b996d1
1396 1396 b-I 0:426bada5c675
1397 1397 b-Z -1:000000000000
1398 1398
1399 1399 Test the above using obsstore "by the way". Not directly related to strip, but
1400 1400 we have reusable code here
1401 1401
1402 1402 $ cd $TESTTMP/scmutilcleanup.obsstore
1403 1403 $ cat >> .hg/hgrc <<EOF
1404 1404 > [experimental]
1405 1405 > evolution=true
1406 1406 > evolution.track-operation=1
1407 1407 > EOF
1408 1408
1409 1409 $ hg testnodescleanup --config extensions.t=$TESTTMP/scmutilcleanup.py
1410 1410 4 new orphan changesets
1411 1411
1412 1412 $ rm .hg/localtags
1413 1413 $ hg log -G -T '{rev}:{node|short} {desc} {bookmarks}' -r 'sort(all(), topo)'
1414 1414 * 12:1473d4b996d1 G2 b-F@divergent3 b-G
1415 1415 |
1416 1416 | * 11:d11b3456a873 F2 b-F
1417 1417 | |
1418 1418 | * 8:5cb05ba470a7 H
1419 1419 |/|
1420 1420 | o 4:7fb047a69f22 E b-F@divergent1
1421 1421 | |
1422 1422 | | * 10:7c78f703e465 D2 b-D
1423 1423 | | |
1424 1424 | | x 6:26805aba1e60 C
1425 1425 | | |
1426 1426 | | x 3:112478962961 B
1427 1427 | |/
1428 1428 x | 1:1fc8102cda62 G
1429 1429 /
1430 1430 o 0:426bada5c675 A b-B b-C b-I
1431 1431
1432 1432 $ hg debugobsolete
1433 1433 1fc8102cda6204549f031015641606ccf5513ec3 1473d4b996d1d1b121de6b39fab6a04fbf9d873e 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'replace', 'user': 'test'}
1434 1434 64a8289d249234b9886244d379f15e6b650b28e3 d11b3456a873daec7c7bc53e5622e8df6d741bd2 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '13', 'operation': 'replace', 'user': 'test'}
1435 1435 f585351a92f85104bff7c284233c338b10eb1df7 7c78f703e465d73102cc8780667ce269c5208a40 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '9', 'operation': 'replace', 'user': 'test'}
1436 1436 48b9aae0607f43ff110d84e6883c151942add5ab 0 {0000000000000000000000000000000000000000} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'replace', 'user': 'test'}
1437 1437 112478962961147124edd43549aedd1a335e44bf 0 {426bada5c67598ca65036d57d9e4b64b0c1ce7a0} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'replace', 'user': 'test'}
1438 1438 08ebfeb61bac6e3f12079de774d285a0d6689eba 0 {426bada5c67598ca65036d57d9e4b64b0c1ce7a0} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'replace', 'user': 'test'}
1439 1439 26805aba1e600a82e93661149f2313866a221a7b 0 {112478962961147124edd43549aedd1a335e44bf} (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '0', 'operation': 'replace', 'user': 'test'}
1440 1440 $ cd ..
1441 1441
1442 1442 Test that obsmarkers are restored even when not using generaldelta
1443 1443
1444 1444 $ hg --config format.usegeneraldelta=no init issue5678
1445 1445 $ cd issue5678
1446 1446 $ cat >> .hg/hgrc <<EOF
1447 1447 > [experimental]
1448 1448 > evolution=true
1449 1449 > EOF
1450 1450 $ echo a > a
1451 1451 $ hg ci -Aqm a
1452 1452 $ hg ci --amend -m a2
1453 1453 $ hg debugobsolete
1454 1454 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 489bac576828490c0bb8d45eac9e5e172e4ec0a8 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'test'}
1455 1455 $ hg strip .
1456 1456 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1457 1457 saved backup bundle to $TESTTMP/issue5678/.hg/strip-backup/489bac576828-bef27e14-backup.hg
1458 1458 $ hg unbundle -q .hg/strip-backup/*
1459 1459 $ hg debugobsolete
1460 1460 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 489bac576828490c0bb8d45eac9e5e172e4ec0a8 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'test'}
1461 1461 $ cd ..
@@ -1,806 +1,806 b''
1 1 $ cat >> $HGRCPATH << EOF
2 2 > [experimental]
3 3 > hook-track-tags=1
4 4 > [hooks]
5 5 > txnclose.track-tag=sh ${TESTTMP}/taghook.sh
6 6 > EOF
7 7
8 8 $ cat << EOF > taghook.sh
9 9 > #!/bin/sh
10 10 > # escape the "$" otherwise the test runner interpret it when writting the
11 11 > # file...
12 12 > if [ -n "\$HG_TAG_MOVED" ]; then
13 13 > echo 'hook: tag changes detected'
14 14 > sed 's/^/hook: /' .hg/changes/tags.changes
15 15 > fi
16 16 > EOF
17 17 $ hg init test
18 18 $ cd test
19 19
20 20 $ echo a > a
21 21 $ hg add a
22 22 $ hg commit -m "test"
23 23 $ hg history
24 24 changeset: 0:acb14030fe0a
25 25 tag: tip
26 26 user: test
27 27 date: Thu Jan 01 00:00:00 1970 +0000
28 28 summary: test
29 29
30 30
31 31 $ hg tag ' '
32 32 abort: tag names cannot consist entirely of whitespace
33 33 [10]
34 34
35 35 (this tests also that editor is not invoked, if '--edit' is not
36 36 specified)
37 37
38 38 $ HGEDITOR=cat hg tag "bleah"
39 39 hook: tag changes detected
40 40 hook: +A acb14030fe0a21b60322c440ad2d20cf7685a376 bleah
41 41 $ hg history
42 42 changeset: 1:d4f0d2909abc
43 43 tag: tip
44 44 user: test
45 45 date: Thu Jan 01 00:00:00 1970 +0000
46 46 summary: Added tag bleah for changeset acb14030fe0a
47 47
48 48 changeset: 0:acb14030fe0a
49 49 tag: bleah
50 50 user: test
51 51 date: Thu Jan 01 00:00:00 1970 +0000
52 52 summary: test
53 53
54 54
55 55 $ echo foo >> .hgtags
56 56 $ hg tag "bleah2"
57 57 abort: working copy of .hgtags is changed
58 58 (please commit .hgtags manually)
59 59 [255]
60 60
61 61 $ hg revert .hgtags
62 62 $ hg tag -r 0 x y z y y z
63 63 abort: tag names must be unique
64 64 [10]
65 65 $ hg tag tap nada dot tip
66 66 abort: the name 'tip' is reserved
67 67 [255]
68 68 $ hg tag .
69 69 abort: the name '.' is reserved
70 70 [255]
71 71 $ hg tag null
72 72 abort: the name 'null' is reserved
73 73 [255]
74 74 $ hg tag "bleah"
75 75 abort: tag 'bleah' already exists (use -f to force)
76 76 [10]
77 77 $ hg tag "blecch" "bleah"
78 78 abort: tag 'bleah' already exists (use -f to force)
79 79 [10]
80 80
81 81 $ hg tag --remove "blecch"
82 82 abort: tag 'blecch' does not exist
83 83 [10]
84 84 $ hg tag --remove "bleah" "blecch" "blough"
85 85 abort: tag 'blecch' does not exist
86 86 [10]
87 87
88 88 $ hg tag -r 0 "bleah0"
89 89 hook: tag changes detected
90 90 hook: +A acb14030fe0a21b60322c440ad2d20cf7685a376 bleah0
91 91 $ hg tag -l -r 1 "bleah1"
92 92 $ hg tag gack gawk gorp
93 93 hook: tag changes detected
94 94 hook: +A 336fccc858a4eb69609a291105009e484a6b6b8d gack
95 95 hook: +A 336fccc858a4eb69609a291105009e484a6b6b8d gawk
96 96 hook: +A 336fccc858a4eb69609a291105009e484a6b6b8d gorp
97 97 $ hg tag -f gack
98 98 hook: tag changes detected
99 99 hook: -M 336fccc858a4eb69609a291105009e484a6b6b8d gack
100 100 hook: +M 799667b6f2d9b957f73fa644a918c2df22bab58f gack
101 101 $ hg tag --remove gack gorp
102 102 hook: tag changes detected
103 103 hook: -R 799667b6f2d9b957f73fa644a918c2df22bab58f gack
104 104 hook: -R 336fccc858a4eb69609a291105009e484a6b6b8d gorp
105 105
106 106 $ hg tag "bleah "
107 107 abort: tag 'bleah' already exists (use -f to force)
108 108 [10]
109 109 $ hg tag " bleah"
110 110 abort: tag 'bleah' already exists (use -f to force)
111 111 [10]
112 112 $ hg tag " bleah"
113 113 abort: tag 'bleah' already exists (use -f to force)
114 114 [10]
115 115 $ hg tag -r 0 " bleahbleah "
116 116 hook: tag changes detected
117 117 hook: +A acb14030fe0a21b60322c440ad2d20cf7685a376 bleahbleah
118 118 $ hg tag -r 0 " bleah bleah "
119 119 hook: tag changes detected
120 120 hook: +A acb14030fe0a21b60322c440ad2d20cf7685a376 bleah bleah
121 121
122 122 $ cat .hgtags
123 123 acb14030fe0a21b60322c440ad2d20cf7685a376 bleah
124 124 acb14030fe0a21b60322c440ad2d20cf7685a376 bleah0
125 125 336fccc858a4eb69609a291105009e484a6b6b8d gack
126 126 336fccc858a4eb69609a291105009e484a6b6b8d gawk
127 127 336fccc858a4eb69609a291105009e484a6b6b8d gorp
128 128 336fccc858a4eb69609a291105009e484a6b6b8d gack
129 129 799667b6f2d9b957f73fa644a918c2df22bab58f gack
130 130 799667b6f2d9b957f73fa644a918c2df22bab58f gack
131 131 0000000000000000000000000000000000000000 gack
132 132 336fccc858a4eb69609a291105009e484a6b6b8d gorp
133 133 0000000000000000000000000000000000000000 gorp
134 134 acb14030fe0a21b60322c440ad2d20cf7685a376 bleahbleah
135 135 acb14030fe0a21b60322c440ad2d20cf7685a376 bleah bleah
136 136
137 137 $ cat .hg/localtags
138 138 d4f0d2909abc9290e2773c08837d70c1794e3f5a bleah1
139 139
140 140 tagging on a non-head revision
141 141
142 142 $ hg update 0
143 143 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
144 144 $ hg tag -l localblah
145 145 $ hg tag "foobar"
146 146 abort: working directory is not at a branch head (use -f to force)
147 147 [10]
148 148 $ hg tag -f "foobar"
149 149 hook: tag changes detected
150 150 hook: +A acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
151 151 $ cat .hgtags
152 152 acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
153 153 $ cat .hg/localtags
154 154 d4f0d2909abc9290e2773c08837d70c1794e3f5a bleah1
155 155 acb14030fe0a21b60322c440ad2d20cf7685a376 localblah
156 156
157 157 $ hg tag -l 'xx
158 158 > newline'
159 159 abort: '\n' cannot be used in a name
160 160 [255]
161 161 $ hg tag -l 'xx:xx'
162 162 abort: ':' cannot be used in a name
163 163 [255]
164 164
165 165 cloning local tags
166 166
167 167 $ cd ..
168 168 $ hg -R test log -r0:5
169 169 changeset: 0:acb14030fe0a
170 170 tag: bleah
171 171 tag: bleah bleah
172 172 tag: bleah0
173 173 tag: bleahbleah
174 174 tag: foobar
175 175 tag: localblah
176 176 user: test
177 177 date: Thu Jan 01 00:00:00 1970 +0000
178 178 summary: test
179 179
180 180 changeset: 1:d4f0d2909abc
181 181 tag: bleah1
182 182 user: test
183 183 date: Thu Jan 01 00:00:00 1970 +0000
184 184 summary: Added tag bleah for changeset acb14030fe0a
185 185
186 186 changeset: 2:336fccc858a4
187 187 tag: gawk
188 188 user: test
189 189 date: Thu Jan 01 00:00:00 1970 +0000
190 190 summary: Added tag bleah0 for changeset acb14030fe0a
191 191
192 192 changeset: 3:799667b6f2d9
193 193 user: test
194 194 date: Thu Jan 01 00:00:00 1970 +0000
195 195 summary: Added tag gack, gawk, gorp for changeset 336fccc858a4
196 196
197 197 changeset: 4:154eeb7c0138
198 198 user: test
199 199 date: Thu Jan 01 00:00:00 1970 +0000
200 200 summary: Added tag gack for changeset 799667b6f2d9
201 201
202 202 changeset: 5:b4bb47aaff09
203 203 user: test
204 204 date: Thu Jan 01 00:00:00 1970 +0000
205 205 summary: Removed tag gack, gorp
206 206
207 207 $ hg clone -q -rbleah1 test test1
208 208 hook: tag changes detected
209 209 hook: +A acb14030fe0a21b60322c440ad2d20cf7685a376 bleah
210 210 $ hg -R test1 parents --style=compact
211 211 1[tip] d4f0d2909abc 1970-01-01 00:00 +0000 test
212 212 Added tag bleah for changeset acb14030fe0a
213 213
214 214 $ hg clone -q -r5 test#bleah1 test2
215 215 hook: tag changes detected
216 216 hook: +A acb14030fe0a21b60322c440ad2d20cf7685a376 bleah
217 217 hook: +A acb14030fe0a21b60322c440ad2d20cf7685a376 bleah0
218 218 hook: +A 336fccc858a4eb69609a291105009e484a6b6b8d gawk
219 219 $ hg -R test2 parents --style=compact
220 220 5[tip] b4bb47aaff09 1970-01-01 00:00 +0000 test
221 221 Removed tag gack, gorp
222 222
223 223 $ hg clone -q -U test#bleah1 test3
224 224 hook: tag changes detected
225 225 hook: +A acb14030fe0a21b60322c440ad2d20cf7685a376 bleah
226 226 $ hg -R test3 parents --style=compact
227 227
228 228 $ cd test
229 229
230 230 Issue601: hg tag doesn't do the right thing if .hgtags or localtags
231 231 doesn't end with EOL
232 232
233 233 $ "$PYTHON" << EOF
234 234 > f = open('.hg/localtags'); last = f.readlines()[-1][:-1]; f.close()
235 235 > f = open('.hg/localtags', 'w'); f.write(last); f.close()
236 236 > EOF
237 237 $ cat .hg/localtags; echo
238 238 acb14030fe0a21b60322c440ad2d20cf7685a376 localblah
239 239 $ hg tag -l localnewline
240 240 $ cat .hg/localtags; echo
241 241 acb14030fe0a21b60322c440ad2d20cf7685a376 localblah
242 242 c2899151f4e76890c602a2597a650a72666681bf localnewline
243 243
244 244
245 245 $ "$PYTHON" << EOF
246 246 > f = open('.hgtags'); last = f.readlines()[-1][:-1]; f.close()
247 247 > f = open('.hgtags', 'w'); f.write(last); f.close()
248 248 > EOF
249 249 $ hg ci -m'broken manual edit of .hgtags'
250 250 $ cat .hgtags; echo
251 251 acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
252 252 $ hg tag newline
253 253 hook: tag changes detected
254 254 hook: +A a0eea09de1eeec777b46f2085260a373b2fbc293 newline
255 255 $ cat .hgtags; echo
256 256 acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
257 257 a0eea09de1eeec777b46f2085260a373b2fbc293 newline
258 258
259 259
260 260 tag and branch using same name
261 261
262 262 $ hg branch tag-and-branch-same-name
263 263 marked working directory as branch tag-and-branch-same-name
264 264 (branches are permanent and global, did you want a bookmark?)
265 265 $ hg ci -m"discouraged"
266 266 $ hg tag tag-and-branch-same-name
267 267 warning: tag tag-and-branch-same-name conflicts with existing branch name
268 268 hook: tag changes detected
269 269 hook: +A fc93d2ea1cd78e91216c6cfbbf26747c10ce11ae tag-and-branch-same-name
270 270
271 271 test custom commit messages
272 272
273 273 $ cat > editor.sh << '__EOF__'
274 274 > echo "==== before editing"
275 275 > cat "$1"
276 276 > echo "===="
277 277 > echo "custom tag message" > "$1"
278 278 > echo "second line" >> "$1"
279 279 > __EOF__
280 280
281 281 at first, test saving last-message.txt
282 282
283 283 (test that editor is not invoked before transaction starting)
284 284
285 285 $ cat > .hg/hgrc << '__EOF__'
286 286 > [hooks]
287 287 > # this failure occurs before editor invocation
288 288 > pretag.test-saving-lastmessage = false
289 289 > __EOF__
290 290 $ rm -f .hg/last-message.txt
291 291 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg tag custom-tag -e
292 292 abort: pretag.test-saving-lastmessage hook exited with status 1
293 293 [255]
294 294 $ test -f .hg/last-message.txt
295 295 [1]
296 296
297 297 (test that editor is invoked and commit message is saved into
298 298 "last-message.txt")
299 299
300 300 $ cat >> .hg/hgrc << '__EOF__'
301 301 > [hooks]
302 302 > pretag.test-saving-lastmessage =
303 303 > # this failure occurs after editor invocation
304 304 > pretxncommit.unexpectedabort = false
305 305 > __EOF__
306 306
307 307 (this tests also that editor is invoked, if '--edit' is specified,
308 308 regardless of '--message')
309 309
310 310 $ rm -f .hg/last-message.txt
311 311 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg tag custom-tag -e -m "foo bar"
312 312 ==== before editing
313 313 foo bar
314 314
315 315
316 316 HG: Enter commit message. Lines beginning with 'HG:' are removed.
317 317 HG: Leave message empty to abort commit.
318 318 HG: --
319 319 HG: user: test
320 320 HG: branch 'tag-and-branch-same-name'
321 321 HG: changed .hgtags
322 322 ====
323 323 transaction abort!
324 324 rollback completed
325 325 note: commit message saved in .hg/last-message.txt
326 326 note: use 'hg commit --logfile .hg/last-message.txt --edit' to reuse it
327 327 abort: pretxncommit.unexpectedabort hook exited with status 1
328 328 [255]
329 329 $ cat .hg/last-message.txt
330 330 custom tag message
331 331 second line
332 332
333 333 $ cat >> .hg/hgrc << '__EOF__'
334 334 > [hooks]
335 335 > pretxncommit.unexpectedabort =
336 336 > __EOF__
337 337 $ hg status .hgtags
338 338 M .hgtags
339 339 $ hg revert --no-backup -q .hgtags
340 340
341 341 then, test custom commit message itself
342 342
343 343 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg tag custom-tag -e
344 344 ==== before editing
345 345 Added tag custom-tag for changeset 75a534207be6
346 346
347 347
348 348 HG: Enter commit message. Lines beginning with 'HG:' are removed.
349 349 HG: Leave message empty to abort commit.
350 350 HG: --
351 351 HG: user: test
352 352 HG: branch 'tag-and-branch-same-name'
353 353 HG: changed .hgtags
354 354 ====
355 355 hook: tag changes detected
356 356 hook: +A 75a534207be6b03576e0c7a4fa5708d045f1c876 custom-tag
357 357 $ hg log -l1 --template "{desc}\n"
358 358 custom tag message
359 359 second line
360 360
361 361
362 362 local tag with .hgtags modified
363 363
364 364 $ hg tag hgtags-modified
365 365 hook: tag changes detected
366 366 hook: +A 0f26aaea6f74c3ed6c4aad8844403c9ba128d23a hgtags-modified
367 367 $ hg rollback
368 368 repository tip rolled back to revision 13 (undo commit)
369 369 working directory now based on revision 13
370 370 $ hg st
371 371 M .hgtags
372 372 ? .hgtags.orig
373 373 ? editor.sh
374 374 $ hg tag --local baz
375 375 $ hg revert --no-backup .hgtags
376 376
377 377
378 378 tagging when at named-branch-head that's not a topo-head
379 379
380 380 $ hg up default
381 381 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
382 382 $ hg merge -t internal:local
383 383 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
384 384 (branch merge, don't forget to commit)
385 385 $ hg ci -m 'merge named branch'
386 386 hook: tag changes detected
387 387 hook: -R acb14030fe0a21b60322c440ad2d20cf7685a376 bleah
388 388 hook: -R acb14030fe0a21b60322c440ad2d20cf7685a376 bleah bleah
389 389 hook: -R acb14030fe0a21b60322c440ad2d20cf7685a376 bleah0
390 390 hook: -R acb14030fe0a21b60322c440ad2d20cf7685a376 bleahbleah
391 391 hook: -R 336fccc858a4eb69609a291105009e484a6b6b8d gawk
392 392 $ hg up 13
393 393 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
394 394 $ hg tag new-topo-head
395 395 hook: tag changes detected
396 396 hook: +A 0f26aaea6f74c3ed6c4aad8844403c9ba128d23a new-topo-head
397 397
398 398 tagging on null rev
399 399
400 400 $ hg up null
401 401 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
402 402 $ hg tag nullrev
403 403 abort: working directory is not at a branch head (use -f to force)
404 404 [10]
405 405
406 406 $ hg init empty
407 407 $ hg tag -R empty nullrev
408 408 abort: cannot tag null revision
409 409 [10]
410 410
411 411 $ hg tag -R empty -r 00000000000 -f nulltag
412 412 abort: cannot tag null revision
413 413 [10]
414 414
415 415 issue5539: pruned tags do not appear in .hgtags
416 416
417 417 $ cat >> $HGRCPATH << EOF
418 418 > [experimental]
419 419 > evolution.exchange = True
420 420 > evolution.createmarkers=True
421 421 > EOF
422 422 $ hg up e4d483960b9b --quiet
423 423 $ echo aaa >>a
424 424 $ hg ci -maaa
425 425 $ hg log -r . -T "{node}\n"
426 426 743b3afd5aa69f130c246806e48ad2e699f490d2
427 427 $ hg tag issue5539
428 428 hook: tag changes detected
429 429 hook: +A 743b3afd5aa69f130c246806e48ad2e699f490d2 issue5539
430 430 $ cat .hgtags
431 431 acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
432 432 a0eea09de1eeec777b46f2085260a373b2fbc293 newline
433 433 743b3afd5aa69f130c246806e48ad2e699f490d2 issue5539
434 434 $ hg log -r . -T "{node}\n"
435 435 abeb261f0508ecebcd345ce21e7a25112df417aa
436 436 (mimic 'hg prune' command by obsoleting current changeset and then moving to its parent)
437 437 $ hg debugobsolete abeb261f0508ecebcd345ce21e7a25112df417aa --record-parents
438 438 1 new obsolescence markers
439 439 obsoleted 1 changesets
440 440 $ hg up ".^" --quiet
441 441 $ cat .hgtags
442 442 acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
443 443 a0eea09de1eeec777b46f2085260a373b2fbc293 newline
444 444 $ echo bbb >>a
445 445 $ hg ci -mbbb
446 446 $ hg log -r . -T "{node}\n"
447 447 089dd20da58cae34165c37b064539c6ac0c6a0dd
448 448 $ hg tag issue5539
449 449 hook: tag changes detected
450 450 hook: -M 743b3afd5aa69f130c246806e48ad2e699f490d2 issue5539
451 451 hook: +M 089dd20da58cae34165c37b064539c6ac0c6a0dd issue5539
452 452 $ hg id
453 453 0accf560a709 tip
454 454 $ cat .hgtags
455 455 acb14030fe0a21b60322c440ad2d20cf7685a376 foobar
456 456 a0eea09de1eeec777b46f2085260a373b2fbc293 newline
457 457 089dd20da58cae34165c37b064539c6ac0c6a0dd issue5539
458 458 $ hg tags
459 459 tip 19:0accf560a709
460 460 issue5539 18:089dd20da58c
461 461 new-topo-head 13:0f26aaea6f74
462 462 baz 13:0f26aaea6f74
463 463 custom-tag 12:75a534207be6
464 464 tag-and-branch-same-name 11:fc93d2ea1cd7
465 465 newline 9:a0eea09de1ee
466 466 localnewline 8:c2899151f4e7
467 467 localblah 0:acb14030fe0a
468 468 foobar 0:acb14030fe0a
469 469
470 470 $ cd ..
471 471
472 472 tagging on an uncommitted merge (issue2542)
473 473
474 474 $ hg init repo-tag-uncommitted-merge
475 475 $ cd repo-tag-uncommitted-merge
476 476 $ echo c1 > f1
477 477 $ hg ci -Am0
478 478 adding f1
479 479 $ echo c2 > f2
480 480 $ hg ci -Am1
481 481 adding f2
482 482 $ hg co -q 0
483 483 $ hg branch b1
484 484 marked working directory as branch b1
485 485 (branches are permanent and global, did you want a bookmark?)
486 486 $ hg ci -m2
487 487 $ hg up default
488 488 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
489 489 $ hg merge b1
490 490 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
491 491 (branch merge, don't forget to commit)
492 492
493 493 $ hg tag t1
494 494 abort: uncommitted merge
495 [255]
495 [20]
496 496 $ hg status
497 497 $ hg tag --rev 1 t2
498 498 abort: uncommitted merge
499 [255]
499 [20]
500 500 $ hg tag --rev 1 --local t3
501 501 $ hg tags -v
502 502 tip 2:2a156e8887cc
503 503 t3 1:c3adabd1a5f4 local
504 504
505 505 $ cd ..
506 506
507 507 commit hook on tag used to be run without write lock - issue3344
508 508
509 509 $ hg init repo-tag
510 510 $ touch repo-tag/test
511 511 $ hg -R repo-tag commit -A -m "test"
512 512 adding test
513 513 $ hg init repo-tag-target
514 514 $ cat > "$TESTTMP/issue3344.sh" <<EOF
515 515 > hg push "$TESTTMP/repo-tag-target"
516 516 > EOF
517 517 $ hg -R repo-tag --config hooks.commit="sh ../issue3344.sh" tag tag
518 518 hook: tag changes detected
519 519 hook: +A be090ea6625635128e90f7d89df8beeb2bcc1653 tag
520 520 pushing to $TESTTMP/repo-tag-target
521 521 searching for changes
522 522 adding changesets
523 523 adding manifests
524 524 adding file changes
525 525 added 2 changesets with 2 changes to 2 files
526 526 hook: tag changes detected
527 527 hook: +A be090ea6625635128e90f7d89df8beeb2bcc1653 tag
528 528
529 529 automatically merge resolvable tag conflicts (i.e. tags that differ in rank)
530 530 create two clones with some different tags as well as some common tags
531 531 check that we can merge tags that differ in rank
532 532
533 533 $ hg init repo-automatic-tag-merge
534 534 $ cd repo-automatic-tag-merge
535 535 $ echo c0 > f0
536 536 $ hg ci -A -m0
537 537 adding f0
538 538 $ hg tag tbase
539 539 hook: tag changes detected
540 540 hook: +A 6cee5c8f3e5b4ae1a3996d2f6489c3e08eb5aea7 tbase
541 541 $ hg up -qr '.^'
542 542 $ hg log -r 'wdir()' -T "{latesttagdistance}\n"
543 543 1
544 544 $ hg up -q
545 545 $ hg log -r 'wdir()' -T "{latesttagdistance}\n"
546 546 2
547 547 $ cd ..
548 548 $ hg clone repo-automatic-tag-merge repo-automatic-tag-merge-clone
549 549 updating to branch default
550 550 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
551 551 $ cd repo-automatic-tag-merge-clone
552 552 $ echo c1 > f1
553 553 $ hg ci -A -m1
554 554 adding f1
555 555 $ hg tag t1 t2 t3
556 556 hook: tag changes detected
557 557 hook: +A 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t1
558 558 hook: +A 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t2
559 559 hook: +A 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t3
560 560 $ hg tag --remove t2
561 561 hook: tag changes detected
562 562 hook: -R 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t2
563 563 $ hg tag t5
564 564 hook: tag changes detected
565 565 hook: +A 875517b4806a848f942811a315a5bce30804ae85 t5
566 566 $ echo c2 > f2
567 567 $ hg ci -A -m2
568 568 adding f2
569 569 $ hg tag -f t3
570 570 hook: tag changes detected
571 571 hook: -M 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t3
572 572 hook: +M 79505d5360b07e3e79d1052e347e73c02b8afa5b t3
573 573
574 574 $ cd ../repo-automatic-tag-merge
575 575 $ echo c3 > f3
576 576 $ hg ci -A -m3
577 577 adding f3
578 578 $ hg tag -f t4 t5 t6
579 579 hook: tag changes detected
580 580 hook: +A 9aa4e1292a27a248f8d07339bed9931d54907be7 t4
581 581 hook: +A 9aa4e1292a27a248f8d07339bed9931d54907be7 t5
582 582 hook: +A 9aa4e1292a27a248f8d07339bed9931d54907be7 t6
583 583
584 584 $ hg up -q '.^'
585 585 $ hg log -r 'wdir()' -T "{changessincelatesttag} changes since {latesttag}\n"
586 586 1 changes since t4:t5:t6
587 587 $ hg log -r '.' -T "{changessincelatesttag} changes since {latesttag}\n"
588 588 0 changes since t4:t5:t6
589 589 $ echo c5 > f3
590 590 $ hg log -r 'wdir()' -T "{changessincelatesttag} changes since {latesttag}\n"
591 591 1 changes since t4:t5:t6
592 592 $ hg up -qC
593 593
594 594 $ hg tag --remove t5
595 595 hook: tag changes detected
596 596 hook: -R 9aa4e1292a27a248f8d07339bed9931d54907be7 t5
597 597 $ echo c4 > f4
598 598 $ hg log -r '.' -T "{changessincelatesttag} changes since {latesttag}\n"
599 599 2 changes since t4:t6
600 600 $ hg log -r '.' -T "{latesttag % '{latesttag}\n'}"
601 601 t4
602 602 t6
603 603 $ hg log -r '.' -T "{latesttag('t4') % 'T: {tag}, C: {changes}, D: {distance}\n'}"
604 604 T: t4, C: 2, D: 2
605 605 $ hg log -r '.' -T "{latesttag('re:\d') % 'T: {tag}, C: {changes}, D: {distance}\n'}"
606 606 T: t4, C: 2, D: 2
607 607 T: t6, C: 2, D: 2
608 608 $ hg log -r . -T '{join(latesttag(), "*")}\n'
609 609 t4*t6
610 610 $ hg ci -A -m4
611 611 adding f4
612 612 $ hg log -r 'wdir()' -T "{changessincelatesttag} changes since {latesttag}\n"
613 613 4 changes since t4:t6
614 614 $ hg tag t2
615 615 hook: tag changes detected
616 616 hook: +A 929bca7b18d067cbf3844c3896319a940059d748 t2
617 617 $ hg tag -f t6
618 618 hook: tag changes detected
619 619 hook: -M 9aa4e1292a27a248f8d07339bed9931d54907be7 t6
620 620 hook: +M 09af2ce14077a94effef208b49a718f4836d4338 t6
621 621
622 622 $ cd ../repo-automatic-tag-merge-clone
623 623 $ hg pull
624 624 pulling from $TESTTMP/repo-automatic-tag-merge
625 625 searching for changes
626 626 adding changesets
627 627 adding manifests
628 628 adding file changes
629 629 added 6 changesets with 6 changes to 3 files (+1 heads)
630 630 new changesets 9aa4e1292a27:b325cc5b642c
631 631 hook: tag changes detected
632 632 hook: +A 929bca7b18d067cbf3844c3896319a940059d748 t2
633 633 hook: +A 9aa4e1292a27a248f8d07339bed9931d54907be7 t4
634 634 hook: -R 875517b4806a848f942811a315a5bce30804ae85 t5
635 635 hook: +A 09af2ce14077a94effef208b49a718f4836d4338 t6
636 636 (run 'hg heads' to see heads, 'hg merge' to merge)
637 637 $ hg merge --tool internal:tagmerge
638 638 merging .hgtags
639 639 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
640 640 (branch merge, don't forget to commit)
641 641 $ hg status
642 642 M .hgtags
643 643 M f3
644 644 M f4
645 645 $ hg resolve -l
646 646 R .hgtags
647 647 $ cat .hgtags
648 648 9aa4e1292a27a248f8d07339bed9931d54907be7 t4
649 649 9aa4e1292a27a248f8d07339bed9931d54907be7 t6
650 650 9aa4e1292a27a248f8d07339bed9931d54907be7 t6
651 651 09af2ce14077a94effef208b49a718f4836d4338 t6
652 652 6cee5c8f3e5b4ae1a3996d2f6489c3e08eb5aea7 tbase
653 653 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t1
654 654 929bca7b18d067cbf3844c3896319a940059d748 t2
655 655 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t2
656 656 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t3
657 657 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t2
658 658 0000000000000000000000000000000000000000 t2
659 659 875517b4806a848f942811a315a5bce30804ae85 t5
660 660 9aa4e1292a27a248f8d07339bed9931d54907be7 t5
661 661 9aa4e1292a27a248f8d07339bed9931d54907be7 t5
662 662 0000000000000000000000000000000000000000 t5
663 663 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t3
664 664 79505d5360b07e3e79d1052e347e73c02b8afa5b t3
665 665
666 666 check that the merge tried to minimize the diff with the first merge parent
667 667
668 668 $ hg diff --git -r 'p1()' .hgtags
669 669 diff --git a/.hgtags b/.hgtags
670 670 --- a/.hgtags
671 671 +++ b/.hgtags
672 672 @@ -1,9 +1,17 @@
673 673 +9aa4e1292a27a248f8d07339bed9931d54907be7 t4
674 674 +9aa4e1292a27a248f8d07339bed9931d54907be7 t6
675 675 +9aa4e1292a27a248f8d07339bed9931d54907be7 t6
676 676 +09af2ce14077a94effef208b49a718f4836d4338 t6
677 677 6cee5c8f3e5b4ae1a3996d2f6489c3e08eb5aea7 tbase
678 678 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t1
679 679 +929bca7b18d067cbf3844c3896319a940059d748 t2
680 680 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t2
681 681 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t3
682 682 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t2
683 683 0000000000000000000000000000000000000000 t2
684 684 875517b4806a848f942811a315a5bce30804ae85 t5
685 685 +9aa4e1292a27a248f8d07339bed9931d54907be7 t5
686 686 +9aa4e1292a27a248f8d07339bed9931d54907be7 t5
687 687 +0000000000000000000000000000000000000000 t5
688 688 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t3
689 689 79505d5360b07e3e79d1052e347e73c02b8afa5b t3
690 690
691 691 detect merge tag conflicts
692 692
693 693 $ hg update -C -r tip
694 694 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
695 695 $ hg tag t7
696 696 hook: tag changes detected
697 697 hook: +A b325cc5b642c5b465bdbe8c09627cb372de3d47d t7
698 698 $ hg update -C -r 'first(sort(head()))'
699 699 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
700 700 $ printf "%s %s\n" `hg log -r . --template "{node} t7"` >> .hgtags
701 701 $ hg commit -m "manually add conflicting t7 tag"
702 702 hook: tag changes detected
703 703 hook: -R 929bca7b18d067cbf3844c3896319a940059d748 t2
704 704 hook: +A 875517b4806a848f942811a315a5bce30804ae85 t5
705 705 hook: -M b325cc5b642c5b465bdbe8c09627cb372de3d47d t7
706 706 hook: +M ea918d56be86a4afc5a95312e8b6750e1428d9d2 t7
707 707 $ hg merge --tool internal:tagmerge
708 708 merging .hgtags
709 709 automatic .hgtags merge failed
710 710 the following 1 tags are in conflict: t7
711 711 automatic tag merging of .hgtags failed! (use 'hg resolve --tool :merge' or another merge tool of your choice)
712 712 2 files updated, 0 files merged, 0 files removed, 1 files unresolved
713 713 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
714 714 [1]
715 715 $ hg resolve -l
716 716 U .hgtags
717 717 $ cat .hgtags
718 718 6cee5c8f3e5b4ae1a3996d2f6489c3e08eb5aea7 tbase
719 719 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t1
720 720 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t2
721 721 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t3
722 722 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t2
723 723 0000000000000000000000000000000000000000 t2
724 724 875517b4806a848f942811a315a5bce30804ae85 t5
725 725 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t3
726 726 79505d5360b07e3e79d1052e347e73c02b8afa5b t3
727 727 ea918d56be86a4afc5a95312e8b6750e1428d9d2 t7
728 728
729 729 $ cd ..
730 730
731 731 handle the loss of tags
732 732
733 733 $ hg clone repo-automatic-tag-merge-clone repo-merge-lost-tags
734 734 updating to branch default
735 735 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
736 736 $ cd repo-merge-lost-tags
737 737 $ echo c5 > f5
738 738 $ hg ci -A -m5
739 739 adding f5
740 740 $ hg tag -f t7
741 741 hook: tag changes detected
742 742 hook: -M ea918d56be86a4afc5a95312e8b6750e1428d9d2 t7
743 743 hook: +M fd3a9e394ce3afb354a496323bf68ac1755a30de t7
744 744 $ hg update -r 'p1(t7)'
745 745 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
746 746 $ printf '' > .hgtags
747 747 $ hg commit -m 'delete all tags'
748 748 created new head
749 749 $ hg log -r 'max(t7::)'
750 750 changeset: 17:ffe462b50880
751 751 user: test
752 752 date: Thu Jan 01 00:00:00 1970 +0000
753 753 summary: Added tag t7 for changeset fd3a9e394ce3
754 754
755 755 $ hg update -r 'max(t7::)'
756 756 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
757 757 $ hg merge -r tip --tool internal:tagmerge
758 758 merging .hgtags
759 759 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
760 760 (branch merge, don't forget to commit)
761 761 $ hg resolve -l
762 762 R .hgtags
763 763 $ cat .hgtags
764 764 6cee5c8f3e5b4ae1a3996d2f6489c3e08eb5aea7 tbase
765 765 0000000000000000000000000000000000000000 tbase
766 766 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t1
767 767 0000000000000000000000000000000000000000 t1
768 768 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t2
769 769 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t3
770 770 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t2
771 771 0000000000000000000000000000000000000000 t2
772 772 875517b4806a848f942811a315a5bce30804ae85 t5
773 773 0000000000000000000000000000000000000000 t5
774 774 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t3
775 775 79505d5360b07e3e79d1052e347e73c02b8afa5b t3
776 776 0000000000000000000000000000000000000000 t3
777 777 ea918d56be86a4afc5a95312e8b6750e1428d9d2 t7
778 778 0000000000000000000000000000000000000000 t7
779 779 ea918d56be86a4afc5a95312e8b6750e1428d9d2 t7
780 780 fd3a9e394ce3afb354a496323bf68ac1755a30de t7
781 781
782 782 also check that we minimize the diff with the 1st merge parent
783 783
784 784 $ hg diff --git -r 'p1()' .hgtags
785 785 diff --git a/.hgtags b/.hgtags
786 786 --- a/.hgtags
787 787 +++ b/.hgtags
788 788 @@ -1,12 +1,17 @@
789 789 6cee5c8f3e5b4ae1a3996d2f6489c3e08eb5aea7 tbase
790 790 +0000000000000000000000000000000000000000 tbase
791 791 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t1
792 792 +0000000000000000000000000000000000000000 t1
793 793 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t2
794 794 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t3
795 795 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t2
796 796 0000000000000000000000000000000000000000 t2
797 797 875517b4806a848f942811a315a5bce30804ae85 t5
798 798 +0000000000000000000000000000000000000000 t5
799 799 4f3e9b90005b68b4d8a3f4355cedc302a8364f5c t3
800 800 79505d5360b07e3e79d1052e347e73c02b8afa5b t3
801 801 +0000000000000000000000000000000000000000 t3
802 802 ea918d56be86a4afc5a95312e8b6750e1428d9d2 t7
803 803 +0000000000000000000000000000000000000000 t7
804 804 ea918d56be86a4afc5a95312e8b6750e1428d9d2 t7
805 805 fd3a9e394ce3afb354a496323bf68ac1755a30de t7
806 806
@@ -1,1107 +1,1107 b''
1 1 #testcases commandmode continueflag
2 2 $ cat <<EOF >> $HGRCPATH
3 3 > [extensions]
4 4 > transplant=
5 5 > graphlog=
6 6 > EOF
7 7
8 8 #if continueflag
9 9 $ cat >> $HGRCPATH <<EOF
10 10 > [alias]
11 11 > continue = transplant --continue
12 12 > EOF
13 13 #endif
14 14
15 15 $ hg init t
16 16 $ cd t
17 17 $ hg transplant
18 18 abort: no source URL, branch revision, or revision list provided
19 19 [255]
20 20 $ hg transplant --continue --all
21 21 abort: cannot specify both --continue and --all
22 22 [10]
23 23 $ hg transplant --stop --all
24 24 abort: cannot specify both --stop and --all
25 25 [10]
26 26 $ hg transplant --all tip
27 27 abort: --all requires a branch revision
28 28 [255]
29 29 $ hg transplant --all --branch default tip
30 30 abort: --all is incompatible with a revision list
31 31 [255]
32 32 $ echo r1 > r1
33 33 $ hg ci -Amr1 -d'0 0'
34 34 adding r1
35 35 $ hg co -q null
36 36 $ hg transplant tip
37 37 abort: no revision checked out
38 38 [255]
39 39 $ hg up -q
40 40 $ echo r2 > r2
41 41 $ hg ci -Amr2 -d'1 0'
42 42 adding r2
43 43 $ hg up 0
44 44 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
45 45
46 46 $ echo b1 > b1
47 47 $ hg ci -Amb1 -d '0 0'
48 48 adding b1
49 49 created new head
50 50 $ hg merge 1
51 51 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
52 52 (branch merge, don't forget to commit)
53 53 $ hg transplant 1
54 54 abort: outstanding uncommitted merge
55 55 (use 'hg commit' or 'hg merge --abort')
56 [255]
56 [20]
57 57 $ hg up -qC tip
58 58 $ echo b0 > b1
59 59 $ hg transplant 1
60 60 abort: uncommitted changes
61 [255]
61 [20]
62 62 $ hg up -qC tip
63 63 $ echo b2 > b2
64 64 $ hg ci -Amb2 -d '1 0'
65 65 adding b2
66 66 $ echo b3 > b3
67 67 $ hg ci -Amb3 -d '2 0'
68 68 adding b3
69 69
70 70 $ hg log --template '{rev} {parents} {desc}\n'
71 71 4 b3
72 72 3 b2
73 73 2 0:17ab29e464c6 b1
74 74 1 r2
75 75 0 r1
76 76
77 77 $ hg clone . ../rebase
78 78 updating to branch default
79 79 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
80 80 $ hg init ../emptydest
81 81 $ cd ../emptydest
82 82 $ hg transplant --source=../t > /dev/null
83 83 $ cd ../rebase
84 84
85 85 $ hg up -C 1
86 86 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
87 87
88 88 rebase b onto r1
89 89 (this also tests that editor is not invoked if '--edit' is not specified)
90 90
91 91 $ HGEDITOR=cat hg transplant -a -b tip
92 92 applying 37a1297eb21b
93 93 37a1297eb21b transplanted to e234d668f844
94 94 applying 722f4667af76
95 95 722f4667af76 transplanted to 539f377d78df
96 96 applying a53251cdf717
97 97 a53251cdf717 transplanted to ffd6818a3975
98 98 $ hg log --template '{rev} {parents} {desc}\n'
99 99 7 b3
100 100 6 b2
101 101 5 1:d11e3596cc1a b1
102 102 4 b3
103 103 3 b2
104 104 2 0:17ab29e464c6 b1
105 105 1 r2
106 106 0 r1
107 107
108 108 test format of transplant_source
109 109
110 110 $ hg log -r7 --debug | grep transplant_source
111 111 extra: transplant_source=\xa52Q\xcd\xf7\x17g\x9d\x19\x07\xb2\x89\xf9\x91SK\xe0\\\x99z
112 112 $ hg log -r7 -T '{extras}\n'
113 113 branch=defaulttransplant_source=\xa52Q\xcd\xf7\x17g\x9d\x19\x07\xb2\x89\xf9\x91SK\xe0\\\x99z
114 114 $ hg log -r7 -T '{join(extras, " ")}\n'
115 115 branch=default transplant_source=\xa52Q\xcd\xf7\x17g\x9d\x19\x07\xb2\x89\xf9\x91SK\xe0\\\x99z
116 116
117 117 test transplanted revset
118 118
119 119 $ hg log -r 'transplanted()' --template '{rev} {parents} {desc}\n'
120 120 5 1:d11e3596cc1a b1
121 121 6 b2
122 122 7 b3
123 123 $ hg log -r 'transplanted(head())' --template '{rev} {parents} {desc}\n'
124 124 7 b3
125 125 $ hg help revisions.transplanted
126 126 "transplanted([set])"
127 127 Transplanted changesets in set, or all transplanted changesets.
128 128
129 129
130 130 test transplanted keyword
131 131
132 132 $ hg log --template '{rev} {transplanted}\n'
133 133 7 a53251cdf717679d1907b289f991534be05c997a
134 134 6 722f4667af767100cb15b6a79324bf8abbfe1ef4
135 135 5 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21
136 136 4
137 137 3
138 138 2
139 139 1
140 140 0
141 141
142 142 test destination() revset predicate with a transplant of a transplant; new
143 143 clone so subsequent rollback isn't affected
144 144 (this also tests that editor is invoked if '--edit' is specified)
145 145
146 146 $ hg clone -q . ../destination
147 147 $ cd ../destination
148 148 $ hg up -Cq 0
149 149 $ hg branch -q b4
150 150 $ hg ci -qm "b4"
151 151 $ hg status --rev "7^1" --rev 7
152 152 A b3
153 153 $ cat > $TESTTMP/checkeditform.sh <<EOF
154 154 > env | grep HGEDITFORM
155 155 > true
156 156 > EOF
157 157 $ cat > $TESTTMP/checkeditform-n-cat.sh <<EOF
158 158 > env | grep HGEDITFORM
159 159 > cat \$*
160 160 > EOF
161 161 $ HGEDITOR="sh $TESTTMP/checkeditform-n-cat.sh" hg transplant --edit 7
162 162 applying ffd6818a3975
163 163 HGEDITFORM=transplant.normal
164 164 b3
165 165
166 166
167 167 HG: Enter commit message. Lines beginning with 'HG:' are removed.
168 168 HG: Leave message empty to abort commit.
169 169 HG: --
170 170 HG: user: test
171 171 HG: branch 'b4'
172 172 HG: added b3
173 173 ffd6818a3975 transplanted to 502236fa76bb
174 174
175 175
176 176 $ hg log -r 'destination()'
177 177 changeset: 5:e234d668f844
178 178 parent: 1:d11e3596cc1a
179 179 user: test
180 180 date: Thu Jan 01 00:00:00 1970 +0000
181 181 summary: b1
182 182
183 183 changeset: 6:539f377d78df
184 184 user: test
185 185 date: Thu Jan 01 00:00:01 1970 +0000
186 186 summary: b2
187 187
188 188 changeset: 7:ffd6818a3975
189 189 user: test
190 190 date: Thu Jan 01 00:00:02 1970 +0000
191 191 summary: b3
192 192
193 193 changeset: 9:502236fa76bb
194 194 branch: b4
195 195 tag: tip
196 196 user: test
197 197 date: Thu Jan 01 00:00:02 1970 +0000
198 198 summary: b3
199 199
200 200 $ hg log -r 'destination(a53251cdf717)'
201 201 changeset: 7:ffd6818a3975
202 202 user: test
203 203 date: Thu Jan 01 00:00:02 1970 +0000
204 204 summary: b3
205 205
206 206 changeset: 9:502236fa76bb
207 207 branch: b4
208 208 tag: tip
209 209 user: test
210 210 date: Thu Jan 01 00:00:02 1970 +0000
211 211 summary: b3
212 212
213 213
214 214 test subset parameter in reverse order
215 215 $ hg log -r 'reverse(all()) and destination(a53251cdf717)'
216 216 changeset: 9:502236fa76bb
217 217 branch: b4
218 218 tag: tip
219 219 user: test
220 220 date: Thu Jan 01 00:00:02 1970 +0000
221 221 summary: b3
222 222
223 223 changeset: 7:ffd6818a3975
224 224 user: test
225 225 date: Thu Jan 01 00:00:02 1970 +0000
226 226 summary: b3
227 227
228 228
229 229 back to the original dir
230 230 $ cd ../rebase
231 231
232 232 rollback the transplant
233 233 $ hg rollback
234 234 repository tip rolled back to revision 4 (undo transplant)
235 235 working directory now based on revision 1
236 236 $ hg tip -q
237 237 4:a53251cdf717
238 238 $ hg parents -q
239 239 1:d11e3596cc1a
240 240 $ hg status
241 241 ? b1
242 242 ? b2
243 243 ? b3
244 244
245 245 $ hg clone ../t ../prune
246 246 updating to branch default
247 247 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
248 248 $ cd ../prune
249 249
250 250 $ hg up -C 1
251 251 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
252 252
253 253 rebase b onto r1, skipping b2
254 254
255 255 $ hg transplant -a -b tip -p 3
256 256 applying 37a1297eb21b
257 257 37a1297eb21b transplanted to e234d668f844
258 258 applying a53251cdf717
259 259 a53251cdf717 transplanted to 7275fda4d04f
260 260 $ hg log --template '{rev} {parents} {desc}\n'
261 261 6 b3
262 262 5 1:d11e3596cc1a b1
263 263 4 b3
264 264 3 b2
265 265 2 0:17ab29e464c6 b1
266 266 1 r2
267 267 0 r1
268 268
269 269 test same-parent transplant with --log
270 270
271 271 $ hg clone -r 1 ../t ../sameparent
272 272 adding changesets
273 273 adding manifests
274 274 adding file changes
275 275 added 2 changesets with 2 changes to 2 files
276 276 new changesets 17ab29e464c6:d11e3596cc1a
277 277 updating to branch default
278 278 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
279 279 $ cd ../sameparent
280 280 $ hg transplant --log -s ../prune 5
281 281 searching for changes
282 282 applying e234d668f844
283 283 e234d668f844 transplanted to e07aea8ecf9c
284 284 $ hg log --template '{rev} {parents} {desc}\n'
285 285 2 b1
286 286 (transplanted from e234d668f844e1b1a765f01db83a32c0c7bfa170)
287 287 1 r2
288 288 0 r1
289 289 remote transplant, and also test that transplant doesn't break with
290 290 format-breaking diffopts
291 291
292 292 $ hg clone -r 1 ../t ../remote
293 293 adding changesets
294 294 adding manifests
295 295 adding file changes
296 296 added 2 changesets with 2 changes to 2 files
297 297 new changesets 17ab29e464c6:d11e3596cc1a
298 298 updating to branch default
299 299 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
300 300 $ cd ../remote
301 301 $ hg --config diff.noprefix=True transplant --log -s ../t 2 4
302 302 searching for changes
303 303 applying 37a1297eb21b
304 304 37a1297eb21b transplanted to c19cf0ccb069
305 305 applying a53251cdf717
306 306 a53251cdf717 transplanted to f7fe5bf98525
307 307 $ hg log --template '{rev} {parents} {desc}\n'
308 308 3 b3
309 309 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
310 310 2 b1
311 311 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
312 312 1 r2
313 313 0 r1
314 314
315 315 skip previous transplants
316 316
317 317 $ hg transplant -s ../t -a -b 4
318 318 searching for changes
319 319 applying 722f4667af76
320 320 722f4667af76 transplanted to 47156cd86c0b
321 321 $ hg log --template '{rev} {parents} {desc}\n'
322 322 4 b2
323 323 3 b3
324 324 (transplanted from a53251cdf717679d1907b289f991534be05c997a)
325 325 2 b1
326 326 (transplanted from 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21)
327 327 1 r2
328 328 0 r1
329 329
330 330 skip local changes transplanted to the source
331 331
332 332 $ echo b4 > b4
333 333 $ hg ci -Amb4 -d '3 0'
334 334 adding b4
335 335 $ hg clone ../t ../pullback
336 336 updating to branch default
337 337 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
338 338 $ cd ../pullback
339 339 $ hg transplant -s ../remote -a -b tip
340 340 searching for changes
341 341 applying 4333daefcb15
342 342 4333daefcb15 transplanted to 5f42c04e07cc
343 343
344 344
345 345 remote transplant with pull
346 346
347 347 $ hg serve -R ../t -p $HGPORT -d --pid-file=../t.pid
348 348 $ cat ../t.pid >> $DAEMON_PIDS
349 349
350 350 $ hg clone -r 0 ../t ../rp
351 351 adding changesets
352 352 adding manifests
353 353 adding file changes
354 354 added 1 changesets with 1 changes to 1 files
355 355 new changesets 17ab29e464c6
356 356 updating to branch default
357 357 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
358 358 $ cd ../rp
359 359 $ hg transplant -s http://localhost:$HGPORT/ 37a1297eb21b a53251cdf717
360 360 searching for changes
361 361 searching for changes
362 362 adding changesets
363 363 adding manifests
364 364 adding file changes
365 365 applying a53251cdf717
366 366 a53251cdf717 transplanted to 8d9279348abb
367 367 added 1 changesets with 1 changes to 1 files
368 368 $ hg log --template '{rev} {parents} {desc}\n'
369 369 2 b3
370 370 1 b1
371 371 0 r1
372 372
373 373 remote transplant without pull
374 374 (It was using "2" and "4" (as the previous transplant used to) which referenced
375 375 revision different from one run to another)
376 376
377 377 $ hg pull -q http://localhost:$HGPORT/
378 378 $ hg transplant -s http://localhost:$HGPORT/ 8d9279348abb 722f4667af76
379 379 skipping already applied revision 2:8d9279348abb
380 380 applying 722f4667af76
381 381 722f4667af76 transplanted to 76e321915884
382 382
383 383
384 384 transplant --continue and --stop behaviour
385 385
386 386 $ hg init ../tc
387 387 $ cd ../tc
388 388 $ cat <<EOF > foo
389 389 > foo
390 390 > bar
391 391 > baz
392 392 > EOF
393 393 $ echo toremove > toremove
394 394 $ echo baz > baz
395 395 $ hg ci -Amfoo
396 396 adding baz
397 397 adding foo
398 398 adding toremove
399 399 $ cat <<EOF > foo
400 400 > foo2
401 401 > bar2
402 402 > baz2
403 403 > EOF
404 404 $ rm toremove
405 405 $ echo added > added
406 406 $ hg ci -Amfoo2
407 407 adding added
408 408 removing toremove
409 409 $ echo bar > bar
410 410 $ cat > baz <<EOF
411 411 > before baz
412 412 > baz
413 413 > after baz
414 414 > EOF
415 415 $ hg ci -Ambar
416 416 adding bar
417 417 $ echo bar2 >> bar
418 418 $ hg ci -mbar2
419 419 $ hg up 0
420 420 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
421 421 $ echo foobar > foo
422 422 $ hg ci -mfoobar
423 423 created new head
424 424
425 425 Repo log before transplant
426 426 $ hg glog
427 427 @ changeset: 4:e8643552fde5
428 428 | tag: tip
429 429 | parent: 0:493149fa1541
430 430 | user: test
431 431 | date: Thu Jan 01 00:00:00 1970 +0000
432 432 | summary: foobar
433 433 |
434 434 | o changeset: 3:1dab759070cf
435 435 | | user: test
436 436 | | date: Thu Jan 01 00:00:00 1970 +0000
437 437 | | summary: bar2
438 438 | |
439 439 | o changeset: 2:9d6d6b5a8275
440 440 | | user: test
441 441 | | date: Thu Jan 01 00:00:00 1970 +0000
442 442 | | summary: bar
443 443 | |
444 444 | o changeset: 1:46ae92138f3c
445 445 |/ user: test
446 446 | date: Thu Jan 01 00:00:00 1970 +0000
447 447 | summary: foo2
448 448 |
449 449 o changeset: 0:493149fa1541
450 450 user: test
451 451 date: Thu Jan 01 00:00:00 1970 +0000
452 452 summary: foo
453 453
454 454 $ hg transplant 1:3
455 455 applying 46ae92138f3c
456 456 patching file foo
457 457 Hunk #1 FAILED at 0
458 458 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
459 459 patch failed to apply
460 460 abort: fix up the working directory and run hg transplant --continue
461 461 [255]
462 462
463 463 $ hg transplant --stop
464 464 stopped the interrupted transplant
465 465 working directory is now at e8643552fde5
466 466 Repo log after abort
467 467 $ hg glog
468 468 @ changeset: 4:e8643552fde5
469 469 | tag: tip
470 470 | parent: 0:493149fa1541
471 471 | user: test
472 472 | date: Thu Jan 01 00:00:00 1970 +0000
473 473 | summary: foobar
474 474 |
475 475 | o changeset: 3:1dab759070cf
476 476 | | user: test
477 477 | | date: Thu Jan 01 00:00:00 1970 +0000
478 478 | | summary: bar2
479 479 | |
480 480 | o changeset: 2:9d6d6b5a8275
481 481 | | user: test
482 482 | | date: Thu Jan 01 00:00:00 1970 +0000
483 483 | | summary: bar
484 484 | |
485 485 | o changeset: 1:46ae92138f3c
486 486 |/ user: test
487 487 | date: Thu Jan 01 00:00:00 1970 +0000
488 488 | summary: foo2
489 489 |
490 490 o changeset: 0:493149fa1541
491 491 user: test
492 492 date: Thu Jan 01 00:00:00 1970 +0000
493 493 summary: foo
494 494
495 495 $ hg transplant 1:3
496 496 applying 46ae92138f3c
497 497 file added already exists
498 498 1 out of 1 hunks FAILED -- saving rejects to file added.rej
499 499 patching file foo
500 500 Hunk #1 FAILED at 0
501 501 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
502 502 patch failed to apply
503 503 abort: fix up the working directory and run hg transplant --continue
504 504 [255]
505 505
506 506 transplant -c shouldn't use an old changeset
507 507
508 508 $ hg up -C
509 509 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
510 510 updated to "e8643552fde5: foobar"
511 511 1 other heads for branch "default"
512 512 $ rm added
513 513 $ hg continue
514 514 abort: no transplant to continue (continueflag !)
515 515 abort: no operation in progress (no-continueflag !)
516 [255]
516 [20]
517 517 $ hg transplant --stop
518 518 abort: no interrupted transplant found
519 [255]
519 [20]
520 520 $ hg transplant 1
521 521 applying 46ae92138f3c
522 522 patching file foo
523 523 Hunk #1 FAILED at 0
524 524 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
525 525 patch failed to apply
526 526 abort: fix up the working directory and run hg transplant --continue
527 527 [255]
528 528 $ cp .hg/transplant/journal .hg/transplant/journal.orig
529 529 $ cat .hg/transplant/journal
530 530 # User test
531 531 # Date 0 0
532 532 # Node ID 46ae92138f3ce0249f6789650403286ead052b6d
533 533 # Parent e8643552fde58f57515e19c4b373a57c96e62af3
534 534 foo2
535 535 $ grep -v 'Date' .hg/transplant/journal.orig > .hg/transplant/journal
536 536 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant --continue -e
537 537 abort: filter corrupted changeset (no user or date)
538 538 [255]
539 539 $ cp .hg/transplant/journal.orig .hg/transplant/journal
540 540 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant --continue -e
541 541 HGEDITFORM=transplant.normal
542 542 46ae92138f3c transplanted as 9159dada197d
543 543 $ hg transplant 1:3
544 544 skipping already applied revision 1:46ae92138f3c
545 545 applying 9d6d6b5a8275
546 546 9d6d6b5a8275 transplanted to 2d17a10c922f
547 547 applying 1dab759070cf
548 548 1dab759070cf transplanted to e06a69927eb0
549 549 $ hg locate
550 550 added
551 551 bar
552 552 baz
553 553 foo
554 554
555 555 test multiple revisions, --continue and hg status --verbose
556 556
557 557 $ hg up -qC 0
558 558 $ echo bazbaz > baz
559 559 $ hg ci -Am anotherbaz baz
560 560 created new head
561 561 $ hg transplant 1:3
562 562 applying 46ae92138f3c
563 563 46ae92138f3c transplanted to 1024233ea0ba
564 564 applying 9d6d6b5a8275
565 565 patching file baz
566 566 Hunk #1 FAILED at 0
567 567 1 out of 1 hunks FAILED -- saving rejects to file baz.rej
568 568 patch failed to apply
569 569 abort: fix up the working directory and run hg transplant --continue
570 570 [255]
571 571 $ hg transplant 1:3
572 572 abort: transplant in progress
573 573 (use 'hg transplant --continue' or 'hg transplant --stop')
574 [255]
574 [20]
575 575 $ hg status -v
576 576 A bar
577 577 ? added.rej
578 578 ? baz.rej
579 579 ? foo.rej
580 580 # The repository is in an unfinished *transplant* state.
581 581
582 582 # To continue: hg transplant --continue
583 583 # To stop: hg transplant --stop
584 584
585 585 $ echo fixed > baz
586 586 $ hg continue
587 587 9d6d6b5a8275 transplanted as d80c49962290
588 588 applying 1dab759070cf
589 589 1dab759070cf transplanted to aa0ffe6bd5ae
590 590 $ cd ..
591 591
592 592 Issue1111: Test transplant --merge
593 593
594 594 $ hg init t1111
595 595 $ cd t1111
596 596 $ echo a > a
597 597 $ hg ci -Am adda
598 598 adding a
599 599 $ echo b >> a
600 600 $ hg ci -m appendb
601 601 $ echo c >> a
602 602 $ hg ci -m appendc
603 603 $ hg up -C 0
604 604 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
605 605 $ echo d >> a
606 606 $ hg ci -m appendd
607 607 created new head
608 608
609 609 transplant
610 610
611 611 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant -m 1 -e
612 612 applying 42dc4432fd35
613 613 HGEDITFORM=transplant.merge
614 614 1:42dc4432fd35 merged at a9f4acbac129
615 615 $ hg update -q -C 2
616 616 $ cat > a <<EOF
617 617 > x
618 618 > y
619 619 > z
620 620 > EOF
621 621 $ hg commit -m replace
622 622 $ hg update -q -C 4
623 623 $ hg transplant -m 5
624 624 applying 600a3cdcb41d
625 625 patching file a
626 626 Hunk #1 FAILED at 0
627 627 1 out of 1 hunks FAILED -- saving rejects to file a.rej
628 628 patch failed to apply
629 629 abort: fix up the working directory and run hg transplant --continue
630 630 [255]
631 631 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg transplant --continue -e
632 632 HGEDITFORM=transplant.merge
633 633 600a3cdcb41d transplanted as a3f88be652e0
634 634
635 635 $ cd ..
636 636
637 637 test transplant into empty repository
638 638
639 639 $ hg init empty
640 640 $ cd empty
641 641 $ hg transplant -s ../t -b tip -a
642 642 adding changesets
643 643 adding manifests
644 644 adding file changes
645 645 added 4 changesets with 4 changes to 4 files
646 646 new changesets 17ab29e464c6:a53251cdf717
647 647
648 648 test "--merge" causing pull from source repository on local host
649 649
650 650 $ hg --config extensions.mq= -q strip 2
651 651 $ hg transplant -s ../t --merge tip
652 652 searching for changes
653 653 searching for changes
654 654 adding changesets
655 655 adding manifests
656 656 adding file changes
657 657 applying a53251cdf717
658 658 4:a53251cdf717 merged at 4831f4dc831a
659 659 added 2 changesets with 2 changes to 2 files
660 660
661 661 test interactive transplant
662 662
663 663 $ hg --config extensions.strip= -q strip 0
664 664 $ hg -R ../t log -G --template "{rev}:{node|short}"
665 665 @ 4:a53251cdf717
666 666 |
667 667 o 3:722f4667af76
668 668 |
669 669 o 2:37a1297eb21b
670 670 |
671 671 | o 1:d11e3596cc1a
672 672 |/
673 673 o 0:17ab29e464c6
674 674
675 675 $ hg transplant -q --config ui.interactive=true -s ../t <<EOF
676 676 > ?
677 677 > x
678 678 > q
679 679 > EOF
680 680 0:17ab29e464c6
681 681 apply changeset? [ynmpcq?]: ?
682 682 y: yes, transplant this changeset
683 683 n: no, skip this changeset
684 684 m: merge at this changeset
685 685 p: show patch
686 686 c: commit selected changesets
687 687 q: quit and cancel transplant
688 688 ?: ? (show this help)
689 689 apply changeset? [ynmpcq?]: x
690 690 unrecognized response
691 691 apply changeset? [ynmpcq?]: q
692 692 $ hg transplant -q --config ui.interactive=true -s ../t <<EOF
693 693 > p
694 694 > y
695 695 > n
696 696 > n
697 697 > m
698 698 > c
699 699 > EOF
700 700 0:17ab29e464c6
701 701 apply changeset? [ynmpcq?]: p
702 702 diff -r 000000000000 -r 17ab29e464c6 r1
703 703 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
704 704 +++ b/r1 Thu Jan 01 00:00:00 1970 +0000
705 705 @@ -0,0 +1,1 @@
706 706 +r1
707 707 apply changeset? [ynmpcq?]: y
708 708 1:d11e3596cc1a
709 709 apply changeset? [ynmpcq?]: n
710 710 2:37a1297eb21b
711 711 apply changeset? [ynmpcq?]: n
712 712 3:722f4667af76
713 713 apply changeset? [ynmpcq?]: m
714 714 4:a53251cdf717
715 715 apply changeset? [ynmpcq?]: c
716 716 $ hg log -G --template "{node|short}"
717 717 @ 88be5dde5260
718 718 |\
719 719 | o 722f4667af76
720 720 | |
721 721 | o 37a1297eb21b
722 722 |/
723 723 o 17ab29e464c6
724 724
725 725 $ hg transplant -q --config ui.interactive=true -s ../t <<EOF
726 726 > x
727 727 > ?
728 728 > y
729 729 > q
730 730 > EOF
731 731 1:d11e3596cc1a
732 732 apply changeset? [ynmpcq?]: x
733 733 unrecognized response
734 734 apply changeset? [ynmpcq?]: ?
735 735 y: yes, transplant this changeset
736 736 n: no, skip this changeset
737 737 m: merge at this changeset
738 738 p: show patch
739 739 c: commit selected changesets
740 740 q: quit and cancel transplant
741 741 ?: ? (show this help)
742 742 apply changeset? [ynmpcq?]: y
743 743 4:a53251cdf717
744 744 apply changeset? [ynmpcq?]: q
745 745 $ hg heads --template "{node|short}\n"
746 746 88be5dde5260
747 747
748 748 $ cd ..
749 749
750 750
751 751 #if unix-permissions system-sh
752 752
753 753 test filter
754 754
755 755 $ hg init filter
756 756 $ cd filter
757 757 $ cat <<'EOF' >test-filter
758 758 > #!/bin/sh
759 759 > sed 's/r1/r2/' $1 > $1.new
760 760 > mv $1.new $1
761 761 > EOF
762 762 $ chmod +x test-filter
763 763 $ hg transplant -s ../t -b tip -a --filter ./test-filter
764 764 filtering * (glob)
765 765 applying 17ab29e464c6
766 766 17ab29e464c6 transplanted to e9ffc54ea104
767 767 filtering * (glob)
768 768 applying 37a1297eb21b
769 769 37a1297eb21b transplanted to 348b36d0b6a5
770 770 filtering * (glob)
771 771 applying 722f4667af76
772 772 722f4667af76 transplanted to 0aa6979afb95
773 773 filtering * (glob)
774 774 applying a53251cdf717
775 775 a53251cdf717 transplanted to 14f8512272b5
776 776 $ hg log --template '{rev} {parents} {desc}\n'
777 777 3 b3
778 778 2 b2
779 779 1 b1
780 780 0 r2
781 781 $ cd ..
782 782
783 783
784 784 test filter with failed patch
785 785
786 786 $ cd filter
787 787 $ hg up 0
788 788 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
789 789 $ echo foo > b1
790 790 $ hg ci -Am foo
791 791 adding b1
792 792 adding test-filter
793 793 created new head
794 794 $ hg transplant 1 --filter ./test-filter
795 795 filtering * (glob)
796 796 applying 348b36d0b6a5
797 797 file b1 already exists
798 798 1 out of 1 hunks FAILED -- saving rejects to file b1.rej
799 799 patch failed to apply
800 800 abort: fix up the working directory and run hg transplant --continue
801 801 [255]
802 802 $ cd ..
803 803
804 804 test environment passed to filter
805 805
806 806 $ hg init filter-environment
807 807 $ cd filter-environment
808 808 $ cat <<'EOF' >test-filter-environment
809 809 > #!/bin/sh
810 810 > echo "Transplant by $HGUSER" >> $1
811 811 > echo "Transplant from rev $HGREVISION" >> $1
812 812 > EOF
813 813 $ chmod +x test-filter-environment
814 814 $ hg transplant -s ../t --filter ./test-filter-environment 0
815 815 filtering * (glob)
816 816 applying 17ab29e464c6
817 817 17ab29e464c6 transplanted to 5190e68026a0
818 818
819 819 $ hg log --template '{rev} {parents} {desc}\n'
820 820 0 r1
821 821 Transplant by test
822 822 Transplant from rev 17ab29e464c6ca53e329470efe2a9918ac617a6f
823 823 $ cd ..
824 824
825 825 test transplant with filter handles invalid changelog
826 826
827 827 $ hg init filter-invalid-log
828 828 $ cd filter-invalid-log
829 829 $ cat <<'EOF' >test-filter-invalid-log
830 830 > #!/bin/sh
831 831 > echo "" > $1
832 832 > EOF
833 833 $ chmod +x test-filter-invalid-log
834 834 $ hg transplant -s ../t --filter ./test-filter-invalid-log 0
835 835 filtering * (glob)
836 836 abort: filter corrupted changeset (no user or date)
837 837 [255]
838 838 $ cd ..
839 839
840 840 #endif
841 841
842 842
843 843 test with a win32ext like setup (differing EOLs)
844 844
845 845 $ hg init twin1
846 846 $ cd twin1
847 847 $ echo a > a
848 848 $ echo b > b
849 849 $ echo b >> b
850 850 $ hg ci -Am t
851 851 adding a
852 852 adding b
853 853 $ echo a > b
854 854 $ echo b >> b
855 855 $ hg ci -m changeb
856 856 $ cd ..
857 857
858 858 $ hg init twin2
859 859 $ cd twin2
860 860 $ echo '[patch]' >> .hg/hgrc
861 861 $ echo 'eol = crlf' >> .hg/hgrc
862 862 $ "$PYTHON" -c "open('b', 'wb').write(b'b\r\nb\r\n')"
863 863 $ hg ci -Am addb
864 864 adding b
865 865 $ hg transplant -s ../twin1 tip
866 866 searching for changes
867 867 warning: repository is unrelated
868 868 applying 2e849d776c17
869 869 2e849d776c17 transplanted to 8e65bebc063e
870 870 $ cat b
871 871 a\r (esc)
872 872 b\r (esc)
873 873 $ cd ..
874 874
875 875 test transplant with merge changeset is skipped
876 876
877 877 $ hg init merge1a
878 878 $ cd merge1a
879 879 $ echo a > a
880 880 $ hg ci -Am a
881 881 adding a
882 882 $ hg branch b
883 883 marked working directory as branch b
884 884 (branches are permanent and global, did you want a bookmark?)
885 885 $ hg ci -m branchb
886 886 $ echo b > b
887 887 $ hg ci -Am b
888 888 adding b
889 889 $ hg update default
890 890 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
891 891 $ hg merge b
892 892 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
893 893 (branch merge, don't forget to commit)
894 894 $ hg ci -m mergeb
895 895 $ cd ..
896 896
897 897 $ hg init merge1b
898 898 $ cd merge1b
899 899 $ hg transplant -s ../merge1a tip
900 900 $ cd ..
901 901
902 902 test transplant with merge changeset accepts --parent
903 903
904 904 $ hg init merge2a
905 905 $ cd merge2a
906 906 $ echo a > a
907 907 $ hg ci -Am a
908 908 adding a
909 909 $ hg branch b
910 910 marked working directory as branch b
911 911 (branches are permanent and global, did you want a bookmark?)
912 912 $ hg ci -m branchb
913 913 $ echo b > b
914 914 $ hg ci -Am b
915 915 adding b
916 916 $ hg update default
917 917 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
918 918 $ hg merge b
919 919 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
920 920 (branch merge, don't forget to commit)
921 921 $ hg ci -m mergeb
922 922 $ cd ..
923 923
924 924 $ hg init merge2b
925 925 $ cd merge2b
926 926 $ hg transplant -s ../merge2a --parent tip tip
927 927 abort: be9f9b39483f is not a parent of be9f9b39483f
928 928 [255]
929 929 $ hg transplant -s ../merge2a --parent 0 tip
930 930 applying be9f9b39483f
931 931 be9f9b39483f transplanted to 9959e51f94d1
932 932 $ cd ..
933 933
934 934 test transplanting a patch turning into a no-op
935 935
936 936 $ hg init binarysource
937 937 $ cd binarysource
938 938 $ echo a > a
939 939 $ hg ci -Am adda a
940 940 >>> open('b', 'wb').write(b'\0b1') and None
941 941 $ hg ci -Am addb b
942 942 >>> open('b', 'wb').write(b'\0b2') and None
943 943 $ hg ci -m changeb b
944 944 $ cd ..
945 945
946 946 $ hg clone -r0 binarysource binarydest
947 947 adding changesets
948 948 adding manifests
949 949 adding file changes
950 950 added 1 changesets with 1 changes to 1 files
951 951 new changesets 07f494440405
952 952 updating to branch default
953 953 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
954 954 $ cd binarydest
955 955 $ cp ../binarysource/b b
956 956 $ hg ci -Am addb2 b
957 957 $ hg transplant -s ../binarysource 2
958 958 searching for changes
959 959 applying 7a7d57e15850
960 960 skipping emptied changeset 7a7d57e15850
961 961
962 962 Test empty result in --continue
963 963
964 964 $ hg transplant -s ../binarysource 1
965 965 searching for changes
966 966 applying 645035761929
967 967 file b already exists
968 968 1 out of 1 hunks FAILED -- saving rejects to file b.rej
969 969 patch failed to apply
970 970 abort: fix up the working directory and run hg transplant --continue
971 971 [255]
972 972 $ hg status
973 973 ? b.rej
974 974 $ hg continue
975 975 645035761929 skipped due to empty diff
976 976
977 977 $ cd ..
978 978
979 979 Explicitly kill daemons to let the test exit on Windows
980 980
981 981 $ killdaemons.py
982 982
983 983 Test that patch-ed files are treated as "modified", when transplant is
984 984 aborted by failure of patching, even if none of mode, size and
985 985 timestamp of them isn't changed on the filesystem (see also issue4583)
986 986
987 987 $ cd t
988 988
989 989 $ cat > $TESTTMP/abort.py <<EOF
990 990 > # emulate that patch.patch() is aborted at patching on "abort" file
991 991 > from mercurial import error, extensions, patch as patchmod
992 992 > def patch(orig, ui, repo, patchname,
993 993 > strip=1, prefix=b'', files=None,
994 994 > eolmode=b'strict', similarity=0):
995 995 > if files is None:
996 996 > files = set()
997 997 > r = orig(ui, repo, patchname,
998 998 > strip=strip, prefix=prefix, files=files,
999 999 > eolmode=eolmode, similarity=similarity)
1000 1000 > if b'abort' in files:
1001 1001 > raise error.PatchError('intentional error while patching')
1002 1002 > return r
1003 1003 > def extsetup(ui):
1004 1004 > extensions.wrapfunction(patchmod, 'patch', patch)
1005 1005 > EOF
1006 1006
1007 1007 $ echo X1 > r1
1008 1008 $ hg diff --nodates r1
1009 1009 diff -r a53251cdf717 r1
1010 1010 --- a/r1
1011 1011 +++ b/r1
1012 1012 @@ -1,1 +1,1 @@
1013 1013 -r1
1014 1014 +X1
1015 1015 $ hg commit -m "X1 as r1"
1016 1016
1017 1017 $ echo 'marking to abort patching' > abort
1018 1018 $ hg add abort
1019 1019 $ echo Y1 > r1
1020 1020 $ hg diff --nodates r1
1021 1021 diff -r 22c515968f13 r1
1022 1022 --- a/r1
1023 1023 +++ b/r1
1024 1024 @@ -1,1 +1,1 @@
1025 1025 -X1
1026 1026 +Y1
1027 1027 $ hg commit -m "Y1 as r1"
1028 1028
1029 1029 $ hg update -q -C d11e3596cc1a
1030 1030 $ cat r1
1031 1031 r1
1032 1032
1033 1033 $ cat >> .hg/hgrc <<EOF
1034 1034 > [fakedirstatewritetime]
1035 1035 > # emulate invoking dirstate.write() via repo.status() or markcommitted()
1036 1036 > # at 2000-01-01 00:00
1037 1037 > fakenow = 200001010000
1038 1038 >
1039 1039 > # emulate invoking patch.internalpatch() at 2000-01-01 00:00
1040 1040 > [fakepatchtime]
1041 1041 > fakenow = 200001010000
1042 1042 >
1043 1043 > [extensions]
1044 1044 > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py
1045 1045 > fakepatchtime = $TESTDIR/fakepatchtime.py
1046 1046 > abort = $TESTTMP/abort.py
1047 1047 > EOF
1048 1048 $ hg transplant "22c515968f13::"
1049 1049 applying 22c515968f13
1050 1050 22c515968f13 transplanted to * (glob)
1051 1051 applying e38700ba9dd3
1052 1052 intentional error while patching
1053 1053 abort: fix up the working directory and run hg transplant --continue
1054 1054 [255]
1055 1055 $ cat >> .hg/hgrc <<EOF
1056 1056 > [hooks]
1057 1057 > fakedirstatewritetime = !
1058 1058 > fakepatchtime = !
1059 1059 > [extensions]
1060 1060 > abort = !
1061 1061 > EOF
1062 1062
1063 1063 $ cat r1
1064 1064 Y1
1065 1065 $ hg debugstate | grep ' r1$'
1066 1066 n 644 3 unset r1
1067 1067 $ hg status -A r1
1068 1068 M r1
1069 1069
1070 1070 Test that rollback by unexpected failure after transplanting the first
1071 1071 revision restores dirstate correctly.
1072 1072
1073 1073 $ hg rollback -q
1074 1074 $ rm -f abort
1075 1075 $ hg update -q -C d11e3596cc1a
1076 1076 $ hg parents -T "{node|short}\n"
1077 1077 d11e3596cc1a
1078 1078 $ hg status -A
1079 1079 C r1
1080 1080 C r2
1081 1081
1082 1082 $ cat >> .hg/hgrc <<EOF
1083 1083 > [hooks]
1084 1084 > # emulate failure at transplanting the 2nd revision
1085 1085 > pretxncommit.abort = test ! -f abort
1086 1086 > EOF
1087 1087 $ hg transplant "22c515968f13::"
1088 1088 applying 22c515968f13
1089 1089 22c515968f13 transplanted to * (glob)
1090 1090 applying e38700ba9dd3
1091 1091 transaction abort!
1092 1092 rollback completed
1093 1093 abort: pretxncommit.abort hook exited with status 1
1094 1094 [255]
1095 1095 $ cat >> .hg/hgrc <<EOF
1096 1096 > [hooks]
1097 1097 > pretxncommit.abort = !
1098 1098 > EOF
1099 1099
1100 1100 $ hg parents -T "{node|short}\n"
1101 1101 d11e3596cc1a
1102 1102 $ hg status -A
1103 1103 M r1
1104 1104 ? abort
1105 1105 C r2
1106 1106
1107 1107 $ cd ..
@@ -1,596 +1,596 b''
1 1 Test uncommit - set up the config
2 2
3 3 $ cat >> $HGRCPATH <<EOF
4 4 > [experimental]
5 5 > evolution.createmarkers=True
6 6 > evolution.allowunstable=True
7 7 > [extensions]
8 8 > uncommit =
9 9 > drawdag=$TESTDIR/drawdag.py
10 10 > EOF
11 11
12 12 Build up a repo
13 13
14 14 $ hg init repo
15 15 $ cd repo
16 16 $ hg bookmark foo
17 17
18 18 Help for uncommit
19 19
20 20 $ hg help uncommit
21 21 hg uncommit [OPTION]... [FILE]...
22 22
23 23 uncommit part or all of a local changeset
24 24
25 25 This command undoes the effect of a local commit, returning the affected
26 26 files to their uncommitted state. This means that files modified or
27 27 deleted in the changeset will be left unchanged, and so will remain
28 28 modified in the working directory.
29 29
30 30 If no files are specified, the commit will be pruned, unless --keep is
31 31 given.
32 32
33 33 (use 'hg help -e uncommit' to show help for the uncommit extension)
34 34
35 35 options ([+] can be repeated):
36 36
37 37 --keep allow an empty commit after uncommitting
38 38 --allow-dirty-working-copy allow uncommit with outstanding changes
39 39 -n --note TEXT store a note on uncommit
40 40 -I --include PATTERN [+] include names matching the given patterns
41 41 -X --exclude PATTERN [+] exclude names matching the given patterns
42 42 -m --message TEXT use text as commit message
43 43 -l --logfile FILE read commit message from file
44 44 -d --date DATE record the specified date as commit date
45 45 -u --user USER record the specified user as committer
46 46 -D --currentdate record the current date as commit date
47 47 -U --currentuser record the current user as committer
48 48
49 49 (some details hidden, use --verbose to show complete help)
50 50
51 51 Uncommit with no commits should fail
52 52
53 53 $ hg uncommit
54 54 abort: cannot uncommit null changeset
55 55 (no changeset checked out)
56 56 [255]
57 57
58 58 Create some commits
59 59
60 60 $ touch files
61 61 $ hg add files
62 62 $ for i in a ab abc abcd abcde; do echo $i > files; echo $i > file-$i; hg add file-$i; hg commit -m "added file-$i"; done
63 63 $ ls -A
64 64 .hg
65 65 file-a
66 66 file-ab
67 67 file-abc
68 68 file-abcd
69 69 file-abcde
70 70 files
71 71
72 72 $ hg log -G -T '{rev}:{node} {desc}' --hidden
73 73 @ 4:6c4fd43ed714e7fcd8adbaa7b16c953c2e985b60 added file-abcde
74 74 |
75 75 o 3:6db330d65db434145c0b59d291853e9a84719b24 added file-abcd
76 76 |
77 77 o 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abc
78 78 |
79 79 o 1:69a232e754b08d568c4899475faf2eb44b857802 added file-ab
80 80 |
81 81 o 0:3004d2d9b50883c1538fc754a3aeb55f1b4084f6 added file-a
82 82
83 83 Simple uncommit off the top, also moves bookmark
84 84
85 85 $ hg bookmark
86 86 * foo 4:6c4fd43ed714
87 87 $ hg uncommit
88 88 $ hg status
89 89 M files
90 90 A file-abcde
91 91 $ hg bookmark
92 92 * foo 3:6db330d65db4
93 93
94 94 $ hg log -G -T '{rev}:{node} {desc}' --hidden
95 95 x 4:6c4fd43ed714e7fcd8adbaa7b16c953c2e985b60 added file-abcde
96 96 |
97 97 @ 3:6db330d65db434145c0b59d291853e9a84719b24 added file-abcd
98 98 |
99 99 o 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abc
100 100 |
101 101 o 1:69a232e754b08d568c4899475faf2eb44b857802 added file-ab
102 102 |
103 103 o 0:3004d2d9b50883c1538fc754a3aeb55f1b4084f6 added file-a
104 104
105 105
106 106 Recommit
107 107
108 108 $ hg commit -m 'new change abcde'
109 109 $ hg status
110 110 $ hg heads -T '{rev}:{node} {desc}'
111 111 5:0c07a3ccda771b25f1cb1edbd02e683723344ef1 new change abcde (no-eol)
112 112
113 113 Uncommit of non-existent and unchanged files aborts
114 114 $ hg uncommit nothinghere
115 115 abort: cannot uncommit "nothinghere"
116 116 (file does not exist)
117 117 [255]
118 118 $ hg status
119 119 $ hg uncommit file-abc
120 120 abort: cannot uncommit "file-abc"
121 121 (file was not changed in working directory parent)
122 122 [255]
123 123 $ hg status
124 124
125 125 Try partial uncommit, also moves bookmark
126 126
127 127 $ hg bookmark
128 128 * foo 5:0c07a3ccda77
129 129 $ hg uncommit files
130 130 $ hg status
131 131 M files
132 132 $ hg bookmark
133 133 * foo 6:3727deee06f7
134 134 $ hg heads -T '{rev}:{node} {desc}'
135 135 6:3727deee06f72f5ffa8db792ee299cf39e3e190b new change abcde (no-eol)
136 136 $ hg log -r . -p -T '{rev}:{node} {desc}'
137 137 6:3727deee06f72f5ffa8db792ee299cf39e3e190b new change abcdediff -r 6db330d65db4 -r 3727deee06f7 file-abcde
138 138 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
139 139 +++ b/file-abcde Thu Jan 01 00:00:00 1970 +0000
140 140 @@ -0,0 +1,1 @@
141 141 +abcde
142 142
143 143 $ hg log -G -T '{rev}:{node} {desc}' --hidden
144 144 @ 6:3727deee06f72f5ffa8db792ee299cf39e3e190b new change abcde
145 145 |
146 146 | x 5:0c07a3ccda771b25f1cb1edbd02e683723344ef1 new change abcde
147 147 |/
148 148 | x 4:6c4fd43ed714e7fcd8adbaa7b16c953c2e985b60 added file-abcde
149 149 |/
150 150 o 3:6db330d65db434145c0b59d291853e9a84719b24 added file-abcd
151 151 |
152 152 o 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abc
153 153 |
154 154 o 1:69a232e754b08d568c4899475faf2eb44b857802 added file-ab
155 155 |
156 156 o 0:3004d2d9b50883c1538fc754a3aeb55f1b4084f6 added file-a
157 157
158 158 $ hg commit -m 'update files for abcde'
159 159
160 160 Uncommit with dirty state
161 161
162 162 $ echo "foo" >> files
163 163 $ cat files
164 164 abcde
165 165 foo
166 166 $ hg status
167 167 M files
168 168 $ hg uncommit
169 169 abort: uncommitted changes
170 170 (requires --allow-dirty-working-copy to uncommit)
171 [255]
171 [20]
172 172 $ hg uncommit files
173 173 abort: uncommitted changes
174 174 (requires --allow-dirty-working-copy to uncommit)
175 [255]
175 [20]
176 176 $ cat files
177 177 abcde
178 178 foo
179 179 $ hg commit --amend -m "files abcde + foo"
180 180
181 181 Testing the 'experimental.uncommitondirtywdir' config
182 182
183 183 $ echo "bar" >> files
184 184 $ hg uncommit
185 185 abort: uncommitted changes
186 186 (requires --allow-dirty-working-copy to uncommit)
187 [255]
187 [20]
188 188 $ hg uncommit --config experimental.uncommitondirtywdir=True
189 189 $ hg commit -m "files abcde + foo"
190 190
191 191 Uncommit in the middle of a stack, does not move bookmark
192 192
193 193 $ hg checkout '.^^^'
194 194 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
195 195 (leaving bookmark foo)
196 196 $ hg log -r . -p -T '{rev}:{node} {desc}'
197 197 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abcdiff -r 69a232e754b0 -r abf2df566fc1 file-abc
198 198 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
199 199 +++ b/file-abc Thu Jan 01 00:00:00 1970 +0000
200 200 @@ -0,0 +1,1 @@
201 201 +abc
202 202 diff -r 69a232e754b0 -r abf2df566fc1 files
203 203 --- a/files Thu Jan 01 00:00:00 1970 +0000
204 204 +++ b/files Thu Jan 01 00:00:00 1970 +0000
205 205 @@ -1,1 +1,1 @@
206 206 -ab
207 207 +abc
208 208
209 209 $ hg bookmark
210 210 foo 9:48e5bd7cd583
211 211 $ hg uncommit
212 212 3 new orphan changesets
213 213 $ hg status
214 214 M files
215 215 A file-abc
216 216 $ hg heads -T '{rev}:{node} {desc}'
217 217 9:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo (no-eol)
218 218 $ hg bookmark
219 219 foo 9:48e5bd7cd583
220 220 $ hg commit -m 'new abc'
221 221 created new head
222 222
223 223 Partial uncommit in the middle, does not move bookmark
224 224
225 225 $ hg checkout '.^'
226 226 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
227 227 $ hg log -r . -p -T '{rev}:{node} {desc}'
228 228 1:69a232e754b08d568c4899475faf2eb44b857802 added file-abdiff -r 3004d2d9b508 -r 69a232e754b0 file-ab
229 229 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
230 230 +++ b/file-ab Thu Jan 01 00:00:00 1970 +0000
231 231 @@ -0,0 +1,1 @@
232 232 +ab
233 233 diff -r 3004d2d9b508 -r 69a232e754b0 files
234 234 --- a/files Thu Jan 01 00:00:00 1970 +0000
235 235 +++ b/files Thu Jan 01 00:00:00 1970 +0000
236 236 @@ -1,1 +1,1 @@
237 237 -a
238 238 +ab
239 239
240 240 $ hg bookmark
241 241 foo 9:48e5bd7cd583
242 242 $ hg uncommit file-ab
243 243 1 new orphan changesets
244 244 $ hg status
245 245 A file-ab
246 246
247 247 $ hg heads -T '{rev}:{node} {desc}\n'
248 248 11:8eb87968f2edb7f27f27fe676316e179de65fff6 added file-ab
249 249 10:5dc89ca4486f8a88716c5797fa9f498d13d7c2e1 new abc
250 250 9:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo
251 251
252 252 $ hg bookmark
253 253 foo 9:48e5bd7cd583
254 254 $ hg commit -m 'update ab'
255 255 $ hg status
256 256 $ hg heads -T '{rev}:{node} {desc}\n'
257 257 12:f21039c59242b085491bb58f591afc4ed1c04c09 update ab
258 258 10:5dc89ca4486f8a88716c5797fa9f498d13d7c2e1 new abc
259 259 9:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo
260 260
261 261 $ hg log -G -T '{rev}:{node} {desc}' --hidden
262 262 @ 12:f21039c59242b085491bb58f591afc4ed1c04c09 update ab
263 263 |
264 264 o 11:8eb87968f2edb7f27f27fe676316e179de65fff6 added file-ab
265 265 |
266 266 | * 10:5dc89ca4486f8a88716c5797fa9f498d13d7c2e1 new abc
267 267 | |
268 268 | | * 9:48e5bd7cd583eb24164ef8b89185819c84c96ed7 files abcde + foo
269 269 | | |
270 270 | | | x 8:84beeba0ac30e19521c036e4d2dd3a5fa02586ff files abcde + foo
271 271 | | |/
272 272 | | | x 7:0977fa602c2fd7d8427ed4e7ee15ea13b84c9173 update files for abcde
273 273 | | |/
274 274 | | * 6:3727deee06f72f5ffa8db792ee299cf39e3e190b new change abcde
275 275 | | |
276 276 | | | x 5:0c07a3ccda771b25f1cb1edbd02e683723344ef1 new change abcde
277 277 | | |/
278 278 | | | x 4:6c4fd43ed714e7fcd8adbaa7b16c953c2e985b60 added file-abcde
279 279 | | |/
280 280 | | * 3:6db330d65db434145c0b59d291853e9a84719b24 added file-abcd
281 281 | | |
282 282 | | x 2:abf2df566fc193b3ac34d946e63c1583e4d4732b added file-abc
283 283 | |/
284 284 | x 1:69a232e754b08d568c4899475faf2eb44b857802 added file-ab
285 285 |/
286 286 o 0:3004d2d9b50883c1538fc754a3aeb55f1b4084f6 added file-a
287 287
288 288 Uncommit with draft parent
289 289
290 290 $ hg uncommit
291 291 $ hg phase -r .
292 292 11: draft
293 293 $ hg commit -m 'update ab again'
294 294
295 295 Phase is preserved
296 296
297 297 $ hg uncommit --keep --config phases.new-commit=secret
298 298 note: keeping empty commit
299 299 $ hg phase -r .
300 300 14: draft
301 301 $ hg commit --amend -m 'update ab again'
302 302
303 303 Uncommit with public parent
304 304
305 305 $ hg phase -p "::.^"
306 306 $ hg uncommit
307 307 $ hg phase -r .
308 308 11: public
309 309
310 310 Partial uncommit with public parent
311 311
312 312 $ echo xyz > xyz
313 313 $ hg add xyz
314 314 $ hg commit -m "update ab and add xyz"
315 315 $ hg uncommit xyz
316 316 $ hg status
317 317 A xyz
318 318 $ hg phase -r .
319 319 17: draft
320 320 $ hg phase -r ".^"
321 321 11: public
322 322
323 323 Uncommit with --keep or experimental.uncommit.keep leaves an empty changeset
324 324
325 325 $ cd $TESTTMP
326 326 $ hg init repo1
327 327 $ cd repo1
328 328 $ hg debugdrawdag <<'EOS'
329 329 > Q
330 330 > |
331 331 > P
332 332 > EOS
333 333 $ hg up Q -q
334 334 $ hg uncommit --keep
335 335 note: keeping empty commit
336 336 $ hg log -G -T '{desc} FILES: {files}'
337 337 @ Q FILES:
338 338 |
339 339 | x Q FILES: Q
340 340 |/
341 341 o P FILES: P
342 342
343 343 $ cat >> .hg/hgrc <<EOF
344 344 > [experimental]
345 345 > uncommit.keep=True
346 346 > EOF
347 347 $ hg ci --amend
348 348 $ hg uncommit
349 349 note: keeping empty commit
350 350 $ hg log -G -T '{desc} FILES: {files}'
351 351 @ Q FILES:
352 352 |
353 353 | x Q FILES: Q
354 354 |/
355 355 o P FILES: P
356 356
357 357 $ hg status
358 358 A Q
359 359 $ hg ci --amend
360 360 $ hg uncommit --no-keep
361 361 $ hg log -G -T '{desc} FILES: {files}'
362 362 x Q FILES: Q
363 363 |
364 364 @ P FILES: P
365 365
366 366 $ hg status
367 367 A Q
368 368 $ cd ..
369 369 $ rm -rf repo1
370 370
371 371 Testing uncommit while merge
372 372
373 373 $ hg init repo2
374 374 $ cd repo2
375 375
376 376 Create some history
377 377
378 378 $ touch a
379 379 $ hg add a
380 380 $ for i in 1 2 3; do echo $i > a; hg commit -m "a $i"; done
381 381 $ hg checkout 0
382 382 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
383 383 $ touch b
384 384 $ hg add b
385 385 $ for i in 1 2 3; do echo $i > b; hg commit -m "b $i"; done
386 386 created new head
387 387 $ hg log -G -T '{rev}:{node} {desc}' --hidden
388 388 @ 5:2cd56cdde163ded2fbb16ba2f918c96046ab0bf2 b 3
389 389 |
390 390 o 4:c3a0d5bb3b15834ffd2ef9ef603e93ec65cf2037 b 2
391 391 |
392 392 o 3:49bb009ca26078726b8870f1edb29fae8f7618f5 b 1
393 393 |
394 394 | o 2:990982b7384266e691f1bc08ca36177adcd1c8a9 a 3
395 395 | |
396 396 | o 1:24d38e3cf160c7b6f5ffe82179332229886a6d34 a 2
397 397 |/
398 398 o 0:ea4e33293d4d274a2ba73150733c2612231f398c a 1
399 399
400 400
401 401 Add and expect uncommit to fail on both merge working dir and merge changeset
402 402
403 403 $ hg merge 2
404 404 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
405 405 (branch merge, don't forget to commit)
406 406
407 407 $ hg uncommit
408 408 abort: outstanding uncommitted merge
409 409 (requires --allow-dirty-working-copy to uncommit)
410 [255]
410 [20]
411 411
412 412 $ hg uncommit --config experimental.uncommitondirtywdir=True
413 413 abort: cannot uncommit while merging
414 414 [255]
415 415
416 416 $ hg status
417 417 M a
418 418 $ hg commit -m 'merge a and b'
419 419
420 420 $ hg uncommit
421 421 abort: cannot uncommit merge changeset
422 422 [255]
423 423
424 424 $ hg status
425 425 $ hg log -G -T '{rev}:{node} {desc}' --hidden
426 426 @ 6:c03b9c37bc67bf504d4912061cfb527b47a63c6e merge a and b
427 427 |\
428 428 | o 5:2cd56cdde163ded2fbb16ba2f918c96046ab0bf2 b 3
429 429 | |
430 430 | o 4:c3a0d5bb3b15834ffd2ef9ef603e93ec65cf2037 b 2
431 431 | |
432 432 | o 3:49bb009ca26078726b8870f1edb29fae8f7618f5 b 1
433 433 | |
434 434 o | 2:990982b7384266e691f1bc08ca36177adcd1c8a9 a 3
435 435 | |
436 436 o | 1:24d38e3cf160c7b6f5ffe82179332229886a6d34 a 2
437 437 |/
438 438 o 0:ea4e33293d4d274a2ba73150733c2612231f398c a 1
439 439
440 440
441 441 Rename a->b, then remove b in working copy. Result should remove a.
442 442
443 443 $ hg co -q 0
444 444 $ hg mv a b
445 445 $ hg ci -qm 'move a to b'
446 446 $ hg rm b
447 447 $ hg uncommit --config experimental.uncommitondirtywdir=True
448 448 $ hg st --copies
449 449 R a
450 450 $ hg revert a
451 451
452 452 Rename a->b, then rename b->c in working copy. Result should rename a->c.
453 453
454 454 $ hg co -q 0
455 455 $ hg mv a b
456 456 $ hg ci -qm 'move a to b'
457 457 $ hg mv b c
458 458 $ hg uncommit --config experimental.uncommitondirtywdir=True
459 459 $ hg st --copies
460 460 A c
461 461 a
462 462 R a
463 463 $ hg revert a
464 464 $ hg forget c
465 465 $ rm c
466 466
467 467 Copy a->b1 and a->b2, then rename b1->c in working copy. Result should copy a->b2 and a->c.
468 468
469 469 $ hg co -q 0
470 470 $ hg cp a b1
471 471 $ hg cp a b2
472 472 $ hg ci -qm 'move a to b1 and b2'
473 473 $ hg mv b1 c
474 474 $ hg uncommit --config experimental.uncommitondirtywdir=True
475 475 $ hg st --copies
476 476 A b2
477 477 a
478 478 A c
479 479 a
480 480 $ cd ..
481 481
482 482 --allow-dirty-working-copy should also work on a dirty PATH
483 483
484 484 $ hg init issue5977
485 485 $ cd issue5977
486 486 $ echo 'super critical info!' > a
487 487 $ hg ci -Am 'add a'
488 488 adding a
489 489 $ echo 'foo' > b
490 490 $ hg add b
491 491 $ hg status
492 492 A b
493 493 $ hg uncommit a
494 494 note: keeping empty commit
495 495 $ cat a
496 496 super critical info!
497 497 $ hg log
498 498 changeset: 1:656ba143d384
499 499 tag: tip
500 500 parent: -1:000000000000
501 501 user: test
502 502 date: Thu Jan 01 00:00:00 1970 +0000
503 503 summary: add a
504 504
505 505 $ hg ci -Am 'add b'
506 506 $ echo 'foo bar' > b
507 507 $ hg uncommit b
508 508 abort: uncommitted changes
509 509 (requires --allow-dirty-working-copy to uncommit)
510 [255]
510 [20]
511 511 $ hg uncommit --allow-dirty-working-copy b
512 512 $ hg log
513 513 changeset: 3:30fa958635b2
514 514 tag: tip
515 515 parent: 1:656ba143d384
516 516 user: test
517 517 date: Thu Jan 01 00:00:00 1970 +0000
518 518 summary: add b
519 519
520 520 changeset: 1:656ba143d384
521 521 parent: -1:000000000000
522 522 user: test
523 523 date: Thu Jan 01 00:00:00 1970 +0000
524 524 summary: add a
525 525
526 526 Removes can be uncommitted
527 527
528 528 $ hg ci -m 'modified b'
529 529 $ hg rm b
530 530 $ hg ci -m 'remove b'
531 531 $ hg uncommit b
532 532 note: keeping empty commit
533 533 $ hg status
534 534 R b
535 535
536 536 Uncommitting a directory won't run afoul of the checks that an explicit file
537 537 can be uncommitted.
538 538
539 539 $ mkdir dir
540 540 $ echo 1 > dir/file.txt
541 541 $ hg ci -Aqm 'add file in directory'
542 542 $ hg uncommit dir -m 'uncommit with message' -u 'different user' \
543 543 > -d 'Jun 30 12:12:12 1980 +0000'
544 544 $ hg status
545 545 A dir/file.txt
546 546 $ hg log -r .
547 547 changeset: 8:b4dd26dc42e0
548 548 tag: tip
549 549 parent: 6:2278a4c24330
550 550 user: different user
551 551 date: Mon Jun 30 12:12:12 1980 +0000
552 552 summary: uncommit with message
553 553
554 554 Bad option combinations
555 555
556 556 $ hg rollback -q --config ui.rollback=True
557 557 $ hg uncommit -U --user 'user'
558 558 abort: cannot specify both --user and --currentuser
559 559 [10]
560 560 $ hg uncommit -D --date today
561 561 abort: cannot specify both --date and --currentdate
562 562 [10]
563 563
564 564 `uncommit <dir>` and `cd <dir> && uncommit .` behave the same...
565 565
566 566 $ echo 2 > dir/file2.txt
567 567 $ hg ci -Aqm 'add file2 in directory'
568 568 $ hg uncommit dir
569 569 note: keeping empty commit
570 570 $ hg status
571 571 A dir/file2.txt
572 572
573 573 $ hg rollback -q --config ui.rollback=True
574 574 $ cd dir
575 575 $ hg uncommit . -n 'this is a note'
576 576 note: keeping empty commit
577 577 $ hg status
578 578 A dir/file2.txt
579 579 $ cd ..
580 580
581 581 ... and errors out the same way when nothing can be uncommitted
582 582
583 583 $ hg rollback -q --config ui.rollback=True
584 584 $ mkdir emptydir
585 585 $ hg uncommit emptydir
586 586 abort: cannot uncommit "emptydir"
587 587 (file was untracked in working directory parent)
588 588 [255]
589 589
590 590 $ cd emptydir
591 591 $ hg uncommit .
592 592 abort: cannot uncommit "emptydir"
593 593 (file was untracked in working directory parent)
594 594 [255]
595 595 $ hg status
596 596 $ cd ..
@@ -1,706 +1,706 b''
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [commands]
3 3 > status.verbose=1
4 4 > EOF
5 5
6 6 # Construct the following history tree:
7 7 #
8 8 # @ 5:e1bb631146ca b1
9 9 # |
10 10 # o 4:a4fdb3b883c4 0:b608b9236435 b1
11 11 # |
12 12 # | o 3:4b57d2520816 1:44592833ba9f
13 13 # | |
14 14 # | | o 2:063f31070f65
15 15 # | |/
16 16 # | o 1:44592833ba9f
17 17 # |/
18 18 # o 0:b608b9236435
19 19
20 20 $ mkdir b1
21 21 $ cd b1
22 22 $ hg init
23 23 $ echo foo > foo
24 24 $ echo zero > a
25 25 $ hg init sub
26 26 $ echo suba > sub/suba
27 27 $ hg --cwd sub ci -Am addsuba
28 28 adding suba
29 29 $ echo 'sub = sub' > .hgsub
30 30 $ hg ci -qAm0
31 31 $ echo one > a ; hg ci -m1
32 32 $ echo two > a ; hg ci -m2
33 33 $ hg up -q 1
34 34 $ echo three > a ; hg ci -qm3
35 35 $ hg up -q 0
36 36 $ hg branch -q b1
37 37 $ echo four > a ; hg ci -qm4
38 38 $ echo five > a ; hg ci -qm5
39 39
40 40 Initial repo state:
41 41
42 42 $ hg log -G --template '{rev}:{node|short} {parents} {branches}\n'
43 43 @ 5:ff252e8273df b1
44 44 |
45 45 o 4:d047485b3896 0:60829823a42a b1
46 46 |
47 47 | o 3:6efa171f091b 1:0786582aa4b1
48 48 | |
49 49 | | o 2:bd10386d478c
50 50 | |/
51 51 | o 1:0786582aa4b1
52 52 |/
53 53 o 0:60829823a42a
54 54
55 55
56 56 Make sure update doesn't assume b1 is a repository if invoked from outside:
57 57
58 58 $ cd ..
59 59 $ hg update b1
60 60 abort: no repository found in '$TESTTMP' (.hg not found)!
61 61 [255]
62 62 $ cd b1
63 63
64 64 Test helper functions:
65 65
66 66 $ revtest () {
67 67 > msg=$1
68 68 > dirtyflag=$2 # 'clean', 'dirty' or 'dirtysub'
69 69 > startrev=$3
70 70 > targetrev=$4
71 71 > opt=$5
72 72 > hg up -qC $startrev
73 73 > test $dirtyflag = dirty && echo dirty > foo
74 74 > test $dirtyflag = dirtysub && echo dirty > sub/suba
75 75 > hg up $opt $targetrev
76 76 > hg parent --template 'parent={rev}\n'
77 77 > hg stat -S
78 78 > }
79 79
80 80 $ norevtest () {
81 81 > msg=$1
82 82 > dirtyflag=$2 # 'clean', 'dirty' or 'dirtysub'
83 83 > startrev=$3
84 84 > opt=$4
85 85 > hg up -qC $startrev
86 86 > test $dirtyflag = dirty && echo dirty > foo
87 87 > test $dirtyflag = dirtysub && echo dirty > sub/suba
88 88 > hg up $opt
89 89 > hg parent --template 'parent={rev}\n'
90 90 > hg stat -S
91 91 > }
92 92
93 93 Test cases are documented in a table in the update function of merge.py.
94 94 Cases are run as shown in that table, row by row.
95 95
96 96 $ norevtest 'none clean linear' clean 4
97 97 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 98 parent=5
99 99
100 100 $ norevtest 'none clean same' clean 2
101 101 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 102 updated to "bd10386d478c: 2"
103 103 1 other heads for branch "default"
104 104 parent=2
105 105
106 106
107 107 $ revtest 'none clean linear' clean 1 2
108 108 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 109 parent=2
110 110
111 111 $ revtest 'none clean same' clean 2 3
112 112 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
113 113 parent=3
114 114
115 115 $ revtest 'none clean cross' clean 3 4
116 116 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 117 parent=4
118 118
119 119
120 120 $ revtest 'none dirty linear' dirty 1 2
121 121 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
122 122 parent=2
123 123 M foo
124 124
125 125 $ revtest 'none dirtysub linear' dirtysub 1 2
126 126 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
127 127 parent=2
128 128 M sub/suba
129 129
130 130 $ revtest 'none dirty same' dirty 2 3
131 131 abort: uncommitted changes
132 132 (commit or update --clean to discard changes)
133 133 parent=2
134 134 M foo
135 135
136 136 $ revtest 'none dirtysub same' dirtysub 2 3
137 137 abort: uncommitted changes
138 138 (commit or update --clean to discard changes)
139 139 parent=2
140 140 M sub/suba
141 141
142 142 $ revtest 'none dirty cross' dirty 3 4
143 143 abort: uncommitted changes
144 144 (commit or update --clean to discard changes)
145 145 parent=3
146 146 M foo
147 147
148 148 $ norevtest 'none dirty cross' dirty 2
149 149 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 150 updated to "bd10386d478c: 2"
151 151 1 other heads for branch "default"
152 152 parent=2
153 153 M foo
154 154
155 155 $ revtest 'none dirtysub cross' dirtysub 3 4
156 156 abort: uncommitted changes
157 157 (commit or update --clean to discard changes)
158 158 parent=3
159 159 M sub/suba
160 160
161 161 $ revtest '-C dirty linear' dirty 1 2 -C
162 162 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
163 163 parent=2
164 164
165 165 $ revtest '-c dirty linear' dirty 1 2 -c
166 166 abort: uncommitted changes
167 167 parent=1
168 168 M foo
169 169
170 170 $ revtest '-m dirty linear' dirty 1 2 -m
171 171 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
172 172 parent=2
173 173 M foo
174 174
175 175 $ revtest '-m dirty cross' dirty 3 4 -m
176 176 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
177 177 parent=4
178 178 M foo
179 179
180 180 $ revtest '-c dirtysub linear' dirtysub 1 2 -c
181 181 abort: uncommitted changes in subrepository "sub"
182 182 parent=1
183 183 M sub/suba
184 184
185 185 $ norevtest '-c clean same' clean 2 -c
186 186 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
187 187 updated to "bd10386d478c: 2"
188 188 1 other heads for branch "default"
189 189 parent=2
190 190
191 191 $ revtest '-cC dirty linear' dirty 1 2 -cC
192 192 abort: cannot specify both --clean and --check
193 193 parent=1
194 194 M foo
195 195
196 196 $ revtest '-mc dirty linear' dirty 1 2 -mc
197 197 abort: cannot specify both --check and --merge
198 198 parent=1
199 199 M foo
200 200
201 201 $ revtest '-mC dirty linear' dirty 1 2 -mC
202 202 abort: cannot specify both --clean and --merge
203 203 parent=1
204 204 M foo
205 205
206 206 $ echo '[commands]' >> .hg/hgrc
207 207 $ echo 'update.check = abort' >> .hg/hgrc
208 208
209 209 $ revtest 'none dirty linear' dirty 1 2
210 210 abort: uncommitted changes
211 211 parent=1
212 212 M foo
213 213
214 214 $ revtest 'none dirty linear' dirty 1 2 -c
215 215 abort: uncommitted changes
216 216 parent=1
217 217 M foo
218 218
219 219 $ revtest 'none dirty linear' dirty 1 2 -C
220 220 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
221 221 parent=2
222 222
223 223 $ echo 'update.check = none' >> .hg/hgrc
224 224
225 225 $ revtest 'none dirty cross' dirty 3 4
226 226 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
227 227 parent=4
228 228 M foo
229 229
230 230 $ revtest 'none dirty linear' dirty 1 2
231 231 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 232 parent=2
233 233 M foo
234 234
235 235 $ revtest 'none dirty linear' dirty 1 2 -c
236 236 abort: uncommitted changes
237 237 parent=1
238 238 M foo
239 239
240 240 $ revtest 'none dirty linear' dirty 1 2 -C
241 241 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
242 242 parent=2
243 243
244 244 $ hg co -qC 3
245 245 $ echo dirty >> a
246 246 $ hg co --tool :merge3 4
247 247 merging a
248 248 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
249 249 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
250 250 use 'hg resolve' to retry unresolved file merges
251 251 [1]
252 252 $ hg log -G --template '{rev}:{node|short} {parents} {branches}\n'
253 253 o 5:ff252e8273df b1
254 254 |
255 255 @ 4:d047485b3896 0:60829823a42a b1
256 256 |
257 257 | % 3:6efa171f091b 1:0786582aa4b1
258 258 | |
259 259 | | o 2:bd10386d478c
260 260 | |/
261 261 | o 1:0786582aa4b1
262 262 |/
263 263 o 0:60829823a42a
264 264
265 265 $ hg st
266 266 M a
267 267 ? a.orig
268 268 # Unresolved merge conflicts:
269 269 #
270 270 # a
271 271 #
272 272 # To mark files as resolved: hg resolve --mark FILE
273 273
274 274 $ cat a
275 275 <<<<<<< working copy: 6efa171f091b - test: 3
276 276 three
277 277 dirty
278 278 ||||||| base
279 279 three
280 280 =======
281 281 four
282 282 >>>>>>> destination: d047485b3896 b1 - test: 4
283 283 $ rm a.orig
284 284
285 285 $ echo 'update.check = noconflict' >> .hg/hgrc
286 286
287 287 $ revtest 'none dirty cross' dirty 3 4
288 288 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
289 289 parent=4
290 290 M foo
291 291
292 292 $ revtest 'none dirty linear' dirty 1 2
293 293 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
294 294 parent=2
295 295 M foo
296 296
297 297 $ revtest 'none dirty linear' dirty 1 2 -c
298 298 abort: uncommitted changes
299 299 parent=1
300 300 M foo
301 301
302 302 $ revtest 'none dirty linear' dirty 1 2 -C
303 303 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
304 304 parent=2
305 305
306 306 Locally added file is allowed
307 307 $ hg up -qC 3
308 308 $ echo a > bar
309 309 $ hg add bar
310 310 $ hg up -q 4
311 311 $ hg st
312 312 A bar
313 313 $ hg forget bar
314 314 $ rm bar
315 315
316 316 Locally removed file is allowed
317 317 $ hg up -qC 3
318 318 $ hg rm foo
319 319 $ hg up -q 4
320 320
321 321 File conflict is not allowed
322 322 $ hg up -qC 3
323 323 $ echo dirty >> a
324 324 $ hg up -q 4
325 325 abort: conflicting changes
326 326 (commit or update --clean to discard changes)
327 327 [255]
328 328 $ hg up -m 4
329 329 merging a
330 330 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
331 331 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
332 332 use 'hg resolve' to retry unresolved file merges
333 333 [1]
334 334 $ rm a.orig
335 335 $ hg status
336 336 M a
337 337 # Unresolved merge conflicts:
338 338 #
339 339 # a
340 340 #
341 341 # To mark files as resolved: hg resolve --mark FILE
342 342
343 343 $ hg resolve -l
344 344 U a
345 345
346 346 Try to make empty commit while there are conflicts
347 347 $ hg revert -r . a
348 348 $ rm a.orig
349 349 $ hg ci -m empty
350 350 abort: unresolved merge conflicts (see 'hg help resolve')
351 351 [255]
352 352 $ hg resolve -m a
353 353 (no more unresolved files)
354 354 $ hg resolve -l
355 355 R a
356 356 $ hg ci -m empty
357 357 nothing changed
358 358 [1]
359 359 $ hg resolve -l
360 360
361 361 Change/delete conflict is not allowed
362 362 $ hg up -qC 3
363 363 $ hg rm foo
364 364 $ hg up -q 4
365 365
366 366 Uses default value of "linear" when value is misspelled
367 367 $ echo 'update.check = linyar' >> .hg/hgrc
368 368
369 369 $ revtest 'dirty cross' dirty 3 4
370 370 abort: uncommitted changes
371 371 (commit or update --clean to discard changes)
372 372 parent=3
373 373 M foo
374 374
375 375 Setup for later tests
376 376 $ revtest 'none dirty linear' dirty 1 2 -c
377 377 abort: uncommitted changes
378 378 parent=1
379 379 M foo
380 380
381 381 $ cd ..
382 382
383 383 Test updating to null revision
384 384
385 385 $ hg init null-repo
386 386 $ cd null-repo
387 387 $ echo a > a
388 388 $ hg add a
389 389 $ hg ci -m a
390 390 $ hg up -qC 0
391 391 $ echo b > b
392 392 $ hg add b
393 393 $ hg up null
394 394 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
395 395 $ hg st
396 396 A b
397 397 $ hg up -q 0
398 398 $ hg st
399 399 A b
400 400 $ hg up -qC null
401 401 $ hg st
402 402 ? b
403 403 $ cd ..
404 404
405 405 Test updating with closed head
406 406 ---------------------------------------------------------------------
407 407
408 408 $ hg clone -U -q b1 closed-heads
409 409 $ cd closed-heads
410 410
411 411 Test updating if at least one non-closed branch head exists
412 412
413 413 if on the closed branch head:
414 414 - update to "."
415 415 - "updated to a closed branch head ...." message is displayed
416 416 - "N other heads for ...." message is displayed
417 417
418 418 $ hg update -q -C 3
419 419 $ hg commit --close-branch -m 6
420 420 $ norevtest "on closed branch head" clean 6
421 421 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
422 422 no open descendant heads on branch "default", updating to a closed head
423 423 (committing will reopen the head, use 'hg heads .' to see 1 other heads)
424 424 parent=6
425 425
426 426 if descendant non-closed branch head exists, and it is only one branch head:
427 427 - update to it, even if its revision is less than closed one
428 428 - "N other heads for ...." message isn't displayed
429 429
430 430 $ norevtest "non-closed 2 should be chosen" clean 1
431 431 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
432 432 parent=2
433 433
434 434 if all descendant branch heads are closed, but there is another branch head:
435 435 - update to the tipmost descendant head
436 436 - "updated to a closed branch head ...." message is displayed
437 437 - "N other heads for ...." message is displayed
438 438
439 439 $ norevtest "all descendant branch heads are closed" clean 3
440 440 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
441 441 no open descendant heads on branch "default", updating to a closed head
442 442 (committing will reopen the head, use 'hg heads .' to see 1 other heads)
443 443 parent=6
444 444
445 445 Test updating if all branch heads are closed
446 446
447 447 if on the closed branch head:
448 448 - update to "."
449 449 - "updated to a closed branch head ...." message is displayed
450 450 - "all heads of branch ...." message is displayed
451 451
452 452 $ hg update -q -C 2
453 453 $ hg commit --close-branch -m 7
454 454 $ norevtest "all heads of branch default are closed" clean 6
455 455 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
456 456 no open descendant heads on branch "default", updating to a closed head
457 457 (committing will reopen branch "default")
458 458 parent=6
459 459
460 460 if not on the closed branch head:
461 461 - update to the tipmost descendant (closed) head
462 462 - "updated to a closed branch head ...." message is displayed
463 463 - "all heads of branch ...." message is displayed
464 464
465 465 $ norevtest "all heads of branch default are closed" clean 1
466 466 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
467 467 no open descendant heads on branch "default", updating to a closed head
468 468 (committing will reopen branch "default")
469 469 parent=7
470 470
471 471 $ cd ..
472 472
473 473 Test updating if "default" branch doesn't exist and no revision is
474 474 checked out (= "default" is used as current branch)
475 475
476 476 $ hg init no-default-branch
477 477 $ cd no-default-branch
478 478
479 479 $ hg branch foobar
480 480 marked working directory as branch foobar
481 481 (branches are permanent and global, did you want a bookmark?)
482 482 $ echo a > a
483 483 $ hg commit -m "#0" -A
484 484 adding a
485 485 $ echo 1 >> a
486 486 $ hg commit -m "#1"
487 487 $ hg update -q 0
488 488 $ echo 3 >> a
489 489 $ hg commit -m "#2"
490 490 created new head
491 491 $ hg commit --close-branch -m "#3"
492 492
493 493 if there is at least one non-closed branch head:
494 494 - update to the tipmost branch head
495 495
496 496 $ norevtest "non-closed 1 should be chosen" clean null
497 497 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
498 498 parent=1
499 499
500 500 if all branch heads are closed
501 501 - update to "tip"
502 502 - "updated to a closed branch head ...." message is displayed
503 503 - "all heads for branch "XXXX" are closed" message is displayed
504 504
505 505 $ hg update -q -C 1
506 506 $ hg commit --close-branch -m "#4"
507 507
508 508 $ norevtest "all branches are closed" clean null
509 509 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
510 510 no open descendant heads on branch "foobar", updating to a closed head
511 511 (committing will reopen branch "foobar")
512 512 parent=4
513 513
514 514 $ cd ../b1
515 515
516 516 Test obsolescence behavior
517 517 ---------------------------------------------------------------------
518 518
519 519 successors should be taken in account when checking head destination
520 520
521 521 $ cat << EOF >> $HGRCPATH
522 522 > [ui]
523 523 > logtemplate={rev}:{node|short} {desc|firstline}
524 524 > [experimental]
525 525 > evolution.createmarkers=True
526 526 > EOF
527 527
528 528 Test no-argument update to a successor of an obsoleted changeset
529 529
530 530 $ hg log -G
531 531 o 5:ff252e8273df 5
532 532 |
533 533 o 4:d047485b3896 4
534 534 |
535 535 | o 3:6efa171f091b 3
536 536 | |
537 537 | | o 2:bd10386d478c 2
538 538 | |/
539 539 | @ 1:0786582aa4b1 1
540 540 |/
541 541 o 0:60829823a42a 0
542 542
543 543 $ hg book bm -r 3
544 544 $ hg status
545 545 M foo
546 546
547 547 We add simple obsolescence marker between 3 and 4 (indirect successors)
548 548
549 549 $ hg id --debug -i -r 3
550 550 6efa171f091b00a3c35edc15d48c52a498929953
551 551 $ hg id --debug -i -r 4
552 552 d047485b3896813b2a624e86201983520f003206
553 553 $ hg debugobsolete 6efa171f091b00a3c35edc15d48c52a498929953 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
554 554 1 new obsolescence markers
555 555 obsoleted 1 changesets
556 556 $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa d047485b3896813b2a624e86201983520f003206
557 557 1 new obsolescence markers
558 558
559 559 Test that 5 is detected as a valid destination from 3 and also accepts moving
560 560 the bookmark (issue4015)
561 561
562 562 $ hg up --quiet --hidden 3
563 563 $ hg up 5
564 564 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
565 565 $ hg book bm
566 566 moving bookmark 'bm' forward from 6efa171f091b
567 567 $ hg bookmarks
568 568 * bm 5:ff252e8273df
569 569
570 570 Test that we abort before we warn about the hidden commit if the working
571 571 directory is dirty
572 572 $ echo conflict > a
573 573 $ hg up --hidden 3
574 574 abort: uncommitted changes
575 575 (commit or update --clean to discard changes)
576 576 [255]
577 577
578 578 Test that we still warn also when there are conflicts
579 579 $ hg up -m --hidden 3
580 580 merging a
581 581 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
582 582 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
583 583 use 'hg resolve' to retry unresolved file merges
584 584 (leaving bookmark bm)
585 585 updated to hidden changeset 6efa171f091b
586 586 (hidden revision '6efa171f091b' was rewritten as: d047485b3896)
587 587 [1]
588 588
589 589 Test that statuses are reported properly before and after merge resolution.
590 590 $ rm a.orig
591 591 $ hg resolve -l
592 592 U a
593 593 $ hg status
594 594 M a
595 595 M foo
596 596 # Unresolved merge conflicts:
597 597 #
598 598 # a
599 599 #
600 600 # To mark files as resolved: hg resolve --mark FILE
601 601
602 602
603 603 $ hg revert -r . a
604 604
605 605 $ rm a.orig
606 606 $ hg resolve -l
607 607 U a
608 608 $ hg status
609 609 M foo
610 610 # Unresolved merge conflicts:
611 611 #
612 612 # a
613 613 #
614 614 # To mark files as resolved: hg resolve --mark FILE
615 615
616 616 $ hg status -Tjson
617 617 [
618 618 {
619 619 "itemtype": "file",
620 620 "path": "foo",
621 621 "status": "M"
622 622 },
623 623 {
624 624 "itemtype": "file",
625 625 "path": "a",
626 626 "unresolved": true
627 627 }
628 628 ]
629 629
630 630 $ hg resolve -m
631 631 (no more unresolved files)
632 632
633 633 $ hg resolve -l
634 634 R a
635 635 $ hg status
636 636 M foo
637 637 # No unresolved merge conflicts.
638 638
639 639 $ hg status -Tjson
640 640 [
641 641 {
642 642 "itemtype": "file",
643 643 "path": "foo",
644 644 "status": "M"
645 645 }
646 646 ]
647 647
648 648 Test that 4 is detected as the no-argument destination from 3 and also moves
649 649 the bookmark with it
650 650 $ hg up --quiet 0 # we should be able to update to 3 directly
651 651 $ hg status
652 652 M foo
653 653 $ hg up --quiet --hidden 3 # but not implemented yet.
654 654 updated to hidden changeset 6efa171f091b
655 655 (hidden revision '6efa171f091b' was rewritten as: d047485b3896)
656 656 $ hg book -f bm
657 657 $ hg up
658 658 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
659 659 updating bookmark bm
660 660 $ hg book
661 661 * bm 4:d047485b3896
662 662
663 663 Test that 5 is detected as a valid destination from 1
664 664 $ hg up --quiet 0 # we should be able to update to 3 directly
665 665 $ hg up --quiet --hidden 3 # but not implemented yet.
666 666 updated to hidden changeset 6efa171f091b
667 667 (hidden revision '6efa171f091b' was rewritten as: d047485b3896)
668 668 $ hg up 5
669 669 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
670 670
671 671 Test that 5 is not detected as a valid destination from 2
672 672 $ hg up --quiet 0
673 673 $ hg up --quiet 2
674 674 $ hg up 5
675 675 abort: uncommitted changes
676 676 (commit or update --clean to discard changes)
677 677 [255]
678 678
679 679 Test that we don't crash when updating from a pruned changeset (i.e. has no
680 680 successors). Behavior should probably be that we update to the first
681 681 non-obsolete parent but that will be decided later.
682 682 $ hg id --debug -r 2
683 683 bd10386d478cd5a9faf2e604114c8e6da62d3889
684 684 $ hg up --quiet 0
685 685 $ hg up --quiet 2
686 686 $ hg debugobsolete bd10386d478cd5a9faf2e604114c8e6da62d3889
687 687 1 new obsolescence markers
688 688 obsoleted 1 changesets
689 689 $ hg up
690 690 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
691 691
692 692 Test experimental revset support
693 693
694 694 $ hg log -r '_destupdate()'
695 695 2:bd10386d478c 2 (no-eol)
696 696
697 697 Test that boolean flags allow --no-flag specification to override [defaults]
698 698 $ cat >> $HGRCPATH <<EOF
699 699 > [defaults]
700 700 > update = --check
701 701 > EOF
702 702 $ hg co 2
703 703 abort: uncommitted changes
704 [255]
704 [20]
705 705 $ hg co --no-check 2
706 706 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -1,45 +1,45 b''
1 1 #require execbit
2 2
3 3 $ rm -rf a
4 4 $ hg init a
5 5 $ cd a
6 6
7 7 $ echo foo > foo
8 8 $ hg ci -qAm0
9 9 $ echo toremove > toremove
10 10 $ echo todelete > todelete
11 11 $ chmod +x foo toremove todelete
12 12 $ hg ci -qAm1
13 13
14 14 Test that local removed/deleted, remote removed works with flags
15 15 $ hg rm toremove
16 16 $ rm todelete
17 17 $ hg co -q 0
18 18
19 19 $ echo dirty > foo
20 20 $ hg up -c
21 21 abort: uncommitted changes
22 [255]
22 [20]
23 23 $ hg up -q
24 24 $ cat foo
25 25 dirty
26 26 $ hg st -A
27 27 M foo
28 28 C todelete
29 29 C toremove
30 30
31 31 Validate update of standalone execute bit change:
32 32
33 33 $ hg up -C 0
34 34 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
35 35 $ chmod -x foo
36 36 $ hg ci -m removeexec
37 37 nothing changed
38 38 [1]
39 39 $ hg up -C 0
40 40 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
41 41 $ hg up
42 42 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
43 43 $ hg st
44 44
45 45 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now